diff --git a/manifest b/manifest index 9d5ac1b65e..293e80acf9 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 569c8aea30..9171da14c6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5e1d247e5b3b5dcf6763f01002e996786db48152 \ No newline at end of file +e3f368cd5ef66a56fd4bd05a77276039e26b9e0e \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 72fba06efd..bfcd2c2536 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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: ** - ** 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. + ** (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.) + ** + ** (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); diff --git a/test/insert4.test b/test/insert4.test index 8e316448c1..f4a45c182c 100644 --- a/test/insert4.test +++ b/test/insert4.test @@ -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