]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add missing comments to fkey.c. Also, change the terminology used for comments and...
authordan <dan@noemail.net>
Wed, 23 Sep 2009 08:43:35 +0000 (08:43 +0000)
committerdan <dan@noemail.net>
Wed, 23 Sep 2009 08:43:35 +0000 (08:43 +0000)
FossilOrigin-Name: 540c2d18e14c277b55f95729fbafc04ca66466b2

manifest
manifest.uuid
src/build.c
src/fkey.c
src/parse.y
src/pragma.c
src/sqliteInt.h

index 6d7e25235bc5faf3c67bd88ebbdc8a22a7bc15df..fa0d67b36e0f368aa989cad7ed39f91c37d8c63c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,5 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Do\snot\srun\sON\sUPDATE\sactions\sof\sa\sforeign\skey\sconstraint\sunless\sat\sleast\sone\ncolumn\svalue\sreally\sdoes\schange.
-D 2009-09-23T03:01:59
+C Add\smissing\scomments\sto\sfkey.c.\sAlso,\schange\sthe\sterminology\sused\sfor\scomments\sand\snames\sin\sfkey.c\sfrom\s"referenced/referencing"\sto\s"parent/child".\sThis\sis\sarguably\sless\scorrect,\sbut\sis\seasier\sto\sfollow.
+D 2009-09-23T08:43:36
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -112,14 +109,14 @@ F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7
 F src/btree.c 9c425425784c5d569bc0309c22251698ba906451
 F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe
 F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705
-F src/build.c b0aee84fe34138eda3b8b4ff2fc0766714139ed3
+F src/build.c a6bd2dd725847bb4870f1b3f87d64730773c92bb
 F src/callback.c 10d237171472865f58fb07d515737238c9e06688
 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
 F src/date.c 657ff12ca0f1195b531561afacbb38b772d16638
 F src/delete.c 15499f5d10047d38e68ce991b3f88cbddb6e0931
 F src/expr.c 8a663240f374a5326ee157df3d27751f58b7676a
 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
-F src/fkey.c de296942345984960792f12ea4236a6bef2f800b
+F src/fkey.c da1a63c1cb22f4b93ceb743ab471a54648466732
 F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
 F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32
 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
@@ -153,11 +150,11 @@ F src/os_unix.c 5369272992c14dd198c02ddfc2fd7a1516906c40
 F src/os_win.c 49a360be4f42d5a63d00be9aa44449ed4d6717e0
 F src/pager.c ebd0a8f2421e8f0ad5b78201440004bf3e1c96d8
 F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f
-F src/parse.y 563ecc9e633bbb465383667380aff1646ecd96f7
+F src/parse.y 2b75a329a5b3cdcb188609d9a30bb339aecfeddd
 F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa
 F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36
 F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c
-F src/pragma.c 031ab58adab8c9ecc5fec68301a8bd899265ec37
+F src/pragma.c c25d0d15dd0bbc5ec34e9760629353358705a447
 F src/prepare.c 9803fc01f0db29ac4a17fa662902af285f37c06b
 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1
 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
@@ -167,7 +164,7 @@ F src/select.c 1d0a13137532321b4364f964e46f057d271691e3
 F src/shell.c d0171721c7402b368e257ddfc09ed54d0c74070c
 F src/sqlite.h.in 5af8181f815831a8672c3834c60e6b4418448bcc
 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
-F src/sqliteInt.h 35f0d4203924272b5ae849c88b7b857eda3bb272
+F src/sqliteInt.h 98ad725d6915a1f1c618ff317e7b09d79efffe57
 F src/sqliteLimit.h 504a3161886d2938cbd163054ad620b8356df758
 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -756,14 +753,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 98853f6104076c50ea92175e17a3254bfbbd4619
-R 827ec94cc2eb4c57959ec45f4b3d8098
-U drh
-Z ec752338f3000365feb24aa823e5d734
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.6 (GNU/Linux)
-
-iD8DBQFKuY+roxKgR168RlERAq1TAKCDsc87lQ7RxW/6hlefk/rm32wxUACdGuQz
-1VFw5lE97EA4c5Tiy8ftXks=
-=0G/k
------END PGP SIGNATURE-----
+P 71ac8e28e55ff0250ebe2fe239159ce2458d9165
+R 2ced32012b9c68891c37e1608fd90d15
+U dan
+Z 2fdc28dc4452275cfa2178c7993f269c
index 3c61b0a6f34d0057e1dc5b6ceb1b32f02a1ecae7..eab8d53d665dfb4d0bdb65ae9db72123ad230a6a 100644 (file)
@@ -1 +1 @@
-71ac8e28e55ff0250ebe2fe239159ce2458d9165
\ No newline at end of file
+540c2d18e14c277b55f95729fbafc04ca66466b2
\ No newline at end of file
index 06c0a0fc1b6c263857077c3b5f881422e8969aff..53d8b32407e784dd8f656af5deda7f43409bc998 100644 (file)
@@ -2226,9 +2226,8 @@ void sqlite3CreateForeignKey(
     }
   }
   pFKey->isDeferred = 0;
