]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a problem with single-pass multi-row UPDATE statements that invoke REPLACE
authordan <dan@noemail.net>
Wed, 11 Jan 2017 19:03:08 +0000 (19:03 +0000)
committerdan <dan@noemail.net>
Wed, 11 Jan 2017 19:03:08 +0000 (19:03 +0000)
conflict handling.

FossilOrigin-Name: 0a2b8e1b9dc600b5a93622e8eea6218649df5e0f

manifest
manifest.uuid
src/update.c
test/update2.test

index 519075486569f1414e5bdfa02fa9c17ad5c1b5ff..95612d61fb01414806d81a156f02915ea06b6728 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\spreventing\sUPDATE\sstatements\sthat\suse\sa\srange-scan\son\sthe\sPK\nindex\sof\sa\sWITHOUT\sROWID\stable\sfrom\susing\sa\sone-pass\sstrategy.
-D 2017-01-11T15:42:14.353
+C Fix\sa\sproblem\swith\ssingle-pass\smulti-row\sUPDATE\sstatements\sthat\sinvoke\sREPLACE\nconflict\shandling.
+D 2017-01-11T19:03:08.308
 F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
@@ -451,7 +451,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
 F src/tokenize.c 5c2f516876fc27fbd7753913f032f49eb89e83b5
 F src/treeview.c 4e44ade3bfe59d82005039f72e09333ce2b4162c
 F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
-F src/update.c 117bb080654b8c382b590b369f174ec11cca5bb0
+F src/update.c 4ed0fcccccd6488ee8a3bc02df10979b503f25d7
 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
 F src/util.c a88b0466fddf445ce752226d4698ca3faada620a
 F src/vacuum.c 33c174b28886b2faf26e503b5a49a1c01a9b1c16
@@ -1351,7 +1351,7 @@ F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
 F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
 F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
-F test/update2.test 2b678973f882d11496f998c01b310be4c4a65a70
+F test/update2.test 7f72ef307d58be09a8ef61db23a7ef60e5af019d
 F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568
 F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
 F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
@@ -1544,7 +1544,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 46db23ccd116ce5b9d949f9293be8a2818411b46
-R a3998d49adecb7e21ad8d0444ff44124
+P cab86c90945126c24c40cf2dedd053a8c46d00d6
+R 91b60fc45aced248f038aee86694df0e
 U dan
-Z c5a6bc3f18beb290c2a3c904ee96a0c7
+Z f69bdc261645bb06f02a001c239441ef
index 294d40d2937f584890be6b6e16e0c54cd3e5ea78..c670eb3052c185e34f2a9f05dab4685ce11018dd 100644 (file)
@@ -1 +1 @@
-cab86c90945126c24c40cf2dedd053a8c46d00d6
\ No newline at end of file
+0a2b8e1b9dc600b5a93622e8eea6218649df5e0f
\ No newline at end of file
index acf43034ff631b24927dfa95ada6175d94cb14e4..242d36b739e70687c820cc2b4cbb7d6e79a3d01f 100644 (file)
@@ -135,6 +135,7 @@ void sqlite3Update(
   int addrOpen;          /* Address of OP_OpenEphemeral */
   int iPk;               /* First of nPk cells holding PRIMARY KEY value */
   i16 nPk;               /* Number of components of the PRIMARY KEY */
+  int bReplace = 0;      /* True if REPLACE conflict resolution might happen */
 
   /* Register Allocations */
   int regRowCount = 0;   /* A count of rows changed */
@@ -294,6 +295,11 @@ void sqlite3Update(
         if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
           reg = ++pParse->nMem;
           pParse->nMem += pIdx->nColumn;
+          if( (onError==OE_Replace)
+           || (onError==OE_Default && pIdx->onError==OE_Replace) 
+          ){
+            bReplace = 1;
+          }
           break;
         }
       }
@@ -301,6 +307,11 @@ void sqlite3Update(
     if( reg==0 ) aToOpen[j+1] = 0;
     aRegIdx[j] = reg;
   }
+  if( bReplace ){
+    /* If REPLACE conflict resolution might be invoked, open cursors on all 
+    ** indexes in case they are needed to delete records.  */
+    memset(aToOpen, 1, nIdx+1);
+  }
 
   /* Begin generating code. */
   v = sqlite3GetVdbe(pParse);
@@ -374,9 +385,15 @@ void sqlite3Update(
     sqlite3VdbeSetP4KeyInfo(pParse, pPk);
   }
 
-  /* Begin the database scan. */
+  /* Begin the database scan. 
+  **
+  ** Do not consider a single-pass strategy for a multi-row update if
+  ** there are any triggers or foreign keys to process, or rows may
+  ** be deleted as a result of REPLACE conflict handling. Any of these
+  ** things might disturb a cursor being used to scan through the table
+  ** or index, causing a single-pass approach to malfunction.  */
   flags = WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE;
-  if( pParse->nested==0 && pTrigger==0 && hasFK==0 && chngKey==0 ){
+  if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
     flags |= WHERE_ONEPASS_MULTIROW;
   }
   pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
