]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When preparing an UPDATE statement, avoid generating VDBE code for those foreign...
authordan <dan@noemail.net>
Thu, 5 Sep 2013 18:40:29 +0000 (18:40 +0000)
committerdan <dan@noemail.net>
Thu, 5 Sep 2013 18:40:29 +0000 (18:40 +0000)
FossilOrigin-Name: e940b5de49baa1d6a4cf859fbbc0e0df86ac5dbf

manifest
manifest.uuid
src/delete.c
src/fkey.c
src/insert.c
src/sqliteInt.h
src/update.c
test/fkey7.test [new file with mode: 0644]
test/permutations.test

index e283ebdf5d758b0ce9ead880f6775d6988e3974e..60cf6a27a283720de5a21d28051a499712bf708a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Rearrange\sthe\sorder\sof\sconditions\sin\san\s"if"\sstatement\sto\sfacilitate\stesting.
-D 2013-09-04T18:14:53.187
+C When\spreparing\san\sUPDATE\sstatement,\savoid\sgenerating\sVDBE\scode\sfor\sthose\sforeign\skey\srelated\sactions\sand\sconstraint\schecks\sthat\smay\sbe\sseen\sto\sbe\sunnecessary\sby\sconsidering\sthe\ssubset\sof\stable\scolumns\spotentially\smodified\sby\sthe\sUPDATE.
+D 2013-09-05T18:40:29.445
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -171,16 +171,16 @@ F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
-F src/delete.c 2317c814866d9aa71fea16b3faf4fdd4d6a49b94
+F src/delete.c 2dc64ca360b7d7da481183ea920a813a0c203c97
 F src/expr.c 4d89bd03a04fcdb5ff71d86b4e0cc7d3230797b8
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c 914a6bbd987d857c41ac9d244efa6641f36faadb
+F src/fkey.c be866cd8c4fa6cae98ba33109578fd1a3311ee5b
 F src/func.c 5b064acd303b3e74f019ab551d423ff6cace4023
 F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c a66bcdc956145369c1a876709f47f69476973e15
+F src/insert.c a271771db927873c850da8928d6ee9fc058fb9fe
 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
@@ -221,7 +221,7 @@ F src/shell.c d920a891ca09b8bd262cced7fb0ab9d723f7a747
 F src/sqlite.h.in ec40aa958a270416fb04b4f72210357bf163d2c5
 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
-F src/sqliteInt.h 600086a5082e2291b0aeeefcfbb546f2bbda67b2
+F src/sqliteInt.h a083fc4effb15c15946a36919793c311bc247b57
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -273,7 +273,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2
 F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e
-F src/update.c 7d9d38e4f341ada7d79035ea969cdefb8b9014d1
+F src/update.c f5182157f5d0d0a97bc5f5e3c9bdba0dfbe08f08
 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
 F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9
 F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8
@@ -462,6 +462,7 @@ F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
 F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
 F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f
 F test/fkey6.test c555f7fc45d842cc84b0d3ff93951ce2b8c25fc8
+F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
 F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51
 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
 F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
@@ -717,7 +718,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
 F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
 F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
-F test/permutations.test 9cd3bba6f1e8658442d0dcfcef6c2e5efd131bdc
+F test/permutations.test 72f4f8881d388163ddbbeec0a6ed812e863ea2e6
 F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
 F test/pragma2.test 224f0381f9411a78ae685cac24c13656a62021b7
 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
@@ -1110,7 +1111,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P f929e9b41f2f69b086be3dd48fe557adbba7ae5c
-R cc5fb62c722092188f8662d393b9b48e
-U drh
-Z 3d3da6aaad5a89de084de232785827d1
+P 8462fb43c275a70db59c4339650225deeadeef00
+R d3b1cfd48ce8f1b63476cb1b87cb1fe9
+U dan
+Z ebec4296d6724df2f506ca7d4f2e487f
index 05fd8fd0c56dbeef7e0c9d1e2e88c6acb0b99f41..1ea65c845ec573c1729e4c74285f151594e99331 100644 (file)
@@ -1 +1 @@
-8462fb43c275a70db59c4339650225deeadeef00
\ No newline at end of file
+e940b5de49baa1d6a4cf859fbbc0e0df86ac5dbf
\ No newline at end of file
index af64afc65cc082bf899e8e6ed8890d3ac3a1287d..50a35cd16517e0f18228762636474d2e2ff4eb3a 100644 (file)
@@ -536,7 +536,7 @@ void sqlite3GenerateRowDelete(
     /* Do FK processing. This call checks that any FK constraints that
     ** refer to this table (i.e. constraints attached to other tables) 
     ** are not violated by deleting this row.  */
-    sqlite3FkCheck(pParse, pTab, iOld, 0);
+    sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
   }
 
   /* Delete the index and table entries. Skip this step if pTab is really
@@ -553,7 +553,7 @@ void sqlite3GenerateRowDelete(
   /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
   ** handle rows (possibly in other tables) that refer via a foreign key
   ** to the row just deleted. */ 