-  pFKey->deleteConf = (u8)(flags & 0xff);
-  pFKey->updateConf = (u8)((flags >> 8 ) & 0xff);
-  pFKey->insertConf = (u8)((flags >> 16 ) & 0xff);
+  pFKey->aAction[0] = (u8)(flags & 0xff);            /* ON DELETE action */
+  pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff);    /* ON UPDATE action */
 
   pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, 
       pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey
index 1e64310ead3d66e8cd1d596782f14ac00833ac9c..2dc93b66a2e33c3e944c0386187c86f802494987 100644 (file)
 **
 ** INSERT operations:
 **
-**   I.1) For each FK for which the table is the referencing table, search
-**        the referenced table for a match. If none is found, throw an 
+**   I.1) For each FK for which the table is the child table, search
+**        the parent table for a match. If none is found, throw an 
 **        exception for an immediate FK, or increment the counter for a
 **        deferred FK.
 **
-**   I.2) For each deferred FK for which the table is the referenced table, 
-**        search the referencing table for rows that correspond to the new
-**        row in the referenced table. Decrement the counter for each row
+**   I.2) For each deferred FK for which the table is the parent table, 
+**        search the child table for rows that correspond to the new
+**        row in the parent table. Decrement the counter for each row
 **        found (as the constraint is now satisfied).
 **
 ** DELETE operations:
 **
-**   D.1) For each deferred FK for which the table is the referencing table, 
-**        search the referenced table for a row that corresponds to the 
-**        deleted row in the referencing table. If such a row is not found, 
+**   D.1) For each deferred FK for which the table is the child table, 
+**        search the parent table for a row that corresponds to the 
+**        deleted row in the child table. If such a row is not found, 
 **        decrement the counter.
 **
-**   D.2) For each FK for which the table is the referenced table, search 
-**        the referencing table for rows that correspond to the deleted row 
-**        in the referenced table. For each found, throw an exception for an
+**   D.2) For each FK for which the table is the parent table, search 
+**        the child table for rows that correspond to the deleted row 
+**        in the parent table. For each found, throw an exception for an
 **        immediate FK, or increment the counter for a deferred FK.
 **
 ** UPDATE operations:
@@ -87,9 +87,8 @@
 **
 ** TODO: How should dropping a table be handled? How should renaming a 
 ** table be handled?
