Update the xfer optimization code so that the xfer optimization can be used

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
This commit is contained in:
drh 2011-11-04 14:36:02 +00:00
parent d28286181d
commit ccdf1baebf
4 changed files with 101 additions and 51 deletions

View File

@ -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

View File

@ -1 +1 @@
5e1d247e5b3b5dcf6763f01002e996786db48152
e3f368cd5ef66a56fd4bd05a77276039e26b9e0e

View 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:
**
** 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);

View 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