]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Update the xfer optimization code so that the xfer optimization can be used
authordrh <drh@noemail.net>
Fri, 4 Nov 2011 14:36:02 +0000 (14:36 +0000)
committerdrh <drh@noemail.net>
Fri, 4 Nov 2011 14:36:02 +0000 (14:36 +0000)
with INTEGER PRIMARY KEY ON CONFLICT ... as long as the destination table
is initially empty.  Improvements to the comments on the xfer optimization.
New test cases added.

FossilOrigin-Name: e3f368cd5ef66a56fd4bd05a77276039e26b9e0e

manifest
manifest.uuid
src/insert.c
test/insert4.test

index 9d5ac1b65ecf80c9082754e31797508dac7fcea5..293e80acf9cb915de442d4a123cee0583c8ae4f2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sa\smemcpy()\sin\ssqlite3FileSuffix()\sto\smemmove()\son\sthe\sgrounds\sthat\sthe\ssource\sand\sdestination\smay\soverlap.
-D 2011-11-04T12:05:52.920
+C Update\sthe\sxfer\soptimization\scode\sso\sthat\sthe\sxfer\soptimization\scan\sbe\sused\nwith\sINTEGER\sPRIMARY\sKEY\sON\sCONFLICT\s...\sas\slong\sas\sthe\sdestination\stable\nis\sinitially\sempty.\s\sImprovements\sto\sthe\scomments\son\sthe\sxfer\soptimization.\nNew\stest\scases\sadded.
+D 2011-11-04T14:36:02.150
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -142,7 +142,7 @@ F src/global.c e230227de13601714b29f9363028514aada5ae2f
 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c bfe25a1d333658bd6f79fded6581d8a6962ce272
+F src/insert.c 9794a963911da8157ad0dc39726c9c695b1c4692
 F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
@@ -525,7 +525,7 @@ F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
 F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
 F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
 F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
-F test/insert4.test 03f1644c4f2393e26e6b12d105ce375811178ace
+F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
 F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
 F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
@@ -974,7 +974,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P e6f825748a1d510c8f5529c79656bb5db9216231
-R 68c2a70c630ede591afbf936bbf79f75
-U dan
-Z 793b5014c789d9668df6d06bf87edd80
+P 5e1d247e5b3b5dcf6763f01002e996786db48152
+R 97a1f3cbe9769d6539f852a4f755f4f2
+U drh
+Z c28f9972b18a5ec408f3c191a9ff9538
index 569c8aea30eed9508632f52b4685e194eeb748ea..9171da14c6727a1bd939b4ee71691bc9e0cfd4f3 100644 (file)
@@ -1 +1 @@
-5e1d247e5b3b5dcf6763f01002e996786db48152
\ No newline at end of file
+e3f368cd5ef66a56fd4bd05a77276039e26b9e0e
\ No newline at end of file
index 72fba06efde3838b3e2a743bb65b303b14d02a4c..bfcd2c25361a730880fb5725e7fbc327827b895c 100644 (file)
@@ -1578,31 +1578,25 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
 **
 **     INSERT INTO tab1 SELECT * FROM tab2;
 **
-** This optimization is only attempted if
+** The xfer optimization transfers raw records from tab2 over to tab1.  
+** Columns are not decoded and reassemblied, which greatly improves
+** performance.  Raw index records are transferred in the same way.
 **
-**    (1)  tab1 and tab2 have identical schemas including all the
-**         same indices and constraints
+** The xfer optimization is only attempted if tab1 and tab2 are compatible.
+** There are lots of rules for determining compatibility - see comments
+** embedded in the code for details.
 **
-**    (2)  tab1 and tab2 are different tables
+** This routine returns TRUE if the optimization is guaranteed to be used.
+** Sometimes the xfer optimization will only work if the destination table
+** is empty - a factor that can only be determined at run-time.  In that
+** case, this routine generates code for the xfer optimization but also
+** does a test to see if the destination table is empty and jumps over the
+** xfer optimization code if the test fails.  In that case, this routine
+** returns FALSE so that the caller will know to go ahead and generate
+** an unoptimized transfer.  This routine also returns FALSE if there
+** is no chance that the xfer optimization can be applied.
 **