-  sqlite3FkActions(pParse, pTab, 0, iOld);
+  sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
 
   /* Invoke AFTER DELETE trigger programs. */
   sqlite3CodeRowTrigger(pParse, pTrigger, 
index bb59c656f6be009f314bafd7663abe1f050ce0a2..1947c2ee203f2c2e85a268ad3537a4d945cdc02d 100644 (file)
@@ -682,6 +682,70 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
   }
 }
 
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the child table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is 
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** child key for FK constraint *p are modified.
+*/
+static int fkChildIsModified(
+  Table *pTab,                    /* Table being updated */
+  FKey *p,                        /* Foreign key for which pTab is the child */
+  int *aChange,                   /* Array indicating modified columns */
+  int bChngRowid                  /* True if rowid is modified by this update */
+){
+  int i;
+  for(i=0; i<p->nCol; i++){
+    int iChildKey = p->aCol[i].iFrom;
+    if( aChange[iChildKey]>=0 ) return 1;
+    if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
+  }
+  return 0;
+}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the parent table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is 
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** parent key for FK constraint *p are modified.
+*/
+static int fkParentIsModified(
+  Table *pTab, 
+  FKey *p, 
+  int *aChange, 
+  int bChngRowid
+){
+  int i;
+  for(i=0; i<p->nCol; i++){
+    char *zKey = p->aCol[i].zCol;
+    int iKey;
+    for(iKey=0; iKey<pTab->nCol; iKey++){
+      if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
+        Column *pCol = &pTab->aCol[iKey];
+        if( zKey ){
+          if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
+        }else if( pCol->colFlags & COLFLAG_PRIMKEY ){
+          return 1;
+        }
+      }
+    }
+  }
+  return 0;
+}
+
 /*
 ** This function is called when inserting, deleting or updating a row of
 ** table pTab to generate VDBE code to perform foreign key constraint 
@@ -706,7 +770,9 @@ void sqlite3FkCheck(
   Parse *pParse,                  /* Parse context */
   Table *pTab,                    /* Row is being deleted from this table */ 
   int regOld,                     /* Previous row data is stored here */
-  int regNew                      /* New row data is stored here */
+  int regNew,                     /* New row data is stored here */
+  int *aChange,                   /* Array indicating UPDATEd columns (or 0) */
+  int bChngRowid                  /* True if rowid is UPDATEd */
 ){
   sqlite3 *db = pParse->db;       /* Database handle */
   FKey *pFKey;                    /* Used to iterate through FKs */
@@ -734,6 +800,13 @@ void sqlite3FkCheck(
     int i;
     int isIgnore = 0;
 
+    if( aChange 
+     && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
+     && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 
+    ){
+      continue;
+    }
+
     /* 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 
@@ -816,6 +889,10 @@ void sqlite3FkCheck(
     SrcList *pSrc;
     int *aiCol = 0;
 
+    if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
+      continue;
+    }
+
     if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) 
      && !pParse->pToplevel && !pParse->isMultiWrite 
     ){
@@ -889,6 +966,7 @@ u32 sqlite3FkOldmask(
   return mask;
 }
 
+
 /*
 ** This function is called before generating code to update or delete a 
 ** row contained in table pTab. If the operation is a DELETE, then
@@ -918,32 +996,16 @@ int sqlite3FkRequired(
     }else{
       /* This is an UPDATE. Foreign key processing is only required if the
       ** operation modifies one or more child or parent key columns. */
-      int i;
       FKey *p;
 
       /* Check if any child key columns are being modified. */
       for(p=pTab->pFKey; p; p=p->pNextFrom){
-        for(i=0; i<p->nCol; i++){
-          int iChildKey = p->aCol[i].iFrom;
-          if( aChange[iChildKey]>=0 ) return 1;
-          if( iChildKey==pTab->iPKey && chngRowid ) return 1;
-        }
+        if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
       }
 
       /* Check if any parent key columns are being modified. */
       for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
-        for(i=0; i<p->nCol; i++){
-          char *zKey = p->aCol[i].zCol;
-          int iKey;
-          for(iKey=0; iKey<pTab->nCol; iKey++){
-            Column *pCol = &pTab->aCol[iKey];
-            if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey)
-                      : (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){
-              if( aChange[iKey]>=0 ) return 1;
-              if( iKey==pTab->iPKey && chngRowid ) return 1;
-            }
-          }
-        }
+        if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
       }
     }
   }
@@ -1169,7 +1231,9 @@ void sqlite3FkActions(
   Parse *pParse,                  /* Parse context */
   Table *pTab,                    /* Table being updated or deleted from */
   ExprList *pChanges,             /* Change-list for UPDATE, NULL for DELETE */
-  int regOld                      /* Address of array containing old row */
+  int regOld,                     /* Address of array containing old row */
+  int *aChange,                   /* Array indicating UPDATEd columns (or 0) */
+  int bChngRowid                  /* True if rowid is UPDATEd */
 ){
   /* If foreign-key support is enabled, iterate through all FKs that 
   ** refer to table pTab. If there is an action associated with the FK 
@@ -1178,9 +1242,11 @@ void sqlite3FkActions(
   if( pParse->db->flags&SQLITE_ForeignKeys ){
     FKey *pFKey;                  /* Iterator variable */
     for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
-      Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
-      if( pAction ){
-        sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
+      if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
+        Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
+        if( pAct ){
+          sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
+        }
       }
     }
   }
