/ Check-in [170711ca]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Parse foreign key constraints and populate internal data structures appropriately. Constraints are still not enforced. (CVS 738)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:170711ca65dc894d0486b9d575edb8f1708250fb
User & Date: drh 2002-08-31 18:53:06
Context
2002-09-01
23:20
Relax the locking requirements on BTree cursors. Any number of read and write cursors can be open at the same time now, but a write cannot occur as long as one or more read cursors are open.

Before this change, one or more read cursors could be open on a table, or a single write cursor, but not both. Both policies have the same desirable effect: they prevent writes to a table while a sequential scan of that table is underway. But the new policy is a little less restrictive. Both policies prevent an UPDATE from occurring inside a SELECT (which is what we want) but the new policy allows a SELECT to occur inside an UPDATE. (CVS 739) check-in: 8c2a0836 user: drh tags: trunk

2002-08-31
18:53
Parse foreign key constraints and populate internal data structures appropriately. Constraints are still not enforced. (CVS 738) check-in: 170711ca user: drh tags: trunk
17:02
Version 2.7.1 (CVS 737) check-in: 5f51e13d user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/build.c.

    21     21   **     COPY
    22     22   **     VACUUM
    23     23   **     BEGIN TRANSACTION
    24     24   **     COMMIT
    25     25   **     ROLLBACK
    26     26   **     PRAGMA
    27     27   **
    28         -** $Id: build.c,v 1.110 2002/08/24 18:24:53 drh Exp $
           28  +** $Id: build.c,v 1.111 2002/08/31 18:53:06 drh Exp $
    29     29   */
    30     30   #include "sqliteInt.h"
    31     31   #include <ctype.h>
    32     32   
    33     33   /*
    34     34   ** This routine is called when a new SQL statement is beginning to
    35     35   ** be parsed.  Check to see if the schema for the database needs
................................................................................
   148    148   ** if there were schema changes during the transaction.
   149    149   */
   150    150   void sqliteResetInternalSchema(sqlite *db){
   151    151     HashElem *pElem;
   152    152     Hash temp1;
   153    153     Hash temp2;
   154    154   
          155  +  sqliteHashClear(&db->aFKey);
   155    156     temp1 = db->tblHash;
   156    157     temp2 = db->trigHash;
   157    158     sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
   158    159     sqliteHashClear(&db->idxHash);
   159    160     for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
   160    161       Trigger *pTrigger = sqliteHashData(pElem);
   161    162       sqliteDeleteTrigger(pTrigger);
................................................................................
   190    191   }
   191    192   
   192    193   /*
   193    194   ** Remove the memory data structures associated with the given
   194    195   ** Table.  No changes are made to disk by this routine.
   195    196   **
   196    197   ** This routine just deletes the data structure.  It does not unlink
   197         -** the table data structure from the hash table.  But it does destroy
   198         -** memory structures of the indices associated with the table.
          198  +** the table data structure from the hash table.  Nor does it remove
          199  +** foreign keys from the sqlite.aFKey hash table.  But it does destroy
          200  +** memory structures of the indices and foreign keys associated with 
          201  +** the table.
   199    202   **
   200    203   ** Indices associated with the table are unlinked from the "db"
   201    204   ** data structure if db!=NULL.  If db==NULL, indices attached to
   202    205   ** the table are deleted, but it is assumed they have already been
   203    206   ** unlinked.
   204    207   */
   205    208   void sqliteDeleteTable(sqlite *db, Table *pTable){
   206    209     int i;
   207    210     Index *pIndex, *pNext;
          211  +  FKey *pFKey, *pNextFKey;
          212  +
   208    213     if( pTable==0 ) return;
          214  +
          215  +  /* Delete all indices associated with this table
          216  +  */
          217  +  for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
          218  +    pNext = pIndex->pNext;
          219  +    sqliteDeleteIndex(db, pIndex);
          220  +  }
          221  +
          222  +  /* Delete all foreign keys associated with this table.  The keys
          223  +  ** should have already been unlinked from the db->aFKey hash table 
          224  +  */
          225  +  for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
          226  +    pNextFKey = pFKey->pNextFrom;
          227  +    assert( sqliteHashFind(&db->aFKey,pFKey->zTo,strlen(pFKey->zTo)+1)!=pFKey );
          228  +    sqliteFree(pFKey);
          229  +  }
          230  +
          231  +  /* Delete the Table structure itself.
          232  +  */
   209    233     for(i=0; i<pTable->nCol; i++){
   210    234       sqliteFree(pTable->aCol[i].zName);
   211    235       sqliteFree(pTable->aCol[i].zDflt);
   212    236       sqliteFree(pTable->aCol[i].zType);
   213    237     }
   214         -  for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
   215         -    pNext = pIndex->pNext;
   216         -    sqliteDeleteIndex(db, pIndex);
   217         -  }
   218    238     sqliteFree(pTable->zName);
   219    239     sqliteFree(pTable->aCol);
   220    240     sqliteSelectDelete(pTable->pSelect);
   221    241     sqliteFree(pTable);
   222    242   }
   223    243   
   224    244   /*
   225    245   ** Unlink the given table from the hash tables and the delete the
   226         -** table structure with all its indices.
          246  +** table structure with all its indices and foreign keys.
   227    247   */
   228    248   static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){
   229    249     Table *pOld;
          250  +  FKey *pF1, *pF2;
   230    251     assert( db!=0 );
   231    252     pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, 0);
   232    253     assert( pOld==0 || pOld==p );
          254  +  for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
          255  +    int nTo = strlen(pF1->zTo) + 1;
          256  +    pF2 = sqliteHashFind(&db->aFKey, pF1->zTo, nTo);
          257  +    if( pF2==pF1 ){
          258  +      sqliteHashInsert(&db->aFKey, pF1->zTo, nTo, pF1->pNextTo);
          259  +    }else{
          260  +      while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
          261  +      if( pF2 ){
          262  +        pF2->pNextTo = pF1->pNextTo;
          263  +      }
          264  +    }
          265  +  }
   233    266     sqliteDeleteTable(db, p);
   234    267   }
   235    268   
   236    269   /*
   237    270   ** Construct the name of a user table or index from a token.
   238    271   **
   239    272   ** Space to hold the name is obtained from sqliteMalloc() and must
................................................................................
   735    768     if( p==0 ) return;
   736    769   
   737    770     /* Add the table to the in-memory representation of the database.
   738    771     */
   739    772     assert( pParse->nameClash==0 || pParse->initFlag==1 );
   740    773     if( pParse->explain==0 && pParse->nameClash==0 ){
   741    774       Table *pOld;
          775  +    FKey *pFKey;
   742    776       pOld = sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p);
   743    777       if( pOld ){
   744    778         assert( p==pOld );  /* Malloc must have failed inside HashInsert() */
   745    779         return;
   746    780       }
          781  +    for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
          782  +      int nTo = strlen(pFKey->zTo) + 1;
          783  +      pFKey->pNextTo = sqliteHashFind(&db->aFKey, pFKey->zTo, nTo);
          784  +      sqliteHashInsert(&db->aFKey, pFKey->zTo, nTo, pFKey);
          785  +    }
   747    786       pParse->pNewTable = 0;
   748    787       db->nTable++;
   749    788       db->flags |= SQLITE_InternChanges;
   750    789     }
   751    790   
   752    791     /* If the table is generated from a SELECT, then construct the
   753    792     ** list of columns and the text of the table.
................................................................................
  1136   1175         zType[i] = 'n';
  1137   1176       }
  1138   1177     }
  1139   1178     zType[n] = 0;
  1140   1179     sqliteVdbeChangeP3(v, -1, zType, n);
  1141   1180     sqliteFree(zType);
  1142   1181   }
         1182  +
         1183  +/*
         1184  +** This routine is called to create a new foreign key on the table
         1185  +** currently under construction.  pFromCol determines which columns
         1186  +** in the current table point to the foreign key.  If pFromCol==0 then
         1187  +** connect the key to the last column inserted.  pTo is the name of
         1188  +** the table referred to.  pToCol is a list of tables in the other
         1189  +** pTo table that the foreign key points to.  flags contains all
         1190  +** information about the conflict resolution algorithms specified
         1191  +** in the ON DELETE, ON UPDATE and ON INSERT clauses.
         1192  +**
         1193  +** An FKey structure is created and added to the table currently
         1194  +** under construction in the pParse->pNewTable field.  The new FKey
         1195  +** is not linked into db->aFKey at this point - that does not happen
         1196  +** until sqliteEndTable().
         1197  +**
         1198  +** The foreign key is set for IMMEDIATE processing.  A subsequent call
         1199  +** to sqliteDeferForeignKey() might change this to DEFERRED.
         1200  +*/
         1201  +void sqliteCreateForeignKey(
         1202  +  Parse *pParse,       /* Parsing context */
         1203  +  IdList *pFromCol,    /* Columns in this table that point to other table */
         1204  +  Token *pTo,          /* Name of the other table */
         1205  +  IdList *pToCol,      /* Columns in the other table */
         1206  +  int flags            /* Conflict resolution algorithms. */
         1207  +){
         1208  +  Table *p = pParse->pNewTable;
         1209  +  int nByte;
         1210  +  int i;
         1211  +  int nCol;
         1212  +  char *z;
         1213  +  FKey *pFKey = 0;
         1214  +
         1215  +  assert( pTo!=0 );
         1216  +  if( p==0 || pParse->nErr ) goto fk_end;
         1217  +  if( pFromCol==0 ){
         1218  +    int iCol = p->nCol-1;
         1219  +    if( iCol<0 ) goto fk_end;
         1220  +    if( pToCol && pToCol->nId!=1 ){
         1221  +      sqliteSetNString(&pParse->zErrMsg, "foreign key on ", -1,
         1222  +         p->aCol[iCol].zName, -1, 
         1223  +         " should reference only one column of table ", -1,
         1224  +         pTo->z, pTo->n, 0);
         1225  +      pParse->nErr++;
         1226  +      goto fk_end;
         1227  +    }
         1228  +    nCol = 1;
         1229  +  }else if( pToCol && pToCol->nId!=pFromCol->nId ){
         1230  +    sqliteSetString(&pParse->zErrMsg, 
         1231  +        "number of columns in foreign key does not match the number of "
         1232  +        "columns in the referenced table", 0);
         1233  +    pParse->nErr++;
         1234  +    goto fk_end;
         1235  +  }else{
         1236  +    nCol = pFromCol->nId;
         1237  +  }
         1238  +  nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1;
         1239  +  if( pToCol ){
         1240  +    for(i=0; i<pToCol->nId; i++){
         1241  +      nByte += strlen(pToCol->a[i].zName) + 1;
         1242  +    }
         1243  +  }
         1244  +  pFKey = sqliteMalloc( nByte );
         1245  +  if( pFKey==0 ) goto fk_end;
         1246  +  pFKey->pFrom = p;
         1247  +  pFKey->pNextFrom = p->pFKey;
         1248  +  pFKey->zTo = z = (char*)&pFKey[1];
         1249  +  memcpy(z, pTo->z, pTo->n);
         1250  +  z[pTo->n] = 0;
         1251  +  z += pTo->n+1;
         1252  +  pFKey->pNextTo = 0;
         1253  +  pFKey->nCol = nCol;
         1254  +  pFKey->aCol = (struct sColMap*)z;
         1255  +  z += sizeof(struct sColMap)*nCol;
         1256  +  if( pFromCol==0 ){
         1257  +    pFKey->aCol[0].iFrom = p->nCol-1;
         1258  +  }else{
         1259  +    for(i=0; i<nCol; i++){
         1260  +      int j;
         1261  +      for(j=0; j<p->nCol; j++){
         1262  +        if( sqliteStrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){
         1263  +          pFKey->aCol[i].iFrom = j;
         1264  +          break;
         1265  +        }
         1266  +      }
         1267  +      if( j>=p->nCol ){
         1268  +        sqliteSetString(&pParse->zErrMsg, "unknown column \"", 
         1269  +          pFromCol->a[i].zName, "\" in foreign key definition", 0);
         1270  +        pParse->nErr++;
         1271  +        goto fk_end;
         1272  +      }
         1273  +    }
         1274  +  }
         1275  +  if( pToCol ){
         1276  +    for(i=0; i<nCol; i++){
         1277  +      int n = strlen(pToCol->a[i].zName);
         1278  +      pFKey->aCol[i].zCol = z;
         1279  +      memcpy(z, pToCol->a[i].zName, n);
         1280  +      z[n] = 0;
         1281  +      z += n+1;
         1282  +    }
         1283  +  }
         1284  +  pFKey->isDeferred = 0;
         1285  +  pFKey->deleteConf = flags & 0xff;
         1286  +  pFKey->updateConf = (flags >> 8 ) & 0xff;
         1287  +  pFKey->insertConf = (flags >> 16 ) & 0xff;
         1288  +
         1289  +  /* Link the foreign key to the table as the last step.
         1290  +  */
         1291  +  p->pFKey = pFKey;
         1292  +  pFKey = 0;
         1293  +
         1294  +fk_end:
         1295  +  sqliteFree(pFKey);
         1296  +  sqliteIdListDelete(pFromCol);
         1297  +  sqliteIdListDelete(pToCol);
         1298  +}
         1299  +
         1300  +/*
         1301  +** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
         1302  +** clause is seen as part of a foreign key definition.  The isDeferred
         1303  +** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
         1304  +** The behavior of the most recently created foreign key is adjusted
         1305  +** accordingly.
         1306  +*/
         1307  +void sqliteDeferForeignKey(Parse *pParse, int isDeferred){
         1308  +  Table *pTab;
         1309  +  FKey *pFKey;
         1310  +  if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return;
         1311  +  pFKey->isDeferred = isDeferred;
         1312  +}
  1143   1313   
  1144   1314   /*
  1145   1315   ** Create a new index for an SQL table.  pIndex is the name of the index 
  1146   1316   ** and pTable is the name of the table that is to be indexed.  Both will 
  1147   1317   ** be NULL for a primary key or an index that is created to satisfy a
  1148   1318   ** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse->pNewTable
  1149   1319   ** as the table to be indexed.  pParse->pNewTable is a table that is

Changes to src/main.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** Main file for the SQLite library.  The routines in this file
    13     13   ** implement the programmer interface to the library.  Routines in
    14     14   ** other files are for internal use by SQLite and should not be
    15     15   ** accessed by users of the library.
    16     16   **
    17         -** $Id: main.c,v 1.99 2002/08/29 23:59:48 drh Exp $
           17  +** $Id: main.c,v 1.100 2002/08/31 18:53:06 drh Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include "os.h"
    21     21   #include <ctype.h>
    22     22   
    23     23   /*
    24     24   ** A pointer to this structure is used to communicate information
................................................................................
   350    350     db = sqliteMalloc( sizeof(sqlite) );
   351    351     if( pzErrMsg ) *pzErrMsg = 0;
   352    352     if( db==0 ) goto no_mem_on_open;
   353    353     sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
   354    354     sqliteHashInit(&db->idxHash, SQLITE_HASH_STRING, 0);
   355    355     sqliteHashInit(&db->trigHash, SQLITE_HASH_STRING, 0);
   356    356     sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1);
          357  +  sqliteHashInit(&db->aFKey, SQLITE_HASH_STRING, 1);
   357    358     sqliteRegisterBuiltinFunctions(db);
   358    359     db->onError = OE_Default;
   359    360     db->priorNewRowid = 0;
   360    361     db->magic = SQLITE_MAGIC_BUSY;
   361    362     
   362    363     /* Open the backend database driver */
   363    364     rc = sqliteBtreeOpen(zFilename, mode, MAX_PAGES, &db->pBe);