-*/
-
-/*
+**
+**
 ** Query API Notes
 ** ---------------
 **
 ** row are required by the FK processing VDBE code (i.e. if FKs were
 ** implemented using triggers, which of the old.* columns would be 
 ** accessed). No information is required by the code-generator before
-** coding an INSERT operation.
+** coding an INSERT operation. The functions used by the UPDATE/DELETE
+** generation code to query for this information are:
+**
+**   sqlite3FkRequired() - Test to see if FK processing is required.
+**   sqlite3FkOldmask()  - Query for the set of required old.* columns.
+**
 **
+** Externally accessible module functions
+** --------------------------------------
+**
+**   sqlite3FkCheck()    - Check for foreign key violations.
+**   sqlite3FkActions()  - Code triggers for ON UPDATE/ON DELETE actions.
+**   sqlite3FkDelete()   - Delete an FKey structure.
 */
 
 /*
 */
 
 /*
-** ON UPDATE and ON DELETE clauses
-** -------------------------------
-*/
-
-/*
-** Externally accessible module functions
-** --------------------------------------
-**
-**   sqlite3FkRequired()
-**   sqlite3FkOldmask()
-**
-**   sqlite3FkCheck()
-**   sqlite3FkActions()
-**
-**   sqlite3FkDelete()
-** 
-*/
-
-/*
-** A foreign key constraint requires that the key columns in the referenced
+** A foreign key constraint requires that the key columns in the parent
 ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
-** Given that pTo is the referenced table for foreign key constraint
-** pFKey, check that the columns in pTo are indeed subject to a such a
-** constraint. If they are not, return non-zero and leave an error in pParse.
+** Given that pParent is the parent table for foreign key constraint pFKey, 
+** search the schema a unique index on the parent key columns. 
 **
-** If an error does not occur, return zero.
+** If successful, zero is returned. If the parent key is an INTEGER PRIMARY 
+** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx 
+** is set to point to the unique index. 
+** 
+** If the parent key consists of a single column (the foreign key constraint
+** is not a composite foreign key), output variable *paiCol is set to NULL.
+** Otherwise, it is set to point to an allocated array of size N, where
+** N is the number of columns in the parent key. The first element of the
+** array is the index of the child table column that is mapped by the FK
+** constraint to the parent table column stored in the left-most column
+** of index *ppIdx. The second element of the array is the index of the
+** child table column that corresponds to the second left-most column of
+** *ppIdx, and so on.
+**
+** If the required index cannot be found, either because:
+**
+**   1) The named parent key columns do not exist, or
+**
+**   2) The named parent key columns do exist, but are not subject to a
+**      UNIQUE or PRIMARY KEY constraint, or
+**
+**   3) No parent key columns were provided explicitly as part of the
+**      foreign key definition, and the parent table does not have a
+**      PRIMARY KEY, or
+**
+**   4) No parent key columns were provided explicitly as part of the
+**      foreign key definition, and the PRIMARY KEY of the parent table 
+**      consists of a a different number of columns to the child key in 
+**      the child table.
+**
+** then non-zero is returned, and a "foreign key mismatch" error loaded
+** into pParse. If an OOM error occurs, non-zero is returned and the
+** pParse->db->mallocFailed flag is set.
 */
 static int locateFkeyIndex(
   Parse *pParse,                  /* Parse context to store any error in */
-  Table *pTo,                     /* Referenced table */
+  Table *pParent,                 /* Parent table of FK constraint pFKey */
   FKey *pFKey,                    /* Foreign key to find index for */
-  Index **ppIdx,                  /* OUT: Unique index on referenced table */
+  Index **ppIdx,                  /* OUT: Unique index on parent table */
   int **paiCol                    /* OUT: Map of index columns in pFKey */
 ){
-  Index *pIdx = 0;
-  int *aiCol = 0;
-  int nCol = pFKey->nCol;
-  char *zFirst = pFKey->aCol[0].zCol;
+  Index *pIdx = 0;                    /* Value to return via *ppIdx */
+  int *aiCol = 0;                     /* Value to return via *paiCol */
+  int nCol = pFKey->nCol;             /* Number of columns in parent key */
+  char *zKey = pFKey->aCol[0].zCol;   /* Name of left-most parent key column */
 
   /* The caller is responsible for zeroing output parameters. */
   assert( ppIdx && *ppIdx==0 );
   assert( !paiCol || *paiCol==0 );
 
   /* If this is a non-composite (single column) foreign key, check if it 
-  ** maps to the INTEGER PRIMARY KEY of table pTo. If so, leave *ppIdx 
+  ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx 
   ** and *paiCol set to zero and return early. 
   **
   ** Otherwise, for a composite foreign key (more than one column), allocate
@@ -177,14 +199,14 @@ static int locateFkeyIndex(
     **
     **   1) The FK is explicitly mapped to "rowid", "oid" or "_rowid_", or
     **   2) There is an explicit INTEGER PRIMARY KEY column and the FK is
-    **      implicitly mapped to the primary key of table pTo, or
+    **      implicitly mapped to the primary key of table pParent, or
     **   3) The FK is explicitly mapped to a column declared as INTEGER
     **      PRIMARY KEY.
     */
-    if( zFirst && sqlite3IsRowid(zFirst) ) return 0;
-    if( pTo->iPKey>=0 ){
-      if( !zFirst ) return 0;
-      if( !sqlite3StrICmp(pTo->aCol[pTo->iPKey].zName, zFirst) ) return 0;
+    if( zKey && sqlite3IsRowid(zKey) ) return 0;
+    if( pParent->iPKey>=0 ){
+      if( !zKey ) return 0;
+      if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0;
     }
   }else if( paiCol ){
     assert( nCol>1 );
@@ -193,27 +215,27 @@ static int locateFkeyIndex(
     *paiCol = aiCol;
   }
 
-  for(pIdx=pTo->pIndex; pIdx; pIdx=pIdx->pNext){
+  for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
     if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ 
       /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
       ** of columns. If each indexed column corresponds to a foreign key
       ** column of pFKey, then this index is a winner.  */
 
-      if( zFirst==0 ){
-        /* If zFirst is NULL, then this foreign key is implicitly mapped to 
-        ** the PRIMARY KEY of table pTo. The PRIMARY KEY index may be 
+      if( zKey==0 ){
+        /* If zKey is NULL, then this foreign key is implicitly mapped to 
+        ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be 
         ** identified by the test (Index.autoIndex==2).  */
         if( pIdx->autoIndex==2 ){
           if( aiCol ) memcpy(aiCol, pIdx->aiColumn, sizeof(int)*nCol);
           break;
         }
       }else{
-        /* If zFirst is non-NULL, then this foreign key was declared to
-        ** map to an explicit list of columns in table pTo. Check if this
+        /* If zKey is non-NULL, then this foreign key was declared to
+        ** map to an explicit list of columns in table pParent. Check if this
         ** index matches those columns.  */
         int i, j;
         for(i=0; i<nCol; i++){
-          char *zIdxCol = pTo->aCol[pIdx->aiColumn[i]].zName;
+          char *zIdxCol = pParent->aCol[pIdx->aiColumn[i]].zName;
           for(j=0; j<nCol; j++){
             if( sqlite3StrICmp(pFKey->aCol[j].zCol, zIdxCol)==0 ){
               if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom;
@@ -237,34 +259,60 @@ static int locateFkeyIndex(
   return 0;
 }
 
-static void fkCheckReference(
+/*
+** This function is called when a row is inserted into the child table of 
+** foreign key constraint pFKey and, if pFKey is deferred, when a row is
+** deleted from the child table of pFKey. If an SQL UPDATE is executed on
+** the child table of pFKey, this function is invoked twice for each row
+** affected - once to "delete" the old row, and then again to "insert" the
+** new row.
+**
+** Each time it is called, this function generates VDBE code to locate the
+** row in the parent table that corresponds to the row being inserted into 
+** or deleted from the child table. If the parent row can be found, no 
+** special action is taken. Otherwise, if the parent row can *not* be
+** found in the parent table:
+**
+**   Operation | FK type   | Action taken
+**   --------------------------------------------------------------------------
+**   INSERT      immediate   Throw a "foreign key constraint failed" exception.
+**
+**   INSERT      deferred    Increment the "deferred constraint counter".
+**
+**   DELETE      deferred    Decrement the "deferred constraint counter".
+**
+** This function is never called for a delete on the child table of an
+** immediate foreign key constraint. These operations are identified in
+** the comment at the top of this file (fkey.c) as "I.1" and "D.1".
+*/
+static void fkLookupParent(
   Parse *pParse,        /* Parse context */
   int iDb,              /* Index of database housing pTab */
-  Table *pTab,          /* Table referenced by FK pFKey */
-  Index *pIdx,          /* Index ensuring uniqueness of FK in pTab */
-  FKey *pFKey,          /* Foreign key to check */
-  int *aiCol,           /* Map from FK column to referencing table column */
-  int regData,          /* Address of array containing referencing row */
+  Table *pTab,          /* Parent table of FK pFKey */
+  Index *pIdx,          /* Unique index on parent key columns in pTab */
+  FKey *pFKey,          /* Foreign key constraint */
+  int *aiCol,           /* Map from parent key columns to child table columns */
+  int regData,          /* Address of array containing child table row */
   int nIncr             /* If deferred FK, increment counter by this */
 ){
-  int i;
-  Vdbe *v = sqlite3GetVdbe(pParse);
-  int iCur = pParse->nTab - 1;
-  int iOk = sqlite3VdbeMakeLabel(v);
+  int i;                                    /* Iterator variable */
+  Vdbe *v = sqlite3GetVdbe(pParse);         /* Vdbe to add code to */
+  int iCur = pParse->nTab - 1;              /* Cursor number to use */
+  int iOk = sqlite3VdbeMakeLabel(v);        /* jump here if parent key found */
 
   assert( pFKey->isDeferred || nIncr==1 );
 
-  /* Check if any of the key columns in the referencing table are 
+  /* Check if any of the key columns in the child table row are
   ** NULL. If any are, then the constraint is satisfied. No need
-  ** to search for a matching row in the referenced table.  */
+  ** to search for a matching row in the parent table.  */
   for(i=0; i<pFKey->nCol; i++){
     int iReg = aiCol[i] + regData + 1;
     sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk);
   }
 
   if( pIdx==0 ){
-    /* If pIdx is NULL, then the foreign key constraint references the
-    ** INTEGER PRIMARY KEY column in the referenced table (table pTab).  */
+    /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY
+    ** column of the parent table (table pTab).  */
     int iReg = pFKey->aCol[0].iFrom + regData + 1;
     sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
     sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iReg);
@@ -307,12 +355,37 @@ static void fkCheckReference(
   sqlite3VdbeResolveLabel(v, iOk);
 }
 
-static void fkScanReferences(
+/*
+** This function is called to generate code executed when a row is deleted
+** from the parent table of foreign key constraint pFKey and, if pFKey is 
+** deferred, when a row is inserted into the same table. When generating
+** code for an SQL UPDATE operation, this function may be called twice -
+** once to "delete" the old row and once to "insert" the new row.
+**
+** The code generated by this function scans through the rows in the child
+** table that correspond to the parent table row being deleted or inserted.
+** For each child row found, one of the following actions is taken:
+**
+**   Operation | FK type   | Action taken
+**   --------------------------------------------------------------------------
+**   DELETE      immediate   Throw a "foreign key constraint failed" exception.
+**
+**   DELETE      deferred    Increment the "deferred constraint counter".
+**                           Or, if the ON (UPDATE|DELETE) action is RESTRICT,
+**                           throw a "foreign key constraint failed" exception.
+**
+**   INSERT      deferred    Decrement the "deferred constraint counter".
+**
+** This function is never called for an INSERT operation on the parent table
+** of an immediate foreign key constraint. These operations are identified in
+** the comment at the top of this file (fkey.c) as "I.2" and "D.2".
+*/
+static void fkScanChildren(
   Parse *pParse,                  /* Parse context */
   SrcList *pSrc,                  /* SrcList containing the table to scan */
   Index *pIdx,                    /* Foreign key index */
   FKey *pFKey,                    /* Foreign key relationship */
-  int *aiCol,                     /* Map from FK to referenced table columns */
+  int *aiCol,                     /* Map from pIdx cols to child table cols */
   int regData,                    /* Referenced table data starts here */
   int nIncr                       /* Amount to increment deferred counter by */
 ){
@@ -323,11 +396,11 @@ static void fkScanReferences(
   WhereInfo *pWInfo;              /* Context used by sqlite3WhereXXX() */
 
   for(i=0; i<pFKey->nCol; i++){
-    Expr *pLeft;                  /* Value from deleted row */
-    Expr *pRight;                 /* Column ref to referencing table */
+    Expr *pLeft;                  /* Value from parent table row */
+    Expr *pRight;                 /* Column ref to child table */
     Expr *pEq;                    /* Expression (pLeft = pRight) */
-    int iCol;                     /* Index of column in referencing table */ 
-    const char *zCol;             /* Name of column in referencing table */
+    int iCol;                     /* Index of column in child table */ 
+    const char *zCol;             /* Name of column in child table */
 
     pLeft = sqlite3Expr(db, TK_REGISTER, 0);
     if( pLeft ){
@@ -374,7 +447,7 @@ static void fkScanReferences(
 
 /*
 ** This function returns a pointer to the head of a linked list of FK
-** constraints that refer to the table passed as an argument. For example,
+** constraints for which table pTab is the parent table. For example,
 ** given the following schema:
 **
 **   CREATE TABLE t1(a PRIMARY KEY);
@@ -383,13 +456,22 @@ static void fkScanReferences(
 ** Calling this function with table "t1" as an argument returns a pointer
 ** to the FKey structure representing the foreign key constraint on table
 ** "t2". Calling this function with "t2" as the argument would return a
-** NULL pointer (as there are no FK constraints that refer to t2).
+** NULL pointer (as there are no FK constraints for which t2 is the parent
+** table).
 */
 static FKey *fkRefering(Table *pTab){
   int nName = sqlite3Strlen30(pTab->zName);
   return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName, nName);
 }
 
+/*
+** The second argument is a Trigger structure allocated by the 
+** fkActionTrigger() routine. This function deletes the Trigger structure
+** and all of its sub-components.
+**
+** The Trigger structure or any of its sub-components may be allocated from
+** the lookaside buffer belonging to database handle dbMem.
+*/
 static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
   if( p ){
     TriggerStep *pStep = p->step_list;
@@ -400,6 +482,29 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
   }
 }
 
+/*
+** This function is called when inserting, deleting or updating a row of
+** table pTab to generate VDBE code to perform foreign key constraint 
+** processing for the operation.
+**
+** For a DELETE operation, parameter regOld is passed the index of the
+** first register in an array of (pTab->nCol+1) registers containing the
+** rowid of the row being deleted, followed by each of the column values
+** of the row being deleted, from left to right. Parameter regNew is passed
+** zero in this case.
+**
+** For an UPDATE operation, regOld is the first in an array of (pTab->nCol+1)
+** registers containing the old rowid and column values of the row being
+** updated, and regNew is the first in an array of the same size containing
+** the corresponding new values. Parameter pChanges is passed the list of
+** columns being updated by the statement.
+**
+** For an INSERT operation, regOld is passed zero and regNew is passed the
+** first register of an array of (pTab->nCol+1) registers containing the new
+** row data.
+**
+** If an error occurs, an error message is left in the pParse structure.
+*/
 void sqlite3FkCheck(
   Parse *pParse,                  /* Parse context */
   Table *pTab,                    /* Row is being deleted from this table */ 
@@ -425,21 +530,25 @@ void sqlite3FkCheck(
   iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
   zDb = db->aDb[iDb].zName;
 
-  /* Loop through all the foreign key constraints attached to the table. */
+  /* Loop through all the foreign key constraints for which pTab is the
+  ** child table (the table that the foreign key definition is part of).  */
   for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
-    Table *pTo;                   /* Table referenced by this FK */
+    Table *pTo;                   /* Parent table of foreign key pFKey */
     Index *pIdx = 0;              /* Index on key columns in pTo */
     int *aiFree = 0;
     int *aiCol;
     int iCol;
     int i;
 
+    /* If this is a DELETE operation and the foreign key is not deferred,
+    ** nothing to do. A DELETE on the child table cannot cause the FK 
+    ** constraint to fail.  */
     if( pFKey->isDeferred==0 && regNew==0 ) continue;
 
-    /* Find the table this foreign key references. Also find a unique 
-    ** index on the referenced table that corresponds to the key columns. 
-    ** If either of these things cannot be located, set an error in pParse
-    ** and return early.  */
+    /* Find the parent table of this foreign key. Also find a unique index 
+    ** on the parent key columns in the parent table. If either of these 
+    ** schema items cannot be located, set an error in pParse and return 
+    ** early.  */
     pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
     if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ) return;
     assert( pFKey->nCol==1 || (aiFree && pIdx) );
@@ -461,17 +570,17 @@ void sqlite3FkCheck(
       }
     }
 
-    /* Take a shared-cache advisory read-lock on the referenced table.
-    ** Allocate a cursor to use to search the unique index on the FK 
-    ** columns in the referenced table.  */
+    /* Take a shared-cache advisory read-lock on the parent table. Allocate 
+    ** a cursor to use to search the unique index on the parent key columns 
+    ** in the parent table.  */
     sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName);
     pParse->nTab++;
 
     if( regOld!=0 && pFKey->isDeferred ){
-      fkCheckReference(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1);
+      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1);
     }
     if( regNew!=0 ){
-      fkCheckReference(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1);
+      fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1);
     }
 
     sqlite3DbFree(db, aiFree);
@@ -494,16 +603,16 @@ void sqlite3FkCheck(
     */
     if( pFKey->isDeferred==0 ){
       if( regOld==0 ) continue;                                     /* 1 */
-      if( regNew!=0 && pFKey->updateConf>OE_Restrict ) continue;    /* 2 */
-      if( regNew==0 && pFKey->deleteConf>OE_Restrict ) continue;    /* 3 */
+      if( regNew!=0 && pFKey->aAction[1]>OE_Restrict ) continue;    /* 2 */
+      if( regNew==0 && pFKey->aAction[0]>OE_Restrict ) continue;    /* 3 */
     }
 
     if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return;
     assert( aiCol || pFKey->nCol==1 );
 
-    /* Check if this update statement has modified any of the key columns
-    ** for this foreign key constraint. If it has not, there is no need
-    ** to search the referencing table for rows in violation. This is
+    /* Check if this update statement has modified any of the child key 
+    ** columns for this foreign key constraint. If it has not, there is 
+    ** no need to search the child table for rows in violation. This is
     ** just an optimization. Things would work fine without this check.  */
     if( pChanges ){
       /* TODO */
@@ -519,8 +628,8 @@ void sqlite3FkCheck(
       pSrc->a->iCursor = pParse->nTab++;
   
       /* If this is an UPDATE, and none of the columns associated with this
-      ** FK have been modified, do not scan the referencing table. Unlike
-      ** the compile-time test implemented above, this is not just an 
+      ** FK have been modified, do not scan the child table. Unlike the 
+      ** compile-time test implemented above, this is not just an 
       ** optimization. It is required so that immediate foreign keys do not 
       ** throw exceptions when the user executes a statement like:
       **
@@ -537,18 +646,17 @@ void sqlite3FkCheck(
       }
   
       if( regNew!=0 && pFKey->isDeferred ){
-        fkScanReferences(pParse, pSrc, pIdx, pFKey, aiCol, regNew, -1);
+        fkScanChildren(pParse, pSrc, pIdx, pFKey, aiCol, regNew, -1);
       }
       if( regOld!=0 ){
         /* If there is a RESTRICT action configured for the current operation
-        ** on the referenced table of this FK, then throw an exception 
+        ** on the parent table of this FK, then throw an exception 
         ** immediately if the FK constraint is violated, even if this is a
         ** deferred trigger. That's what RESTRICT means. To defer checking
         ** the constraint, the FK should specify NO ACTION (represented
         ** using OE_None). NO ACTION is the default.  */
-        fkScanReferences(pParse, pSrc, pIdx, pFKey, aiCol, regOld, 
-            (pChanges!=0 && pFKey->updateConf!=OE_Restrict)
-         || (pChanges==0 && pFKey->deleteConf!=OE_Restrict)
+        fkScanChildren(pParse, pSrc, pIdx, pFKey, aiCol, regOld, 
+            pFKey->aAction[pChanges!=0]!=OE_Restrict
         );
       }
   
@@ -619,8 +727,37 @@ int sqlite3FkRequired(
   return 0;
 }
 
+/*
+** This function is called when an UPDATE or DELETE operation is being 
+** compiled on table pTab, which is the parent table of foreign-key pFKey.
+** If the current operation is an UPDATE, then the pChanges parameter is
+** passed a pointer to the list of columns being modified. If it is a
+** DELETE, pChanges is passed a NULL pointer.
+**
+** It returns a pointer to a Trigger structure containing a trigger
+** equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
+** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is
+** returned (these actions require no special handling by the triggers
+** sub-system, code for them is created by fkScanChildren()).
+**
+** For example, if pFKey is the foreign key and pTab is table "p" in 
+** the following schema:
+**
+**   CREATE TABLE p(pk PRIMARY KEY);
+**   CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE);
+**
+** then the returned trigger structure is equivalent to:
+**
+**   CREATE TRIGGER ... DELETE ON p BEGIN
+**     DELETE FROM c WHERE ck = old.pk;
+**   END;
+**
+** The returned pointer is cached as part of the foreign key object. It
+** is eventually freed along with the rest of the foreign key object by 
+** sqlite3FkDelete().
+*/
 static Trigger *fkActionTrigger(
-  Parse *pParse,
+  Parse *pParse,                  /* Parse context */
   Table *pTab,                    /* Table being updated or deleted from */
   FKey *pFKey,                    /* Foreign key to get action for */
   ExprList *pChanges              /* Change-list for UPDATE, NULL for DELETE */
@@ -628,21 +765,17 @@ static Trigger *fkActionTrigger(
   sqlite3 *db = pParse->db;       /* Database handle */
   int action;                     /* One of OE_None, OE_Cascade etc. */
   Trigger *pTrigger;              /* Trigger definition to return */
+  int iAction = (pChanges!=0);    /* 1 for UPDATE, 0 for DELETE */
 
-  if( pChanges ){
-    action = pFKey->updateConf;
-    pTrigger = pFKey->pOnUpdate;
-  }else{
-    action = pFKey->deleteConf;
-    pTrigger = pFKey->pOnDelete;
-  }
+  action = pFKey->aAction[iAction];
+  pTrigger = pFKey->apTrigger[iAction];
 
   assert( OE_SetNull>OE_Restrict && OE_SetDflt>OE_Restrict );
   assert( OE_Cascade>OE_Restrict && OE_None<OE_Restrict );
 
   if( action>OE_Restrict && !pTrigger ){
     u8 enableLookaside;           /* Copy of db->lookaside.bEnabled */
-    char const *zFrom;            /* Name of referencing table */
+    char const *zFrom;            /* Name of child table */
     int nFrom;                    /* Length in bytes of zFrom */
     Index *pIdx = 0;              /* Parent key index for this FK */
     int *aiCol = 0;               /* child table cols -> parent key cols */
@@ -658,9 +791,9 @@ static Trigger *fkActionTrigger(
     for(i=0; i<pFKey->nCol; i++){
       Token tOld = { "old", 3 };  /* Literal "old" token */
       Token tNew = { "new", 3 };  /* Literal "new" token */
-      Token tFromCol;             /* Name of column in referencing table */
-      Token tToCol;               /* Name of column in referenced table */
-      int iFromCol;               /* Idx of column in referencing table */
+      Token tFromCol;             /* Name of column in child table */
+      Token tToCol;               /* Name of column in parent table */
+      int iFromCol;               /* Idx of column in child table */
       Expr *pEq;                  /* tFromCol = OLD.tToCol */
 
       iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
@@ -766,16 +899,8 @@ static Trigger *fkActionTrigger(
     pStep->pTrig = pTrigger;
     pTrigger->pSchema = pTab->pSchema;
     pTrigger->pTabSchema = pTab->pSchema;
-
-    if( pChanges ){
-      pFKey->pOnUpdate = pTrigger;
-      pTrigger->op = TK_UPDATE;
-      pStep->op = TK_UPDATE;
-    }else{
-      pFKey->pOnDelete = pTrigger;
-      pTrigger->op = TK_DELETE;
-      pStep->op = (action==OE_Cascade)?TK_DELETE:TK_UPDATE;
-    }
+    pFKey->apTrigger[iAction] = pTrigger;
+    pTrigger->op = (pChanges ? TK_UPDATE : TK_DELETE);
   }
 
   return pTrigger;
@@ -833,8 +958,8 @@ void sqlite3FkDelete(Table *pTab){
 
     /* Delete any triggers created to implement actions for this FK. */
 #ifndef SQLITE_OMIT_TRIGGER
-    fkTriggerDelete(pTab->dbMem, pFKey->pOnDelete);
-    fkTriggerDelete(pTab->dbMem, pFKey->pOnUpdate);
+    fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[0]);
+    fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[1]);
 #endif
 
     /* Delete the memory allocated for the FK structure. */
index 8fe4ef02778b7c661e70f54ea1794d101350c3fe..32dedfe01891de3eb5cd394e83097b3e9d60b8d9 100644 (file)
@@ -314,7 +314,7 @@ autoinc(X) ::= AUTOINCR.  {X = 1;}
 // check fails.
 //
 %type refargs {int}
-refargs(A) ::= .                     { A = OE_None * 0x010101; }
+refargs(A) ::= .                     { A = OE_None * 0x000101; }
 refargs(A) ::= refargs(X) refarg(Y). { A = (X & ~Y.mask) | Y.value; }
 %type refarg {struct {int value; int mask;}}
 refarg(A) ::= MATCH nm.              { A.value = 0;     A.mask = 0x000000; }
index d933735b914cc00368cb460a1ec29e5302c62bec..3d779a75fa984ee2b7b5dbfc6eec0ba40576384e 100644 (file)
@@ -981,8 +981,8 @@ void sqlite3Pragma(
           int j;
           for(j=0; j<pFK->nCol; j++){
             char *zCol = pFK->aCol[j].zCol;
-            char *zOnUpdate = (char *)actionName(pFK->updateConf);
-            char *zOnDelete = (char *)actionName(pFK->deleteConf);
+            char *zOnDelete = (char *)actionName(pFK->aAction[0]);
+            char *zOnUpdate = (char *)actionName(pFK->aAction[1]);
             sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
             sqlite3VdbeAddOp2(v, OP_Integer, j, 2);
             sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0);
index 17aed293b270ae72070028af838a495c9c2f5bd6..b028efe35e591862e2f5a6e4e5552cc837dea3df 100644 (file)
@@ -1290,11 +1290,8 @@ struct FKey {
   FKey *pPrevTo;    /* Previous foreign key on table named zTo */
   int nCol;         /* Number of columns in this key */
   u8 isDeferred;    /* True if constraint checking is deferred till COMMIT */
-  u8 updateConf;    /* How to resolve conflicts that occur on UPDATE */
-  u8 deleteConf;    /* How to resolve conflicts that occur on DELETE */
-  u8 insertConf;    /* How to resolve conflicts that occur on INSERT */
-  Trigger *pOnUpdate;  /* Trigger for AFTER UPDATE ON zTo */
-  Trigger *pOnDelete;  /* Trigger for AFTER DELETE ON zTo */
+  u8 aAction[2];          /* ON DELETE and ON UPDATE actions, respectively */
+  Trigger *apTrigger[2];  /* Triggers for aAction[] actions */
   struct sColMap {  /* Mapping of columns in pFrom to columns in zTo */
     int iFrom;         /* Index of column in pFrom */
     char *zCol;        /* Name of column in zTo.  If 0 use PRIMARY KEY */