@@ -436,34 +453,20 @@ void sqlite3Update(
 
   labelBreak = sqlite3VdbeMakeLabel(v);
   if( !isView ){
-    int iAddrOnce = 0;
-    /* 
-    ** Open every index that needs updating.  Note that if any
-    ** index could potentially invoke a REPLACE conflict resolution 
-    ** action, then we need to open all indices because we might need
-    ** to be deleting some records.
-    */
-    if( onError==OE_Replace ){
-      memset(aToOpen, 1, nIdx+1);
-    }else{
-      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-        if( pIdx->onError==OE_Replace ){
-          memset(aToOpen, 1, nIdx+1);
-          break;
-        }
-      }
-    }
+    int addrOnce = 0;
+
+    /* Open every index that needs updating. */
     if( eOnePass!=ONEPASS_OFF ){
       if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
       if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
     }
 
     if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
-      iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+      addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
     }
     sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
                                0, 0);
-    if( iAddrOnce ) sqlite3VdbeJumpHere(v, iAddrOnce);
+    if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
   }
 
   /* Top of the update loop */
@@ -602,7 +605,6 @@ void sqlite3Update(
 
   if( !isView ){
     int addr1 = 0;        /* Address of jump instruction */
-    int bReplace = 0;     /* True if REPLACE conflict resolution might happen */
 
     /* Do constraint checks. */
     assert( regOldRowid>0 );
index 8aafb2438b9f22c0b03e02bf906beafff9b29f99..61ac9792da20957d83f60947500cc9ea24a0982c 100644 (file)
@@ -84,14 +84,20 @@ do_execsql_test 3.1 {
 #-------------------------------------------------------------------------
 #
 foreach {tn sql} {
-  1 { CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c) }
-  2 { CREATE TABLE b1(a INT PRIMARY KEY, b, c) WITHOUT ROWID }
+  1 { 
+    CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c);
+    CREATE TABLE c1(a INTEGER PRIMARY KEY, b, c, d)
+  }
+  2 { 
+    CREATE TABLE b1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
+    CREATE TABLE c1(a INT PRIMARY KEY, b, c, d) WITHOUT ROWID;
+  }
 } {
-  execsql { DROP TABLE IF EXISTS b1 }
+  execsql { DROP TABLE IF EXISTS b1; DROP TABLE IF EXISTS c1; }
   execsql $sql
+
   do_execsql_test 4.$tn.0 {
     CREATE UNIQUE INDEX b1c ON b1(c);
-
     INSERT INTO b1 VALUES(1, 'a', 1);
     INSERT INTO b1 VALUES(2, 'b', 15);
     INSERT INTO b1 VALUES(3, 'c', 3);
@@ -112,7 +118,63 @@ foreach {tn sql} {
     6 f 16
     7 g 17
   }
-}
 
+  do_execsql_test 4.$tn.2 {
+    CREATE INDEX c1d ON c1(d, b);
+    CREATE UNIQUE INDEX c1c ON c1(c, b);
+
+    INSERT INTO c1 VALUES(1, 'a', 1,  1);
+    INSERT INTO c1 VALUES(2, 'a', 15, 2);
+    INSERT INTO c1 VALUES(3, 'a', 3,  3);
+    INSERT INTO c1 VALUES(4, 'a', 4,  4);
+    INSERT INTO c1 VALUES(5, 'a', 5,  5);
+    INSERT INTO c1 VALUES(6, 'a', 6,  6);
+    INSERT INTO c1 VALUES(7, 'a', 7,  7);
+  }
+
+  do_execsql_test 4.$tn.3 {
+    UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7;
+    SELECT * FROM c1 ORDER BY a;
+  } {
+    1 a 1 1
+    3 a 3 3
+    4 a 14 4
+    5 a 15 5
+    6 a 16 6
+    7 a 17 7
+  }
+
+  do_execsql_test 4.$tn.4 { PRAGMA integrity_check } ok
+
+  do_execsql_test 4.$tn.5 {
+    DROP INDEX c1d;
+    DROP INDEX c1c;
+    DELETE FROM c1;
+
+    INSERT INTO c1 VALUES(1, 'a', 1,  1);
+    INSERT INTO c1 VALUES(2, 'a', 15, 2);
+    INSERT INTO c1 VALUES(3, 'a', 3,  3);
+    INSERT INTO c1 VALUES(4, 'a', 4,  4);
+    INSERT INTO c1 VALUES(5, 'a', 5,  5);
+    INSERT INTO c1 VALUES(6, 'a', 6,  6);
+    INSERT INTO c1 VALUES(7, 'a', 7,  7);
+
+    CREATE INDEX c1d ON c1(d);
+    CREATE UNIQUE INDEX c1c ON c1(c);
+  }
+
+  do_execsql_test 4.$tn.6 {
+    UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7;
+    SELECT * FROM c1 ORDER BY a;
+  } {
+    1 a 1 1
+    3 a 3 3
+    4 a 14 4
+    5 a 15 5
+    6 a 16 6
+    7 a 17 7
+  }
+}
 
 finish_test
+