................................................................................
   462    463       FuncDef *pFunc, *pNext;
   463    464       for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){
   464    465         pNext = pFunc->pNext;
   465    466         sqliteFree(pFunc);
   466    467       }
   467    468     }
   468    469     sqliteHashClear(&db->aFunc);
          470  +  sqliteHashClear(&db->aFKey);
   469    471     sqliteFree(db);
   470    472   }
   471    473   
   472    474   /*
   473    475   ** Return TRUE if the given SQL string ends in a semicolon.
   474    476   **
   475    477   ** Special handling is require for CREATE TRIGGER statements.

Changes to src/md5.c.

   289    289     zBuf[j] = 0;
   290    290   }
   291    291   
   292    292   /*
   293    293   ** A TCL command for md5.  The argument is the text to be hashed.  The
   294    294   ** Result is the hash in base64.  
   295    295   */
   296         -static int md5_cmd(ClientData cd, Tcl_Interp *interp, int argc, char **argv){
          296  +static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
   297    297     MD5Context ctx;
   298    298     unsigned char digest[16];
   299    299   
   300    300     if( argc!=2 ){
   301    301       Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], 
   302    302           " TEXT\"", 0);
   303    303       return TCL_ERROR;
................................................................................
   309    309     return TCL_OK;
   310    310   }
   311    311   
   312    312   /*
   313    313   ** A TCL command to take the md5 hash of a file.  The argument is the
   314    314   ** name of the file.
   315    315   */
   316         -static int md5file_cmd(ClientData cd, Tcl_Interp*interp, int argc, char **argv){
          316  +static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
   317    317     FILE *in;
   318    318     MD5Context ctx;
   319    319     unsigned char digest[16];
   320    320     char zBuf[10240];
   321    321   
   322    322     if( argc!=2 ){
   323    323       Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], 
................................................................................
   343    343     return TCL_OK;
   344    344   }
   345    345   
   346    346   /*
   347    347   ** Register the two TCL commands above with the TCL interpreter.
   348    348   */
   349    349   int Md5_Init(Tcl_Interp *interp){
   350         -  Tcl_CreateCommand(interp, "md5", md5_cmd, 0, 0);
   351         -  Tcl_CreateCommand(interp, "md5file", md5file_cmd, 0, 0);
          350  +  Tcl_CreateCommand(interp, "md5", (Tcl_CmdProc*)md5_cmd, 0, 0);
          351  +  Tcl_CreateCommand(interp, "md5file", (Tcl_CmdProc*)md5file_cmd, 0, 0);
   352    352     return TCL_OK;
   353    353   }
   354    354   
   355    355   /*
   356    356   ** During testing, the special md5sum() aggregate function is available.
   357    357   ** inside SQLite.  The following routines implement that function.
   358    358   */