-**    (3)  There must be no triggers on tab1
-**
-**    (4)  The result set of the SELECT statement is "*"
-**
-**    (5)  The SELECT statement has no WHERE, HAVING, ORDER BY, GROUP BY,
-**         or LIMIT clause.
-**
-**    (6)  The SELECT statement is a simple (not a compound) select that
-**         contains only tab2 in its FROM clause
-**
-** This method for implementing the INSERT transfers raw records from
-** tab2 over to tab1.  The columns are not decoded.  Raw records from
-** the indices of tab2 are transfered to tab1 as well.  In so doing,
-** the resulting tab1 has much less fragmentation.
-**
-** This routine returns TRUE if the optimization is attempted.  If any
-** of the conditions above fail so that the optimization should not
-** be attempted, then this routine returns FALSE.
+** This optimization is particularly useful at making VACUUM run faster.
 */
 static int xferOptimization(
   Parse *pParse,        /* Parser context */
@@ -1642,9 +1636,6 @@ static int xferOptimization(
     if( pDest->iPKey>=0 ) onError = pDest->keyConf;
     if( onError==OE_Default ) onError = OE_Abort;
   }
-  if( onError!=OE_Abort && onError!=OE_Rollback ){
-    return 0;   /* Cannot do OR REPLACE or OR IGNORE or OR FAIL */
-  }
   assert(pSelect->pSrc);   /* allocated even if there is no FROM clause */
   if( pSelect->pSrc->nSrc!=1 ){
     return 0;   /* FROM clause must have exactly one term */
@@ -1749,16 +1740,12 @@ static int xferOptimization(
   }
 #endif
   if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
-    return 0;
+    return 0;  /* xfer opt does not play well with PRAGMA count_changes */
   }
 
-  /* If we get this far, it means either:
-  **
-  **    *   We can always do the transfer if the table contains an
-  **        an integer primary key
-  **
-  **    *   We can conditionally do the transfer if the destination
-  **        table is empty.
+  /* If we get this far, it means that the xfer optimization is at
+  ** least a possibility, though it might only work if the destination
+  ** table (tab1) is initially empty.
   */
 #ifdef SQLITE_TEST
   sqlite3_xferopt_count++;
@@ -1770,16 +1757,23 @@ static int xferOptimization(
   iDest = pParse->nTab++;
   regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
   sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
-  if( (pDest->iPKey<0 && pDest->pIndex!=0) || destHasUniqueIdx ){
-    /* If tables do not have an INTEGER PRIMARY KEY and there
-    ** are indices to be copied and the destination is not empty,
-    ** we have to disallow the transfer optimization because the
-    ** the rowids might change which will mess up indexing.
+  if( (pDest->iPKey<0 && pDest->pIndex!=0)          /* (1) */
+   || destHasUniqueIdx                              /* (2) */
+   || (onError!=OE_Abort && onError!=OE_Rollback)   /* (3) */
+  ){
+    /* In some circumstances, we are able to run the xfer optimization
+    ** only if the destination table is initially empty.  This code makes
+    ** that determination.  Conditions under which the destination must
+    ** be empty:
+    **
+    ** (1) There is no INTEGER PRIMARY KEY but there are indices.
+    **     (If the destination is not initially empty, the rowid fields
+    **     of index entries might need to change.)
+    **
+    ** (2) The destination has a unique index.  (The xfer optimization 
+    **     is unable to test uniqueness.)
     **
-    ** Or if the destination has a UNIQUE index and is not empty,
-    ** we also disallow the transfer optimization because we cannot
-    ** insure that all entries in the union of DEST and SRC will be
-    ** unique.
+    ** (3) onError is something other than OE_Abort and OE_Rollback.
     */
     addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);
     emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
index 8e316448c148fdb7e8eb6ac3bb5a50d1ee4e5f8c..f4a45c182cc5a90f446218890857ef53e1e6303d 100644 (file)
@@ -504,5 +504,61 @@ do_test insert4-8.11 {
   }
 } {1 2} 
 
+do_test insert4-8.21 {
+  execsql {
+    DROP TABLE IF EXISTS t1;
+    DROP TABLE IF EXISTS t2;
+    CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);
+    CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT REPLACE, y);
+    INSERT INTO t2 VALUES(1,3);
+    INSERT INTO t1 SELECT * FROM t2;
+    SELECT * FROM t1;
+  }
+} {1 3}
+do_test insert4-8.22 {
+  execsql {
+    DROP TABLE IF EXISTS t1;
+    DROP TABLE IF EXISTS t2;
+    CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT IGNORE, b);
+    CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT IGNORE, y);
+    INSERT INTO t2 VALUES(1,3);
+    INSERT INTO t1 SELECT * FROM t2;
+    SELECT * FROM t1;
+  }
+} {1 3}
+do_test insert4-8.23 {
+  execsql {
+    DROP TABLE IF EXISTS t1;
+    DROP TABLE IF EXISTS t2;
+    CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ABORT, b);
+    CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ABORT, y);
+    INSERT INTO t2 VALUES(1,3);
+    INSERT INTO t1 SELECT * FROM t2;
+    SELECT * FROM t1;
+  }
+} {1 3}
+do_test insert4-8.24 {
+  execsql {
+    DROP TABLE IF EXISTS t1;
+    DROP TABLE IF EXISTS t2;
+    CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT FAIL, b);
+    CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT FAIL, y);
+    INSERT INTO t2 VALUES(1,3);
+    INSERT INTO t1 SELECT * FROM t2;
+    SELECT * FROM t1;
+  }
+} {1 3}
+do_test insert4-8.25 {
+  execsql {
+    DROP TABLE IF EXISTS t1;
+    DROP TABLE IF EXISTS t2;
+    CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, b);
+    CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, y);
+    INSERT INTO t2 VALUES(1,3);
+    INSERT INTO t1 SELECT * FROM t2;
+    SELECT * FROM t1;
+  }
+} {1 3}
+
 
 finish_test