index 1c2cabb938b56a16ffbbc8a821180af5a4173a36..32cfb83fa8c21a584926224f59208be107760499 100644 (file)
@@ -1031,7 +1031,7 @@ void sqlite3Insert(
       sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
           keyColumn>=0, 0, onError, endOfLoop, &isReplace
       );
-      sqlite3FkCheck(pParse, pTab, 0, regIns);
+      sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
       sqlite3CompleteInsertion(
           pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0
       );
index 49264238b849932eba0536ca6466032a578a31d4..d74903fda1608c6112b91641ccb879109dc94f23 100644 (file)
@@ -3209,18 +3209,18 @@ const char *sqlite3JournalModename(int);
 ** provided (enforcement of FK constraints requires the triggers sub-system).
 */
 #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-  void sqlite3FkCheck(Parse*, Table*, int, int);
+  void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
   void sqlite3FkDropTable(Parse*, SrcList *, Table*);
-  void sqlite3FkActions(Parse*, Table*, ExprList*, int);
+  void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
   int sqlite3FkRequired(Parse*, Table*, int*, int);
   u32 sqlite3FkOldmask(Parse*, Table*);
   FKey *sqlite3FkReferences(Table *);
 #else
-  #define sqlite3FkActions(a,b,c,d)
+  #define sqlite3FkActions(a,b,c,d,e,f)
   #define sqlite3FkCheck(a,b,c,d)
   #define sqlite3FkDropTable(a,b,c)
-  #define sqlite3FkOldmask(a,b)      0
-  #define sqlite3FkRequired(a,b,c,d) 0
+  #define sqlite3FkOldmask(a,b)          0
+  #define sqlite3FkRequired(a,b,c,d,e,f) 0
 #endif
 #ifndef SQLITE_OMIT_FOREIGN_KEY
   void sqlite3FkDelete(sqlite3 *, Table*);
index 5077f64e35094587ccba290d117343af0941ccfc..0f760a460933290c8e1ae07869bd63d117dc127b 100644 (file)
@@ -488,7 +488,7 @@ void sqlite3Update(
 
     /* Do FK constraint checks. */
     if( hasFK ){
-      sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
+      sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngRowid);
     }
 
     /* Delete the index entries associated with the current record.  */
@@ -502,7 +502,7 @@ void sqlite3Update(
     sqlite3VdbeJumpHere(v, j1);
 
     if( hasFK ){
-      sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
+      sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngRowid);
     }
   
     /* Insert the new index entries and the new record. */
@@ -512,7 +512,7 @@ void sqlite3Update(
     ** handle rows (possibly in other tables) that refer via a foreign key
     ** to the row just updated. */ 
     if( hasFK ){
-      sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
+      sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngRowid);
     }
   }
 
diff --git a/test/fkey7.test b/test/fkey7.test
new file mode 100644 (file)
index 0000000..c2682ed
--- /dev/null
@@ -0,0 +1,54 @@
+# 2001 September 15
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests for foreign keys.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fkey7
+
+ifcapable {!foreignkey} {
+  finish_test
+  return
+}
+
+do_execsql_test 1.1 {
+  PRAGMA foreign_keys = 1;
+
+  CREATE TABLE s1(a PRIMARY KEY, b);
+  CREATE TABLE par(a, b REFERENCES s1, c UNIQUE, PRIMARY KEY(a));
+
+  CREATE TABLE c1(a, b REFERENCES par);
+  CREATE TABLE c2(a, b REFERENCES par);
+  CREATE TABLE c3(a, b REFERENCES par(c));
+}
+
+proc auth {op tbl args} {
+  if {$op == "SQLITE_READ"} { set ::tbls($tbl) 1 }
+  return "SQLITE_OK"
+}
+db auth auth
+db cache size 0
+proc do_tblsread_test {tn sql tbllist} {
+  array unset ::tbls
+  uplevel [list execsql $sql]
+  uplevel [list do_test $tn {lsort [array names ::tbls]} $tbllist]
+}
+
+do_tblsread_test 1.2 { UPDATE par SET b=? WHERE a=? } {par s1}
+do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
+do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
+do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
+
+
+finish_test
index 47c4665cdd7a27b367bacddf433afb6d570b7eaf..7468b89673bb28116f4221fbf2e17c8ef7f75ac8 100644 (file)
@@ -509,7 +509,7 @@ test_suite "utf16" -description {
 } -files {
     alter.test alter3.test
     analyze.test analyze3.test analyze4.test analyze5.test analyze6.test
-    analyze7.test analyze8.test analyze9.test analyzeA.test
+    analyze7.test analyze8.test analyze9.test analyzeA.test analyzeB.test
     auth.test bind.test blob.test capi2.test capi3.test collate1.test
     collate2.test collate3.test collate4.test collate5.test collate6.test
     conflict.test date.test delete.test expr.test fkey1.test func.test