Changes to src/parse.y.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains SQLite's grammar for SQL.  Process this file
    13     13   ** using the lemon parser generator to generate C code that runs
    14     14   ** the parser.  Lemon will also generate a header file containing
    15     15   ** numeric codes for all of the tokens.
    16     16   **
    17         -** @(#) $Id: parse.y,v 1.82 2002/08/24 18:24:54 drh Exp $
           17  +** @(#) $Id: parse.y,v 1.83 2002/08/31 18:53:07 drh Exp $
    18     18   */
    19     19   %token_prefix TK_
    20     20   %token_type {Token}
    21     21   %default_type {Token}
    22     22   %extra_argument {Parse *pParse}
    23     23   %syntax_error {
    24     24     sqliteSetString(&pParse->zErrMsg,"syntax error",0);
................................................................................
   165    165   // UNIQUE constraints.
   166    166   //
   167    167   ccons ::= NULL onconf.
   168    168   ccons ::= NOT NULL onconf(R).               {sqliteAddNotNull(pParse, R);}
   169    169   ccons ::= PRIMARY KEY sortorder onconf(R).  {sqliteAddPrimaryKey(pParse,0,R);}
   170    170   ccons ::= UNIQUE onconf(R).            {sqliteCreateIndex(pParse,0,0,0,R,0,0);}
   171    171   ccons ::= CHECK LP expr RP onconf.
   172         -ccons ::= references.
   173         -ccons ::= defer_subclause.
          172  +ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
          173  +                                {sqliteCreateForeignKey(pParse,0,&T,TA,R);}
          174  +ccons ::= defer_subclause(D).   {sqliteDeferForeignKey(pParse,D);}
   174    175   ccons ::= COLLATE id(C).  {
   175    176      sqliteAddCollateType(pParse, sqliteCollateType(pParse, &C));
   176    177   }
   177    178   
   178         -// A REFERENCES clause is parsed but the current implementation does not
   179         -// do anything with it.
   180         -//
   181         -references ::= REFERENCES nm LP idxlist RP refargs.
   182         -references ::= REFERENCES nm refargs.
   183         -refargs ::= .
   184         -refargs ::= refargs refarg.
   185         -refarg ::= MATCH nm.
   186         -refarg ::= ON DELETE refact.
   187         -refarg ::= ON UPDATE refact.
   188         -refact ::= SET NULL.
   189         -refact ::= SET DEFAULT.
   190         -refact ::= CASCADE.
   191         -refact ::= RESTRICT.
   192         -defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt.
   193         -defer_subclause ::= DEFERRABLE init_deferred_pred_opt.
   194         -init_deferred_pred_opt ::= .
   195         -init_deferred_pred_opt ::= INITIALLY DEFERRED.
   196         -init_deferred_pred_opt ::= INITIALLY IMMEDIATE.
          179  +// The next group of rules parses the arguments to a REFERENCES clause
          180  +// that determine if the referential integrity checking is deferred or
          181  +// or immediate and which determine what action to take if a ref-integ
          182  +// check fails.
          183  +//
          184  +%type refargs {int}
          185  +refargs(A) ::= .                     { A = OE_Restrict * 0x010101; }
          186  +refargs(A) ::= refargs(X) refarg(Y). { A = (X & Y.mask) | Y.value; }
          187  +%type refarg {struct {int value; int mask;}}
          188  +refarg(A) ::= MATCH nm.              { A.value = 0;     A.mask = 0x000000; }
          189  +refarg(A) ::= ON DELETE refact(X).   { A.value = X;     A.mask = 0x0000ff; }
          190  +refarg(A) ::= ON UPDATE refact(X).   { A.value = X<<8;  A.mask = 0x00ff00; }
          191  +refarg(A) ::= ON INSERT refact(X).   { A.value = X<<16; A.mask = 0xff0000; }
          192  +%type refact {int}
          193  +refact(A) ::= SET NULL.              { A = OE_SetNull; }
          194  +refact(A) ::= SET DEFAULT.           { A = OE_SetDflt; }
          195  +refact(A) ::= CASCADE.               { A = OE_Cascade; }
          196  +refact(A) ::= RESTRICT.              { A = OE_Restrict; }
          197  +%type defer_subclause {int}
          198  +defer_subclause(A) ::= NOT DEFERRABLE init_deferred_pred_opt(X).  {A = X;}
          199  +defer_subclause(A) ::= DEFERRABLE init_deferred_pred_opt(X).      {A = X;}
          200  +%type init_deferred_pred_opt {int}
          201  +init_deferred_pred_opt(A) ::= .                       {A = 0;}
          202  +init_deferred_pred_opt(A) ::= INITIALLY DEFERRED.     {A = 1;}
          203  +init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE.    {A = 0;}
   197    204   
   198    205   // For the time being, the only constraint we care about is the primary
   199    206   // key and UNIQUE.  Both create indices.
   200    207   //
   201    208   conslist_opt ::= .
   202    209   conslist_opt ::= COMMA conslist.
   203    210   conslist ::= conslist COMMA tcons.
................................................................................
   205    212   conslist ::= tcons.
   206    213   tcons ::= CONSTRAINT nm.
   207    214   tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R).
   208    215                                                {sqliteAddPrimaryKey(pParse,X,R);}
   209    216   tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
   210    217                                          {sqliteCreateIndex(pParse,0,0,X,R,0,0);}
   211    218   tcons ::= CHECK expr onconf.
   212         -tcons ::= FOREIGN KEY LP idxlist RP references defer_subclause_opt.
   213         -defer_subclause_opt ::= .
   214         -defer_subclause_opt ::= defer_subclause.
          219  +tcons ::= FOREIGN KEY LP idxlist(FA) RP
          220  +          REFERENCES nm(T) idxlist_opt(TA) refargs(R) defer_subclause_opt(D). {
          221  +    sqliteCreateForeignKey(pParse, FA, &T, TA, R);
          222  +    sqliteDeferForeignKey(pParse, D);
          223  +}
          224  +%type defer_subclause_opt {int}
          225  +defer_subclause_opt(A) ::= .                    {A = 0;}
          226  +defer_subclause_opt(A) ::= defer_subclause(X).  {A = X;}
   215    227   
   216    228   // The following is a non-standard extension that allows us to declare the
   217    229   // default behavior when there is a constraint conflict.
   218    230   //
   219    231   %type onconf {int}
   220    232   %type orconf {int}
   221    233   %type resolvetype {int}
................................................................................
   673    685   
   674    686   %type uniqueflag {int}
   675    687   uniqueflag(A) ::= UNIQUE.  { A = OE_Abort; }
   676    688   uniqueflag(A) ::= .        { A = OE_None; }
   677    689   
   678    690   %type idxlist {IdList*}
   679    691   %destructor idxlist {sqliteIdListDelete($$);}
          692  +%type idxlist_opt {IdList*}
          693  +%destructor idxlist_opt {sqliteIdListDelete($$);}
   680    694   %type idxitem {Token}
   681    695   
   682         -idxlist(A) ::= idxlist(X) COMMA idxitem(Y).  
   683         -     {A = sqliteIdListAppend(X,&Y);}
   684         -idxlist(A) ::= idxitem(Y).
   685         -     {A = sqliteIdListAppend(0,&Y);}
   686         -idxitem(A) ::= nm(X).          {A = X;}
          696  +idxlist_opt(A) ::= .                         {A = 0;}
          697  +idxlist_opt(A) ::= LP idxlist(X) RP.         {A = X;}
          698  +idxlist(A) ::= idxlist(X) COMMA idxitem(Y).  {A = sqliteIdListAppend(X,&Y);}
          699  +idxlist(A) ::= idxitem(Y).                   {A = sqliteIdListAppend(0,&Y);}
          700  +idxitem(A) ::= nm(X).                        {A = X;}
   687    701   
   688    702   ///////////////////////////// The DROP INDEX command /////////////////////////
   689    703   //
   690    704   
   691    705   cmd ::= DROP INDEX nm(X).      {sqliteDropIndex(pParse, &X);}
   692    706   
   693    707   

Changes to src/sqliteInt.h.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.144 2002/08/28 03:00:59 drh Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.145 2002/08/31 18:53:07 drh Exp $
    15     15   */
    16     16   #include "sqlite.h"
    17     17   #include "hash.h"
    18     18   #include "vdbe.h"
    19     19   #include "parse.h"
    20     20   #include "btree.h"
    21     21   #include <stdio.h>
................................................................................
   171    171   typedef struct WhereLevel WhereLevel;
   172    172   typedef struct Select Select;
   173    173   typedef struct AggExpr AggExpr;
   174    174   typedef struct FuncDef FuncDef;
   175    175   typedef struct Trigger Trigger;
   176    176   typedef struct TriggerStep TriggerStep;
   177    177   typedef struct TriggerStack TriggerStack;
          178  +typedef struct FKey FKey;
   178    179   
   179    180   /*
   180    181   ** Each database is an instance of the following structure.
   181    182   **
   182    183   ** The sqlite.file_format is initialized by the database file
   183    184   ** and helps determines how the data in the database file is
   184    185   ** represented.  This field allows newer versions of the library
................................................................................
   202    203     int nTable;                   /* Number of tables in the database */
   203    204     void *pBusyArg;               /* 1st Argument to the busy callback */
   204    205     int (*xBusyCallback)(void *,const char*,int);  /* The busy callback */
   205    206     Hash tblHash;                 /* All tables indexed by name */
   206    207     Hash idxHash;                 /* All (named) indices indexed by name */
   207    208     Hash trigHash;                /* All triggers indexed by name */
   208    209     Hash aFunc;                   /* All functions that can be in SQL exprs */
          210  +  Hash aFKey;                   /* Foreign keys indexed by to-table */
   209    211     int lastRowid;                /* ROWID of most recent insert */
   210    212     int priorNewRowid;            /* Last randomly generated ROWID */
   211    213     int onError;                  /* Default conflict algorithm */
   212    214     int magic;                    /* Magic number for detect library misuse */
   213    215     int nChange;                  /* Number of rows changed */
   214    216     int recursionDepth;           /* Number of nested calls to sqlite_exec() */
   215    217   };
................................................................................
   326    328     Select *pSelect; /* NULL for tables.  Points to definition if a view. */
   327    329     u8 readOnly;     /* True if this table should not be written by the user */
   328    330     u8 isTemp;       /* True if stored in db->pBeTemp instead of db->pBe */
   329    331     u8 isTransient;  /* True if automatically deleted when VDBE finishes */
   330    332     u8 hasPrimKey;   /* True if there exists a primary key */
   331    333     u8 keyConf;      /* What to do in case of uniqueness conflict on iPKey */
   332    334     Trigger *pTrigger; /* List of SQL triggers on this table */
          335  +  FKey *pFKey;       /* Linked list of all foreign keys in this table */
   333    336   };
   334    337   
   335    338   /*
   336         -** SQLite supports 5 different ways to resolve a contraint
          339  +** Each foreign key constraint is an instance of the following structure.
          340  +**
          341  +** A foreign key is associated with two tables.  The "from" table is
          342  +** the table that contains the REFERENCES clause that creates the foreign
          343  +** key.  The "to" table is the table that is named in the REFERENCES clause.
          344  +** Consider this example:
          345  +**
          346  +**     CREATE TABLE ex1(
          347  +**       a INTEGER PRIMARY KEY,
          348  +**       b INTEGER CONSTRAINT fk1 REFERENCES ex2(x)
          349  +**     );
          350  +**
          351  +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
          352  +**
          353  +** Each REFERENCES clause generates an instance of the following structure
          354  +** which is attached to the from-table.  The to-table need not exist when
          355  +** the from-table is created.  The existance of the to-table is not checked
          356  +** until an attempt is made to insert data into the from-table.
          357  +**
          358  +** The sqlite.aFKey hash table stores pointers to to this structure
          359  +** given the name of a to-table.  For each to-table, all foreign keys
          360  +** associated with that table are on a linked list using the FKey.pNextTo
          361  +** field.
          362  +*/
          363  +struct FKey {
          364  +  Table *pFrom;     /* The table that constains the REFERENCES clause */
          365  +  FKey *pNextFrom;  /* Next foreign key in pFrom */
          366  +  char *zTo;        /* Name of table that the key points to */
          367  +  FKey *pNextTo;    /* Next foreign key that points to zTo */
          368  +  int nCol;         /* Number of columns in this key */
          369  +  struct sColMap {  /* Mapping of columns in pFrom to columns in zTo */
          370  +    int iFrom;         /* Index of column in pFrom */
          371  +    char *zCol;        /* Name of column in zTo.  If 0 use PRIMARY KEY */
          372  +  } *aCol;          /* One entry for each of nCol column s */
          373  +  u8 isDeferred;    /* True if constraint checking is deferred till COMMIT */
          374  +  u8 updateConf;    /* How to resolve conflicts that occur on UPDATE */
          375  +  u8 deleteConf;    /* How to resolve conflicts that occur on DELETE */
          376  +  u8 insertConf;    /* How to resolve conflicts that occur on INSERT */
          377  +};
          378  +
          379  +/*
          380  +** SQLite supports many different ways to resolve a contraint
   337    381   ** error.  ROLLBACK processing means that a constraint violation
   338    382   ** causes the operation in process to fail and for the current transaction
   339    383   ** to be rolled back.  ABORT processing means the operation in process
   340    384   ** fails and any prior changes from that one operation are backed out,
   341    385   ** but the transaction is not rolled back.  FAIL processing means that
   342    386   ** the operation in progress stops and returns an error code.  But prior
   343    387   ** changes due to the same operation are not backed out and no rollback
   344    388   ** occurs.  IGNORE means that the particular row that caused the constraint
   345    389   ** error is not inserted or updated.  Processing continues and no error
   346    390   ** is returned.  REPLACE means that preexisting database rows that caused
   347    391   ** a UNIQUE constraint violation are removed so that the new insert or
   348    392   ** update can proceed.  Processing continues and no error is reported.
          393  +**
          394  +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys.
          395  +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
          396  +** same as ROLLBACK for DEFERRED keys.  SETNULL means that the foreign
          397  +** key is set to NULL.  CASCADE means that a DELETE or UPDATE of the
          398  +** referenced table row is propagated into the row that holds the
          399  +** foreign key.
   349    400   ** 
   350    401   ** The following there symbolic values are used to record which type
   351    402   ** of action to take.
   352    403   */
   353    404   #define OE_None     0   /* There is no constraint to check */
   354    405   #define OE_Rollback 1   /* Fail the operation and rollback the transaction */
   355    406   #define OE_Abort    2   /* Back out changes but do no rollback transaction */
   356    407   #define OE_Fail     3   /* Stop the operation but leave all prior changes */
   357    408   #define OE_Ignore   4   /* Ignore the error. Do not do the INSERT or UPDATE */
   358    409   #define OE_Replace  5   /* Delete existing record, then do INSERT or UPDATE */
   359         -#define OE_Default  9   /* Do whatever the default action is */
          410  +
          411  +#define OE_Restrict 6   /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
          412  +#define OE_SetNull  7   /* Set the foreign key value to NULL */
          413  +#define OE_SetDflt  8   /* Set the foreign key value to its default */
          414  +#define OE_Cascade  9   /* Cascade the changes */
          415  +
          416  +#define OE_Default  99  /* Do whatever the default action is */
   360    417   
   361    418   /*
   362    419   ** Each SQL index is represented in memory by an
   363    420   ** instance of the following structure.
   364    421   **
   365    422   ** The columns of the table that are to be indexed are described
   366    423   ** by the aiColumn[] field of this structure.  For example, suppose
................................................................................
   943   1000   void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
   944   1001   TriggerStep *sqliteTriggerSelectStep(Select*);
   945   1002   TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
   946   1003   TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
   947   1004   TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
   948   1005   void sqliteDeleteTrigger(Trigger*);
   949   1006   int sqliteJoinType(Parse*, Token*, Token*, Token*);
         1007  +void sqliteCreateForeignKey(Parse*, IdList*, Token*, IdList*, int);
         1008  +void sqliteDeferForeignKey(Parse*, int);

Changes to src/tclsqlite.c.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** A TCL Interface to SQLite
    13     13   **
    14         -** $Id: tclsqlite.c,v 1.39 2002/07/15 20:58:48 drh Exp $
           14  +** $Id: tclsqlite.c,v 1.40 2002/08/31 18:53:08 drh Exp $
    15     15   */
    16     16   #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
    17     17   
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
................................................................................
   639    639   ** (Hence there is no namespace.  There is no point in using a namespace
   640    640   ** if the extension only supplies one new name!)  The "sqlite" command is
   641    641   ** used to open a new SQLite database.  See the DbMain() routine above
   642    642   ** for additional information.
   643    643   */
   644    644   int Sqlite_Init(Tcl_Interp *interp){
   645    645     Tcl_InitStubs(interp, "8.0", 0);
   646         -  Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0);
          646  +  Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0);
   647    647     Tcl_PkgProvide(interp, "sqlite", "2.0");
   648    648     return TCL_OK;
   649    649   }
   650    650   int Tclsqlite_Init(Tcl_Interp *interp){
   651    651     Tcl_InitStubs(interp, "8.0", 0);
   652         -  Tcl_CreateCommand(interp, "sqlite", DbMain, 0, 0);
          652  +  Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0);
   653    653     Tcl_PkgProvide(interp, "sqlite", "2.0");
   654    654     return TCL_OK;
   655    655   }
   656    656   int Sqlite_SafeInit(Tcl_Interp *interp){
   657    657     return TCL_OK;
   658    658   }
   659    659   int Tclsqlite_SafeInit(Tcl_Interp *interp){

Changes to src/test1.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the printf() interface to SQLite.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test1.c,v 1.12 2002/07/10 21:26:01 drh Exp $
           16  +** $Id: test1.c,v 1.13 2002/08/31 18:53:08 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
    22     22   
    23     23   /*
................................................................................
   234    234   ** The effect is similar to trying to use the same database connection from
   235    235   ** two threads at the same time.
   236    236   **
   237    237   ** The original motivation for this routine was to be able to call the
   238    238   ** sqlite_create_function function while a query is in progress in order
   239    239   ** to test the SQLITE_MISUSE detection logic.
   240    240   */
   241         -static int sqlite_test_create_function(
          241  +static int test_create_function(
   242    242     void *NotUsed,
   243    243     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   244    244     int argc,              /* Number of arguments */
   245    245     char **argv            /* Text of each argument */
   246    246   ){
   247    247     sqlite *db;
   248    248     extern void Md5_Register(sqlite*);
................................................................................
   284    284   ** to create a function named "x_count".  This function does the same thing
   285    285   ** as the "md5sum" function.
   286    286   **
   287    287   ** The original motivation for this routine was to be able to call the
   288    288   ** sqlite_create_aggregate function while a query is in progress in order
   289    289   ** to test the SQLITE_MISUSE detection logic.
   290    290   */
   291         -static int sqlite_test_create_aggregate(
          291  +static int test_create_aggregate(
   292    292     void *NotUsed,
   293    293     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   294    294     int argc,              /* Number of arguments */
   295    295     char **argv            /* Text of each argument */
   296    296   ){
   297    297     sqlite *db;
   298    298     if( argc!=2 ){
................................................................................
   480    480   }
   481    481   
   482    482   /*
   483    483   ** Usage:   sqlite_register_test_function  DB  NAME
   484    484   **
   485    485   ** Register the test SQL function on the database DB under the name NAME.
   486    486   */
   487         -static int sqlite_register_test_function(
          487  +static int test_register_func(
   488    488     void *NotUsed,
   489    489     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   490    490     int argc,              /* Number of arguments */
   491    491     char **argv            /* Text of each argument */
   492    492   ){
   493    493     sqlite *db;
   494    494     int rc;
................................................................................
   507    507   }
   508    508   
   509    509   /*
   510    510   ** Register commands with the TCL interpreter.
   511    511   */
   512    512   int Sqlitetest1_Init(Tcl_Interp *interp){
   513    513     extern int sqlite_search_count;
   514         -  Tcl_CreateCommand(interp, "sqlite_mprintf_int", sqlite_mprintf_int, 0, 0);
   515         -  Tcl_CreateCommand(interp, "sqlite_mprintf_str", sqlite_mprintf_str, 0, 0);
   516         -  Tcl_CreateCommand(interp, "sqlite_mprintf_double", sqlite_mprintf_double,0,0);
   517         -  Tcl_CreateCommand(interp, "sqlite_open", sqlite_test_open, 0, 0);
   518         -  Tcl_CreateCommand(interp, "sqlite_last_insert_rowid", test_last_rowid, 0, 0);
   519         -  Tcl_CreateCommand(interp, "sqlite_exec_printf", test_exec_printf, 0, 0);
   520         -  Tcl_CreateCommand(interp, "sqlite_get_table_printf", test_get_table_printf,
   521         -      0, 0);
   522         -  Tcl_CreateCommand(interp, "sqlite_close", sqlite_test_close, 0, 0);
   523         -  Tcl_CreateCommand(interp, "sqlite_create_function", 
   524         -      sqlite_test_create_function, 0, 0);
   525         -  Tcl_CreateCommand(interp, "sqlite_create_aggregate",
   526         -      sqlite_test_create_aggregate, 0, 0);
   527         -  Tcl_CreateCommand(interp, "sqlite_register_test_function",
   528         -      sqlite_register_test_function, 0, 0);
          514  +  static struct {
          515  +     char *zName;
          516  +     Tcl_CmdProc *xProc;
          517  +  } aCmd[] = {
          518  +     { "sqlite_mprintf_int",             (Tcl_CmdProc*)sqlite_mprintf_int    },
          519  +     { "sqlite_mprintf_str",             (Tcl_CmdProc*)sqlite_mprintf_str    },
          520  +     { "sqlite_mprintf_double",          (Tcl_CmdProc*)sqlite_mprintf_double },
          521  +     { "sqlite_open",                    (Tcl_CmdProc*)sqlite_test_open      },
          522  +     { "sqlite_last_insert_rowid",       (Tcl_CmdProc*)test_last_rowid       },
          523  +     { "sqlite_exec_printf",             (Tcl_CmdProc*)test_exec_printf      },
          524  +     { "sqlite_get_table_printf",        (Tcl_CmdProc*)test_get_table_printf },
          525  +     { "sqlite_close",                   (Tcl_CmdProc*)sqlite_test_close     },
          526  +     { "sqlite_create_function",         (Tcl_CmdProc*)test_create_function  },
          527  +     { "sqlite_create_aggregate",        (Tcl_CmdProc*)test_create_aggregate },
          528  +     { "sqlite_register_test_function",  (Tcl_CmdProc*)test_register_func    },
          529  +     { "sqlite_abort",                   (Tcl_CmdProc*)sqlite_abort          },
          530  +#ifdef MEMORY_DEBUG
          531  +     { "sqlite_malloc_fail",             (Tcl_CmdProc*)sqlite_malloc_fail    },
          532  +     { "sqlite_malloc_stat",             (Tcl_CmdProc*)sqlite_malloc_stat    },
          533  +#endif
          534  +  };
          535  +  int i;
          536  +
          537  +  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
          538  +    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
          539  +  }
   529    540     Tcl_LinkVar(interp, "sqlite_search_count", 
   530    541         (char*)&sqlite_search_count, TCL_LINK_INT);
   531         -#ifdef MEMORY_DEBUG
   532         -  Tcl_CreateCommand(interp, "sqlite_malloc_fail", sqlite_malloc_fail, 0, 0);
   533         -  Tcl_CreateCommand(interp, "sqlite_malloc_stat", sqlite_malloc_stat, 0, 0);
   534         -#endif
   535         -  Tcl_CreateCommand(interp, "sqlite_abort", sqlite_abort, 0, 0);
   536    542     return TCL_OK;
   537    543   }

Changes to src/test2.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the pager.c module in SQLite.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test2.c,v 1.9 2002/08/12 12:29:57 drh Exp $
           16  +** $Id: test2.c,v 1.10 2002/08/31 18:53:08 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "pager.h"
    20     20   #include "tcl.h"
    21     21   #include <stdlib.h>
    22     22   #include <string.h>
    23     23   
................................................................................
    58     58   **
    59     59   ** Open a new pager
    60     60   */
    61     61   static int pager_open(
    62     62     void *NotUsed,
    63     63     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    64     64     int argc,              /* Number of arguments */
    65         -  char **argv            /* Text of each argument */
           65  +  const char **argv      /* Text of each argument */
    66     66   ){
    67     67     Pager *pPager;
    68     68     int nPage;
    69     69     int rc;
    70     70     char zBuf[100];
    71     71     if( argc!=3 ){
    72     72       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
    89     89   **
    90     90   ** Close the given pager.
    91     91   */
    92     92   static int pager_close(
    93     93     void *NotUsed,
    94     94     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    95     95     int argc,              /* Number of arguments */
    96         -  char **argv            /* Text of each argument */
           96  +  const char **argv      /* Text of each argument */
    97     97   ){
    98     98     Pager *pPager;
    99     99     int rc;
   100    100     if( argc!=2 ){
   101    101       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   102    102          " ID\"", 0);
   103    103       return TCL_ERROR;
................................................................................
   116    116   **
   117    117   ** Rollback changes
   118    118   */
   119    119   static int pager_rollback(
   120    120     void *NotUsed,
   121    121     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   122    122     int argc,              /* Number of arguments */
   123         -  char **argv            /* Text of each argument */
          123  +  const char **argv      /* Text of each argument */
   124    124   ){
   125    125     Pager *pPager;
   126    126     int rc;
   127    127     if( argc!=2 ){
   128    128       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   129    129          " ID\"", 0);
   130    130       return TCL_ERROR;
................................................................................
   143    143   **
   144    144   ** Commit all changes
   145    145   */
   146    146   static int pager_commit(
   147    147     void *NotUsed,
   148    148     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   149    149     int argc,              /* Number of arguments */
   150         -  char **argv            /* Text of each argument */
          150  +  const char **argv      /* Text of each argument */
   151    151   ){
   152    152     Pager *pPager;
   153    153     int rc;
   154    154     if( argc!=2 ){
   155    155       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   156    156          " ID\"", 0);
   157    157       return TCL_ERROR;
................................................................................
   170    170   **
   171    171   ** Start a new checkpoint.
   172    172   */
   173    173   static int pager_ckpt_begin(
   174    174     void *NotUsed,
   175    175     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   176    176     int argc,              /* Number of arguments */
   177         -  char **argv            /* Text of each argument */
          177  +  const char **argv      /* Text of each argument */
   178    178   ){
   179    179     Pager *pPager;
   180    180     int rc;
   181    181     if( argc!=2 ){
   182    182       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   183    183          " ID\"", 0);
   184    184       return TCL_ERROR;
................................................................................
   197    197   **
   198    198   ** Rollback changes to a checkpoint
   199    199   */
   200    200   static int pager_ckpt_rollback(
   201    201     void *NotUsed,
   202    202     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   203    203     int argc,              /* Number of arguments */
   204         -  char **argv            /* Text of each argument */
          204  +  const char **argv      /* Text of each argument */
   205    205   ){
   206    206     Pager *pPager;
   207    207     int rc;
   208    208     if( argc!=2 ){
   209    209       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   210    210          " ID\"", 0);
   211    211       return TCL_ERROR;
................................................................................
   224    224   **
   225    225   ** Commit changes to a checkpoint
   226    226   */
   227    227   static int pager_ckpt_commit(
   228    228     void *NotUsed,
   229    229     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   230    230     int argc,              /* Number of arguments */
   231         -  char **argv            /* Text of each argument */
          231  +  const char **argv      /* Text of each argument */
   232    232   ){
   233    233     Pager *pPager;
   234    234     int rc;
   235    235     if( argc!=2 ){
   236    236       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   237    237          " ID\"", 0);
   238    238       return TCL_ERROR;
................................................................................
   251    251   **
   252    252   ** Return pager statistics.
   253    253   */
   254    254   static int pager_stats(
   255    255     void *NotUsed,
   256    256     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   257    257     int argc,              /* Number of arguments */
   258         -  char **argv            /* Text of each argument */
          258  +  const char **argv      /* Text of each argument */
   259    259   ){
   260    260     Pager *pPager;
   261    261     int i, *a;
   262    262     if( argc!=2 ){
   263    263       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   264    264          " ID\"", 0);
   265    265       return TCL_ERROR;
................................................................................
   284    284   **
   285    285   ** Return the size of the database file.
   286    286   */
   287    287   static int pager_pagecount(
   288    288     void *NotUsed,
   289    289     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   290    290     int argc,              /* Number of arguments */
   291         -  char **argv            /* Text of each argument */
          291  +  const char **argv      /* Text of each argument */
   292    292   ){
   293    293     Pager *pPager;
   294    294     char zBuf[100];
   295    295     if( argc!=2 ){
   296    296       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   297    297          " ID\"", 0);
   298    298       return TCL_ERROR;
................................................................................
   308    308   **
   309    309   ** Return a pointer to a page from the database.
   310    310   */
   311    311   static int page_get(
   312    312     void *NotUsed,
   313    313     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   314    314     int argc,              /* Number of arguments */
   315         -  char **argv            /* Text of each argument */
          315  +  const char **argv      /* Text of each argument */
   316    316   ){
   317    317     Pager *pPager;
   318    318     char zBuf[100];
   319    319     void *pPage;
   320    320     int pgno;
   321    321     int rc;
   322    322     if( argc!=3 ){
................................................................................
   342    342   ** Return a pointer to a page if the page is already in cache.
   343    343   ** If not in cache, return an empty string.
   344    344   */
   345    345   static int page_lookup(
   346    346     void *NotUsed,
   347    347     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   348    348     int argc,              /* Number of arguments */
   349         -  char **argv            /* Text of each argument */
          349  +  const char **argv      /* Text of each argument */
   350    350   ){
   351    351     Pager *pPager;
   352    352     char zBuf[100];
   353    353     void *pPage;
   354    354     int pgno;
   355    355     if( argc!=3 ){
   356    356       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   372    372   **
   373    373   ** Drop a pointer to a page.
   374    374   */
   375    375   static int page_unref(
   376    376     void *NotUsed,
   377    377     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   378    378     int argc,              /* Number of arguments */
   379         -  char **argv            /* Text of each argument */
          379  +  const char **argv      /* Text of each argument */
   380    380   ){
   381    381     void *pPage;
   382    382     int rc;
   383    383     if( argc!=2 ){
   384    384       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   385    385          " PAGE\"", 0);
   386    386       return TCL_ERROR;
................................................................................
   399    399   **
   400    400   ** Return the content of a page
   401    401   */
   402    402   static int page_read(
   403    403     void *NotUsed,
   404    404     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   405    405     int argc,              /* Number of arguments */
   406         -  char **argv            /* Text of each argument */
          406  +  const char **argv      /* Text of each argument */
   407    407   ){
   408    408     char zBuf[100];
   409    409     void *pPage;
   410    410     if( argc!=2 ){
   411    411       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   412    412          " PAGE\"", 0);
   413    413       return TCL_ERROR;
................................................................................
   423    423   **
   424    424   ** Return the page number for a page.
   425    425   */
   426    426   static int page_number(
   427    427     void *NotUsed,
   428    428     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   429    429     int argc,              /* Number of arguments */
   430         -  char **argv            /* Text of each argument */
          430  +  const char **argv      /* Text of each argument */
   431    431   ){
   432    432     char zBuf[100];
   433    433     void *pPage;
   434    434     if( argc!=2 ){
   435    435       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   436    436          " PAGE\"", 0);
   437    437       return TCL_ERROR;
................................................................................
   447    447   **
   448    448   ** Write something into a page.
   449    449   */
   450    450   static int page_write(
   451    451     void *NotUsed,
   452    452     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   453    453     int argc,              /* Number of arguments */
   454         -  char **argv            /* Text of each argument */
          454  +  const char **argv      /* Text of each argument */
   455    455   ){
   456    456     void *pPage;
   457    457     int rc;
   458    458     if( argc!=3 ){
   459    459       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   460    460          " PAGE DATA\"", 0);
   461    461       return TCL_ERROR;
................................................................................
   472    472   }
   473    473   
   474    474   /*
   475    475   ** Register commands with the TCL interpreter.
   476    476   */
   477    477   int Sqlitetest2_Init(Tcl_Interp *interp){
   478    478     extern int sqlite_io_error_pending;
   479         -  Tcl_CreateCommand(interp, "pager_open", pager_open, 0, 0);
   480         -  Tcl_CreateCommand(interp, "pager_close", pager_close, 0, 0);
   481         -  Tcl_CreateCommand(interp, "pager_commit", pager_commit, 0, 0);
   482         -  Tcl_CreateCommand(interp, "pager_rollback", pager_rollback, 0, 0);
   483         -  Tcl_CreateCommand(interp, "pager_ckpt_begin", pager_ckpt_begin, 0, 0);
   484         -  Tcl_CreateCommand(interp, "pager_ckpt_commit", pager_ckpt_commit, 0, 0);
   485         -  Tcl_CreateCommand(interp, "pager_ckpt_rollback", pager_ckpt_rollback, 0, 0);
   486         -  Tcl_CreateCommand(interp, "pager_stats", pager_stats, 0, 0);
   487         -  Tcl_CreateCommand(interp, "pager_pagecount", pager_pagecount, 0, 0);
   488         -  Tcl_CreateCommand(interp, "page_get", page_get, 0, 0);
   489         -  Tcl_CreateCommand(interp, "page_lookup", page_lookup, 0, 0);
   490         -  Tcl_CreateCommand(interp, "page_unref", page_unref, 0, 0);
   491         -  Tcl_CreateCommand(interp, "page_read", page_read, 0, 0);
   492         -  Tcl_CreateCommand(interp, "page_write", page_write, 0, 0);
   493         -  Tcl_CreateCommand(interp, "page_number", page_number, 0, 0);
          479  +  static struct {
          480  +    char *zName;
          481  +    Tcl_CmdProc *xProc;
          482  +  } aCmd[] = {
          483  +    { "pager_open",              (Tcl_CmdProc*)pager_open          },
          484  +    { "pager_close",             (Tcl_CmdProc*)pager_close         },
          485  +    { "pager_commit",            (Tcl_CmdProc*)pager_commit        },
          486  +    { "pager_rollback",          (Tcl_CmdProc*)pager_rollback      },
          487  +    { "pager_ckpt_begin",        (Tcl_CmdProc*)pager_ckpt_begin    },
          488  +    { "pager_ckpt_commit",       (Tcl_CmdProc*)pager_ckpt_commit   },
          489  +    { "pager_ckpt_rollback",     (Tcl_CmdProc*)pager_ckpt_rollback },
          490  +    { "pager_stats",             (Tcl_CmdProc*)pager_stats         },
          491  +    { "pager_pagecount",         (Tcl_CmdProc*)pager_pagecount     },
          492  +    { "page_get",                (Tcl_CmdProc*)page_get            },
          493  +    { "page_lookup",             (Tcl_CmdProc*)page_lookup         },
          494  +    { "page_unref",              (Tcl_CmdProc*)page_unref          },
          495  +    { "page_read",               (Tcl_CmdProc*)page_read           },
          496  +    { "page_write",              (Tcl_CmdProc*)page_write          },
          497  +    { "page_number",             (Tcl_CmdProc*)page_number         },
          498  +  };
          499  +  int i;
          500  +  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
          501  +    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
          502  +  }
   494    503     Tcl_LinkVar(interp, "sqlite_io_error_pending",
   495    504        (char*)&sqlite_io_error_pending, TCL_LINK_INT);
   496    505   #ifdef SQLITE_TEST
   497    506     Tcl_LinkVar(interp, "pager_old_format",
   498    507        (char*)&pager_old_format, TCL_LINK_INT);
   499    508   #endif
   500    509     return TCL_OK;
   501    510   }

Changes to src/test3.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Code for testing the btree.c module in SQLite.  This code
    13     13   ** is not included in the SQLite library.  It is used for automated
    14     14   ** testing of the SQLite library.
    15     15   **
    16         -** $Id: test3.c,v 1.18 2002/08/11 20:10:48 drh Exp $
           16  +** $Id: test3.c,v 1.19 2002/08/31 18:53:08 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "pager.h"
    20     20   #include "btree.h"
    21     21   #include "tcl.h"
    22     22   #include <stdlib.h>
    23     23   #include <string.h>
................................................................................
    53     53   **
    54     54   ** Open a new database
    55     55   */
    56     56   static int btree_open(
    57     57     void *NotUsed,
    58     58     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    59     59     int argc,              /* Number of arguments */
    60         -  char **argv            /* Text of each argument */
           60  +  const char **argv      /* Text of each argument */
    61     61   ){
    62     62     Btree *pBt;
    63     63     int rc;
    64     64     char zBuf[100];
    65     65     if( argc!=2 ){
    66     66       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
    67     67          " FILENAME\"", 0);
................................................................................
    85     85   **
    86     86   ** Close the given database.
    87     87   */
    88     88   static int btree_close(
    89     89     void *NotUsed,
    90     90     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    91     91     int argc,              /* Number of arguments */
    92         -  char **argv            /* Text of each argument */
           92  +  const char **argv      /* Text of each argument */
    93     93   ){
    94     94     Btree *pBt;
    95     95     int rc;
    96     96     if( argc!=2 ){
    97     97       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
    98     98          " ID\"", 0);
    99     99       return TCL_ERROR;
................................................................................
   112    112   **
   113    113   ** Start a new transaction
   114    114   */
   115    115   static int btree_begin_transaction(
   116    116     void *NotUsed,
   117    117     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   118    118     int argc,              /* Number of arguments */
   119         -  char **argv            /* Text of each argument */
          119  +  const char **argv      /* Text of each argument */
   120    120   ){
   121    121     Btree *pBt;
   122    122     int rc;
   123    123     if( argc!=2 ){
   124    124       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   125    125          " ID\"", 0);
   126    126       return TCL_ERROR;
................................................................................
   139    139   **
   140    140   ** Rollback changes
   141    141   */
   142    142   static int btree_rollback(
   143    143     void *NotUsed,
   144    144     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   145    145     int argc,              /* Number of arguments */
   146         -  char **argv            /* Text of each argument */
          146  +  const char **argv      /* Text of each argument */
   147    147   ){
   148    148     Btree *pBt;
   149    149     int rc;
   150    150     if( argc!=2 ){
   151    151       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   152    152          " ID\"", 0);
   153    153       return TCL_ERROR;
................................................................................
   166    166   **
   167    167   ** Commit all changes
   168    168   */
   169    169   static int btree_commit(
   170    170     void *NotUsed,
   171    171     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   172    172     int argc,              /* Number of arguments */
   173         -  char **argv            /* Text of each argument */
          173  +  const char **argv      /* Text of each argument */
   174    174   ){
   175    175     Btree *pBt;
   176    176     int rc;
   177    177     if( argc!=2 ){
   178    178       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   179    179          " ID\"", 0);
   180    180       return TCL_ERROR;
................................................................................
   193    193   **
   194    194   ** Create a new table in the database
   195    195   */
   196    196   static int btree_create_table(
   197    197     void *NotUsed,
   198    198     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   199    199     int argc,              /* Number of arguments */
   200         -  char **argv            /* Text of each argument */
          200  +  const char **argv      /* Text of each argument */
   201    201   ){
   202    202     Btree *pBt;
   203    203     int rc, iTable;
   204    204     char zBuf[30];
   205    205     if( argc!=2 ){
   206    206       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   207    207          " ID\"", 0);
................................................................................
   223    223   **
   224    224   ** Delete an entire table from the database
   225    225   */
   226    226   static int btree_drop_table(
   227    227     void *NotUsed,
   228    228     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   229    229     int argc,              /* Number of arguments */
   230         -  char **argv            /* Text of each argument */
          230  +  const char **argv      /* Text of each argument */
   231    231   ){
   232    232     Btree *pBt;
   233    233     int iTable;
   234    234     int rc;
   235    235     if( argc!=3 ){
   236    236       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   237    237          " ID TABLENUM\"", 0);
................................................................................
   252    252   **
   253    253   ** Remove all entries from the given table but keep the table around.
   254    254   */
   255    255   static int btree_clear_table(
   256    256     void *NotUsed,
   257    257     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   258    258     int argc,              /* Number of arguments */
   259         -  char **argv            /* Text of each argument */
          259  +  const char **argv      /* Text of each argument */
   260    260   ){
   261    261     Btree *pBt;
   262    262     int iTable;
   263    263     int rc;
   264    264     if( argc!=3 ){
   265    265       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   266    266          " ID TABLENUM\"", 0);
................................................................................
   281    281   **
   282    282   ** Return meta data
   283    283   */
   284    284   static int btree_get_meta(
   285    285     void *NotUsed,
   286    286     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   287    287     int argc,              /* Number of arguments */
   288         -  char **argv            /* Text of each argument */
          288  +  const char **argv      /* Text of each argument */
   289    289   ){
   290    290     Btree *pBt;
   291    291     int rc;
   292    292     int i;
   293    293     int aMeta[SQLITE_N_BTREE_META];
   294    294     if( argc!=2 ){
   295    295       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   315    315   **
   316    316   ** Return meta data
   317    317   */
   318    318   static int btree_update_meta(
   319    319     void *NotUsed,
   320    320     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   321    321     int argc,              /* Number of arguments */
   322         -  char **argv            /* Text of each argument */
          322  +  const char **argv      /* Text of each argument */
   323    323   ){
   324    324     Btree *pBt;
   325    325     int rc;
   326    326     int i;
   327    327     int aMeta[SQLITE_N_BTREE_META];
   328    328   
   329    329     if( argc!=2+SQLITE_N_BTREE_META ){
................................................................................
   350    350   **
   351    351   ** Print a disassembly of a page on standard output
   352    352   */
   353    353   static int btree_page_dump(
   354    354     void *NotUsed,
   355    355     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   356    356     int argc,              /* Number of arguments */
   357         -  char **argv            /* Text of each argument */
          357  +  const char **argv      /* Text of each argument */
   358    358   ){
   359    359     Btree *pBt;
   360    360     int iPage;
   361    361     int rc;
   362    362   
   363    363     if( argc!=3 ){
   364    364       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   380    380   **
   381    381   ** Print a disassembly of a page and all its child pages on standard output
   382    382   */
   383    383   static int btree_tree_dump(
   384    384     void *NotUsed,
   385    385     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   386    386     int argc,              /* Number of arguments */
   387         -  char **argv            /* Text of each argument */
          387  +  const char **argv      /* Text of each argument */
   388    388   ){
   389    389     Btree *pBt;
   390    390     int iPage;
   391    391     int rc;
   392    392   
   393    393     if( argc!=3 ){
   394    394       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   410    410   **
   411    411   ** Returns pager statistics
   412    412   */
   413    413   static int btree_pager_stats(
   414    414     void *NotUsed,
   415    415     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   416    416     int argc,              /* Number of arguments */
   417         -  char **argv            /* Text of each argument */
          417  +  const char **argv      /* Text of each argument */
   418    418   ){
   419    419     Btree *pBt;
   420    420     int i;
   421    421     int *a;
   422    422   
   423    423     if( argc!=2 ){
   424    424       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   445    445   **
   446    446   ** Print out all outstanding pages.
   447    447   */
   448    448   static int btree_pager_ref_dump(
   449    449     void *NotUsed,
   450    450     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   451    451     int argc,              /* Number of arguments */
   452         -  char **argv            /* Text of each argument */
          452  +  const char **argv      /* Text of each argument */
   453    453   ){
   454    454     Btree *pBt;
   455    455   
   456    456     if( argc!=2 ){
   457    457       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   458    458          " ID\"", 0);
   459    459       return TCL_ERROR;
................................................................................
   470    470   ** formatting and linkage.  Return a line of text for each problem found.
   471    471   ** Return an empty string if everything worked.
   472    472   */
   473    473   static int btree_integrity_check(
   474    474     void *NotUsed,
   475    475     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   476    476     int argc,              /* Number of arguments */
   477         -  char **argv            /* Text of each argument */
          477  +  const char **argv      /* Text of each argument */
   478    478   ){
   479    479     Btree *pBt;
   480    480     char *zResult;
   481    481     int nRoot;
   482    482     int *aRoot;
   483    483     int i;
   484    484   
................................................................................
   506    506   **
   507    507   ** Create a new cursor.  Return the ID for the cursor.
   508    508   */
   509    509   static int btree_cursor(
   510    510     void *NotUsed,
   511    511     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   512    512     int argc,              /* Number of arguments */
   513         -  char **argv            /* Text of each argument */
          513  +  const char **argv      /* Text of each argument */
   514    514   ){
   515    515     Btree *pBt;
   516    516     int iTable;
   517    517     BtCursor *pCur;
   518    518     int rc;
   519    519     int wrFlag;
   520    520     char zBuf[30];
................................................................................
   542    542   **
   543    543   ** Close a cursor opened using btree_cursor.
   544    544   */
   545    545   static int btree_close_cursor(
   546    546     void *NotUsed,
   547    547     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   548    548     int argc,              /* Number of arguments */
   549         -  char **argv            /* Text of each argument */
          549  +  const char **argv      /* Text of each argument */
   550    550   ){
   551    551     BtCursor *pCur;
   552    552     int rc;
   553    553   
   554    554     if( argc!=2 ){
   555    555       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   556    556          " ID\"", 0);
................................................................................
   570    570   **
   571    571   ** Move the cursor to the entry with the given key.
   572    572   */
   573    573   static int btree_move_to(
   574    574     void *NotUsed,
   575    575     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   576    576     int argc,              /* Number of arguments */
   577         -  char **argv            /* Text of each argument */
          577  +  const char **argv      /* Text of each argument */
   578    578   ){
   579    579     BtCursor *pCur;
   580    580     int rc;
   581    581     int res;
   582    582     char zBuf[20];
   583    583   
   584    584     if( argc!=3 ){
................................................................................
   604    604   **
   605    605   ** Delete the entry that the cursor is pointing to
   606    606   */
   607    607   static int btree_delete(
   608    608     void *NotUsed,
   609    609     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   610    610     int argc,              /* Number of arguments */
   611         -  char **argv            /* Text of each argument */
          611  +  const char **argv      /* Text of each argument */
   612    612   ){
   613    613     BtCursor *pCur;
   614    614     int rc;
   615    615   
   616    616     if( argc!=2 ){
   617    617       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   618    618          " ID\"", 0);
................................................................................
   633    633   ** Create a new entry with the given key and data.  If an entry already
   634    634   ** exists with the same key the old entry is overwritten.
   635    635   */
   636    636   static int btree_insert(
   637    637     void *NotUsed,
   638    638     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   639    639     int argc,              /* Number of arguments */
   640         -  char **argv            /* Text of each argument */
          640  +  const char **argv      /* Text of each argument */
   641    641   ){
   642    642     BtCursor *pCur;
   643    643     int rc;
   644    644   
   645    645     if( argc!=4 ){
   646    646       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   647    647          " ID KEY DATA\"", 0);
................................................................................
   662    662   **
   663    663   ** Move the cursor to the next entry in the table.
   664    664   */
   665    665   static int btree_next(
   666    666     void *NotUsed,
   667    667     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   668    668     int argc,              /* Number of arguments */
   669         -  char **argv            /* Text of each argument */
          669  +  const char **argv      /* Text of each argument */
   670    670   ){
   671    671     BtCursor *pCur;
   672    672     int rc;
   673    673     int res = 0;
   674    674     char zBuf[100];
   675    675   
   676    676     if( argc!=2 ){
................................................................................
   694    694   **
   695    695   ** Move the cursor to the first entry in the table.
   696    696   */
   697    697   static int btree_first(
   698    698     void *NotUsed,
   699    699     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   700    700     int argc,              /* Number of arguments */
   701         -  char **argv            /* Text of each argument */
          701  +  const char **argv      /* Text of each argument */
   702    702   ){
   703    703     BtCursor *pCur;
   704    704     int rc;
   705    705     int res = 0;
   706    706     char zBuf[100];
   707    707   
   708    708     if( argc!=2 ){
................................................................................
   726    726   **
   727    727   ** Return the key for the entry at which the cursor is pointing.
   728    728   */
   729    729   static int btree_key(
   730    730     void *NotUsed,
   731    731     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   732    732     int argc,              /* Number of arguments */
   733         -  char **argv            /* Text of each argument */
          733  +  const char **argv      /* Text of each argument */
   734    734   ){
   735    735     BtCursor *pCur;
   736    736     int rc;
   737    737     int n;
   738    738     char *zBuf;
   739    739   
   740    740     if( argc!=2 ){
................................................................................
   764    764   **
   765    765   ** Return the data for the entry at which the cursor is pointing.
   766    766   */
   767    767   static int btree_data(
   768    768     void *NotUsed,
   769    769     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   770    770     int argc,              /* Number of arguments */
   771         -  char **argv            /* Text of each argument */
          771  +  const char **argv      /* Text of each argument */
   772    772   ){
   773    773     BtCursor *pCur;
   774    774     int rc;
   775    775     int n;
   776    776     char *zBuf;
   777    777   
   778    778     if( argc!=2 ){
................................................................................
   802    802   **
   803    803   ** Return the number of bytes of payload
   804    804   */
   805    805   static int btree_payload_size(
   806    806     void *NotUsed,
   807    807     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   808    808     int argc,              /* Number of arguments */
   809         -  char **argv            /* Text of each argument */
          809  +  const char **argv      /* Text of each argument */
   810    810   ){
   811    811     BtCursor *pCur;
   812    812     int n1, n2;
   813    813     char zBuf[50];
   814    814   
   815    815     if( argc!=2 ){
   816    816       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
................................................................................
   841    841   **   aResult[6] =  Page number of the left child of this entry
   842    842   **   aResult[7] =  Page number of the right child for the whole page
   843    843   */
   844    844   static int btree_cursor_dump(
   845    845     void *NotUsed,
   846    846     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   847    847     int argc,              /* Number of arguments */
   848         -  char **argv            /* Text of each argument */
          848  +  const char **argv      /* Text of each argument */
   849    849   ){
   850    850     BtCursor *pCur;
   851    851     int rc;
   852    852     int i, j;
   853    853     int aResult[8];
   854    854     char zBuf[400];
   855    855   
................................................................................
   873    873     return SQLITE_OK;
   874    874   }
   875    875   
   876    876   /*
   877    877   ** Register commands with the TCL interpreter.
   878    878   */
   879    879   int Sqlitetest3_Init(Tcl_Interp *interp){
   880         -  Tcl_CreateCommand(interp, "btree_open", btree_open, 0, 0);
   881         -  Tcl_CreateCommand(interp, "btree_close", btree_close, 0, 0);
   882         -  Tcl_CreateCommand(interp, "btree_begin_transaction",
   883         -      btree_begin_transaction, 0, 0);
   884         -  Tcl_CreateCommand(interp, "btree_commit", btree_commit, 0, 0);
   885         -  Tcl_CreateCommand(interp, "btree_rollback", btree_rollback, 0, 0);
   886         -  Tcl_CreateCommand(interp, "btree_create_table", btree_create_table, 0, 0);
   887         -  Tcl_CreateCommand(interp, "btree_drop_table", btree_drop_table, 0, 0);
   888         -  Tcl_CreateCommand(interp, "btree_clear_table", btree_clear_table, 0, 0);
   889         -  Tcl_CreateCommand(interp, "btree_get_meta", btree_get_meta, 0, 0);
   890         -  Tcl_CreateCommand(interp, "btree_update_meta", btree_update_meta, 0, 0);
   891         -  Tcl_CreateCommand(interp, "btree_page_dump", btree_page_dump, 0, 0);
   892         -  Tcl_CreateCommand(interp, "btree_tree_dump", btree_tree_dump, 0, 0);
   893         -  Tcl_CreateCommand(interp, "btree_pager_stats", btree_pager_stats, 0, 0);
   894         -  Tcl_CreateCommand(interp, "btree_pager_ref_dump", btree_pager_ref_dump, 0, 0);
   895         -  Tcl_CreateCommand(interp, "btree_cursor", btree_cursor, 0, 0);
   896         -  Tcl_CreateCommand(interp, "btree_close_cursor", btree_close_cursor, 0, 0);
   897         -  Tcl_CreateCommand(interp, "btree_move_to", btree_move_to, 0, 0);
   898         -  Tcl_CreateCommand(interp, "btree_delete", btree_delete, 0, 0);
   899         -  Tcl_CreateCommand(interp, "btree_insert", btree_insert, 0, 0);
   900         -  Tcl_CreateCommand(interp, "btree_next", btree_next, 0, 0);
   901         -  Tcl_CreateCommand(interp, "btree_key", btree_key, 0, 0);
   902         -  Tcl_CreateCommand(interp, "btree_data", btree_data, 0, 0);
   903         -  Tcl_CreateCommand(interp, "btree_payload_size", btree_payload_size, 0, 0);
   904         -  Tcl_CreateCommand(interp, "btree_first", btree_first, 0, 0);
   905         -  Tcl_CreateCommand(interp, "btree_cursor_dump", btree_cursor_dump, 0, 0);
   906         -  Tcl_CreateCommand(interp, "btree_integrity_check", btree_integrity_check,0,0);
          880  +  static struct {
          881  +     char *zName;
          882  +     Tcl_CmdProc *xProc;
          883  +  } aCmd[] = {
          884  +     { "btree_open",               (Tcl_CmdProc*)btree_open               },
          885  +     { "btree_close",              (Tcl_CmdProc*)btree_close              },
          886  +     { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
          887  +     { "btree_commit",             (Tcl_CmdProc*)btree_commit             },
          888  +     { "btree_rollback",           (Tcl_CmdProc*)btree_rollback           },
          889  +     { "btree_create_table",       (Tcl_CmdProc*)btree_create_table       },
          890  +     { "btree_drop_table",         (Tcl_CmdProc*)btree_drop_table         },
          891  +     { "btree_clear_table",        (Tcl_CmdProc*)btree_clear_table        },
          892  +     { "btree_get_meta",           (Tcl_CmdProc*)btree_get_meta           },
          893  +     { "btree_update_meta",        (Tcl_CmdProc*)btree_update_meta        },
          894  +     { "btree_page_dump",          (Tcl_CmdProc*)btree_page_dump          },
          895  +     { "btree_tree_dump",          (Tcl_CmdProc*)btree_tree_dump          },
          896  +     { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
          897  +     { "btree_pager_ref_dump",     (Tcl_CmdProc*)btree_pager_ref_dump     },
          898  +     { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
          899  +     { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
          900  +     { "btree_move_to",            (Tcl_CmdProc*)btree_move_to            },
          901  +     { "btree_delete",             (Tcl_CmdProc*)btree_delete             },
          902  +     { "btree_insert",             (Tcl_CmdProc*)btree_insert             },
          903  +     { "btree_next",               (Tcl_CmdProc*)btree_next               },
          904  +     { "btree_key",                (Tcl_CmdProc*)btree_key                },
          905  +     { "btree_data",               (Tcl_CmdProc*)btree_data               },
          906  +     { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
          907  +     { "btree_first",              (Tcl_CmdProc*)btree_first              },
          908  +     { "btree_cursor_dump",        (Tcl_CmdProc*)btree_cursor_dump        },
          909  +     { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
          910  +  };
          911  +  int i;
          912  +
          913  +  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
          914  +    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
          915  +  }
   907    916     Tcl_LinkVar(interp, "pager_refinfo_enable", (char*)&pager_refinfo_enable,
   908    917        TCL_LINK_INT);
   909    918     Tcl_LinkVar(interp, "btree_native_byte_order",(char*)&btree_native_byte_order,
   910    919        TCL_LINK_INT);
   911    920     return TCL_OK;
   912    921   }

Added test/fkey1.test.

            1  +# 2001 September 15
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.
           12  +#
           13  +# This file implements tests for foreign keys.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +
           19  +# Create a table and some data to work with.
           20  +#
           21  +do_test fkey1-1.0 {
           22  +  execsql {
           23  +    CREATE TABLE t1(
           24  +      a INTEGER PRIMARY KEY,
           25  +      b INTEGER
           26  +           REFERENCES t1 ON DELETE CASCADE
           27  +           REFERENCES t2,
           28  +      c TEXT,
           29  +      FOREIGN KEY (b,c) REFERENCES t2(x,y) ON UPDATE CASCADE
           30  +    );
           31  +  }
           32  +} {}
           33  +do_test fkey1-1.1 {
           34  +  execsql {
           35  +    CREATE TABLE t2(
           36  +      x INTEGER PRIMARY KEY,
           37  +      y TEXT
           38  +    );
           39  +  }
           40  +} {}
           41  +
           42  +
           43  +
           44  +finish_test

Changes to test/table.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this file is testing the CREATE TABLE statement.
    13     13   #
    14         -# $Id: table.test,v 1.19 2002/08/13 23:02:58 drh Exp $
           14  +# $Id: table.test,v 1.20 2002/08/31 18:53:09 drh Exp $
    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   
    19     19   # Create a basic table and verify it is added to sqlite_master
    20     20   #
    21     21   do_test table-1.1 {
................................................................................
   429    429       DROP TABLE t6;
   430    430       CREATE TABLE t6(a,b,c,
   431    431         FOREIGN KEY (b,c) REFERENCES t4(x,y) MATCH PARTIAL
   432    432           ON UPDATE SET NULL ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
   433    433       );
   434    434     }
   435    435   } {0 {}}
          436  +do_test table-10.9 {
          437  +  catchsql {
          438  +    DROP TABLE t6;
          439  +    CREATE TABLE t6(a,b,c,
          440  +      FOREIGN KEY (b,c) REFERENCES t4(x)
          441  +    );
          442  +  }
          443  +} {1 {number of columns in foreign key does not match the number of columns in the referenced table}}
          444  +do_test table-10.10 {
          445  +  catchsql {DROP TABLE t6}
          446  +  catchsql {
          447  +    CREATE TABLE t6(a,b,c,
          448  +      FOREIGN KEY (b,c) REFERENCES t4(x,y,z)
          449  +    );
          450  +  }
          451  +} {1 {number of columns in foreign key does not match the number of columns in the referenced table}}
          452  +do_test table-10.11 {
          453  +  catchsql {DROP TABLE t6}
          454  +  catchsql {
          455  +    CREATE TABLE t6(a,b, c REFERENCES t4(x,y));
          456  +  }
          457  +} {1 {foreign key on c should reference only one column of table t4}}
          458  +do_test table-10.12 {
          459  +  catchsql {DROP TABLE t6}
          460  +  catchsql {
          461  +    CREATE TABLE t6(a,b,c,
          462  +      FOREIGN KEY (b,x) REFERENCES t4(x,y)
          463  +    );
          464  +  }
          465  +} {1 {unknown column "x" in foreign key definition}}
          466  +do_test table-10.13 {
          467  +  catchsql {DROP TABLE t6}
          468  +  catchsql {
          469  +    CREATE TABLE t6(a,b,c,
          470  +      FOREIGN KEY (x,b) REFERENCES t4(x,y)
          471  +    );
          472  +  }
          473  +} {1 {unknown column "x" in foreign key definition}}
          474  +
   436    475   
   437    476   # Test for the "typeof" function.
   438    477   #
   439    478   do_test table-11.1 {
   440    479     execsql {
   441    480       CREATE TABLE t7(
   442    481          a integer primary key,