diff options
author | drh <drh@noemail.net> | 2013-06-26 11:43:18 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2013-06-26 11:43:18 +0000 |
commit | 4580cfb93ea377f5bae4a182cf6d1e15deade81e (patch) | |
tree | e5de67133da8d3d85a535ca836ef279de4bc0445 | |
parent | adbae616bdbbd264514df275958ef566250b524b (diff) | |
parent | 2c036cff3d2fa5e993448ef1aeaf9e1eab388749 (diff) | |
download | sqlite-4580cfb93ea377f5bae4a182cf6d1e15deade81e.tar.gz sqlite-4580cfb93ea377f5bae4a182cf6d1e15deade81e.zip |
Cut over the next generation query planner. Increase the version number
to 3.8.0.
FossilOrigin-Name: 0fe31f60cadc5fe5a9d87e110bfaed5fd026cba1
124 files changed, 3723 insertions, 3009 deletions
@@ -1 +1 @@ -3.7.17 +3.8.0 diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index ccd2345bf..0719fbd5f 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1464,7 +1464,7 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ ** strategy is possible. */ pInfo->idxNum = FTS3_FULLSCAN_SEARCH; - pInfo->estimatedCost = 500000; + pInfo->estimatedCost = 5000000; for(i=0; i<pInfo->nConstraint; i++){ struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; if( pCons->usable==0 ) continue; diff --git a/ext/misc/closure.c b/ext/misc/closure.c index 213b7633e..260a78304 100644 --- a/ext/misc/closure.c +++ b/ext/misc/closure.c @@ -826,11 +826,17 @@ static int closureBestIndex( int iPlan = 0; int i; int idx = 1; + int seenMatch = 0; const struct sqlite3_index_constraint *pConstraint; closure_vtab *pVtab = (closure_vtab*)pTab; + double rCost = 10000000.0; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ + if( pConstraint->iColumn==CLOSURE_COL_ROOT + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + seenMatch = 1; + } if( pConstraint->usable==0 ) continue; if( (iPlan & 1)==0 && pConstraint->iColumn==CLOSURE_COL_ROOT @@ -839,6 +845,7 @@ static int closureBestIndex( iPlan |= 1; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; + rCost /= 100.0; } if( (iPlan & 0x0000f0)==0 && pConstraint->iColumn==CLOSURE_COL_DEPTH @@ -849,6 +856,7 @@ static int closureBestIndex( iPlan |= idx<<4; pIdxInfo->aConstraintUsage[i].argvIndex = ++idx; if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002; + rCost /= 5.0; } if( (iPlan & 0x000f00)==0 && pConstraint->iColumn==CLOSURE_COL_TABLENAME @@ -857,6 +865,7 @@ static int closureBestIndex( iPlan |= idx<<8; pIdxInfo->aConstraintUsage[i].argvIndex = ++idx; pIdxInfo->aConstraintUsage[i].omit = 1; + rCost /= 5.0; } if( (iPlan & 0x00f000)==0 && pConstraint->iColumn==CLOSURE_COL_IDCOLUMN @@ -891,7 +900,8 @@ static int closureBestIndex( ){ pIdxInfo->orderByConsumed = 1; } - pIdxInfo->estimatedCost = (double)10000; + if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30; + pIdxInfo->estimatedCost = rCost; return SQLITE_OK; } diff --git a/ext/misc/fuzzer.c b/ext/misc/fuzzer.c index 642b8f9e9..fe41cda8c 100644 --- a/ext/misc/fuzzer.c +++ b/ext/misc/fuzzer.c @@ -1077,9 +1077,16 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int iDistTerm = -1; int iRulesetTerm = -1; int i; + int seenMatch = 0; const struct sqlite3_index_constraint *pConstraint; + double rCost = 1e12; + pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ + if( pConstraint->iColumn==0 + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ + seenMatch = 1; + } if( pConstraint->usable==0 ) continue; if( (iPlan & 1)==0 && pConstraint->iColumn==0 @@ -1088,6 +1095,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ iPlan |= 1; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; + rCost /= 1e6; } if( (iPlan & 2)==0 && pConstraint->iColumn==1 @@ -1096,6 +1104,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ){ iPlan |= 2; iDistTerm = i; + rCost /= 10.0; } if( (iPlan & 4)==0 && pConstraint->iColumn==2 @@ -1104,6 +1113,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ iPlan |= 4; pIdxInfo->aConstraintUsage[i].omit = 1; iRulesetTerm = i; + rCost /= 10.0; } } if( iPlan & 2 ){ @@ -1122,7 +1132,8 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ){ pIdxInfo->orderByConsumed = 1; } - pIdxInfo->estimatedCost = (double)10000; + if( seenMatch && (iPlan&1)==0 ) rCost = 1e99; + pIdxInfo->estimatedCost = rCost; return SQLITE_OK; } diff --git a/ext/rtree/rtree6.test b/ext/rtree/rtree6.test index 92edc8d10..17985ee52 100644 --- a/ext/rtree/rtree6.test +++ b/ext/rtree/rtree6.test @@ -74,36 +74,36 @@ do_test rtree6-1.5 { do_eqp_test rtree6.2.1 { SELECT * FROM t1,t2 WHERE k=+ii AND x1<10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)} - 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca} + 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_eqp_test rtree6.2.2 { SELECT * FROM t1,t2 WHERE k=ii AND x1<10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)} - 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca} + 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_eqp_test rtree6.2.3 { SELECT * FROM t1,t2 WHERE k=ii } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)} - 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:} + 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_eqp_test rtree6.2.4 { SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10 } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb (~0 rows)} - 0 1 1 {SCAN TABLE t2 (~100000 rows)} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb} + 0 1 1 {SCAN TABLE t2} } do_eqp_test rtree6.2.5 { SELECT * FROM t1,t2 WHERE k=ii AND x1<v } { - 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)} - 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:} + 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_execsql_test rtree6-3.1 { diff --git a/ext/rtree/rtree8.test b/ext/rtree/rtree8.test index bf22cbf48..578a1468b 100644 --- a/ext/rtree/rtree8.test +++ b/ext/rtree/rtree8.test @@ -168,4 +168,3 @@ do_test rtree8-5.3 { finish_test - @@ -593,6 +593,9 @@ soaktest: testfixture$(EXE) sqlite3$(EXE) fulltestonly: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/full.test +queryplantest: testfixture$(EXE) sqlite3$(EXE) + ./testfixture$(EXE) $(TOP)/test/permutations.test queryplanner + test: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test @@ -1,12 +1,12 @@ -C Remove\san\sunused\sfunction\sdeclaration\sfrom\sthe\sFTS3\ssource\scode. -D 2013-06-21T18:36:44.416 +C Cut\sover\sthe\snext\sgeneration\squery\splanner.\s\sIncrease\sthe\sversion\snumber\nto\s3.8.0. +D 2013-06-26T11:43:18.908 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.msc 7d226394826f060f232c0a02a468e8651819b7c2 F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION 05c7bd63b96f31cfdef5c766ed91307ac121f5aa +F VERSION f135b651727f978b7191bd6fa12c7fc1e13e13ac F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 @@ -78,7 +78,7 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c 3cb4afd0accf903fbe66e2cc48d6f44e5ccb8a76 +F ext/fts3/fts3.c 6dbb5c424144465782d5bf0d23be89907c972454 F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3Int.h a50cd231e906da818f00f0a81845bbf7bbeba002 F ext/fts3/fts3_aux.c b02632f6dd0e375ce97870206d914ea6d8df5ccd @@ -107,8 +107,8 @@ F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c 7538f98eab2854cf17fa5f7797bffa6c76e3863b F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/misc/amatch.c eae8454cd9dcb287b2a3ec2e65a865a4ac5f0d06 -F ext/misc/closure.c 40788c54c59190a1f52f6492a260d8894a246fe9 -F ext/misc/fuzzer.c 51bd96960b6b077d41d6f3cedefbcb57f29efaa2 +F ext/misc/closure.c 997c20ddf35f85ab399f4a02a557a9baa822ec32 +F ext/misc/fuzzer.c 136533c53cfce0957f0b48fa11dba27e21c5c01d F ext/misc/ieee754.c 2565ce373d842977efe0922dc50b8a41b3289556 F ext/misc/nextchar.c 1131e2b36116ffc6fe6b2e3464bfdace27978b1e F ext/misc/percentile.c 4fb5e46c4312b0be74e8e497ac18f805f0e3e6c5 @@ -124,9 +124,9 @@ F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e -F ext/rtree/rtree6.test 3ff9113b4a872fa935309e3511cd9b7cdb4d2472 +F ext/rtree/rtree6.test fb94b98c1145b7f44c72635d11492f35349ab27e F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971 -F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d +F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34 F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2 @@ -138,7 +138,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt f2b23a6bde8f1c6e86b957e4d94eab0add520b0d -F main.mk e536751ac719806209c51f5dc63022a5dd40c631 +F main.mk c4335dbdb004d37e00e23d6ff226e55df0db5b21 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -159,19 +159,19 @@ F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168 F src/analyze.c 27e541b9b5b48b41eb899b22a49ff42384899151 F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c b266767351ae2d847716c56fcb2a1fea7c761c03 +F src/backup.c 43b348822db3e4cef48b2ae5a445fbeb6c73a165 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btree.c 7fba377c29573adfc6091832e27ee1fcbefb51d0 F src/btree.h 6fa8a3ff2483d0bb64a9f0105a8cedeac9e00cca F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2 -F src/build.c 1ecf68522356a90471b07178e186277090d0b027 +F src/build.c 42239cfd95533e4aacf4d58b4724c8f858de5ced F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c aeabdabeeeaa0584127f291baa9617153d334778 -F src/expr.c ac9d259eea3123faa05fabe6dd8717696aca72b1 +F src/expr.c 2b47ae9da6c9f34eff6736962ea2e102c6c4a755 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c e16942bd5c8a868ac53287886464a5ed0e72b179 F src/func.c 5c50c1ea31fd864b0fe921fe1a8d4c55acd609ef @@ -191,7 +191,7 @@ F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f -F src/memjournal.c 41a598445c8f249bd9b26ecae700c60dcf34cbf3 +F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785 F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea F src/mutex_noop.c 7682796b7d8d39bf1c138248858efcd10c9e1553 @@ -202,7 +202,7 @@ F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_unix.c 9eafa5458cf2ff684ddccff82c9bb113c7cad847 -F src/os_win.c 5f018dbd4cec25c5b47e11432b946a7d2ccee60b +F src/os_win.c 074cb2b9bca6a1c2bd72acf04666cdc554bfaa9b F src/pager.c 79df56da9dd49aceaa4cac207484a9a82cba40ae F src/pager.h 5cb78b8e1adfd5451e600be7719f5a99d87ac3b1 F src/parse.y 9acfcc83ddbf0cf82f0ed9582ccf0ad6c366ff37 @@ -210,22 +210,22 @@ F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c d23d07716de96c7c0c2503ec5051a4384c3fb938 F src/pragma.c 67a611bd4be0754f27ee13eb87932c3b14415862 -F src/prepare.c 743e484233c51109666d402f470523553b41797c +F src/prepare.c 2306be166bbeddf454e18bf8b21dba8388d05328 F src/printf.c bff529ed47657098c55c9910b9c69b1b3b1a1353 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 89f9003e8316ee3a172795459efc2a0274e1d5a8 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 22ee971346a736ddefdc4497d07c92f2e9978afc +F src/select.c d5a1b9bc3fb451e68ce907df253c6ac17e7310f7 F src/shell.c ab6eea968c8745be3aa74e45fedb37d057b4cd0d F src/sqlite.h.in 5b390ca5d94e09e56e7fee6a51ddde4721b89f8e F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5 -F src/sqliteInt.h 46fb17f604ce941551fe64c342dbeb4dbed3edaa +F src/sqliteInt.h 82a0f3e7c3410cc748c80268e0831dba5ea98cb4 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c b8835978e853a89bf58de88acc943a5ca94d752e -F src/test1.c 6d2a340eea1d866bf7059894491652a69a7ee802 +F src/test1.c 5f9837aee1a0708ab2dbf67ce0b9c18e658cd36b F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df @@ -272,7 +272,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12 F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e -F src/update.c 4c0c6864c4349ba292042e984a56d15985b57f4e +F src/update.c 8e76c3d03e4b7b21cb250bd2df0c05e12993e577 F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 F src/vacuum.c ddf21cc9577c4cb459d08bee9863a78ec000c5bb @@ -289,29 +289,29 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83 F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 -F src/where.c 5c4cbc1e5205d8d534c483fa4495c81513b45dea +F src/where.c 8d6c07d9641bf107e03a2b613550d90b8c7f4a82 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 -F test/all.test 705b516d4df89be56b0f52577df0966e93d2ce72 +F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/analyze.test f8ab7d15858b4093b06caf5e57e2a5ff7104bdae -F test/analyze3.test c3c7f6c3951900c188cf94b2d5ee3246d6b3ff89 -F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045 -F test/analyze5.test 713354664c5ff1853ab2cbcb740f0cf5cb7c802e -F test/analyze6.test aa8dae5066bbed35c5f45a507fb87f2d342f2c99 -F test/analyze7.test bd09e89264c664d8d8d2450b6866dcdfae080a13 -F test/analyze8.test 4ca170de2ba30ccb1af2c0406803db72262f9691 +F test/analyze3.test 69863b446539f8849a996c2aa0b50461c9cecea4 +F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213 +F test/analyze5.test 3e57f192307be931f9ab2f6ff456f9063358ac77 +F test/analyze6.test cdbf9887d40ab41301f570fe85c6e1525dd3b1c9 +F test/analyze7.test 7de3ab66e1981303e783102a012d62cb876bf1d5 +F test/analyze8.test ea4972c76820ac8d6a0754e6f5b851292f0f5a61 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a -F test/async5.test 0dd8701bd588bf6e70c2557a22ae3f22b2567b4c +F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0 F test/atof1.test 9bf1d25180a2e05fc12ce3940cc8003033642f68 F test/attach.test 0d112b7713611fdf0340260192749737135fda5f F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966 @@ -322,20 +322,20 @@ F test/auth.test 4a4c3b034fff7750513520defa910f376c96ab49 F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf -F test/autoindex1.test f88146c4c889ea0afbb620e49d83b5fbf5ee4d06 +F test/autoindex1.test 314f12f87667861ac965c41587f9df82c42fff65 F test/autovacuum.test 9f22a7733f39c56ef6a5665d10145ac25d8cb574 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 F test/backcompat.test ecd841f3a3bfb81518721879cc56a760670e3198 F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62 F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf -F test/backup4.test 4d90389daeb781fe718816a4fc836ad1b06791d8 +F test/backup4.test 2a2e4a64388090b152de753fd9e123f28f6a3bd4 F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135 F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa -F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070 +F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 7c79f1ef0c6c2c2bc1e7bd895596fab32bfb4796 F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 @@ -351,7 +351,7 @@ F test/boundary3.tcl 8901d6a503d0bf64251dd81cc74e5ad3add4b119 F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45 F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983 F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b -F test/btreefault.test f52c593513bda80a506c848325c73c840590884d +F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3 F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0 F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de F test/capi2.test e8b18cc61090b6e5e388f54d6b125d711d1b265a @@ -359,14 +359,14 @@ F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0 F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1 -F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde +F test/capi3e.test ad90088b18b0367125ff2d4b5400153fd2f99aab F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 F test/check.test 2eb93611139a7dfaed3be80067c7dc5ceb5fb287 -F test/close.test e37610d60e9c9b9979a981f3f50071d7dff28616 +F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815 F test/closure01.test dbb28f1ea9eeaf0a53ec5bc0fed352e479def8c7 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 F test/collate1.test b709989e6e6ff6e1d2bd64231c2c1d8146846c9e -F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49 +F test/collate2.test 285cef1b58ce2b3cf074a386f763ce753c81c97f F test/collate3.test 79558a286362cb9ed603c6fa543f1cda7f563f0f F test/collate4.test 031f7265c13308b724ba3c49f41cc04612bd92b1 F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6 @@ -378,6 +378,7 @@ F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 F test/colmeta.test 087c42997754b8c648819832241daf724f813322 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b F test/conflict.test 0b3922d2304a14a47e3ccd61bbd6824327af659b +F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4 F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0 F test/corrupt3.test 889d7cdb811800303aa722d7813fe8a4299cf726 @@ -392,7 +393,7 @@ F test/corruptB.test 20d4a20cbed23958888c3e8995b424a47223d647 F test/corruptC.test 62a767fe64acb1975f58cc6171192839c783edbb F test/corruptD.test 3b09903a2e2fe07ecafe775fea94177f8a4bb34f F test/corruptE.test d3a3d7e864a95978195741744dda4abfd8286018 -F test/corruptF.test 984b1706c9c0e4248141b056c21124612628d12e +F test/corruptF.test 1c7b6f77cf3f237fb7fbb5b61d6c921fd4c7b993 F test/count.test 454e1ce985c94d13efeac405ce54439f49336163 F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62 F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f @@ -414,18 +415,18 @@ F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab -F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66 +F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e -F test/distinct.test 84da1414b2e6887fffd5ed571311b344c5b082ce +F test/distinct.test 44028aaf161a5e80a2f229622b3a174d3b352810 F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 -F test/e_createtable.test d4e17024b1831e7480f5736cf4e02516a5c90927 +F test/e_createtable.test ddf3b2e4506e0813f46b69ccf55757c5570cc181 F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5 F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235 F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894 -F test/e_fkey.test 79a985d95340c6072a884359426ce95cf33e656a +F test/e_fkey.test 17cfb40002d165299681f39aac0cb5890c359935 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe F test/e_reindex.test 5e6dff3a060b5234d496f6e84c3e59a94b4dce4d @@ -439,17 +440,17 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473 F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 -F test/eqp.test 46aa946dd55c90635327898275d3e533d23a9845 +F test/eqp.test 8ef4d89dc44d8988b3ea6e60902905d739508db0 F test/errmsg.test 050717f1c6a5685de9c79f5f9f6b83d7c592f73a F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 -F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb +F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75 F test/exclusive2.test 881193eccec225cfed9d7f744b65e57f26adee25 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30 F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d -F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e +F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7 F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a -F test/filefmt.test dbee33e57818249cdffbbb7b13464635217beff1 +F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146 F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey2.test 06e0b4cc9e1b3271ae2ae6feeb19755468432111 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e @@ -494,7 +495,7 @@ F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654 F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0 -F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0 +F test/fts3aa.test ad272d695095a55c16a5091dbdcce7c5f55da605 F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9 F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63 F test/fts3ad.test e40570cb6f74f059129ad48bcef3d7cbc20dda49 @@ -508,43 +509,43 @@ F test/fts3ak.test bd14deafe9d1586e8e9bf032411026ac4f8c925d F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 -F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9 -F test/fts3atoken.test fb398ab50aa232489e2a17f9b29d7ad3a3885f36 -F test/fts3auto.test 74315a7377403a57ba82a652a33704197fe1e4be -F test/fts3aux1.test 03cec2dea379931c219dd4406817485caa69d1d8 +F test/fts3ao.test 71b0675e3df5c512a5a03daaa95ee1916de23dda +F test/fts3atoken.test fca30fd86db9241d571c637751e9a8a2f50f1451 +F test/fts3auto.test b981fea19b132b4e6878f50d7c1f369b28f68eb9 +F test/fts3aux1.test f8f287a4a73f381f8fa15b6a70f36245f903d221 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c F test/fts3conf.test ee8500c86dd58ec075e8831a1e216a79989436de -F test/fts3corrupt.test 7b0f91780ca36118d73324ec803187208ad33b32 +F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 F test/fts3d.test bf640d79722b720fa1c81834c48cdaa45d531b1a F test/fts3defer.test 0be4440b73a2e651fc1e472066686d6ada4b9963 -F test/fts3defer2.test 83f8744407b7663e36716a9066302d53d49ddf8b +F test/fts3defer2.test a3b6cbeabaf28c9398652a4d101ea224d9358479 F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297 F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a -F test/fts3expr3.test 1bfb762b53a794f990f3dffaae8bbea5736422f7 +F test/fts3expr3.test 9e91b8edbcb197bf2e92161aa7696446d96dce5f F test/fts3fault.test cb72dccb0a3b9f730f16c5240f3fcb9303eb1660 F test/fts3fault2.test 3198eef2804deea7cac8403e771d9cbcb752d887 F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641 -F test/fts3malloc.test 1e3df7598534e77a7072aad610d46554cface3e9 -F test/fts3matchinfo.test ecb08f586d027eb03941bcfcded6cb9d8ccb3a66 +F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6 +F test/fts3matchinfo.test 3f297e14e3f0d5be8595246f5fcd426625cc5881 F test/fts3near.test 12895557870b0f9af7cc0be81a0171abb2d12f12 F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1 -F test/fts3prefix2.test 477ca96e67f60745b7ac931cfa6e9b080c562da5 -F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2 +F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce +F test/fts3query.test 4fefd43ff24993bc2c9b2778f2bec0cc7629e7ed F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0 -F test/fts3shared.test c2f60e152e8554549eb25f0a7593ea01389c5037 -F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2 -F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659 -F test/fts3tok1.test 4d9e7401679dc71f6b2f76416309b923210bfdbe -F test/fts3tok_err.test 41e5413139c2ef536ffadfcd1584ee50b1665ec0 +F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e +F test/fts3snippet.test 24d6ff1920a70fd970c401a8525834b4ad12cece +F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca +F test/fts3tok1.test b10d0a12a0ab5f905cea1200b745de233f37443f +F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d F test/fts4aa.test 95f448fb02c4a976968b08d1b4ce134e720946ae F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8 -F test/fts4content.test 6efc53b4fd03cab167e6998d2b0b7d4b7d419ee6 +F test/fts4content.test 2e7252557d6d24afa101d9ba1de710d6140e6d06 F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7 F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891 @@ -562,7 +563,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzer1.test 41bd5aa6ae0cf18d06342a4476e3cad98604ae48 +F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36 F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536 F test/hook.test 45cb22b940c3cc0af616ba7430f666e245711a48 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 @@ -573,20 +574,20 @@ F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617 F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3 F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328 F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19 -F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7 -F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8 +F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4 +F test/incrblob4.test f26502a5697893e5acea268c910f16478c2f0fab F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597 -F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 +F test/incrblobfault.test 280474078f6da9e732cd2a215d3d854969014b6e F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32 F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b -F test/incrvacuum3.test 2ffa9e4a23f072bd7902b9ae6471f8822a6522a7 +F test/incrvacuum3.test 75256fb1377e7c39ef2de62bfc42bbff67be295a F test/incrvacuum_ioerr.test 6ae2f783424e47a0033304808fe27789cf93e635 F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097 F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7 F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026 F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33 -F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad +F test/indexedby.test 0e959308707c808515c3a51363f7a9835027108c F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2 @@ -597,14 +598,14 @@ F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6 F test/instr.test a34e1d46a9eefb098a7167ef0e730a4a3d82fba0 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc -F test/intpkey.test 7af30f6ae852d8d1c2b70e4bf1551946742e92d8 -F test/io.test ecf44cc81664ad54d8253e2d88fc705b6554abe3 +F test/intpkey.test a9674fc6195e0952e4e6105a9981ce1e48e7f215 +F test/io.test 3a7abcef18727cc0f2399e04b0e8903eccae50f8 F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8 F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 -F test/ioerr6.test cf25523b921d1a6a0e5b536cd4acb3c3d979ea52 +F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b F test/join.test 8d63cc4d230a7affafa4b6ab0b97c49b8ccb365c F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 @@ -620,7 +621,7 @@ F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05 F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 -F test/like.test 0e5412f4dac4a849f613e1ef8b529d56a6e31d08 +F test/like.test 935fb4f608e3ea126891496a6e99b9468372bf5c F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da F test/limit.test cc0ab63385239b63c72452b0e93700bf5e8f0b99 F test/loadext.test 92e6dfefd1229c3ef4aaabd87419efd8fa57a7a5 @@ -631,7 +632,7 @@ F test/lock3.test f271375930711ae044080f4fe6d6eda930870d00 F test/lock4.test e175ae13865bc87680607563bafba21f31a26f12 F test/lock5.test 5ad6a1f536036ff1be915cfdd41481aeafda3273 F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5 -F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64 +F test/lock7.test 49f1eaff1cdc491cc5dee3669f3c671d9f172431 F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95 F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2 F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8 @@ -673,7 +674,7 @@ F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738 F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 -F test/misc7.test dd82ec9250b89178b96cd28b2aca70639d21e5b3 +F test/misc7.test 50c02c35ef7924c246eb3d8d71dfbf90ba352f8f F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054 F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022 @@ -686,28 +687,29 @@ F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c -F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a +F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934 F test/notnull.test 2afad748d18fd66d01f66463de73b3e2501fb226 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 -F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae +F test/orderby1.test 9b524aff9147288da43a6d7ddfdcff47fa2303c6 F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99 F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4 +F test/orderby5.test 02eca502a0f97c77ce383b0dfa17e99c6a107b8d F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 -F test/pager1.test 30e63afd425fea12285e9ec5fa1fd000808031f1 +F test/pager1.test 16b649c8f0b38d446acbcff8a7bf055a9be43276 F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f -F test/pagerfault.test 8483e65d33d5636e5b7656204bb274d826e728d9 -F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401 -F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7 +F test/pagerfault.test 7285379906ab2f1108b8e82bbdf2d386cc8ff3ff +F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f +F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8 F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6 F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/percentile.test 4614301e38398df7fdd5f28f4ed8f272b328251b -F test/permutations.test 7161be2569550924a8a437272acabfe9e6f48b86 +F test/permutations.test 461ef4ea10db02cd421dfe5f988eac3e99b5cd9a F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178 F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 @@ -743,7 +745,7 @@ F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38 F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5 F test/schema5.test 0103e4c0313b3725b5ae5600bdca53006ab53db3 F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c -F test/securedel2.test f13a916155f790a6b9de835049641b14ef312986 +F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5 F test/select1.test deba017eed9daa5af33de868676c997e7eebb931 F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56 F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054 @@ -765,11 +767,11 @@ F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257 F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9 F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e -F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d +F test/shared8.test 00a07bf5e1337ecf72e94542bdefdc330d7a2538 F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21 F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5 F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa -F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf +F test/sharedlock.test 927a4b6da11978c82b857dbdb20a932aad732123 F test/shell1.test 4a2f57952719972c6f862134463f8712e953c038 F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59 @@ -793,7 +795,7 @@ F test/spellfix.test bea537caf587df30d430c2c6a8fe9f64b8712834 F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test be8d477306006ec696bc86757cfb34bec79447ce F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 -F test/subquery.test 869562de9e8c5d8147e0451a2ce5b58cf55ce389 +F test/subquery.test 3b97763ada8b3a4092d2c80db3ebc0e7870e7eaf F test/subquery2.test 91e1e364072aeff431d1f9689b15147e421d88c7 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a @@ -807,7 +809,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d -F test/tester.tcl 3b08771e6d601612fe62d13787db0e50aac4cf7b +F test/tester.tcl 63b24679c75a952c51f924de2802b2b57cddd22d F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -821,16 +823,16 @@ F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 -F test/tkt-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6 +F test/tkt-2a5629202f.test 0521bd25658428baa26665aa53ffed9367d33af2 F test/tkt-2d1a5c67d.test d371279946622698ab393ff88cad9f5f6d82960b F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 F test/tkt-31338dca7e.test 6fb8807851964da0d24e942f2e19c7c705b9fb58 F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac -F test/tkt-385a5b56b9.test 7782a382912a51f09f1d1a1442bca1e75f9c549b +F test/tkt-385a5b56b9.test c0a06ada41d7f06b1686da0e718553f853771d1e F test/tkt-38cb5df375.test f3cc8671f1eb604d4ae9cf886ed4366bec656678 F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7 -F test/tkt-3a77c9714e.test 32bb28afa8c63fc76e972e996193139b63551ed9 -F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00 +F test/tkt-3a77c9714e.test b08bca26de1140bdf004a37716582a43d7bd8be8 +F test/tkt-3fe897352e.test 27e26eb0f1811aeba4d65aba43a4c52e99da5e70 F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e F test/tkt-4dd95f6943.test 3d0ce415d2ee15d3d564121960016b9c7be79407 F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894 @@ -839,9 +841,9 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84 F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f F test/tkt-6bfb98dfc0.test 24780633627b5cfc0635a5500c2389ebfb563336 F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf -F test/tkt-78e04e52ea.test 703e0bfb23d543edf0426a97e3bbd0ca346508ec -F test/tkt-7a31705a7e6.test 5a7889fdb095ffbe1622413e0145de1637d421bd -F test/tkt-7bbfb7d442.test dfa5c8097a8c353ae40705d6cddeb1f99c18b81a +F test/tkt-78e04e52ea.test 787b70cfb0488c356266bb8d5ad8a657f9efceb8 +F test/tkt-7a31705a7e6.test e75a2bba4eec801b92c8040eb22096ac6d35e844 +F test/tkt-7bbfb7d442.test 7b2cd79c7a17ae6750e75ec1a7846712a69c9d18 F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8 F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7 F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c @@ -854,15 +856,15 @@ F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898 F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d -F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408 +F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447 F test/tkt-cbd054fa6b.test 9c27ed07b333eed458e5d4543f91ecdcf05aeb19 -F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7 +F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09 F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30 -F test/tkt-f3e5abed55.test 669bb076f2ac573c7398ce00f40cd0ca502043a9 +F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00 F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2 F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 -F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4 +F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035 F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4 F test/tkt-fc7bd6358f.test 634bb4af7d661e82d6b61b80c86727bad698e08f @@ -914,7 +916,7 @@ F test/tkt3346.test 6f67c3ed7db94dfc5df4f5f0b63809a1f611e01a F test/tkt3357.test 77c37c6482b526fe89941ce951c22d011f5922ed F test/tkt3419.test 1bbf36d7ea03b638c15804251287c2391f5c1f6b F test/tkt3424.test 61f831bd2b071bd128fa5d00fbda57e656ca5812 -F test/tkt3442.test 0adb70e9fe9cb750a702065a68ad647409dbc158 +F test/tkt3442.test 53840ec5325bb94544792aad4c20476f81dc26b1 F test/tkt3457.test 44e980fe5334753dcc27b94fa4deabc485a92f74 F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19 F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218 @@ -942,9 +944,9 @@ F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90 F test/tkt3911.test 74cd324f3ba653040cc6d94cc4857b290d12d633 -F test/tkt3918.test e6cdf6bfcfe9ba939d86a4238a9dc55d6eec5d42 +F test/tkt3918.test ea78bf164e4d55cbde0d83c671ef6fbe930a0032 F test/tkt3922.test f26be40ab4fe6c00795629bd2006d96e270d9b1a -F test/tkt3929.test 75a862e45bcb39e9a7944c89b92afa531304afca +F test/tkt3929.test cdf67acf5aa936ec4ffead81db87f8a71fe40e59 F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767 F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd @@ -975,7 +977,7 @@ F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a F test/unique.test cadb172bbd5a2e83cd644d186ccd602085e54edc F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46 -F test/unordered.test 93dce7b6c97a817a4fe26980c484605a4511f614 +F test/unordered.test ef85ac8f2f3c93ed2b9e811b684de73175fc464c F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 F test/uri.test 63e03df051620a18f794b4f4adcdefb3c23b6751 F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae @@ -984,14 +986,14 @@ F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324 F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 -F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b +F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/view.test 4057630287bfa5955628fe90a13d4c225d1c7352 -F test/vtab1.test 4403f987860ebddef1ce2de6db7216421035339d +F test/vtab1.test 1550abf90bc2b996f8c34e0be3fdb251af54fa41 F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391 -F test/vtab6.test c7f290d172609d636fbfc58166eadcb55d5c117c +F test/vtab6.test 5f5380c425e52993560ab4763db4f826d2ba7b09 F test/vtab7.test ae560ebea870ed04e9aa4177cc302f910faaabb5 F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583 F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b @@ -1011,40 +1013,40 @@ F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c F test/wal5.test 8f888b50f66b78821e61ed0e233ded5de378224b F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3 F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd -F test/wal8.test b3ee739fe8f7586aaebdc2367f477ebcf3e3b034 +F test/wal8.test 75c42e1bc4545c277fed212f8fc9b7723cd02216 F test/wal9.test 378e76a9ad09cd9bee06c172ad3547b0129a6750 F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877 F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434 -F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde -F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2 -F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142 -F test/walcrash3.test 595e44c6197f0d0aa509fc135be2fd0209d11a2c +F test/walcksum.test 9afeb96240296c08c72fc524d199c912cfe34daa +F test/walcrash.test 451d79e528add5c42764cea74aa2750754171b25 +F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36 +F test/walcrash3.test e426aa58122d20f2b9fbe9a507f9eb8cab85b8af F test/walfault.test 54ad6e849c727f4da463964b9eb8c8e8e155cf82 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496 F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6 -F test/walro.test a31deb621033442a76c3a61e44929250d06f81b1 -F test/walshared.test 6dda2293880c300baf5d791c307f653094585761 +F test/walro.test 6cc247a0cc9b36aeea2057dd28a922a1cdfbd630 +F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e -F test/where.test 15ac8611c9439a2c5f4a6ac10cfe4c1ec9854c24 -F test/where2.test 399b3178289925a0aa976b3d60ef139740540ecd -F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006 +F test/where.test da54153a4c1571ea1b95659e5bec8119edf786aa +F test/where2.test dcf0ffafe0de55051c1373835a5a57aee6b50094 +F test/where3.test 157071521ceabc06bfd4d37106e4270a8956364d F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b -F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a -F test/where8.test d6a283eb7348a8967d44e2a753f117ab0d21d4f3 +F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8 +F test/where8.test f6b9559723564a042927ee0f22003ac9bed71b21 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test 1b4387c6eacc9a32b28b4d837c27f857c785d0d8 +F test/where9.test 9a7fda4a4512abc26a855e8b2b6572b200f6019b F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 -F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5 -F test/whereD.test 3f3ee93825c94804f1fc91eef2de0d365981759a -F test/whereE.test 7bd34945797efef15819368479bacc34215e4e1d -F test/whereF.test a0e296643cabe5278379bc1a0aa158cf3c54a1c9 +F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a +F test/whereD.test 6c2feb79ef1f68381b07f39017fe5f9b96da8d62 +F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f +F test/whereF.test 136a7301512d72a08a272806c8767066311b7bc1 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 @@ -1093,8 +1095,9 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 +F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 361084e1eb281e985568d19fe217263be92be31d -R 0d6c636b8cfd628682ea33e027cb07be +P 096ae1d8f9a08f92daedece6b0615f4d22b05023 19ab4811d542ba781aeb6a4eb3c74642677225e1 +R f73808040fac6ead247f9d2a50c342d8 U drh -Z 55b5720fc386809651d148c0a8415781 +Z e52529fd895183a80c4903c58cf391c1 diff --git a/manifest.uuid b/manifest.uuid index 773d5a6db..de60777b8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -096ae1d8f9a08f92daedece6b0615f4d22b05023
\ No newline at end of file +0fe31f60cadc5fe5a9d87e110bfaed5fd026cba1
\ No newline at end of file diff --git a/src/backup.c b/src/backup.c index 252f61cfc..6ef9374ec 100644 --- a/src/backup.c +++ b/src/backup.c @@ -15,12 +15,6 @@ #include "sqliteInt.h" #include "btreeInt.h" -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - /* ** Structure allocated for each backup operation. */ diff --git a/src/build.c b/src/build.c index 067b72f7d..0a3922c95 100644 --- a/src/build.c +++ b/src/build.c @@ -2696,6 +2696,7 @@ Index *sqlite3CreateIndex( pIndex->pTable = pTab; pIndex->nColumn = pList->nExpr; pIndex->onError = (u8)onError; + pIndex->uniqNotNull = onError==OE_Abort; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); @@ -2754,6 +2755,7 @@ Index *sqlite3CreateIndex( pIndex->azColl[i] = zColl; requestedSortOrder = pListItem->sortOrder & sortOrderMask; pIndex->aSortOrder[i] = (u8)requestedSortOrder; + if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0; } sqlite3DefaultRowEst(pIndex); @@ -3185,7 +3187,7 @@ SrcList *sqlite3SrcListEnlarge( } pSrc = pNew; nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1; - pSrc->nAlloc = (u16)nGot; + pSrc->nAlloc = (u8)nGot; } /* Move existing slots that come after the newly inserted slots @@ -3193,7 +3195,7 @@ SrcList *sqlite3SrcListEnlarge( for(i=pSrc->nSrc-1; i>=iStart; i--){ pSrc->a[i+nExtra] = pSrc->a[i]; } - pSrc->nSrc += (i16)nExtra; + pSrc->nSrc += (i8)nExtra; /* Zero the newly allocated slots */ memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra); diff --git a/src/expr.c b/src/expr.c index c1a27ebf8..2c0419aa2 100644 --- a/src/expr.c +++ b/src/expr.c @@ -920,6 +920,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){ pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); pItem->sortOrder = pOldItem->sortOrder; pItem->done = 0; + pItem->bSpanIsTab = pOldItem->bSpanIsTab; pItem->iOrderByCol = pOldItem->iOrderByCol; pItem->iAlias = pOldItem->iAlias; } @@ -1596,15 +1597,15 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ /* Could not found an existing table or index to use as the RHS b-tree. ** We will have to generate an ephemeral table to do the job. */ - double savedNQueryLoop = pParse->nQueryLoop; + u32 savedNQueryLoop = pParse->nQueryLoop; int rMayHaveNull = 0; eType = IN_INDEX_EPH; if( prNotFound ){ *prNotFound = rMayHaveNull = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); }else{ - testcase( pParse->nQueryLoop>(double)1 ); - pParse->nQueryLoop = (double)1; + testcase( pParse->nQueryLoop>0 ); + pParse->nQueryLoop = 0; if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){ eType = IN_INDEX_ROWID; } diff --git a/src/memjournal.c b/src/memjournal.c index 05725948f..65ed378b3 100644 --- a/src/memjournal.c +++ b/src/memjournal.c @@ -31,12 +31,6 @@ typedef struct FileChunk FileChunk; */ #define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*))) -/* Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - /* ** The rollback journal is composed of a linked list of these structures. */ diff --git a/src/os_win.c b/src/os_win.c index 07b74cbe2..bdf025aa4 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -85,13 +85,6 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); #endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */ /* -** Macro to find the minimum of two numeric values. -*/ -#ifndef MIN -# define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -/* ** Some Microsoft compilers lack this definition. */ #ifndef INVALID_FILE_ATTRIBUTES diff --git a/src/prepare.c b/src/prepare.c index d78d83cbd..28145aa4e 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -592,7 +592,7 @@ static int sqlite3Prepare( sqlite3VtabUnlockList(db); pParse->db = db; - pParse->nQueryLoop = (double)1; + pParse->nQueryLoop = 0; /* Logarithmic, so 0 really means 1 */ if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -614,7 +614,7 @@ static int sqlite3Prepare( }else{ sqlite3RunParser(pParse, zSql, &zErrMsg); } - assert( 1==(int)pParse->nQueryLoop ); + assert( 0==pParse->nQueryLoop ); if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; diff --git a/src/select.c b/src/select.c index 9705c1905..40b7ec86f 100644 --- a/src/select.c +++ b/src/select.c @@ -1538,8 +1538,8 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); - }else{ - if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n; + }else if( n>=0 && p->nSelectRow>(u64)n ){ + p->nSelectRow = n; } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); @@ -1733,9 +1733,9 @@ static int multiSelect( p->nSelectRow += pPrior->nSelectRow; if( pPrior->pLimit && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit) - && p->nSelectRow > (double)nLimit + && nLimit>0 && p->nSelectRow > (u64)nLimit ){ - p->nSelectRow = (double)nLimit; + p->nSelectRow = nLimit; } if( addr ){ sqlite3VdbeJumpHere(v, addr); @@ -3884,11 +3884,10 @@ static void explainSimpleCount( Index *pIdx /* Index used to optimize scan, or NULL */ ){ if( pParse->explain==2 ){ - char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", + char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s", pTab->zName, - pIdx ? "USING COVERING INDEX " : "", - pIdx ? pIdx->zName : "", - pTab->nRowEst + pIdx ? " USING COVERING INDEX " : "", + pIdx ? pIdx->zName : "" ); sqlite3VdbeAddOp4( pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC @@ -4239,7 +4238,7 @@ int sqlite3Select( /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); - p->nSelectRow = (double)LARGEST_INT64; + p->nSelectRow = LARGEST_INT64; computeLimitRegisters(pParse, p, iEnd); if( p->iLimit==0 && addrSortIndex>=0 ){ sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen; @@ -4267,9 +4266,13 @@ int sqlite3Select( /* Begin the database scan. */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, pDist, 0,0); if( pWInfo==0 ) goto select_end; - if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut; - if( pWInfo->eDistinct ) sDistinct.eTnctType = pWInfo->eDistinct; - if( pOrderBy && pWInfo->nOBSat==pOrderBy->nExpr ) pOrderBy = 0; + if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){ + p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo); + } + if( sqlite3WhereIsDistinct(pWInfo) ){ + sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo); + } + if( pOrderBy && sqlite3WhereIsOrdered(pWInfo) ) pOrderBy = 0; /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral @@ -4282,7 +4285,8 @@ int sqlite3Select( /* Use the standard inner loop. */ selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest, - pWInfo->iContinue, pWInfo->iBreak); + sqlite3WhereContinueLabel(pWInfo), + sqlite3WhereBreakLabel(pWInfo)); /* End the database scan loop. */ @@ -4315,9 +4319,9 @@ int sqlite3Select( for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } - if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100; + if( p->nSelectRow>100 ) p->nSelectRow = 100; }else{ - p->nSelectRow = (double)1; + p->nSelectRow = 1; } @@ -4397,9 +4401,10 @@ int sqlite3Select( ** in the right order to begin with. */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); - pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, 0, 0); + pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, + WHERE_GROUPBY, 0); if( pWInfo==0 ) goto select_end; - if( pWInfo->nOBSat==pGroupBy->nExpr ){ + if( sqlite3WhereIsOrdered(pWInfo) ){ /* The optimizer is able to deliver rows in group by order so ** we do not have to sort. The OP_OpenEphemeral table will be ** cancelled later because we still need to use the pKeyInfo @@ -4680,8 +4685,8 @@ int sqlite3Select( } updateAccumulator(pParse, &sAggInfo); assert( pMinMax==0 || pMinMax->nExpr==1 ); - if( pWInfo->nOBSat>0 ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak); + if( sqlite3WhereIsOrdered(pWInfo) ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo)); VdbeComment((v, "%s() by index", (flag==WHERE_ORDERBY_MIN?"min":"max"))); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4482ed69a..b2cab7a7a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -396,6 +396,12 @@ #endif /* +** Macros to compute minimum and maximum of two numbers. +*/ +#define MIN(A,B) ((A)<(B)?(A):(B)) +#define MAX(A,B) ((A)>(B)?(A):(B)) + +/* ** Check to see if this machine uses EBCDIC. (Yes, believe it or ** not, there are still machines out there that use EBCDIC.) */ @@ -720,9 +726,7 @@ typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; -typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; -typedef struct WhereLevel WhereLevel; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and @@ -1545,6 +1549,7 @@ struct Index { u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ + unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ #ifdef SQLITE_ENABLE_STAT3 int nSample; /* Number of elements in aSample[] */ tRowcnt avgEq; /* Average nEq value for key values not in aSample */ @@ -1891,6 +1896,11 @@ typedef u64 Bitmask; #define BMS ((int)(sizeof(Bitmask)*8)) /* +** A bit in a Bitmask +*/ +#define MASKBIT(n) (((Bitmask)1)<<(n)) + +/* ** The following structure describes the FROM clause of a SELECT statement. ** Each table or subquery in the FROM clause is a separate element of ** the SrcList.a[] array. @@ -1910,8 +1920,8 @@ typedef u64 Bitmask; ** contains more than 63 columns and the 64-th or later column is used. */ struct SrcList { - i16 nSrc; /* Number of tables or subqueries in the FROM clause */ - i16 nAlloc; /* Number of entries allocated in a[] below */ + u8 nSrc; /* Number of tables or subqueries in the FROM clause */ + u8 nAlloc; /* Number of entries allocated in a[] below */ struct SrcList_item { Schema *pSchema; /* Schema to which this item is fixed */ char *zDatabase; /* Name of database holding this table */ @@ -1950,79 +1960,6 @@ struct SrcList { /* -** A WherePlan object holds information that describes a lookup -** strategy. -** -** This object is intended to be opaque outside of the where.c module. -** It is included here only so that that compiler will know how big it -** is. None of the fields in this object should be used outside of -** the where.c module. -** -** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true. -** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx -** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the -** case that more than one of these conditions is true. -*/ -struct WherePlan { - u32 wsFlags; /* WHERE_* flags that describe the strategy */ - u16 nEq; /* Number of == constraints */ - u16 nOBSat; /* Number of ORDER BY terms satisfied */ - double nRow; /* Estimated number of rows (for EQP) */ - union { - Index *pIdx; /* Index when WHERE_INDEXED is true */ - struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ - sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ - } u; -}; - -/* -** For each nested loop in a WHERE clause implementation, the WhereInfo -** structure contains a single instance of this structure. This structure -** is intended to be private to the where.c module and should not be -** access or modified by other modules. -** -** The pIdxInfo field is used to help pick the best index on a -** virtual table. The pIdxInfo pointer contains indexing -** information for the i-th table in the FROM clause before reordering. -** All the pIdxInfo pointers are freed by whereInfoFree() in where.c. -** All other information in the i-th WhereLevel object for the i-th table -** after FROM clause ordering. -*/ -struct WhereLevel { - WherePlan plan; /* query plan for this element of the FROM clause */ - int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */ - int iTabCur; /* The VDBE cursor used to access the table */ - int iIdxCur; /* The VDBE cursor used to access pIdx */ - int addrBrk; /* Jump here to break out of the loop */ - int addrNxt; /* Jump here to start the next IN combination */ - int addrCont; /* Jump here to continue with the next loop cycle */ - int addrFirst; /* First instruction of interior of the loop */ - u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ - int p1, p2; /* Operands of the opcode used to ends the loop */ - union { /* Information that depends on plan.wsFlags */ - struct { - int nIn; /* Number of entries in aInLoop[] */ - struct InLoop { - int iCur; /* The VDBE cursor used by this IN operator */ - int addrInTop; /* Top of the IN loop */ - u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ - } *aInLoop; /* Information about each nested IN operator */ - } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */ - Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ - } u; - double rOptCost; /* "Optimal" cost for this level */ - - /* The following field is really not part of the current level. But - ** we need a place to cache virtual table index information for each - ** virtual table in the FROM clause and the WhereLevel structure is - ** a convenient place since there is one WhereLevel for each FROM clause - ** element. - */ - sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */ -}; - -/* ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin() ** and the WhereInfo.wctrlFlags member. */ @@ -2035,33 +1972,11 @@ struct WhereLevel { #define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ #define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ #define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */ +#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ +#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ -/* -** The WHERE clause processing routine has two halves. The -** first part does the start of the WHERE loop and the second -** half does the tail of the WHERE loop. An instance of -** this structure is returned by the first half and passed -** into the second half to give some continuity. -*/ -struct WhereInfo { - Parse *pParse; /* Parsing and code generating context */ - SrcList *pTabList; /* List of tables in the join */ - u16 nOBSat; /* Number of ORDER BY terms satisfied by indices */ - u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ - u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */ - u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ - u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */ - int iTop; /* The very beginning of the WHERE loop */ - int iContinue; /* Jump here to continue with next record */ - int iBreak; /* Jump here to break out of the loop */ - int nLevel; /* Number of nested loop */ - struct WhereClause *pWC; /* Decomposition of the WHERE clause */ - double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ - double nRowOut; /* Estimated number of output rows */ - WhereLevel a[1]; /* Information about each nest loop in WHERE */ -}; - -/* Allowed values for WhereInfo.eDistinct and DistinctCtx.eTnctType */ +/* Allowed return values from sqlite3WhereIsDistinct() +*/ #define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */ #define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */ #define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */ @@ -2135,7 +2050,7 @@ struct Select { u16 selFlags; /* Various SF_* values */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ - double nSelectRow; /* Estimated number of result rows */ + u64 nSelectRow; /* Estimated number of result rows */ SrcList *pSrc; /* The FROM clause */ Expr *pWhere; /* The WHERE clause */ ExprList *pGroupBy; /* The GROUP BY clause */ @@ -2319,7 +2234,7 @@ struct Parse { /* Information used while coding trigger programs. */ Parse *pToplevel; /* Parse structure for main program (or NULL) */ Table *pTriggerTab; /* Table triggers are being coded for */ - double nQueryLoop; /* Estimated number of iterations of a query */ + u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ @@ -2889,6 +2804,12 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int); void sqlite3WhereEnd(WhereInfo*); +u64 sqlite3WhereOutputRowCount(WhereInfo*); +int sqlite3WhereIsDistinct(WhereInfo*); +int sqlite3WhereIsOrdered(WhereInfo*); +int sqlite3WhereContinueLabel(WhereInfo*); +int sqlite3WhereBreakLabel(WhereInfo*); +int sqlite3WhereOkOnePass(WhereInfo*); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); diff --git a/src/test1.c b/src/test1.c index bd6d4a4cf..8edaedab8 100644 --- a/src/test1.c +++ b/src/test1.c @@ -6302,8 +6302,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3WalTrace; #endif #ifdef SQLITE_TEST - extern char sqlite3_query_plan[]; - static char *query_plan = sqlite3_query_plan; #ifdef SQLITE_ENABLE_FTS3 extern int sqlite3_fts3_enable_parentheses; #endif @@ -6357,8 +6355,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ (char*)&sqlite3_os_type, TCL_LINK_INT); #endif #ifdef SQLITE_TEST - Tcl_LinkVar(interp, "sqlite_query_plan", - (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); + { + static const char *query_plan = "*** OBSOLETE VARIABLE ***"; + Tcl_LinkVar(interp, "sqlite_query_plan", + (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); + } #endif #ifdef SQLITE_DEBUG Tcl_LinkVar(interp, "sqlite_where_trace", diff --git a/src/update.c b/src/update.c index 3ab1ab2a4..5b18ebd6b 100644 --- a/src/update.c +++ b/src/update.c @@ -318,7 +318,7 @@ void sqlite3Update( pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0 ); if( pWInfo==0 ) goto update_cleanup; - okOnePass = pWInfo->okOnePass; + okOnePass = sqlite3WhereOkOnePass(pWInfo); /* Remember the rowid of every item to be updated. */ diff --git a/src/where.c b/src/where.c index e614f4a6d..cdc0e1683 100644 --- a/src/where.c +++ b/src/where.c @@ -27,9 +27,10 @@ #endif #if defined(SQLITE_DEBUG) \ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE)) -# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X +# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X +# define WHERETRACE_ENABLED 1 #else -# define WHERETRACE(X) +# define WHERETRACE(K,X) #endif /* Forward reference @@ -38,7 +39,149 @@ typedef struct WhereClause WhereClause; typedef struct WhereMaskSet WhereMaskSet; typedef struct WhereOrInfo WhereOrInfo; typedef struct WhereAndInfo WhereAndInfo; -typedef struct WhereCost WhereCost; +typedef struct WhereLevel WhereLevel; +typedef struct WhereLoop WhereLoop; +typedef struct WherePath WherePath; +typedef struct WhereTerm WhereTerm; +typedef struct WhereLoopBuilder WhereLoopBuilder; +typedef struct WhereScan WhereScan; + +/* +** Cost X is tracked as 10*log2(X) stored in a 16-bit integer. The +** maximum cost for ordinary tables is 64*(2**63) which becomes 6900. +** (Virtual tables can return a larger cost, but let's assume they do not.) +** So all costs can be stored in a 16-bit unsigned integer without risk +** of overflow. +** +** Costs are estimates, so don't go to the computational trouble to compute +** 10*log2(X) exactly. Instead, a close estimate is used. Any value of +** X<=1 is stored as 0. X=2 is 10. X=3 is 16. X=1000 is 99. etc. +** +** The tool/wherecosttest.c source file implements a command-line program +** that will convert between WhereCost to integers and do addition and +** multiplication on WhereCost values. That command-line program is a +** useful utility to have around when working with this module. +*/ +typedef unsigned short int WhereCost; + +/* +** This object contains information needed to implement a single nested +** loop in WHERE clause. +** +** Contrast this object with WhereLoop. This object describes the +** implementation of the loop. WhereLoop describes the algorithm. +** This object contains a pointer to the WhereLoop algorithm as one of +** its elements. +** +** The WhereInfo object contains a single instance of this object for +** each term in the FROM clause (which is to say, for each of the +** nested loops as implemented). The order of WhereLevel objects determines +** the loop nested order, with WhereInfo.a[0] being the outer loop and +** WhereInfo.a[WhereInfo.nLevel-1] being the inner loop. +*/ +struct WhereLevel { + int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */ + int iTabCur; /* The VDBE cursor used to access the table */ + int iIdxCur; /* The VDBE cursor used to access pIdx */ + int addrBrk; /* Jump here to break out of the loop */ + int addrNxt; /* Jump here to start the next IN combination */ + int addrCont; /* Jump here to continue with the next loop cycle */ + int addrFirst; /* First instruction of interior of the loop */ + u8 iFrom; /* Which entry in the FROM clause */ + u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ + int p1, p2; /* Operands of the opcode used to ends the loop */ + union { /* Information that depends on pWLoop->wsFlags */ + struct { + int nIn; /* Number of entries in aInLoop[] */ + struct InLoop { + int iCur; /* The VDBE cursor used by this IN operator */ + int addrInTop; /* Top of the IN loop */ + u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ + } *aInLoop; /* Information about each nested IN operator */ + } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */ + Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ + } u; + struct WhereLoop *pWLoop; /* The selected WhereLoop object */ +}; + +/* +** Each instance of this object represents an algorithm for evaluating one +** term of a join. Every term of the FROM clause will have at least +** one corresponding WhereLoop object (unless INDEXED BY constraints +** prevent a query solution - which is an error) and many terms of the +** FROM clause will have multiple WhereLoop objects, each describing a +** potential way of implementing that FROM-clause term, together with +** dependencies and cost estimates for using the chosen algorithm. +** +** Query planning consists of building up a collection of these WhereLoop +** objects, then computing a particular sequence of WhereLoop objects, with +** one WhereLoop object per FROM clause term, that satisfy all dependencies +** and that minimize the overall cost. +*/ +struct WhereLoop { + Bitmask prereq; /* Bitmask of other loops that must run first */ + Bitmask maskSelf; /* Bitmask identifying table iTab */ +#ifdef SQLITE_DEBUG + char cId; /* Symbolic ID of this loop for debugging use */ +#endif + u8 iTab; /* Position in FROM clause of table for this loop */ + u8 iSortIdx; /* Sorting index number. 0==None */ + WhereCost rSetup; /* One-time setup cost (ex: create transient index) */ + WhereCost rRun; /* Cost of running each loop */ + WhereCost nOut; /* Estimated number of output rows */ + union { + struct { /* Information for internal btree tables */ + int nEq; /* Number of equality constraints */ + Index *pIndex; /* Index used, or NULL */ + } btree; + struct { /* Information for virtual tables */ + int idxNum; /* Index number */ + u8 needFree; /* True if sqlite3_free(idxStr) is needed */ + u8 isOrdered; /* True if satisfies ORDER BY */ + u16 omitMask; /* Terms that may be omitted */ + char *idxStr; /* Index identifier string */ + } vtab; + } u; + u32 wsFlags; /* WHERE_* flags describing the plan */ + u16 nLTerm; /* Number of entries in aLTerm[] */ + /**** whereLoopXfer() copies fields above ***********************/ +# define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) + u16 nLSlot; /* Number of slots allocated for aLTerm[] */ + WhereTerm **aLTerm; /* WhereTerms used */ + WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ + WhereTerm *aLTermSpace[4]; /* Initial aLTerm[] space */ +}; + +/* Forward declaration of methods */ +static int whereLoopResize(sqlite3*, WhereLoop*, int); + +/* +** Each instance of this object holds a sequence of WhereLoop objects +** that implement some or all of a query plan. +** +** Think of each WhereLoop objects as a node in a graph, which arcs +** showing dependences and costs for travelling between nodes. (That is +** not a completely accurate description because WhereLoop costs are a +** vector, not a scalar, and because dependences are many-to-one, not +** one-to-one as are graph nodes. But it is a useful visualization aid.) +** Then a WherePath object is a path through the graph that visits some +** or all of the WhereLoop objects once. +** +** The "solver" works by creating the N best WherePath objects of length +** 1. Then using those as a basis to compute the N best WherePath objects +** of length 2. And so forth until the length of WherePaths equals the +** number of nodes in the FROM clause. The best (lowest cost) WherePath +** at the end is the choosen query plan. +*/ +struct WherePath { + Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */ + Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ + WhereCost nRow; /* Estimated number of rows generated by this path */ + WhereCost rCost; /* Total cost of this path */ + u8 isOrdered; /* True if this path satisfies ORDER BY */ + u8 isOrderedValid; /* True if the isOrdered field is valid */ + WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */ +}; /* ** The query generator uses an array of instances of this structure to @@ -91,7 +234,6 @@ typedef struct WhereCost WhereCost; ** in prereqRight and prereqAll. The default is 64 bits, hence SQLite ** is only able to process joins with 64 or fewer tables. */ -typedef struct WhereTerm WhereTerm; struct WhereTerm { Expr *pExpr; /* Pointer to the subexpression that is this term */ int iParent; /* Disable pWC->a[iParent] when this term disabled */ @@ -126,6 +268,22 @@ struct WhereTerm { #endif /* +** An instance of the WhereScan object is used as an iterator for locating +** terms in the WHERE clause that are useful to the query planner. +*/ +struct WhereScan { + WhereClause *pOrigWC; /* Original, innermost WhereClause */ + WhereClause *pWC; /* WhereClause currently being scanned */ + char *zCollName; /* Required collating sequence, if not NULL */ + char idxaff; /* Must match this affinity, if zCollName!=NULL */ + unsigned char nEquiv; /* Number of entries in aEquiv[] */ + unsigned char iEquiv; /* Next unused slot in aEquiv[] */ + u32 opMask; /* Acceptable operators */ + int k; /* Resume scanning at this->pWC->a[this->k] */ + int aEquiv[22]; /* Cursor,Column pairs for equivalence classes */ +}; + +/* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. ** @@ -138,11 +296,9 @@ struct WhereTerm { ** subclauses points to the WhereClause object for the whole clause. */ struct WhereClause { - Parse *pParse; /* The parser context */ - WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ + WhereInfo *pWInfo; /* WHERE clause processing context */ WhereClause *pOuter; /* Outer conjunction */ u8 op; /* Split operator. TK_AND or TK_OR */ - u16 wctrlFlags; /* Might include WHERE_AND_ONLY */ int nTerm; /* Number of terms */ int nSlot; /* Number of entries in a[] */ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */ @@ -202,19 +358,55 @@ struct WhereMaskSet { }; /* -** A WhereCost object records a lookup strategy and the estimated -** cost of pursuing that strategy. +** This object is a convenience wrapper holding all information needed +** to construct WhereLoop objects for a particular query. */ -struct WhereCost { - WherePlan plan; /* The lookup strategy */ - double rCost; /* Overall cost of pursuing this search strategy */ - Bitmask used; /* Bitmask of cursors used by this plan */ +struct WhereLoopBuilder { + WhereInfo *pWInfo; /* Information about this WHERE */ + WhereClause *pWC; /* WHERE clause terms */ + ExprList *pOrderBy; /* ORDER BY clause */ + WhereLoop *pNew; /* Template WhereLoop */ + WhereLoop *pBest; /* If non-NULL, store single best loop here */ }; /* -** Bitmasks for the operators that indices are able to exploit. An +** The WHERE clause processing routine has two halves. The +** first part does the start of the WHERE loop and the second +** half does the tail of the WHERE loop. An instance of +** this structure is returned by the first half and passed +** into the second half to give some continuity. +** +** An instance of this object holds the complete state of the query +** planner. +*/ +struct WhereInfo { + Parse *pParse; /* Parsing and code generating context */ + SrcList *pTabList; /* List of tables in the join */ + ExprList *pOrderBy; /* The ORDER BY clause or NULL */ + ExprList *pDistinct; /* DISTINCT ON values, or NULL */ + WhereLoop *pLoops; /* List of all WhereLoop objects */ + Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ + WhereCost nRowOut; /* Estimated number of output rows */ + u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ + u8 bOBSat; /* ORDER BY satisfied by indices */ + u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */ + u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ + u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */ + int iTop; /* The very beginning of the WHERE loop */ + int iContinue; /* Jump here to continue with next record */ + int iBreak; /* Jump here to break out of the loop */ + int nLevel; /* Number of nested loop */ + int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ + WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ + WhereClause sWC; /* Decomposition of the WHERE clause */ + WhereLevel a[1]; /* Information about each nest loop in WHERE */ +}; + +/* +** Bitmasks for the operators on WhereTerm objects. These are all +** operators that are of interest to the query planner. An ** OR-ed combination of these values can be used when searching for -** terms in the where clause. +** particular WhereTerms within a WhereClause. */ #define WO_IN 0x001 #define WO_EQ 0x002 @@ -233,74 +425,88 @@ struct WhereCost { #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* -** Value for wsFlags returned by bestIndex() and stored in -** WhereLevel.wsFlags. These flags determine which search -** strategies are appropriate. -** -** The least significant 12 bits is reserved as a mask for WO_ values above. -** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL. -** But if the table is the right table of a left join, WhereLevel.wsFlags -** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as -** the "op" parameter to findTerm when we are resolving equality constraints. -** ISNULL constraints will then not be used on the right table of a left -** join. Tickets #2177 and #2189. +** These are definitions of bits in the WhereLoop.wsFlags field. +** The particular combination of bits in each WhereLoop help to +** determine the algorithm that WhereLoop represents. */ -#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ -#define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ -#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ -#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ -#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ -#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ -#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ -#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ -#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */ -#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ -#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ -#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ -#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */ -#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */ -#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */ -#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */ -#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */ -#define WHERE_OB_UNIQUE 0x00004000 /* Values in ORDER BY columns are - ** different for every output row */ -#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ -#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ -#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ -#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */ -#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */ +#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR or x IN (...) or x IS NULL */ +#define WHERE_COLUMN_RANGE 0x00000002 /* x<EXPR and/or x>EXPR */ +#define WHERE_COLUMN_IN 0x00000004 /* x IN (...) */ +#define WHERE_COLUMN_NULL 0x00000008 /* x IS NULL */ +#define WHERE_CONSTRAINT 0x0000000f /* Any of the WHERE_COLUMN_xxx values */ +#define WHERE_TOP_LIMIT 0x00000010 /* x<EXPR or x<=EXPR constraint */ +#define WHERE_BTM_LIMIT 0x00000020 /* x>EXPR or x>=EXPR constraint */ +#define WHERE_BOTH_LIMIT 0x00000030 /* Both x>EXPR and x<EXPR */ +#define WHERE_IDX_ONLY 0x00000040 /* Use index only - omit table */ +#define WHERE_IPK 0x00000100 /* x is the INTEGER PRIMARY KEY */ +#define WHERE_INDEXED 0x00000200 /* WhereLoop.u.btree.pIndex is valid */ +#define WHERE_VIRTUALTABLE 0x00000400 /* WhereLoop.u.vtab is valid */ +#define WHERE_IN_ABLE 0x00000800 /* Able to support an IN operator */ +#define WHERE_ONEROW 0x00001000 /* Selects no more than one row */ +#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */ +#define WHERE_TEMP_INDEX 0x00004000 /* Uses an ephemeral index */ + + +/* Convert a WhereCost value (10 times log2(X)) into its integer value X. +** A rough approximation is used. The value returned is not exact. +*/ +static u64 whereCostToInt(WhereCost x){ + u64 n; + if( x<10 ) return 1; + n = x%10; + x /= 10; + if( n>=5 ) n -= 2; + else if( n>=1 ) n -= 1; + if( x>=3 ) return (n+8)<<(x-3); + return (n+8)>>(3-x); +} /* -** This module contains many separate subroutines that work together to -** find the best indices to use for accessing a particular table in a query. -** An instance of the following structure holds context information about the -** index search so that it can be more easily passed between the various -** routines. +** Return the estimated number of output rows from a WHERE clause */ -typedef struct WhereBestIdx WhereBestIdx; -struct WhereBestIdx { - Parse *pParse; /* Parser context */ - WhereClause *pWC; /* The WHERE clause */ - struct SrcList_item *pSrc; /* The FROM clause term to search */ - Bitmask notReady; /* Mask of cursors not available */ - Bitmask notValid; /* Cursors not available for any purpose */ - ExprList *pOrderBy; /* The ORDER BY clause */ - ExprList *pDistinct; /* The select-list if query is DISTINCT */ - sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */ - int i, n; /* Which loop is being coded; # of loops */ - WhereLevel *aLevel; /* Info about outer loops */ - WhereCost cost; /* Lowest cost query plan */ -}; +u64 sqlite3WhereOutputRowCount(WhereInfo *pWInfo){ + return whereCostToInt(pWInfo->nRowOut); +} /* -** Return TRUE if the probe cost is less than the baseline cost +** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this +** WHERE clause returns outputs for DISTINCT processing. */ -static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){ - if( pProbe->rCost<pBaseline->rCost ) return 1; - if( pProbe->rCost>pBaseline->rCost ) return 0; - if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1; - if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1; - return 0; +int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ + return pWInfo->eDistinct; +} + +/* +** Return TRUE if the WHERE clause returns rows in ORDER BY order. +** Return FALSE if the output needs to be sorted. +*/ +int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ + return pWInfo->bOBSat!=0; +} + +/* +** Return the VDBE address or label to jump to in order to continue +** immediately with the next row of a WHERE clause. +*/ +int sqlite3WhereContinueLabel(WhereInfo *pWInfo){ + return pWInfo->iContinue; +} + +/* +** Return the VDBE address or label to jump to in order to break +** out of a WHERE loop. +*/ +int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ + return pWInfo->iBreak; +} + +/* +** Return TRUE if an UPDATE or DELETE statement can operate directly on +** the rowids returned by a WHERE clause. Return FALSE if doing an +** UPDATE or DELETE might change subsequent WHERE clause results. +*/ +int sqlite3WhereOkOnePass(WhereInfo *pWInfo){ + return pWInfo->okOnePass; } /* @@ -308,17 +514,13 @@ static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){ */ static void whereClauseInit( WhereClause *pWC, /* The WhereClause to be initialized */ - Parse *pParse, /* The parsing context */ - WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */ - u16 wctrlFlags /* Might include WHERE_AND_ONLY */ + WhereInfo *pWInfo /* The WHERE processing context */ ){ - pWC->pParse = pParse; - pWC->pMaskSet = pMaskSet; + pWC->pWInfo = pWInfo; pWC->pOuter = 0; pWC->nTerm = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; - pWC->wctrlFlags = wctrlFlags; } /* Forward reference */ @@ -347,7 +549,7 @@ static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){ static void whereClauseClear(WhereClause *pWC){ int i; WhereTerm *a; - sqlite3 *db = pWC->pParse->db; + sqlite3 *db = pWC->pWInfo->pParse->db; for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){ if( a->wtFlags & TERM_DYNAMIC ){ sqlite3ExprDelete(db, a->pExpr); @@ -388,7 +590,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ testcase( wtFlags & TERM_VIRTUAL ); /* EV: R-00211-15100 */ if( pWC->nTerm>=pWC->nSlot ){ WhereTerm *pOld = pWC->a; - sqlite3 *db = pWC->pParse->db; + sqlite3 *db = pWC->pWInfo->pParse->db; pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 ); if( pWC->a==0 ){ if( wtFlags & TERM_DYNAMIC ){ @@ -428,8 +630,8 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ ** the WhereClause.a[] array. The slot[] array grows as needed to contain ** all terms of the WHERE clause. */ -static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ - pWC->op = (u8)op; +static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ + pWC->op = op; if( pExpr==0 ) return; if( pExpr->op!=op ){ whereClauseInsert(pWC, pExpr, 0); @@ -440,9 +642,9 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ } /* -** Initialize an expression mask set (a WhereMaskSet object) +** Initialize a WhereMaskSet object */ -#define initMaskSet(P) memset(P, 0, sizeof(*P)) +#define initMaskSet(P) (P)->n=0 /* ** Return the bitmask for the given cursor number. Return 0 if @@ -453,7 +655,7 @@ static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){ assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); for(i=0; i<pMaskSet->n; i++){ if( pMaskSet->ix[i]==iCursor ){ - return ((Bitmask)1)<<i; + return MASKBIT(i); } } return 0; @@ -473,18 +675,9 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ } /* -** This routine walks (recursively) an expression tree and generates +** These routine walk (recursively) an expression tree and generates ** a bitmask indicating which tables are used in that expression ** tree. -** -** In order for this routine to work, the calling function must have -** previously invoked sqlite3ResolveExprNames() on the expression. See -** the header comment on that routine for additional information. -** The sqlite3ResolveExprNames() routines looks for column names and -** sets their opcodes to TK_COLUMN and their Expr.iTable fields to -** the VDBE cursor number of the table. This routine just has to -** translate the cursor numbers into bitmask values and OR all -** the bitmasks together. */ static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*); static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*); @@ -538,7 +731,7 @@ static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){ /* ** Return TRUE if the given operator is one of the operators that is ** allowed for an indexable WHERE clause term. The allowed operators are -** "=", "<", ">", "<=", ">=", and "IN". +** "=", "<", ">", "<=", ">=", "IN", and "IS NULL" ** ** IMPLEMENTATION-OF: R-59926-26393 To be usable by an index a term must be ** of one of the following forms: column = expression column > expression @@ -565,10 +758,9 @@ static int allowedOp(int op){ ** are converted into "Y op X". ** ** If left/right precedence rules come into play when determining the -** collating -** side of the comparison, it remains associated with the same side after -** the commutation. So "Y collate NOCASE op X" becomes -** "X op Y". This is because any collation sequence on +** collating sequence, then COLLATE operators are adjusted to ensure +** that the collating sequence does not change. For example: +** "Y collate NOCASE op X" becomes "X op Y" because any collation sequence on ** the left hand side of a comparison overrides any collation sequence ** attached to the right. For the same reason the EP_Collate flag ** is not commuted. @@ -626,6 +818,130 @@ static u16 operatorMask(int op){ } /* +** Advance to the next WhereTerm that matches according to the criteria +** established when the pScan object was initialized by whereScanInit(). +** Return NULL if there are no more matching WhereTerms. +*/ +WhereTerm *whereScanNext(WhereScan *pScan){ + int iCur; /* The cursor on the LHS of the term */ + int iColumn; /* The column on the LHS of the term. -1 for IPK */ + Expr *pX; /* An expression being tested */ + WhereClause *pWC; /* Shorthand for pScan->pWC */ + WhereTerm *pTerm; /* The term being tested */ + int k = pScan->k; /* Where to start scanning */ + + while( pScan->iEquiv<=pScan->nEquiv ){ + iCur = pScan->aEquiv[pScan->iEquiv-2]; + iColumn = pScan->aEquiv[pScan->iEquiv-1]; + while( (pWC = pScan->pWC)!=0 ){ + for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){ + if( pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn ){ + if( (pTerm->eOperator & WO_EQUIV)!=0 + && pScan->nEquiv<ArraySize(pScan->aEquiv) + ){ + int j; + pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); + assert( pX->op==TK_COLUMN ); + for(j=0; j<pScan->nEquiv; j+=2){ + if( pScan->aEquiv[j]==pX->iTable + && pScan->aEquiv[j+1]==pX->iColumn ){ + break; + } + } + if( j==pScan->nEquiv ){ + pScan->aEquiv[j] = pX->iTable; + pScan->aEquiv[j+1] = pX->iColumn; + pScan->nEquiv += 2; + } + } + if( (pTerm->eOperator & pScan->opMask)!=0 ){ + /* Verify the affinity and collating sequence match */ + if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ + CollSeq *pColl; + Parse *pParse = pWC->pWInfo->pParse; + pX = pTerm->pExpr; + if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ + continue; + } + assert(pX->pLeft); + pColl = sqlite3BinaryCompareCollSeq(pParse, + pX->pLeft, pX->pRight); + if( pColl==0 ) pColl = pParse->db->pDfltColl; + if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ + continue; + } + } + if( (pTerm->eOperator & WO_EQ)!=0 + && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN + && pX->iTable==pScan->aEquiv[0] + && pX->iColumn==pScan->aEquiv[1] + ){ + continue; + } + pScan->k = k+1; + return pTerm; + } + } + } + pScan->pWC = pScan->pWC->pOuter; + k = 0; + } + pScan->pWC = pScan->pOrigWC; + k = 0; + pScan->iEquiv += 2; + } + return 0; +} + +/* +** Initialize a WHERE clause scanner object. Return a pointer to the +** first match. Return NULL if there are no matches. +** +** The scanner will be searching the WHERE clause pWC. It will look +** for terms of the form "X <op> <expr>" where X is column iColumn of table +** iCur. The <op> must be one of the operators described by opMask. +** +** If the search is for X and the WHERE clause contains terms of the +** form X=Y then this routine might also return terms of the form +** "Y <op> <expr>". The number of levels of transitivity is limited, +** but is enough to handle most commonly occurring SQL statements. +** +** If X is not the INTEGER PRIMARY KEY then X must be compatible with +** index pIdx. +*/ +WhereTerm *whereScanInit( + WhereScan *pScan, /* The WhereScan object being initialized */ + WhereClause *pWC, /* The WHERE clause to be scanned */ + int iCur, /* Cursor to scan for */ + int iColumn, /* Column to scan for */ + u32 opMask, /* Operator(s) to scan for */ + Index *pIdx /* Must be compatible with this index */ +){ + int j; + + /* memset(pScan, 0, sizeof(*pScan)); */ + pScan->pOrigWC = pWC; + pScan->pWC = pWC; + if( pIdx && iColumn>=0 ){ + pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; + for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ + if( NEVER(j>=pIdx->nColumn) ) return 0; + } + pScan->zCollName = pIdx->azColl[j]; + }else{ + pScan->idxaff = 0; + pScan->zCollName = 0; + } + pScan->opMask = opMask; + pScan->k = 0; + pScan->aEquiv[0] = iCur; + pScan->aEquiv[1] = iColumn; + pScan->nEquiv = 2; + pScan->iEquiv = 2; + return whereScanNext(pScan); +} + +/* ** Search for a term in the WHERE clause that is of the form "X <op> <expr>" ** where X is a reference to the iColumn of table iCur and <op> is one of ** the WO_xx operator codes specified by the op parameter. @@ -656,84 +972,20 @@ static WhereTerm *findTerm( u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ){ - WhereTerm *pTerm; /* Term being examined as possible result */ - WhereTerm *pResult = 0; /* The answer to return */ - WhereClause *pWCOrig = pWC; /* Original pWC value */ - int j, k; /* Loop counters */ - Expr *pX; /* Pointer to an expression */ - Parse *pParse; /* Parsing context */ - int iOrigCol = iColumn; /* Original value of iColumn */ - int nEquiv = 2; /* Number of entires in aEquiv[] */ - int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ - int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ - - assert( iCur>=0 ); - aEquiv[0] = iCur; - aEquiv[1] = iColumn; - for(;;){ - for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ - if( pTerm->leftCursor==iCur - && pTerm->u.leftColumn==iColumn - ){ - if( (pTerm->prereqRight & notReady)==0 - && (pTerm->eOperator & op & WO_ALL)!=0 - ){ - if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ - CollSeq *pColl; - char idxaff; - - pX = pTerm->pExpr; - pParse = pWC->pParse; - idxaff = pIdx->pTable->aCol[iOrigCol].affinity; - if( !sqlite3IndexAffinityOk(pX, idxaff) ){ - continue; - } - - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); - if( pColl==0 ) pColl = pParse->db->pDfltColl; - - for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; - } - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ - continue; - } - } - if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){ - pResult = pTerm; - goto findTerm_success; - }else if( pResult==0 ){ - pResult = pTerm; - } - } - if( (pTerm->eOperator & WO_EQUIV)!=0 - && nEquiv<ArraySize(aEquiv) - ){ - pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight); - assert( pX->op==TK_COLUMN ); - for(j=0; j<nEquiv; j+=2){ - if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break; - } - if( j==nEquiv ){ - aEquiv[j] = pX->iTable; - aEquiv[j+1] = pX->iColumn; - nEquiv += 2; - } - } - } + WhereTerm *pResult = 0; + WhereTerm *p; + WhereScan scan; + + p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx); + while( p ){ + if( (p->prereqRight & notReady)==0 ){ + if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){ + return p; } + if( pResult==0 ) pResult = p; } - if( iEquiv>=nEquiv ) break; - iCur = aEquiv[iEquiv++]; - iColumn = aEquiv[iEquiv++]; + p = whereScanNext(&scan); } -findTerm_success: return pResult; } @@ -742,8 +994,6 @@ static void exprAnalyze(SrcList*, WhereClause*, int); /* ** Call exprAnalyze on all terms in a WHERE clause. -** -** */ static void exprAnalyzeAll( SrcList *pTabList, /* the FROM clause */ @@ -975,11 +1225,11 @@ static void exprAnalyzeOrTerm( WhereClause *pWC, /* the complete WHERE clause */ int idxTerm /* Index of the OR-term to be analyzed */ ){ - Parse *pParse = pWC->pParse; /* Parser context */ + WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */ + Parse *pParse = pWInfo->pParse; /* Parser context */ sqlite3 *db = pParse->db; /* Database connection */ WhereTerm *pTerm = &pWC->a[idxTerm]; /* The term to be analyzed */ Expr *pExpr = pTerm->pExpr; /* The expression of the term */ - WhereMaskSet *pMaskSet = pWC->pMaskSet; /* Table use masks */ int i; /* Loop counters */ WhereClause *pOrWc; /* Breakup of pTerm into subterms */ WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */ @@ -998,7 +1248,7 @@ static void exprAnalyzeOrTerm( if( pOrInfo==0 ) return; pTerm->wtFlags |= TERM_ORINFO; pOrWc = &pOrInfo->wc; - whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags); + whereClauseInit(pOrWc, pWInfo); whereSplit(pOrWc, pExpr, TK_OR); exprAnalyzeAll(pSrc, pOrWc); if( db->mallocFailed ) return; @@ -1024,7 +1274,7 @@ static void exprAnalyzeOrTerm( pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; pAndWC = &pAndInfo->wc; - whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags); + whereClauseInit(pAndWC, pWC->pWInfo); whereSplit(pAndWC, pOrTerm->pExpr, TK_AND); exprAnalyzeAll(pSrc, pAndWC); pAndWC->pOuter = pWC; @@ -1033,7 +1283,7 @@ static void exprAnalyzeOrTerm( for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){ assert( pAndTerm->pExpr ); if( allowedOp(pAndTerm->pExpr->op) ){ - b |= getMask(pMaskSet, pAndTerm->leftCursor); + b |= getMask(&pWInfo->sMaskSet, pAndTerm->leftCursor); } } } @@ -1044,10 +1294,10 @@ static void exprAnalyzeOrTerm( ** corresponding TERM_VIRTUAL term */ }else{ Bitmask b; - b = getMask(pMaskSet, pOrTerm->leftCursor); + b = getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor); if( pOrTerm->wtFlags & TERM_VIRTUAL ){ WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; - b |= getMask(pMaskSet, pOther->leftCursor); + b |= getMask(&pWInfo->sMaskSet, pOther->leftCursor); } indexable &= b; if( (pOrTerm->eOperator & WO_EQ)==0 ){ @@ -1109,7 +1359,7 @@ static void exprAnalyzeOrTerm( assert( j==1 ); continue; } - if( (chngToIN & getMask(pMaskSet, pOrTerm->leftCursor))==0 ){ + if( (chngToIN & getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor))==0 ){ /* This term must be of the form t1.a==t2.b where t2 is in the ** chngToIN set but t1 is not. This term will be either preceeded ** or follwed by an inverted copy (t2.b==t1.a). Skip this term @@ -1128,7 +1378,7 @@ static void exprAnalyzeOrTerm( ** on the second iteration */ assert( j==1 ); assert( IsPowerOfTwo(chngToIN) ); - assert( chngToIN==getMask(pMaskSet, iCursor) ); + assert( chngToIN==getMask(&pWInfo->sMaskSet, iCursor) ); break; } testcase( j==1 ); @@ -1177,7 +1427,7 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.leftColumn==iColumn ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); - pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup); + pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup); pLeft = pOrTerm->pExpr->pLeft; } assert( pLeft!=0 ); @@ -1226,6 +1476,7 @@ static void exprAnalyze( WhereClause *pWC, /* the WHERE clause */ int idxTerm /* Index of the term to be analyzed */ ){ + WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */ WhereTerm *pTerm; /* The term to be analyzed */ WhereMaskSet *pMaskSet; /* Set of table index masks */ Expr *pExpr; /* The expression to be analyzed */ @@ -1236,14 +1487,14 @@ static void exprAnalyze( int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ int noCase = 0; /* LIKE/GLOB distinguishes case */ int op; /* Top-level operator. pExpr->op */ - Parse *pParse = pWC->pParse; /* Parsing context */ + Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ if( db->mallocFailed ){ return; } pTerm = &pWC->a[idxTerm]; - pMaskSet = pWC->pMaskSet; + pMaskSet = &pWInfo->sMaskSet; pExpr = pTerm->pExpr; assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE ); prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft); @@ -1521,11 +1772,8 @@ static void exprAnalyze( } /* -** This function searches the expression list passed as the second argument -** for an expression of type TK_COLUMN that refers to the same column and -** uses the same collation sequence as the iCol'th column of index pIdx. -** Argument iBase is the cursor number used for the table that pIdx refers -** to. +** This function searches pList for a entry that matches the iCol-th column +** of index pIdx. ** ** If such an expression is found, its index in pList->a[] is returned. If ** no expression is found, -1 is returned. @@ -1557,76 +1805,17 @@ static int findIndexCol( } /* -** This routine determines if pIdx can be used to assist in processing a -** DISTINCT qualifier. In other words, it tests whether or not using this -** index for the outer loop guarantees that rows with equal values for -** all expressions in the pDistinct list are delivered grouped together. -** -** For example, the query -** -** SELECT DISTINCT a, b, c FROM tbl WHERE a = ? -** -** can benefit from any index on columns "b" and "c". -*/ -static int isDistinctIndex( - Parse *pParse, /* Parsing context */ - WhereClause *pWC, /* The WHERE clause */ - Index *pIdx, /* The index being considered */ - int base, /* Cursor number for the table pIdx is on */ - ExprList *pDistinct, /* The DISTINCT expressions */ - int nEqCol /* Number of index columns with == */ -){ - Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */ - int i; /* Iterator variable */ - - assert( pDistinct!=0 ); - if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0; - testcase( pDistinct->nExpr==BMS-1 ); - - /* Loop through all the expressions in the distinct list. If any of them - ** are not simple column references, return early. Otherwise, test if the - ** WHERE clause contains a "col=X" clause. If it does, the expression - ** can be ignored. If it does not, and the column does not belong to the - ** same table as index pIdx, return early. Finally, if there is no - ** matching "col=X" expression and the column is on the same table as pIdx, - ** set the corresponding bit in variable mask. - */ - for(i=0; i<pDistinct->nExpr; i++){ - WhereTerm *pTerm; - Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr); - if( p->op!=TK_COLUMN ) return 0; - pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0); - if( pTerm ){ - Expr *pX = pTerm->pExpr; - CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - CollSeq *p2 = sqlite3ExprCollSeq(pParse, p); - if( p1==p2 ) continue; - } - if( p->iTable!=base ) return 0; - mask |= (((Bitmask)1) << i); - } - - for(i=nEqCol; mask && i<pIdx->nColumn; i++){ - int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i); - if( iExpr<0 ) break; - mask &= ~(((Bitmask)1) << iExpr); - } - - return (mask==0); -} - - -/* ** Return true if the DISTINCT expression-list passed as the third argument -** is redundant. A DISTINCT list is redundant if the database contains a -** UNIQUE index that guarantees that the result of the query will be distinct -** anyway. +** is redundant. +** +** A DISTINCT list is redundant if the database contains some subset of +** columns that are unique and non-null. */ static int isDistinctRedundant( - Parse *pParse, - SrcList *pTabList, - WhereClause *pWC, - ExprList *pDistinct + Parse *pParse, /* Parsing context */ + SrcList *pTabList, /* The FROM clause */ + WhereClause *pWC, /* The WHERE clause */ + ExprList *pDistinct /* The result set that needs to be DISTINCT */ ){ Table *pTab; Index *pIdx; @@ -1682,21 +1871,76 @@ static int isDistinctRedundant( return 0; } +/* +** The (an approximate) sum of two WhereCosts. This computation is +** not a simple "+" operator because WhereCost is stored as a logarithmic +** value. +** +*/ +static WhereCost whereCostAdd(WhereCost a, WhereCost b){ + static const unsigned char x[] = { + 10, 10, /* 0,1 */ + 9, 9, /* 2,3 */ + 8, 8, /* 4,5 */ + 7, 7, 7, /* 6,7,8 */ + 6, 6, 6, /* 9,10,11 */ + 5, 5, 5, /* 12-14 */ + 4, 4, 4, 4, /* 15-18 */ + 3, 3, 3, 3, 3, 3, /* 19-24 */ + 2, 2, 2, 2, 2, 2, 2, /* 25-31 */ + }; + if( a>=b ){ + if( a>b+49 ) return a; + if( a>b+31 ) return a+1; + return a+x[a-b]; + }else{ + if( b>a+49 ) return b; + if( b>a+31 ) return b+1; + return b+x[b-a]; + } +} + +/* +** Convert an integer into a WhereCost. In other words, compute a +** good approximatation for 10*log2(x). +*/ +static WhereCost whereCost(tRowcnt x){ + static WhereCost a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; + WhereCost y = 40; + if( x<8 ){ + if( x<2 ) return 0; + while( x<8 ){ y -= 10; x <<= 1; } + }else{ + while( x>255 ){ y += 40; x >>= 4; } + while( x>15 ){ y += 10; x >>= 1; } + } + return a[x&7] + y - 10; +} + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Convert a double (as received from xBestIndex of a virtual table) +** into a WhereCost. In other words, compute an approximation for +** 10*log2(x). +*/ +static WhereCost whereCostFromDouble(double x){ + u64 a; + WhereCost e; + assert( sizeof(x)==8 && sizeof(a)==8 ); + if( x<=1 ) return 0; + if( x<=2000000000 ) return whereCost((tRowcnt)x); + memcpy(&a, &x, 8); + e = (a>>52) - 1022; + return e*10; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + /* -** Prepare a crude estimate of the logarithm of the input value. -** The results need not be exact. This is only used for estimating -** the total cost of performing operations with O(logN) or O(NlogN) -** complexity. Because N is just a guess, it is no great tragedy if -** logN is a little off. +** Estimate the logarithm of the input value to base 2. */ -static double estLog(double N){ - double logN = 1; - double x = 10; - while( N>x ){ - logN += 1; - x *= 10; - } - return logN; +static WhereCost estLog(WhereCost N){ + WhereCost x = whereCost(N); + return x>33 ? x - 33 : 0; } /* @@ -1705,7 +1949,7 @@ static double estLog(double N){ ** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines ** are no-ops. */ -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG) +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) static void TRACE_IDX_INPUTS(sqlite3_index_info *p){ int i; if( !sqlite3WhereTrace ) return; @@ -1743,107 +1987,6 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){ #define TRACE_IDX_OUTPUTS(A) #endif -/* -** Required because bestIndex() is called by bestOrClauseIndex() -*/ -static void bestIndex(WhereBestIdx*); - -/* -** This routine attempts to find an scanning strategy that can be used -** to optimize an 'OR' expression that is part of a WHERE clause. -** -** The table associated with FROM clause term pSrc may be either a -** regular B-Tree table or a virtual table. -*/ -static void bestOrClauseIndex(WhereBestIdx *p){ -#ifndef SQLITE_OMIT_OR_OPTIMIZATION - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - const int iCur = pSrc->iCursor; /* The cursor of the table */ - const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ - WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - - /* The OR-clause optimization is disallowed if the INDEXED BY or - ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */ - if( pSrc->notIndexed || pSrc->pIndex!=0 ){ - return; - } - if( pWC->wctrlFlags & WHERE_AND_ONLY ){ - return; - } - - /* Search the WHERE clause terms for a usable WO_OR term. */ - for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ - if( (pTerm->eOperator & WO_OR)!=0 - && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 - && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 - ){ - WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; - WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; - WhereTerm *pOrTerm; - int flags = WHERE_MULTI_OR; - double rTotal = 0; - double nRow = 0; - Bitmask used = 0; - WhereBestIdx sBOI; - - sBOI = *p; - sBOI.pOrderBy = 0; - sBOI.pDistinct = 0; - sBOI.ppIdxInfo = 0; - for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ - WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", - (pOrTerm - pOrWC->a), (pTerm - pWC->a) - )); - if( (pOrTerm->eOperator& WO_AND)!=0 ){ - sBOI.pWC = &pOrTerm->u.pAndInfo->wc; - bestIndex(&sBOI); - }else if( pOrTerm->leftCursor==iCur ){ - WhereClause tempWC; - tempWC.pParse = pWC->pParse; - tempWC.pMaskSet = pWC->pMaskSet; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.a = pOrTerm; - tempWC.wctrlFlags = 0; - tempWC.nTerm = 1; - sBOI.pWC = &tempWC; - bestIndex(&sBOI); - }else{ - continue; - } - rTotal += sBOI.cost.rCost; - nRow += sBOI.cost.plan.nRow; - used |= sBOI.cost.used; - if( rTotal>=p->cost.rCost ) break; - } - - /* If there is an ORDER BY clause, increase the scan cost to account - ** for the cost of the sort. */ - if( p->pOrderBy!=0 ){ - WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", - rTotal, rTotal+nRow*estLog(nRow))); - rTotal += nRow*estLog(nRow); - } - - /* If the cost of scanning using this OR term for optimization is - ** less than the current cost stored in pCost, replace the contents - ** of pCost. */ - WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); - if( rTotal<p->cost.rCost ){ - p->cost.rCost = rTotal; - p->cost.used = used; - p->cost.plan.nRow = nRow; - p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; - p->cost.plan.wsFlags = flags; - p->cost.plan.u.pTerm = pTerm; - } - } - } -#endif /* SQLITE_OMIT_OR_OPTIMIZATION */ -} - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Return TRUE if the WHERE clause term pTerm is of a form where it @@ -1859,88 +2002,13 @@ static int termCanDriveIndex( if( pTerm->leftCursor!=pSrc->iCursor ) return 0; if( (pTerm->eOperator & WO_EQ)==0 ) return 0; if( (pTerm->prereqRight & notReady)!=0 ) return 0; + if( pTerm->u.leftColumn<0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; return 1; } #endif -#ifndef SQLITE_OMIT_AUTOMATIC_INDEX -/* -** If the query plan for pSrc specified in pCost is a full table scan -** and indexing is allows (if there is no NOT INDEXED clause) and it -** possible to construct a transient index that would perform better -** than a full table scan even when the cost of constructing the index -** is taken into account, then alter the query plan to use the -** transient index. -*/ -static void bestAutomaticIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - double nTableRow; /* Rows in the input table */ - double logN; /* log(nTableRow) */ - double costTempIdx; /* per-query cost of the transient index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ - WhereTerm *pWCEnd; /* End of pWC->a[] */ - Table *pTable; /* Table tht might be indexed */ - - if( pParse->nQueryLoop<=(double)1 ){ - /* There is no point in building an automatic index for a single scan */ - return; - } - if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){ - /* Automatic indices are disabled at run-time */ - return; - } - if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 - && (p->cost.plan.wsFlags & WHERE_COVER_SCAN)==0 - ){ - /* We already have some kind of index in use for this query. */ - return; - } - if( pSrc->viaCoroutine ){ - /* Cannot index a co-routine */ - return; - } - if( pSrc->notIndexed ){ - /* The NOT INDEXED clause appears in the SQL. */ - return; - } - if( pSrc->isCorrelated ){ - /* The source is a correlated sub-query. No point in indexing it. */ - return; - } - - assert( pParse->nQueryLoop >= (double)1 ); - pTable = pSrc->pTab; - nTableRow = pTable->nRowEst; - logN = estLog(nTableRow); - costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); - if( costTempIdx>=p->cost.rCost ){ - /* The cost of creating the transient table would be greater than - ** doing the full table scan */ - return; - } - - /* Search for any equality comparison term */ - pWCEnd = &pWC->a[pWC->nTerm]; - for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ - if( termCanDriveIndex(pTerm, pSrc, p->notReady) ){ - WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n", - p->cost.rCost, costTempIdx)); - p->cost.rCost = costTempIdx; - p->cost.plan.nRow = logN + 1; - p->cost.plan.wsFlags = WHERE_TEMP_INDEX; - p->cost.used = pTerm->prereqRight; - break; - } - } -} -#else -# define bestAutomaticIndex(A) /* no-op */ -#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ - #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* @@ -1970,6 +2038,7 @@ static void constructAutomaticIndex( int i; /* Loop counter */ int mxBitCol; /* Maximum column in pSrc->colUsed */ CollSeq *pColl; /* Collating sequence to on a column */ + WhereLoop *pLoop; /* The Loop object */ Bitmask idxCols; /* Bitmap of columns used for indexing */ Bitmask extraCols; /* Bitmap of additional columns */ @@ -1984,21 +2053,25 @@ static void constructAutomaticIndex( nColumn = 0; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; + pLoop = pLevel->pWLoop; idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; - Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; + Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS ); testcase( iCol==BMS-1 ); if( (idxCols & cMask)==0 ){ - nColumn++; + if( whereLoopResize(pParse->db, pLoop, nColumn+1) ) return; + pLoop->aLTerm[nColumn++] = pTerm; idxCols |= cMask; } } } assert( nColumn>0 ); - pLevel->plan.nEq = nColumn; + pLoop->u.btree.nEq = pLoop->nLTerm = nColumn; + pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED + | WHERE_TEMP_INDEX; /* Count the number of additional columns needed to create a ** covering index. A "covering index" is an index that contains all @@ -2008,17 +2081,17 @@ static void constructAutomaticIndex( ** original table changes and the index and table cannot both be used ** if they go out of sync. */ - extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1))); + extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol; testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); for(i=0; i<mxBitCol; i++){ - if( extraCols & (((Bitmask)1)<<i) ) nColumn++; + if( extraCols & MASKBIT(i) ) nColumn++; } - if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ + if( pSrc->colUsed & MASKBIT(BMS-1) ){ nColumn += pTable->nCol - BMS + 1; } - pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ; + pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY; /* Construct the Index object to describe this index */ nByte = sizeof(Index); @@ -2027,7 +2100,7 @@ static void constructAutomaticIndex( nByte += nColumn; /* Index.aSortOrder */ pIdx = sqlite3DbMallocZero(pParse->db, nByte); if( pIdx==0 ) return; - pLevel->plan.u.pIdx = pIdx; + pLoop->u.btree.pIndex = pIdx; pIdx->azColl = (char**)&pIdx[1]; pIdx->aiColumn = (int*)&pIdx->azColl[nColumn]; pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn]; @@ -2039,7 +2112,9 @@ static void constructAutomaticIndex( for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; - Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; + Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); + testcase( iCol==BMS-1 ); + testcase( iCol==BMS ); if( (idxCols & cMask)==0 ){ Expr *pX = pTerm->pExpr; idxCols |= cMask; @@ -2050,18 +2125,18 @@ static void constructAutomaticIndex( } } } - assert( (u32)n==pLevel->plan.nEq ); + assert( (u32)n==pLoop->u.btree.nEq ); /* Add additional columns needed to make the automatic index into ** a covering index */ for(i=0; i<mxBitCol; i++){ - if( extraCols & (((Bitmask)1)<<i) ){ + if( extraCols & MASKBIT(i) ){ pIdx->aiColumn[n] = i; pIdx->azColl[n] = "BINARY"; n++; } } - if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){ + if( pSrc->colUsed & MASKBIT(BMS-1) ){ for(i=BMS-1; i<pTable->nCol; i++){ pIdx->aiColumn[n] = i; pIdx->azColl[n] = "BINARY"; @@ -2073,6 +2148,7 @@ static void constructAutomaticIndex( /* Create the automatic index */ pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx); assert( pLevel->iIdxCur>=0 ); + pLevel->iIdxCur = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0, (char*)pKeyinfo, P4_KEYINFO_HANDOFF); VdbeComment((v, "for %s", pTable->zName)); @@ -2099,11 +2175,12 @@ static void constructAutomaticIndex( ** responsibility of the caller to eventually release the structure ** by passing the pointer returned by this function to sqlite3_free(). */ -static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ - Parse *pParse = p->pParse; - WhereClause *pWC = p->pWC; - struct SrcList_item *pSrc = p->pSrc; - ExprList *pOrderBy = p->pOrderBy; +static sqlite3_index_info *allocateIndexInfo( + Parse *pParse, + WhereClause *pWC, + struct SrcList_item *pSrc, + ExprList *pOrderBy +){ int i, j; int nTerm; struct sqlite3_index_constraint *pIdxCons; @@ -2113,8 +2190,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ int nOrderBy; sqlite3_index_info *pIdxInfo; - WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName)); - /* Count the number of possible WHERE clause constraints referring ** to this virtual table */ for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ @@ -2150,7 +2225,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ + sizeof(*pIdxOrderBy)*nOrderBy ); if( pIdxInfo==0 ){ sqlite3ErrorMsg(pParse, "out of memory"); - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ return 0; } @@ -2206,8 +2280,8 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ /* ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() -** method of the virtual table with the sqlite3_index_info pointer passed -** as the argument. +** method of the virtual table with the sqlite3_index_info object that +** comes in as the 3rd argument to this function. ** ** If an error occurs, pParse is populated with an error message and a ** non-zero value is returned. Otherwise, 0 is returned and the output @@ -2222,7 +2296,6 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ int i; int rc; - WHERETRACE(("xBestIndex for %s\n", pTab->zName)); TRACE_IDX_INPUTS(p); rc = pVtab->pModule->xBestIndex(pVtab, p); TRACE_IDX_OUTPUTS(p); @@ -2248,208 +2321,9 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ return pParse->nErr; } +#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ -/* -** Compute the best index for a virtual table. -** -** The best index is computed by the xBestIndex method of the virtual -** table module. This routine is really just a wrapper that sets up -** the sqlite3_index_info structure that is used to communicate with -** xBestIndex. -** -** In a join, this routine might be called multiple times for the -** same virtual table. The sqlite3_index_info structure is created -** and initialized on the first invocation and reused on all subsequent -** invocations. The sqlite3_index_info structure is also used when -** code is generated to access the virtual table. The whereInfoDelete() -** routine takes care of freeing the sqlite3_index_info structure after -** everybody has finished with it. -*/ -static void bestVirtualIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - Table *pTab = pSrc->pTab; - sqlite3_index_info *pIdxInfo; - struct sqlite3_index_constraint *pIdxCons; - struct sqlite3_index_constraint_usage *pUsage; - WhereTerm *pTerm; - int i, j; - int nOrderBy; - int bAllowIN; /* Allow IN optimizations */ - double rCost; - - /* Make sure wsFlags is initialized to some sane value. Otherwise, if the - ** malloc in allocateIndexInfo() fails and this function returns leaving - ** wsFlags in an uninitialized state, the caller may behave unpredictably. - */ - memset(&p->cost, 0, sizeof(p->cost)); - p->cost.plan.wsFlags = WHERE_VIRTUALTABLE; - - /* If the sqlite3_index_info structure has not been previously - ** allocated and initialized, then allocate and initialize it now. - */ - pIdxInfo = *p->ppIdxInfo; - if( pIdxInfo==0 ){ - *p->ppIdxInfo = pIdxInfo = allocateIndexInfo(p); - } - if( pIdxInfo==0 ){ - return; - } - - /* At this point, the sqlite3_index_info structure that pIdxInfo points - ** to will have been initialized, either during the current invocation or - ** during some prior invocation. Now we just have to customize the - ** details of pIdxInfo for the current invocation and pass it to - ** xBestIndex. - */ - - /* The module name must be defined. Also, by this point there must - ** be a pointer to an sqlite3_vtab structure. Otherwise - ** sqlite3ViewGetColumnNames() would have picked up the error. - */ - assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( sqlite3GetVTable(pParse->db, pTab) ); - - /* Try once or twice. On the first attempt, allow IN optimizations. - ** If an IN optimization is accepted by the virtual table xBestIndex - ** method, but the pInfo->aConstrainUsage.omit flag is not set, then - ** the query will not work because it might allow duplicate rows in - ** output. In that case, run the xBestIndex method a second time - ** without the IN constraints. Usually this loop only runs once. - ** The loop will exit using a "break" statement. - */ - for(bAllowIN=1; 1; bAllowIN--){ - assert( bAllowIN==0 || bAllowIN==1 ); - - /* Set the aConstraint[].usable fields and initialize all - ** output variables to zero. - ** - ** aConstraint[].usable is true for constraints where the right-hand - ** side contains only references to tables to the left of the current - ** table. In other words, if the constraint is of the form: - ** - ** column = expr - ** - ** and we are evaluating a join, then the constraint on column is - ** only valid if all tables referenced in expr occur to the left - ** of the table containing column. - ** - ** The aConstraints[] array contains entries for all constraints - ** on the current table. That way we only have to compute it once - ** even though we might try to pick the best index multiple times. - ** For each attempt at picking an index, the order of tables in the - ** join might be different so we have to recompute the usable flag - ** each time. - */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pUsage = pIdxInfo->aConstraintUsage; - for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - if( (pTerm->prereqRight&p->notReady)==0 - && (bAllowIN || (pTerm->eOperator & WO_IN)==0) - ){ - pIdxCons->usable = 1; - }else{ - pIdxCons->usable = 0; - } - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); - nOrderBy = pIdxInfo->nOrderBy; - if( !p->pOrderBy ){ - pIdxInfo->nOrderBy = 0; - } - - if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ - return; - } - - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ - if( pUsage[i].argvIndex>0 ){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - p->cost.used |= pTerm->prereqRight; - if( (pTerm->eOperator & WO_IN)!=0 ){ - if( pUsage[i].omit==0 ){ - /* Do not attempt to use an IN constraint if the virtual table - ** says that the equivalent EQ constraint cannot be safely omitted. - ** If we do attempt to use such a constraint, some rows might be - ** repeated in the output. */ - break; - } - /* A virtual table that is constrained by an IN clause may not - ** consume the ORDER BY clause because (1) the order of IN terms - ** is not necessarily related to the order of output terms and - ** (2) Multiple outputs from a single IN value will not merge - ** together. */ - pIdxInfo->orderByConsumed = 0; - } - } - } - if( i>=pIdxInfo->nConstraint ) break; - } - - /* The orderByConsumed signal is only valid if all outer loops collectively - ** generate just a single row of output. - */ - if( pIdxInfo->orderByConsumed ){ - for(i=0; i<p->i; i++){ - if( (p->aLevel[i].plan.wsFlags & WHERE_UNIQUE)==0 ){ - pIdxInfo->orderByConsumed = 0; - } - } - } - - /* If there is an ORDER BY clause, and the selected virtual table index - ** does not satisfy it, increase the cost of the scan accordingly. This - ** matches the processing for non-virtual tables in bestBtreeIndex(). - */ - rCost = pIdxInfo->estimatedCost; - if( p->pOrderBy && pIdxInfo->orderByConsumed==0 ){ - rCost += estLog(rCost)*rCost; - } - - /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the - ** inital value of lowestCost in this loop. If it is, then the - ** (cost<lowestCost) test below will never be true. - ** - ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT - ** is defined. - */ - if( (SQLITE_BIG_DBL/((double)2))<rCost ){ - p->cost.rCost = (SQLITE_BIG_DBL/((double)2)); - }else{ - p->cost.rCost = rCost; - } - p->cost.plan.u.pVtabIdx = pIdxInfo; - if( pIdxInfo->orderByConsumed ){ - p->cost.plan.wsFlags |= WHERE_ORDERED; - p->cost.plan.nOBSat = nOrderBy; - }else{ - p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0; - } - p->cost.plan.nEq = 0; - pIdxInfo->nOrderBy = nOrderBy; - - /* Try to find a more efficient access pattern by using multiple indexes - ** to optimize an OR expression within the WHERE clause. - */ - bestOrClauseIndex(p); -} -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - #ifdef SQLITE_ENABLE_STAT3 /* ** Estimate the location of a particular key among all keys in an @@ -2535,9 +2409,10 @@ static int whereKeyStats( assert( pColl->enc==SQLITE_UTF8 ); }else{ pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl); - if( pColl==0 ){ - return SQLITE_ERROR; - } + /* If the collating sequence was unavailable, we should have failed + ** long ago and never reached this point. But we'll check just to + ** be doubly sure. */ + if( NEVER(pColl==0) ) return SQLITE_ERROR; z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); if( !z ){ return SQLITE_NOMEM; @@ -2690,7 +2565,7 @@ static int whereRangeScanEst( int nEq, /* index into p->aCol[] of the range-compared column */ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - double *pRangeDiv /* OUT: Reduce search space by this divisor */ + WhereCost *pRangeDiv /* OUT: Reduce search space by this divisor */ ){ int rc = SQLITE_OK; @@ -2728,13 +2603,13 @@ static int whereRangeScanEst( sqlite3ValueFree(pRangeVal); } if( rc==SQLITE_OK ){ - if( iUpper<=iLower ){ - *pRangeDiv = (double)p->aiRowEst[0]; - }else{ - *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower); + WhereCost iBase = whereCost(p->aiRowEst[0]); + if( iUpper>iLower ){ + iBase -= whereCost(iUpper - iLower); } - WHERETRACE(("range scan regions: %u..%u div=%g\n", - (u32)iLower, (u32)iUpper, *pRangeDiv)); + *pRangeDiv = iBase; + WHERETRACE(0x100, ("range scan regions: %u..%u div=%d\n", + (u32)iLower, (u32)iUpper, *pRangeDiv)); return SQLITE_OK; } } @@ -2744,9 +2619,15 @@ static int whereRangeScanEst( UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); - *pRangeDiv = (double)1; - if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4; - if( pUpper ) *pRangeDiv *= (double)4; + *pRangeDiv = 0; + /* TUNING: Each inequality constraint reduces the search space 4-fold. + ** A BETWEEN operator, therefore, reduces the search space 16-fold */ + if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ){ + *pRangeDiv += 20; assert( 20==whereCost(4) ); + } + if( pUpper ){ + *pRangeDiv += 20; assert( 20==whereCost(4) ); + } return rc; } @@ -2772,7 +2653,7 @@ static int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ - double *pnRow /* Write the revised row estimate here */ + tRowcnt *pnRow /* Write the revised row estimate here */ ){ sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ u8 aff; /* Column affinity */ @@ -2791,7 +2672,7 @@ static int whereEqualScanEst( if( pRhs==0 ) return SQLITE_NOTFOUND; rc = whereKeyStats(pParse, p, pRhs, 0, a); if( rc==SQLITE_OK ){ - WHERETRACE(("equality scan regions: %d\n", (int)a[1])); + WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1])); *pnRow = a[1]; } whereEqualScanEst_cancel: @@ -2821,12 +2702,12 @@ static int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ - double *pnRow /* Write the revised row estimate here */ + tRowcnt *pnRow /* Write the revised row estimate here */ ){ - int rc = SQLITE_OK; /* Subfunction return code */ - double nEst; /* Number of rows for a single term */ - double nRowEst = (double)0; /* New estimate of the number of rows */ - int i; /* Loop counter */ + int rc = SQLITE_OK; /* Subfunction return code */ + tRowcnt nEst; /* Number of rows for a single term */ + tRowcnt nRowEst = 0; /* New estimate of the number of rows */ + int i; /* Loop counter */ assert( p->aSample!=0 ); for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){ @@ -2837,886 +2718,13 @@ static int whereInScanEst( if( rc==SQLITE_OK ){ if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; - WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); + WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst)); } return rc; } #endif /* defined(SQLITE_ENABLE_STAT3) */ /* -** Check to see if column iCol of the table with cursor iTab will appear -** in sorted order according to the current query plan. -** -** Return values: -** -** 0 iCol is not ordered -** 1 iCol has only a single value -** 2 iCol is in ASC order -** 3 iCol is in DESC order -*/ -static int isOrderedColumn( - WhereBestIdx *p, - int iTab, - int iCol -){ - int i, j; - WhereLevel *pLevel = &p->aLevel[p->i-1]; - Index *pIdx; - u8 sortOrder; - for(i=p->i-1; i>=0; i--, pLevel--){ - if( pLevel->iTabCur!=iTab ) continue; - if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - return 1; - } - assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 ); - if( (pIdx = pLevel->plan.u.pIdx)!=0 ){ - if( iCol<0 ){ - sortOrder = 0; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - }else{ - int n = pIdx->nColumn; - for(j=0; j<n; j++){ - if( iCol==pIdx->aiColumn[j] ) break; - } - if( j>=n ) return 0; - sortOrder = pIdx->aSortOrder[j]; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - } - }else{ - if( iCol!=(-1) ) return 0; - sortOrder = 0; - testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ); - } - if( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ){ - assert( sortOrder==0 || sortOrder==1 ); - testcase( sortOrder==1 ); - sortOrder = 1 - sortOrder; - } - return sortOrder+2; - } - return 0; -} - -/* -** This routine decides if pIdx can be used to satisfy the ORDER BY -** clause, either in whole or in part. The return value is the -** cumulative number of terms in the ORDER BY clause that are satisfied -** by the index pIdx and other indices in outer loops. -** -** The table being queried has a cursor number of "base". pIdx is the -** index that is postulated for use to access the table. -** -** The *pbRev value is set to 0 order 1 depending on whether or not -** pIdx should be run in the forward order or in reverse order. -*/ -static int isSortingIndex( - WhereBestIdx *p, /* Best index search context */ - Index *pIdx, /* The index we are testing */ - int base, /* Cursor number for the table to be sorted */ - int *pbRev, /* Set to 1 for reverse-order scan of pIdx */ - int *pbObUnique /* ORDER BY column values will different in every row */ -){ - int i; /* Number of pIdx terms used */ - int j; /* Number of ORDER BY terms satisfied */ - int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */ - int nTerm; /* Number of ORDER BY terms */ - struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */ - Table *pTab = pIdx->pTable; /* Table that owns index pIdx */ - ExprList *pOrderBy; /* The ORDER BY clause */ - Parse *pParse = p->pParse; /* Parser context */ - sqlite3 *db = pParse->db; /* Database connection */ - int nPriorSat; /* ORDER BY terms satisfied by outer loops */ - int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ - int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */ - int outerObUnique; /* Outer loops generate different values in - ** every row for the ORDER BY columns */ - - if( p->i==0 ){ - nPriorSat = 0; - outerObUnique = 1; - }else{ - u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags; - nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - if( (wsFlags & WHERE_ORDERED)==0 ){ - /* This loop cannot be ordered unless the next outer loop is - ** also ordered */ - return nPriorSat; - } - if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){ - /* Only look at the outer-most loop if the OrderByIdxJoin - ** optimization is disabled */ - return nPriorSat; - } - testcase( wsFlags & WHERE_OB_UNIQUE ); - testcase( wsFlags & WHERE_ALL_UNIQUE ); - outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0; - } - pOrderBy = p->pOrderBy; - assert( pOrderBy!=0 ); - if( pIdx->bUnordered ){ - /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot - ** be used for sorting */ - return nPriorSat; - } - nTerm = pOrderBy->nExpr; - uniqueNotNull = pIdx->onError!=OE_None; - assert( nTerm>0 ); - - /* Argument pIdx must either point to a 'real' named index structure, - ** or an index structure allocated on the stack by bestBtreeIndex() to - ** represent the rowid index that is part of every table. */ - assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); - - /* Match terms of the ORDER BY clause against columns of - ** the index. - ** - ** Note that indices have pIdx->nColumn regular columns plus - ** one additional column containing the rowid. The rowid column - ** of the index is also allowed to match against the ORDER BY - ** clause. - */ - j = nPriorSat; - for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){ - Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */ - CollSeq *pColl; /* The collating sequence of pOBExpr */ - int termSortOrder; /* Sort order for this term */ - int iColumn; /* The i-th column of the index. -1 for rowid */ - int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */ - int isEq; /* Subject to an == or IS NULL constraint */ - int isMatch; /* ORDER BY term matches the index term */ - const char *zColl; /* Name of collating sequence for i-th index term */ - WhereTerm *pConstraint; /* A constraint in the WHERE clause */ - - /* If the next term of the ORDER BY clause refers to anything other than - ** a column in the "base" table, then this index will not be of any - ** further use in handling the ORDER BY. */ - pOBExpr = sqlite3ExprSkipCollate(pOBItem->pExpr); - if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){ - break; - } - - /* Find column number and collating sequence for the next entry - ** in the index */ - if( pIdx->zName && i<pIdx->nColumn ){ - iColumn = pIdx->aiColumn[i]; - if( iColumn==pIdx->pTable->iPKey ){ - iColumn = -1; - } - iSortOrder = pIdx->aSortOrder[i]; - zColl = pIdx->azColl[i]; - assert( zColl!=0 ); - }else{ - iColumn = -1; - iSortOrder = 0; - zColl = 0; - } - - /* Check to see if the column number and collating sequence of the - ** index match the column number and collating sequence of the ORDER BY - ** clause entry. Set isMatch to 1 if they both match. */ - if( pOBExpr->iColumn==iColumn ){ - if( zColl ){ - pColl = sqlite3ExprCollSeq(pParse, pOBItem->pExpr); - if( !pColl ) pColl = db->pDfltColl; - isMatch = sqlite3StrICmp(pColl->zName, zColl)==0; - }else{ - isMatch = 1; - } - }else{ - isMatch = 0; - } - - /* termSortOrder is 0 or 1 for whether or not the access loop should - ** run forward or backwards (respectively) in order to satisfy this - ** term of the ORDER BY clause. */ - assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 ); - assert( iSortOrder==0 || iSortOrder==1 ); - termSortOrder = iSortOrder ^ pOBItem->sortOrder; - - /* If X is the column in the index and ORDER BY clause, check to see - ** if there are any X= or X IS NULL constraints in the WHERE clause. */ - pConstraint = findTerm(p->pWC, base, iColumn, p->notReady, - WO_EQ|WO_ISNULL|WO_IN, pIdx); - if( pConstraint==0 ){ - isEq = 0; - }else if( (pConstraint->eOperator & WO_IN)!=0 ){ - isEq = 0; - }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ - uniqueNotNull = 0; - isEq = 1; /* "X IS NULL" means X has only a single value */ - }else if( pConstraint->prereqRight==0 ){ - isEq = 1; /* Constraint "X=constant" means X has only a single value */ - }else{ - Expr *pRight = pConstraint->pExpr->pRight; - if( pRight->op==TK_COLUMN ){ - WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)", - pRight->iTable, pRight->iColumn)); - isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn); - WHERETRACE((" -> isEq=%d\n", isEq)); - - /* If the constraint is of the form X=Y where Y is an ordered value - ** in an outer loop, then make sure the sort order of Y matches the - ** sort order required for X. */ - if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){ - testcase( isEq==2 ); - testcase( isEq==3 ); - break; - } - }else{ - isEq = 0; /* "X=expr" places no ordering constraints on X */ - } - } - if( !isMatch ){ - if( isEq==0 ){ - break; - }else{ - continue; - } - }else if( isEq!=1 ){ - if( sortOrder==2 ){ - sortOrder = termSortOrder; - }else if( termSortOrder!=sortOrder ){ - break; - } - } - j++; - pOBItem++; - if( iColumn<0 ){ - seenRowid = 1; - break; - }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){ - testcase( isEq==0 ); - testcase( isEq==2 ); - testcase( isEq==3 ); - uniqueNotNull = 0; - } - } - if( seenRowid ){ - uniqueNotNull = 1; - }else if( uniqueNotNull==0 || i<pIdx->nColumn ){ - uniqueNotNull = 0; - } - - /* If we have not found at least one ORDER BY term that matches the - ** index, then show no progress. */ - if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat; - - /* Either the outer queries must generate rows where there are no two - ** rows with the same values in all ORDER BY columns, or else this - ** loop must generate just a single row of output. Example: Suppose - ** the outer loops generate A=1 and A=1, and this loop generates B=3 - ** and B=4. Then without the following test, ORDER BY A,B would - ** generate the wrong order output: 1,3 1,4 1,3 1,4 - */ - if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat; - *pbObUnique = uniqueNotNull; - - /* Return the necessary scan order back to the caller */ - *pbRev = sortOrder & 1; - - /* If there was an "ORDER BY rowid" term that matched, or it is only - ** possible for a single row from this table to match, then skip over - ** any additional ORDER BY terms dealing with this table. - */ - if( uniqueNotNull ){ - /* Advance j over additional ORDER BY terms associated with base */ - WhereMaskSet *pMS = p->pWC->pMaskSet; - Bitmask m = ~getMask(pMS, base); - while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){ - j++; - } - } - return j; -} - -/* -** Find the best query plan for accessing a particular table. Write the -** best query plan and its cost into the p->cost. -** -** The lowest cost plan wins. The cost is an estimate of the amount of -** CPU and disk I/O needed to process the requested result. -** Factors that influence cost include: -** -** * The estimated number of rows that will be retrieved. (The -** fewer the better.) -** -** * Whether or not sorting must occur. -** -** * Whether or not there must be separate lookups in the -** index and in the main table. -** -** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in -** the SQL statement, then this function only considers plans using the -** named index. If no such plan is found, then the returned cost is -** SQLITE_BIG_DBL. If a plan is found that uses the named index, -** then the cost is calculated in the usual way. -** -** If a NOT INDEXED clause was attached to the table -** in the SELECT statement, then no indexes are considered. However, the -** selected plan may still take advantage of the built-in rowid primary key -** index. -*/ -static void bestBtreeIndex(WhereBestIdx *p){ - Parse *pParse = p->pParse; /* The parsing context */ - WhereClause *pWC = p->pWC; /* The WHERE clause */ - struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */ - int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ - Index *pProbe; /* An index we are evaluating */ - Index *pIdx; /* Copy of pProbe, or zero for IPK index */ - int eqTermMask; /* Current mask of valid equality operators */ - int idxEqTermMask; /* Index mask of valid equality operators */ - Index sPk; /* A fake index object for the primary key */ - tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ - int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ - int wsFlagMask; /* Allowed flags in p->cost.plan.wsFlag */ - int nPriorSat; /* ORDER BY terms satisfied by outer loops */ - int nOrderBy; /* Number of ORDER BY terms */ - char bSortInit; /* Initializer for bSort in inner loop */ - char bDistInit; /* Initializer for bDist in inner loop */ - - - /* Initialize the cost to a worst-case value */ - memset(&p->cost, 0, sizeof(p->cost)); - p->cost.rCost = SQLITE_BIG_DBL; - - /* If the pSrc table is the right table of a LEFT JOIN then we may not - ** use an index to satisfy IS NULL constraints on that table. This is - ** because columns might end up being NULL if the table does not match - - ** a circumstance which the index cannot help us discover. Ticket #2177. - */ - if( pSrc->jointype & JT_LEFT ){ - idxEqTermMask = WO_EQ|WO_IN; - }else{ - idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL; - } - - if( pSrc->pIndex ){ - /* An INDEXED BY clause specifies a particular index to use */ - pIdx = pProbe = pSrc->pIndex; - wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); - eqTermMask = idxEqTermMask; - }else{ - /* There is no INDEXED BY clause. Create a fake Index object in local - ** variable sPk to represent the rowid primary key index. Make this - ** fake index the first in a chain of Index objects with all of the real - ** indices to follow */ - Index *pFirst; /* First of real indices on the table */ - memset(&sPk, 0, sizeof(Index)); - sPk.nColumn = 1; - sPk.aiColumn = &aiColumnPk; - sPk.aiRowEst = aiRowEstPk; - sPk.onError = OE_Replace; - sPk.pTable = pSrc->pTab; - aiRowEstPk[0] = pSrc->pTab->nRowEst; - aiRowEstPk[1] = 1; - pFirst = pSrc->pTab->pIndex; - if( pSrc->notIndexed==0 ){ - /* The real indices of the table are only considered if the - ** NOT INDEXED qualifier is omitted from the FROM clause */ - sPk.pNext = pFirst; - } - pProbe = &sPk; - wsFlagMask = ~( - WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE - ); - eqTermMask = WO_EQ|WO_IN; - pIdx = 0; - } - - nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0; - if( p->i ){ - nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - bSortInit = nPriorSat<nOrderBy; - bDistInit = 0; - }else{ - nPriorSat = 0; - bSortInit = nOrderBy>0; - bDistInit = p->pDistinct!=0; - } - - /* Loop over all indices looking for the best one to use - */ - for(; pProbe; pIdx=pProbe=pProbe->pNext){ - const tRowcnt * const aiRowEst = pProbe->aiRowEst; - WhereCost pc; /* Cost of using pProbe */ - double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */ - - /* The following variables are populated based on the properties of - ** index being evaluated. They are then used to determine the expected - ** cost and number of rows returned. - ** - ** pc.plan.nEq: - ** Number of equality terms that can be implemented using the index. - ** In other words, the number of initial fields in the index that - ** are used in == or IN or NOT NULL constraints of the WHERE clause. - ** - ** nInMul: - ** The "in-multiplier". This is an estimate of how many seek operations - ** SQLite must perform on the index in question. For example, if the - ** WHERE clause is: - ** - ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6) - ** - ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is - ** set to 9. Given the same schema and either of the following WHERE - ** clauses: - ** - ** WHERE a = 1 - ** WHERE a >= 2 - ** - ** nInMul is set to 1. - ** - ** If there exists a WHERE term of the form "x IN (SELECT ...)", then - ** the sub-select is assumed to return 25 rows for the purposes of - ** determining nInMul. - ** - ** bInEst: - ** Set to true if there was at least one "x IN (SELECT ...)" term used - ** in determining the value of nInMul. Note that the RHS of the - ** IN operator must be a SELECT, not a value list, for this variable - ** to be true. - ** - ** rangeDiv: - ** An estimate of a divisor by which to reduce the search space due - ** to inequality constraints. In the absence of sqlite_stat3 ANALYZE - ** data, a single inequality reduces the search space to 1/4rd its - ** original size (rangeDiv==4). Two inequalities reduce the search - ** space to 1/16th of its original size (rangeDiv==16). - ** - ** bSort: - ** Boolean. True if there is an ORDER BY clause that will require an - ** external sort (i.e. scanning the index being evaluated will not - ** correctly order records). - ** - ** bDist: - ** Boolean. True if there is a DISTINCT clause that will require an - ** external btree. - ** - ** bLookup: - ** Boolean. True if a table lookup is required for each index entry - ** visited. In other words, true if this is not a covering index. - ** This is always false for the rowid primary key index of a table. - ** For other indexes, it is true unless all the columns of the table - ** used by the SELECT statement are present in the index (such an - ** index is sometimes described as a covering index). - ** For example, given the index on (a, b), the second of the following - ** two queries requires table b-tree lookups in order to find the value - ** of column c, but the first does not because columns a and b are - ** both available in the index. - ** - ** SELECT a, b FROM tbl WHERE a = 1; - ** SELECT a, b, c FROM tbl WHERE a = 1; - */ - int bInEst = 0; /* True if "x IN (SELECT...)" seen */ - int nInMul = 1; /* Number of distinct equalities to lookup */ - double rangeDiv = (double)1; /* Estimated reduction in search space */ - int nBound = 0; /* Number of range constraints seen */ - char bSort = bSortInit; /* True if external sort required */ - char bDist = bDistInit; /* True if index cannot help with DISTINCT */ - char bLookup = 0; /* True if not a covering index */ - WhereTerm *pTerm; /* A single term of the WHERE clause */ -#ifdef SQLITE_ENABLE_STAT3 - WhereTerm *pFirstTerm = 0; /* First term matching the index */ -#endif - - WHERETRACE(( - " %s(%s):\n", - pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk") - )); - memset(&pc, 0, sizeof(pc)); - pc.plan.nOBSat = nPriorSat; - - /* Determine the values of pc.plan.nEq and nInMul */ - for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){ - int j = pProbe->aiColumn[pc.plan.nEq]; - pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx); - if( pTerm==0 ) break; - pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); - testcase( pTerm->pWC!=pWC ); - if( pTerm->eOperator & WO_IN ){ - Expr *pExpr = pTerm->pExpr; - pc.plan.wsFlags |= WHERE_COLUMN_IN; - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ - nInMul *= 25; - bInEst = 1; - }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ - /* "x IN (value, value, ...)" */ - nInMul *= pExpr->x.pList->nExpr; - } - }else if( pTerm->eOperator & WO_ISNULL ){ - pc.plan.wsFlags |= WHERE_COLUMN_NULL; - } -#ifdef SQLITE_ENABLE_STAT3 - if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; -#endif - pc.used |= pTerm->prereqRight; - } - - /* If the index being considered is UNIQUE, and there is an equality - ** constraint for all columns in the index, then this search will find - ** at most a single row. In this case set the WHERE_UNIQUE flag to - ** indicate this to the caller. - ** - ** Otherwise, if the search may find more than one row, test to see if - ** there is a range constraint on indexed column (pc.plan.nEq+1) that - ** can be optimized using the index. - */ - if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ - testcase( pc.plan.wsFlags & WHERE_COLUMN_IN ); - testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL ); - if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - pc.plan.wsFlags |= WHERE_UNIQUE; - if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - pc.plan.wsFlags |= WHERE_ALL_UNIQUE; - } - } - }else if( pProbe->bUnordered==0 ){ - int j; - j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]); - if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ - WhereTerm *pTop, *pBtm; - pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx); - pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx); - whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv); - if( pTop ){ - nBound = 1; - pc.plan.wsFlags |= WHERE_TOP_LIMIT; - pc.used |= pTop->prereqRight; - testcase( pTop->pWC!=pWC ); - } - if( pBtm ){ - nBound++; - pc.plan.wsFlags |= WHERE_BTM_LIMIT; - pc.used |= pBtm->prereqRight; - testcase( pBtm->pWC!=pWC ); - } - pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); - } - } - - /* If there is an ORDER BY clause and the index being considered will - ** naturally scan rows in the required order, set the appropriate flags - ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but - ** the index will scan rows in a different order, set the bSort - ** variable. */ - if( bSort && (pSrc->jointype & JT_LEFT)==0 ){ - int bRev = 2; - int bObUnique = 0; - WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat)); - pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique); - WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n", - bRev, bObUnique, pc.plan.nOBSat)); - if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ - pc.plan.wsFlags |= WHERE_ORDERED; - if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE; - } - if( nOrderBy==pc.plan.nOBSat ){ - bSort = 0; - pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE; - } - if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE; - } - - /* If there is a DISTINCT qualifier and this index will scan rows in - ** order of the DISTINCT expressions, clear bDist and set the appropriate - ** flags in pc.plan.wsFlags. */ - if( bDist - && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq) - && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0 - ){ - bDist = 0; - pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT; - } - - /* If currently calculating the cost of using an index (not the IPK - ** index), determine if all required column data may be obtained without - ** using the main table (i.e. if the index is a covering - ** index for this query). If it is, set the WHERE_IDX_ONLY flag in - ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */ - if( pIdx ){ - Bitmask m = pSrc->colUsed; - int j; - for(j=0; j<pIdx->nColumn; j++){ - int x = pIdx->aiColumn[j]; - if( x<BMS-1 ){ - m &= ~(((Bitmask)1)<<x); - } - } - if( m==0 ){ - pc.plan.wsFlags |= WHERE_IDX_ONLY; - }else{ - bLookup = 1; - } - } - - /* - ** Estimate the number of rows of output. For an "x IN (SELECT...)" - ** constraint, do not let the estimate exceed half the rows in the table. - */ - pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul); - if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){ - pc.plan.nRow = aiRowEst[0]/2; - nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]); - } - -#ifdef SQLITE_ENABLE_STAT3 - /* If the constraint is of the form x=VALUE or x IN (E1,E2,...) - ** and we do not think that values of x are unique and if histogram - ** data is available for column x, then it might be possible - ** to get a better estimate on the number of rows based on - ** VALUE and how common that value is according to the histogram. - */ - if( pc.plan.nRow>(double)1 && pc.plan.nEq==1 - && pFirstTerm!=0 && aiRowEst[1]>1 ){ - assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); - if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ - testcase( pFirstTerm->eOperator & WO_EQ ); - testcase( pFirstTerm->eOperator & WO_EQUIV ); - testcase( pFirstTerm->eOperator & WO_ISNULL ); - whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, - &pc.plan.nRow); - }else if( bInEst==0 ){ - assert( pFirstTerm->eOperator & WO_IN ); - whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, - &pc.plan.nRow); - } - } -#endif /* SQLITE_ENABLE_STAT3 */ - - /* Adjust the number of output rows and downward to reflect rows - ** that are excluded by range constraints. - */ - pc.plan.nRow = pc.plan.nRow/rangeDiv; - if( pc.plan.nRow<1 ) pc.plan.nRow = 1; - - /* Experiments run on real SQLite databases show that the time needed - ** to do a binary search to locate a row in a table or index is roughly - ** log10(N) times the time to move from one row to the next row within - ** a table or index. The actual times can vary, with the size of - ** records being an important factor. Both moves and searches are - ** slower with larger records, presumably because fewer records fit - ** on one page and hence more pages have to be fetched. - ** - ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do - ** not give us data on the relative sizes of table and index records. - ** So this computation assumes table records are about twice as big - ** as index records - */ - if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE)) - ==WHERE_IDX_ONLY - && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 - && sqlite3GlobalConfig.bUseCis - && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan) - ){ - /* This index is not useful for indexing, but it is a covering index. - ** A full-scan of the index might be a little faster than a full-scan - ** of the table, so give this case a cost slightly less than a table - ** scan. */ - pc.rCost = aiRowEst[0]*3 + pProbe->nColumn; - pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE; - }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ - /* The cost of a full table scan is a number of move operations equal - ** to the number of rows in the table. - ** - ** We add an additional 4x penalty to full table scans. This causes - ** the cost function to err on the side of choosing an index over - ** choosing a full scan. This 4x full-scan penalty is an arguable - ** decision and one which we expect to revisit in the future. But - ** it seems to be working well enough at the moment. - */ - pc.rCost = aiRowEst[0]*4; - pc.plan.wsFlags &= ~WHERE_IDX_ONLY; - if( pIdx ){ - pc.plan.wsFlags &= ~WHERE_ORDERED; - pc.plan.nOBSat = nPriorSat; - } - }else{ - log10N = estLog(aiRowEst[0]); - pc.rCost = pc.plan.nRow; - if( pIdx ){ - if( bLookup ){ - /* For an index lookup followed by a table lookup: - ** nInMul index searches to find the start of each index range - ** + nRow steps through the index - ** + nRow table searches to lookup the table entry using the rowid - */ - pc.rCost += (nInMul + pc.plan.nRow)*log10N; - }else{ - /* For a covering index: - ** nInMul index searches to find the initial entry - ** + nRow steps through the index - */ - pc.rCost += nInMul*log10N; - } - }else{ - /* For a rowid primary key lookup: - ** nInMult table searches to find the initial entry for each range - ** + nRow steps through the table - */ - pc.rCost += nInMul*log10N; - } - } - - /* Add in the estimated cost of sorting the result. Actual experimental - ** measurements of sorting performance in SQLite show that sorting time - ** adds C*N*log10(N) to the cost, where N is the number of rows to be - ** sorted and C is a factor between 1.95 and 4.3. We will split the - ** difference and select C of 3.0. - */ - if( bSort ){ - double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy); - m *= (double)(pc.plan.nOBSat ? 2 : 3); - pc.rCost += pc.plan.nRow*m; - } - if( bDist ){ - pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3; - } - - /**** Cost of using this index has now been computed ****/ - - /* If there are additional constraints on this table that cannot - ** be used with the current index, but which might lower the number - ** of output rows, adjust the nRow value accordingly. This only - ** matters if the current index is the least costly, so do not bother - ** with this step if we already know this index will not be chosen. - ** Also, never reduce the output row count below 2 using this step. - ** - ** It is critical that the notValid mask be used here instead of - ** the notReady mask. When computing an "optimal" index, the notReady - ** mask will only have one bit set - the bit for the current table. - ** The notValid mask, on the other hand, always has all bits set for - ** tables that are not in outer loops. If notReady is used here instead - ** of notValid, then a optimal index that depends on inner joins loops - ** might be selected even when there exists an optimal index that has - ** no such dependency. - */ - if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){ - int k; /* Loop counter */ - int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */ - int nSkipRange = nBound; /* Number of < constraints to skip */ - Bitmask thisTab; /* Bitmap for pSrc */ - - thisTab = getMask(pWC->pMaskSet, iCur); - for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){ - if( pTerm->wtFlags & TERM_VIRTUAL ) continue; - if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue; - if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ - if( nSkipEq ){ - /* Ignore the first pc.plan.nEq equality matches since the index - ** has already accounted for these */ - nSkipEq--; - }else{ - /* Assume each additional equality match reduces the result - ** set size by a factor of 10 */ - pc.plan.nRow /= 10; - } - }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ - if( nSkipRange ){ - /* Ignore the first nSkipRange range constraints since the index - ** has already accounted for these */ - nSkipRange--; - }else{ - /* Assume each additional range constraint reduces the result - ** set size by a factor of 3. Indexed range constraints reduce - ** the search space by a larger factor: 4. We make indexed range - ** more selective intentionally because of the subjective - ** observation that indexed range constraints really are more - ** selective in practice, on average. */ - pc.plan.nRow /= 3; - } - }else if( (pTerm->eOperator & WO_NOOP)==0 ){ - /* Any other expression lowers the output row count by half */ - pc.plan.nRow /= 2; - } - } - if( pc.plan.nRow<2 ) pc.plan.nRow = 2; - } - - - WHERETRACE(( - " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n" - " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" - " used=0x%llx nOBSat=%d\n", - pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, - p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, - pc.plan.nOBSat - )); - - /* If this index is the best we have seen so far, then record this - ** index and its cost in the p->cost structure. - */ - if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){ - p->cost = pc; - p->cost.plan.wsFlags &= wsFlagMask; - p->cost.plan.u.pIdx = pIdx; - } - - /* If there was an INDEXED BY clause, then only that one index is - ** considered. */ - if( pSrc->pIndex ) break; - - /* Reset masks for the next index in the loop */ - wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); - eqTermMask = idxEqTermMask; - } - - /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag - ** is set, then reverse the order that the index will be scanned - ** in. This is used for application testing, to help find cases - ** where application behavior depends on the (undefined) order that - ** SQLite outputs rows in in the absence of an ORDER BY clause. */ - if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ - p->cost.plan.wsFlags |= WHERE_REVERSE; - } - - assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 ); - assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 ); - assert( pSrc->pIndex==0 - || p->cost.plan.u.pIdx==0 - || p->cost.plan.u.pIdx==pSrc->pIndex - ); - - WHERETRACE((" best index is %s cost=%.1f\n", - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", - p->cost.rCost)); - - bestOrClauseIndex(p); - bestAutomaticIndex(p); - p->cost.plan.wsFlags |= eqTermMask; -} - -/* -** Find the query plan for accessing table pSrc->pTab. Write the -** best query plan and its cost into the WhereCost object supplied -** as the last parameter. This function may calculate the cost of -** both real and virtual table scans. -** -** This function does not take ORDER BY or DISTINCT into account. Nor -** does it remember the virtual table query plan. All it does is compute -** the cost while determining if an OR optimization is applicable. The -** details will be reconsidered later if the optimization is found to be -** applicable. -*/ -static void bestIndex(WhereBestIdx *p){ -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(p->pSrc->pTab) ){ - sqlite3_index_info *pIdxInfo = 0; - p->ppIdxInfo = &pIdxInfo; - bestVirtualIndex(p); - assert( pIdxInfo!=0 || p->pParse->db->mallocFailed ); - if( pIdxInfo && pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - sqlite3DbFree(p->pParse->db, pIdxInfo); - }else -#endif - { - bestBtreeIndex(p); - } -} - -/* ** Disable a term in the WHERE clause. Except, do not disable the term ** if it controls a LEFT OUTER JOIN and it did not originate in the ON ** or USING clause of that join. @@ -3813,6 +2821,7 @@ static int codeEqualityTerm( WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ WhereLevel *pLevel, /* The level of the FROM clause we are working on */ int iEq, /* Index of the equality term within this level */ + int bRev, /* True for reverse-order IN operations */ int iTarget /* Attempt to leave results in this register */ ){ Expr *pX = pTerm->pExpr; @@ -3830,14 +2839,13 @@ static int codeEqualityTerm( int eType; int iTab; struct InLoop *pIn; - u8 bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; + WhereLoop *pLoop = pLevel->pWLoop; - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 - && pLevel->plan.u.pIdx->aSortOrder[iEq] + if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 + && pLoop->u.btree.pIndex!=0 + && pLoop->u.btree.pIndex->aSortOrder[iEq] ){ testcase( iEq==0 ); - testcase( iEq==pLevel->plan.u.pIdx->nColumn-1 ); - testcase( iEq>0 && iEq+1<pLevel->plan.u.pIdx->nColumn ); testcase( bRev ); bRev = !bRev; } @@ -3850,7 +2858,8 @@ static int codeEqualityTerm( } iTab = pX->iTable; sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); - assert( pLevel->plan.wsFlags & WHERE_IN_ABLE ); + assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); + pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ pLevel->addrNxt = sqlite3VdbeMakeLabel(v); } @@ -3920,29 +2929,31 @@ static int codeEqualityTerm( static int codeAllEqualityTerms( Parse *pParse, /* Parsing context */ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ - WhereClause *pWC, /* The WHERE clause */ - Bitmask notReady, /* Which parts of FROM have not yet been coded */ + int bRev, /* Reverse the order of IN operators */ int nExtraReg, /* Number of extra registers to allocate */ char **pzAff /* OUT: Set to point to affinity string */ ){ - int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */ + int nEq; /* The number of == or IN constraints to code */ Vdbe *v = pParse->pVdbe; /* The vm under construction */ Index *pIdx; /* The index being used for this loop */ - int iCur = pLevel->iTabCur; /* The cursor of the table */ WhereTerm *pTerm; /* A single constraint term */ + WhereLoop *pLoop; /* The WhereLoop object */ int j; /* Loop counter */ int regBase; /* Base register */ int nReg; /* Number of registers to allocate */ char *zAff; /* Affinity string to return */ /* This module is only called on query plans that use an index. */ - assert( pLevel->plan.wsFlags & WHERE_INDEXED ); - pIdx = pLevel->plan.u.pIdx; + pLoop = pLevel->pWLoop; + assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ); + nEq = pLoop->u.btree.nEq; + pIdx = pLoop->u.btree.pIndex; + assert( pIdx!=0 ); /* Figure out how many memory cells we will need then allocate them. */ regBase = pParse->nMem + 1; - nReg = pLevel->plan.nEq + nExtraReg; + nReg = pLoop->u.btree.nEq + nExtraReg; pParse->nMem += nReg; zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx)); @@ -3955,14 +2966,13 @@ static int codeAllEqualityTerms( assert( pIdx->nColumn>=nEq ); for(j=0; j<nEq; j++){ int r1; - int k = pIdx->aiColumn[j]; - pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx); - if( pTerm==0 ) break; + pTerm = pLoop->aLTerm[j]; + assert( pTerm!=0 ); /* The following true for indices with redundant columns. ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, regBase+j); + r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j); if( r1!=regBase+j ){ if( nReg==1 ){ sqlite3ReleaseTempReg(pParse, regBase); @@ -4030,16 +3040,15 @@ static void explainAppendTerm( ** It is the responsibility of the caller to free the buffer when it is ** no longer required. */ -static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){ - WherePlan *pPlan = &pLevel->plan; - Index *pIndex = pPlan->u.pIdx; - int nEq = pPlan->nEq; +static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){ + Index *pIndex = pLoop->u.btree.pIndex; + int nEq = pLoop->u.btree.nEq; int i, j; Column *aCol = pTab->aCol; int *aiColumn = pIndex->aiColumn; StrAccum txt; - if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ + if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ return 0; } sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH); @@ -4050,11 +3059,11 @@ static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){ } j = i; - if( pPlan->wsFlags&WHERE_BTM_LIMIT ){ + if( pLoop->wsFlags&WHERE_BTM_LIMIT ){ char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; explainAppendTerm(&txt, i++, z, ">"); } - if( pPlan->wsFlags&WHERE_TOP_LIMIT ){ + if( pLoop->wsFlags&WHERE_TOP_LIMIT ){ char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName; explainAppendTerm(&txt, i, z, "<"); } @@ -4077,20 +3086,22 @@ static void explainOneScan( u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ if( pParse->explain==2 ){ - u32 flags = pLevel->plan.wsFlags; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ char *zMsg; /* Text to add to EQP output */ - sqlite3_int64 nRow; /* Expected number of rows visited by scan */ int iId = pParse->iSelectId; /* Select id (left-most output column) */ int isSearch; /* True for a SEARCH. False for SCAN. */ + WhereLoop *pLoop; /* The controlling WhereLoop object */ + u32 flags; /* Flags that describe this loop */ + pLoop = pLevel->pWLoop; + flags = pLoop->wsFlags; if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return; - isSearch = (pLevel->plan.nEq>0) - || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 - || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); + isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 + || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) + || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN"); if( pItem->pSelect ){ @@ -4102,43 +3113,38 @@ static void explainOneScan( if( pItem->zAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } - if( (flags & WHERE_INDEXED)!=0 ){ - char *zWhere = explainIndexRange(db, pLevel, pItem->pTab); + if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 + && ALWAYS(pLoop->u.btree.pIndex!=0) + ){ + char *zWhere = explainIndexRange(db, pLoop, pItem->pTab); zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg, ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""), ((flags & WHERE_IDX_ONLY)?"COVERING ":""), ((flags & WHERE_TEMP_INDEX)?"":" "), - ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName), + ((flags & WHERE_TEMP_INDEX)?"": pLoop->u.btree.pIndex->zName), zWhere ); sqlite3DbFree(db, zWhere); - }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ + }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); - if( flags&WHERE_ROWID_EQ ){ + if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg); }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg); }else if( flags&WHERE_BTM_LIMIT ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg); - }else if( flags&WHERE_TOP_LIMIT ){ + }else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg); } } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ - sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, - pVtabIdx->idxNum, pVtabIdx->idxStr); + pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif - if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){ - testcase( wctrlFlags & WHERE_ORDERBY_MIN ); - nRow = 1; - }else{ - nRow = (sqlite3_int64)pLevel->plan.nRow; - } - zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow); + zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg); sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC); } } @@ -4154,7 +3160,6 @@ static void explainOneScan( static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ - u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */ Bitmask notReady /* Which tables are currently available */ ){ int j, k; /* Loop counters */ @@ -4163,6 +3168,7 @@ static Bitmask codeOneLoopStart( int omitTable; /* True if we use the index only */ int bRev; /* True if we need to scan in reverse order */ WhereLevel *pLevel; /* The where level to be coded */ + WhereLoop *pLoop; /* The WhereLoop object being coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ Parse *pParse; /* Parsing context */ @@ -4176,13 +3182,14 @@ static Bitmask codeOneLoopStart( pParse = pWInfo->pParse; v = pParse->pVdbe; - pWC = pWInfo->pWC; + pWC = &pWInfo->sWC; pLevel = &pWInfo->a[iLevel]; + pLoop = pLevel->pWLoop; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; - bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; - omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0 - && (wctrlFlags & WHERE_FORCE_TABLE)==0; + bRev = (pWInfo->revMask>>iLevel)&1; + omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 + && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0; VdbeNoopComment((v, "Begin Join Loop %d", iLevel)); /* Create labels for the "break" and "continue" instructions @@ -4219,47 +3226,37 @@ static Bitmask codeOneLoopStart( }else #ifndef SQLITE_OMIT_VIRTUALTABLE - if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ - /* Case 0: The table is a virtual-table. Use the VFilter and VNext + if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + /* Case 1: The table is a virtual-table. Use the VFilter and VNext ** to access the data. */ int iReg; /* P3 Value for OP_VFilter */ int addrNotFound; - sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; - int nConstraint = pVtabIdx->nConstraint; - struct sqlite3_index_constraint_usage *aUsage = - pVtabIdx->aConstraintUsage; - const struct sqlite3_index_constraint *aConstraint = - pVtabIdx->aConstraint; + int nConstraint = pLoop->nLTerm; sqlite3ExprCachePush(pParse); iReg = sqlite3GetTempRange(pParse, nConstraint+2); addrNotFound = pLevel->addrBrk; - for(j=1; j<=nConstraint; j++){ - for(k=0; k<nConstraint; k++){ - if( aUsage[k].argvIndex==j ){ - int iTarget = iReg+j+1; - pTerm = &pWC->a[aConstraint[k].iTermOffset]; - if( pTerm->eOperator & WO_IN ){ - codeEqualityTerm(pParse, pTerm, pLevel, k, iTarget); - addrNotFound = pLevel->addrNxt; - }else{ - sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); - } - break; - } + for(j=0; j<nConstraint; j++){ + int iTarget = iReg+j+2; + pTerm = pLoop->aLTerm[j]; + if( pTerm==0 ) continue; + if( pTerm->eOperator & WO_IN ){ + codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); + addrNotFound = pLevel->addrNxt; + }else{ + sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); } - if( k==nConstraint ) break; } - sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); - sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); - sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr, - pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); - pVtabIdx->needToFreeIdxStr = 0; - for(j=0; j<nConstraint; j++){ - if( aUsage[j].omit ){ - int iTerm = aConstraint[j].iTermOffset; - disableTerm(pLevel, &pWC->a[iTerm]); + sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg); + sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1); + sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, + pLoop->u.vtab.idxStr, + pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC); + pLoop->u.vtab.needFree = 0; + for(j=0; j<nConstraint && j<16; j++){ + if( (pLoop->u.vtab.omitMask>>j)&1 ){ + disableTerm(pLevel, pLoop->aLTerm[j]); } } pLevel->op = OP_VNext; @@ -4270,19 +3267,22 @@ static Bitmask codeOneLoopStart( }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ - if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){ - /* Case 1: We can directly reference a single row using an + if( (pLoop->wsFlags & WHERE_IPK)!=0 + && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0 + ){ + /* Case 2: We can directly reference a single row using an ** equality comparison against the ROWID field. Or ** we reference multiple rows using a "rowid IN (...)" ** construct. */ + assert( pLoop->u.btree.nEq==1 ); iReleaseReg = sqlite3GetTempReg(pParse); - pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); + pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->pExpr!=0 ); assert( omitTable==0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, iReleaseReg); + iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); addrNxt = pLevel->addrNxt; sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); @@ -4290,8 +3290,10 @@ static Bitmask codeOneLoopStart( sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); VdbeComment((v, "pk")); pLevel->op = OP_Noop; - }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){ - /* Case 2: We have an inequality comparison against the ROWID field. + }else if( (pLoop->wsFlags & WHERE_IPK)!=0 + && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 + ){ + /* Case 3: We have an inequality comparison against the ROWID field. */ int testOp = OP_Noop; int start; @@ -4299,8 +3301,11 @@ static Bitmask codeOneLoopStart( WhereTerm *pStart, *pEnd; assert( omitTable==0 ); - pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0); - pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0); + j = 0; + pStart = pEnd = 0; + if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++]; + if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++]; + assert( pStart!=0 || pEnd!=0 ); if( bRev ){ pTerm = pStart; pStart = pEnd; @@ -4355,11 +3360,7 @@ static Bitmask codeOneLoopStart( pLevel->op = bRev ? OP_Prev : OP_Next; pLevel->p1 = iCur; pLevel->p2 = start; - if( pStart==0 && pEnd==0 ){ - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; - }else{ - assert( pLevel->p5==0 ); - } + assert( pLevel->p5==0 ); if( testOp!=OP_Noop ){ iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); @@ -4367,8 +3368,8 @@ static Bitmask codeOneLoopStart( sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); } - }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){ - /* Case 3: A scan using an index. + }else if( pLoop->wsFlags & WHERE_INDEXED ){ + /* Case 4: A scan using an index. ** ** The WHERE clause may contain zero or more equality ** terms ("==" or "IN" operators) that refer to the N @@ -4414,8 +3415,8 @@ static Bitmask codeOneLoopStart( OP_IdxGE, /* 1: (end_constraints && !bRev) */ OP_IdxLT /* 2: (end_constraints && bRev) */ }; - int nEq = pLevel->plan.nEq; /* Number of == or IN terms */ - int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ + int nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ + int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ int r1; /* Temp register */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ @@ -4431,9 +3432,8 @@ static Bitmask codeOneLoopStart( char *zStartAff; /* Affinity for start of range constraint */ char *zEndAff; /* Affinity for end of range constraint */ - pIdx = pLevel->plan.u.pIdx; + pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; - k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]); /* If this loop satisfies a sort order (pOrderBy) request that ** was passed to this function to implement a "SELECT min(x) ..." @@ -4443,8 +3443,8 @@ static Bitmask codeOneLoopStart( ** the first one after the nEq equality constraints in the index, ** this requires some special handling. */ - if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0 - && (pLevel->plan.wsFlags&WHERE_ORDERED) + if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0 + && (pWInfo->bOBSat!=0) && (pIdx->nColumn>nEq) ){ /* assert( pOrderBy->nExpr==1 ); */ @@ -4456,12 +3456,13 @@ static Bitmask codeOneLoopStart( /* Find any inequality constraint terms for the start and end ** of the range. */ - if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){ - pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx); + j = nEq; + if( pLoop->wsFlags & WHERE_BTM_LIMIT ){ + pRangeStart = pLoop->aLTerm[j++]; nExtraReg = 1; } - if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){ - pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx); + if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ + pRangeEnd = pLoop->aLTerm[j++]; nExtraReg = 1; } @@ -4469,9 +3470,7 @@ static Bitmask codeOneLoopStart( ** and store the values of those terms in an array of registers ** starting at regBase. */ - regBase = codeAllEqualityTerms( - pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff - ); + regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff); zEndAff = sqlite3DbStrDup(pParse->db, zStartAff); addrNxt = pLevel->addrNxt; @@ -4485,10 +3484,10 @@ static Bitmask codeOneLoopStart( SWAP(WhereTerm *, pRangeEnd, pRangeStart); } - testcase( pRangeStart && pRangeStart->eOperator & WO_LE ); - testcase( pRangeStart && pRangeStart->eOperator & WO_GE ); - testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE ); - testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE ); + testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 ); + testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 ); + testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 ); + testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 ); startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE); endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE); start_constraints = pRangeStart || nEq>0; @@ -4578,9 +3577,9 @@ static Bitmask codeOneLoopStart( ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); - testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); - testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); - if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ + testcase( pLoop->wsFlags & WHERE_BTM_LIMIT ); + testcase( pLoop->wsFlags & WHERE_TOP_LIMIT ); + if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); } @@ -4599,7 +3598,7 @@ static Bitmask codeOneLoopStart( /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ - if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ + if( pLoop->wsFlags & WHERE_ONEROW ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; @@ -4607,7 +3606,7 @@ static Bitmask codeOneLoopStart( pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; - if( pLevel->plan.wsFlags & WHERE_COVER_SCAN ){ + if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; }else{ assert( pLevel->p5==0 ); @@ -4615,8 +3614,8 @@ static Bitmask codeOneLoopStart( }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION - if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ - /* Case 4: Two or more separately indexed terms connected by OR + if( pLoop->wsFlags & WHERE_MULTI_OR ){ + /* Case 5: Two or more separately indexed terms connected by OR ** ** Example: ** @@ -4669,7 +3668,7 @@ static Bitmask codeOneLoopStart( int ii; /* Loop counter */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ - pTerm = pLevel->plan.u.pTerm; + pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->eOperator & WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); @@ -4688,7 +3687,7 @@ static Bitmask codeOneLoopStart( pOrTab = sqlite3StackAllocRaw(pParse->db, sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); if( pOrTab==0 ) return notReady; - pOrTab->nAlloc = (i16)(nNotReady + 1); + pOrTab->nAlloc = (u8)(nNotReady + 1); pOrTab->nSrc = pOrTab->nAlloc; memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); origSrc = pWInfo->pTabList->a; @@ -4710,7 +3709,7 @@ static Bitmask codeOneLoopStart( ** fall through to the next instruction, just as an OP_Next does if ** called on an uninitialized cursor. */ - if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ + if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ regRowset = ++pParse->nMem; regRowid = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); @@ -4761,11 +3760,11 @@ static Bitmask codeOneLoopStart( WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur); assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed ); if( pSubWInfo ){ - WhereLevel *pLvl; + WhereLoop *pSubLoop; explainOneScan( pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 ); - if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ + if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, @@ -4794,13 +3793,13 @@ static Bitmask codeOneLoopStart( ** pCov to NULL to indicate that no candidate covering index will ** be available. */ - pLvl = &pSubWInfo->a[0]; - if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0 - && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0 - && (ii==0 || pLvl->plan.u.pIdx==pCov) + pSubLoop = pSubWInfo->a[0].pWLoop; + assert( (pSubLoop->wsFlags & WHERE_TEMP_INDEX)==0 ); + if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0 + && (ii==0 || pSubLoop->u.btree.pIndex==pCov) ){ - assert( pLvl->iIdxCur==iCovCur ); - pCov = pLvl->plan.u.pIdx; + assert( pSubWInfo->a[0].iIdxCur==iCovCur ); + pCov = pSubLoop->u.btree.pIndex; }else{ pCov = 0; } @@ -4826,19 +3825,18 @@ static Bitmask codeOneLoopStart( #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ { - /* Case 5: There is no usable index. We must do a complete + /* Case 6: There is no usable index. We must do a complete ** scan of the entire table. */ static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); - assert( omitTable==0 ); pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } - newNotReady = notReady & ~getMask(pWC->pMaskSet, iCur); + newNotReady = notReady & ~getMask(&pWInfo->sMaskSet, iCur); /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. @@ -4888,6 +3886,8 @@ static Bitmask codeOneLoopStart( pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; + testcase( pAlt->eOperator & WO_EQ ); + testcase( pAlt->eOperator & WO_IN ); VdbeNoopComment((v, "begin transitive constraint")); sEq = *pAlt->pExpr; sEq.pLeft = pE->pLeft; @@ -4920,49 +3920,1560 @@ static Bitmask codeOneLoopStart( return newNotReady; } -#if defined(SQLITE_TEST) +#ifdef WHERETRACE_ENABLED /* -** The following variable holds a text description of query plan generated -** by the most recent call to sqlite3WhereBegin(). Each call to WhereBegin -** overwrites the previous. This information is used for testing and -** analysis only. +** Print a WhereLoop object for debugging purposes */ -char sqlite3_query_plan[BMS*2*40]; /* Text of the join */ -static int nQPlan = 0; /* Next free slow in _query_plan[] */ +static void whereLoopPrint(WhereLoop *p, SrcList *pTabList){ + int nb = 1+(pTabList->nSrc+7)/8; + struct SrcList_item *pItem = pTabList->a + p->iTab; + Table *pTab = pItem->pTab; + sqlite3DebugPrintf("%c %2d.%0*llx.%0*llx", p->cId, + p->iTab, nb, p->maskSelf, nb, p->prereq); + sqlite3DebugPrintf(" %8s", + pItem->zAlias ? pItem->zAlias : pTab->zName); + if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ + if( p->u.btree.pIndex ){ + const char *zName = p->u.btree.pIndex->zName; + if( zName==0 ) zName = "ipk"; + if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){ + int i = sqlite3Strlen30(zName) - 1; + while( zName[i]!='_' ) i--; + zName += i; + } + sqlite3DebugPrintf(".%-12s %2d", zName, p->u.btree.nEq); + }else{ + sqlite3DebugPrintf("%16s",""); + } + }else{ + char *z; + if( p->u.vtab.idxStr ){ + z = sqlite3_mprintf("(%d,\"%s\",%x)", + p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask); + }else{ + z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask); + } + sqlite3DebugPrintf(" %-15s", z); + sqlite3_free(z); + } + sqlite3DebugPrintf(" fg %05x N %d", p->wsFlags, p->nLTerm); + sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); +} +#endif + +/* +** Convert bulk memory into a valid WhereLoop that can be passed +** to whereLoopClear harmlessly. +*/ +static void whereLoopInit(WhereLoop *p){ + p->aLTerm = p->aLTermSpace; + p->nLTerm = 0; + p->nLSlot = ArraySize(p->aLTermSpace); + p->wsFlags = 0; +} -#endif /* SQLITE_TEST */ +/* +** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact. +*/ +static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ + if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_TEMP_INDEX) ){ + if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){ + sqlite3_free(p->u.vtab.idxStr); + p->u.vtab.needFree = 0; + p->u.vtab.idxStr = 0; + }else if( (p->wsFlags & WHERE_TEMP_INDEX)!=0 && p->u.btree.pIndex!=0 ){ + sqlite3DbFree(db, p->u.btree.pIndex->zColAff); + sqlite3DbFree(db, p->u.btree.pIndex); + p->u.btree.pIndex = 0; + } + } +} + +/* +** Deallocate internal memory used by a WhereLoop object +*/ +static void whereLoopClear(sqlite3 *db, WhereLoop *p){ + if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm); + whereLoopClearUnion(db, p); + whereLoopInit(p); +} + +/* +** Increase the memory allocation for pLoop->aLTerm[] to be at least n. +*/ +static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ + WhereTerm **paNew; + if( p->nLSlot>=n ) return SQLITE_OK; + n = (n+7)&~7; + paNew = sqlite3DbMallocRaw(db, sizeof(p->aLTerm[0])*n); + if( paNew==0 ) return SQLITE_NOMEM; + memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot); + if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm); + p->aLTerm = paNew; + p->nLSlot = n; + return SQLITE_OK; +} + +/* +** Transfer content from the second pLoop into the first. +*/ +static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ + if( whereLoopResize(db, pTo, pFrom->nLTerm) ) return SQLITE_NOMEM; + whereLoopClearUnion(db, pTo); + memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); + memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0])); + if( pFrom->wsFlags & WHERE_VIRTUALTABLE ){ + pFrom->u.vtab.needFree = 0; + }else if( (pFrom->wsFlags & WHERE_TEMP_INDEX)!=0 ){ + pFrom->u.btree.pIndex = 0; + } + return SQLITE_OK; +} +/* +** Delete a WhereLoop object +*/ +static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ + whereLoopClear(db, p); + sqlite3DbFree(db, p); +} /* ** Free a WhereInfo structure */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ - int i; - for(i=0; i<pWInfo->nLevel; i++){ - sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo; - if( pInfo ){ - /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */ - if( pInfo->needToFreeIdxStr ){ - sqlite3_free(pInfo->idxStr); + whereClauseClear(&pWInfo->sWC); + while( pWInfo->pLoops ){ + WhereLoop *p = pWInfo->pLoops; + pWInfo->pLoops = p->pNextLoop; + whereLoopDelete(db, p); + } + sqlite3DbFree(db, pWInfo); + } +} + +/* +** Insert or replace a WhereLoop entry using the template supplied. +** +** An existing WhereLoop entry might be overwritten if the new template +** is better and has fewer dependencies. Or the template will be ignored +** and no insert will occur if an existing WhereLoop is faster and has +** fewer dependencies than the template. Otherwise a new WhereLoop is +** added based on the template. +** +** If pBuilder->pBest is not NULL then we only care about the very +** best template and that template should be stored in pBuilder->pBest. +** If pBuilder->pBest is NULL then a list of the best templates are stored +** in pBuilder->pWInfo->pLoops. +** +** When accumulating multiple loops (when pBuilder->pBest is NULL) we +** still might overwrite similar loops with the new template if the +** template is better. Loops may be overwritten if the following +** conditions are met: +** +** (1) They have the same iTab. +** (2) They have the same iSortIdx. +** (3) The template has same or fewer dependencies than the current loop +** (4) The template has the same or lower cost than the current loop +** (5) The template uses more terms of the same index but has no additional +** dependencies +*/ +static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ + WhereLoop **ppPrev, *p, *pNext = 0; + WhereInfo *pWInfo = pBuilder->pWInfo; + sqlite3 *db = pWInfo->pParse->db; + + /* If pBuilder->pBest is defined, then only keep track of the single + ** best WhereLoop. pBuilder->pBest->maskSelf==0 indicates that no + ** prior WhereLoops have been evaluated and that the current pTemplate + ** is therefore the first and hence the best and should be retained. + */ + if( (p = pBuilder->pBest)!=0 ){ + if( p->maskSelf!=0 ){ + WhereCost rCost = whereCostAdd(p->rRun,p->rSetup); + WhereCost rTemplate = whereCostAdd(pTemplate->rRun,pTemplate->rSetup); + if( rCost < rTemplate ){ + testcase( rCost==rTemplate-1 ); + goto whereLoopInsert_noop; + } + if( rCost==rTemplate && (p->prereq & pTemplate->prereq)==p->prereq ){ + goto whereLoopInsert_noop; + } + } +#if WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x8 ){ + sqlite3DebugPrintf(p->maskSelf==0 ? "ins-init: " : "ins-best: "); + whereLoopPrint(pTemplate, pWInfo->pTabList); + } +#endif + whereLoopXfer(db, p, pTemplate); + return SQLITE_OK; + } + + /* Search for an existing WhereLoop to overwrite, or which takes + ** priority over pTemplate. + */ + for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){ + if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){ + /* If either the iTab or iSortIdx values for two WhereLoop are different + ** then those WhereLoops need to be considered separately. Neither is + ** a candidate to replace the other. */ + continue; + } + /* In the current implementation, the rSetup value is either zero + ** or the cost of building an automatic index (NlogN) and the NlogN + ** is the same for compatible WhereLoops. */ + assert( p->rSetup==0 || pTemplate->rSetup==0 + || p->rSetup==pTemplate->rSetup ); + + /* whereLoopAddBtree() always generates and inserts the automatic index + ** case first. Hence compatible candidate WhereLoops never have a larger + ** rSetup. Call this SETUP-INVARIANT */ + assert( p->rSetup>=pTemplate->rSetup ); + + if( (p->prereq & pTemplate->prereq)==p->prereq + && p->rSetup<=pTemplate->rSetup + && p->rRun<=pTemplate->rRun + ){ + /* This branch taken when p is equal or better than pTemplate in + ** all of (1) dependences (2) setup-cost, and (3) run-cost. */ + assert( p->rSetup==pTemplate->rSetup ); + if( p->nLTerm<pTemplate->nLTerm + && (p->wsFlags & WHERE_INDEXED)!=0 + && (pTemplate->wsFlags & WHERE_INDEXED)!=0 + && p->u.btree.pIndex==pTemplate->u.btree.pIndex + && p->prereq==pTemplate->prereq + ){ + /* Overwrite an existing WhereLoop with an similar one that uses + ** more terms of the index */ + pNext = p->pNextLoop; + break; + }else{ + /* pTemplate is not helpful. + ** Return without changing or adding anything */ + goto whereLoopInsert_noop; + } + } + if( (p->prereq & pTemplate->prereq)==pTemplate->prereq + && p->rRun>=pTemplate->rRun + && ALWAYS(p->rSetup>=pTemplate->rSetup) /* See SETUP-INVARIANT above */ + ){ + /* Overwrite an existing WhereLoop with a better one: one that is + ** better at one of (1) dependences, (2) setup-cost, or (3) run-cost + ** and is no worse in any of those categories. */ + pNext = p->pNextLoop; + break; + } + } + + /* If we reach this point it means that either p[] should be overwritten + ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new + ** WhereLoop and insert it. + */ +#if WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x8 ){ + if( p!=0 ){ + sqlite3DebugPrintf("ins-del: "); + whereLoopPrint(p, pWInfo->pTabList); + } + sqlite3DebugPrintf("ins-new: "); + whereLoopPrint(pTemplate, pWInfo->pTabList); + } +#endif + if( p==0 ){ + p = sqlite3DbMallocRaw(db, sizeof(WhereLoop)); + if( p==0 ) return SQLITE_NOMEM; + whereLoopInit(p); + } + whereLoopXfer(db, p, pTemplate); + p->pNextLoop = pNext; + *ppPrev = p; + if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ + Index *pIndex = p->u.btree.pIndex; + if( pIndex && pIndex->tnum==0 ){ + p->u.btree.pIndex = 0; + } + } + return SQLITE_OK; + + /* Jump here if the insert is a no-op */ +whereLoopInsert_noop: +#if WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x8 ){ + sqlite3DebugPrintf(pBuilder->pBest ? "ins-skip: " : "ins-noop: "); + whereLoopPrint(pTemplate, pWInfo->pTabList); + } +#endif + return SQLITE_OK; +} + +/* +** We have so far matched pBuilder->pNew->u.btree.nEq terms of the index pIndex. +** Try to match one more. +** +** If pProbe->tnum==0, that means pIndex is a fake index used for the +** INTEGER PRIMARY KEY. +*/ +static int whereLoopAddBtreeIndex( + WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ + struct SrcList_item *pSrc, /* FROM clause term being analyzed */ + Index *pProbe, /* An index on pSrc */ + WhereCost nInMul /* log(Number of iterations due to IN) */ +){ + WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ + Parse *pParse = pWInfo->pParse; /* Parsing context */ + sqlite3 *db = pParse->db; /* Database connection malloc context */ + WhereLoop *pNew; /* Template WhereLoop under construction */ + WhereTerm *pTerm; /* A WhereTerm under consideration */ + int opMask; /* Valid operators for constraints */ + WhereScan scan; /* Iterator for WHERE terms */ + Bitmask saved_prereq; /* Original value of pNew->prereq */ + u16 saved_nLTerm; /* Original value of pNew->nLTerm */ + int saved_nEq; /* Original value of pNew->u.btree.nEq */ + u32 saved_wsFlags; /* Original value of pNew->wsFlags */ + WhereCost saved_nOut; /* Original value of pNew->nOut */ + int iCol; /* Index of the column in the table */ + int rc = SQLITE_OK; /* Return code */ + WhereCost nRowEst; /* Estimated index selectivity */ + WhereCost rLogSize; /* Logarithm of table size */ + WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ + + pNew = pBuilder->pNew; + if( db->mallocFailed ) return SQLITE_NOMEM; + + assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); + assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); + if( pNew->wsFlags & WHERE_BTM_LIMIT ){ + opMask = WO_LT|WO_LE; + }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){ + opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE; + }else{ + opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE; + } + if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); + + assert( pNew->u.btree.nEq<=pProbe->nColumn ); + if( pNew->u.btree.nEq < pProbe->nColumn ){ + iCol = pProbe->aiColumn[pNew->u.btree.nEq]; + nRowEst = whereCost(pProbe->aiRowEst[pNew->u.btree.nEq+1]); + if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1; + }else{ + iCol = -1; + nRowEst = 0; + } + pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol, + opMask, pProbe); + saved_nEq = pNew->u.btree.nEq; + saved_nLTerm = pNew->nLTerm; + saved_wsFlags = pNew->wsFlags; + saved_prereq = pNew->prereq; + saved_nOut = pNew->nOut; + pNew->rSetup = 0; + rLogSize = estLog(whereCost(pProbe->aiRowEst[0])); + for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){ + int nIn = 0; + if( pTerm->prereqRight & pNew->maskSelf ) continue; + pNew->wsFlags = saved_wsFlags; + pNew->u.btree.nEq = saved_nEq; + pNew->nLTerm = saved_nLTerm; + if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ + pNew->aLTerm[pNew->nLTerm++] = pTerm; + pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; + pNew->rRun = rLogSize; /* Baseline cost is log2(N). Adjustments below */ + if( pTerm->eOperator & WO_IN ){ + Expr *pExpr = pTerm->pExpr; + pNew->wsFlags |= WHERE_COLUMN_IN; + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ + nIn = 46; assert( 46==whereCost(25) ); + }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ + /* "x IN (value, value, ...)" */ + nIn = whereCost(pExpr->x.pList->nExpr); + } + pNew->rRun += nIn; + pNew->u.btree.nEq++; + pNew->nOut = nRowEst + nInMul + nIn; + }else if( pTerm->eOperator & (WO_EQ) ){ + assert( (pNew->wsFlags & (WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 + || nInMul==0 ); + pNew->wsFlags |= WHERE_COLUMN_EQ; + if( iCol<0 + || (pProbe->onError!=OE_None && nInMul==0 + && pNew->u.btree.nEq==pProbe->nColumn-1) + ){ + assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 ); + pNew->wsFlags |= WHERE_ONEROW; + } + pNew->u.btree.nEq++; + pNew->nOut = nRowEst + nInMul; + }else if( pTerm->eOperator & (WO_ISNULL) ){ + pNew->wsFlags |= WHERE_COLUMN_NULL; + pNew->u.btree.nEq++; + /* TUNING: IS NULL selects 2 rows */ + nIn = 10; assert( 10==whereCost(2) ); + pNew->nOut = nRowEst + nInMul + nIn; + }else if( pTerm->eOperator & (WO_GT|WO_GE) ){ + testcase( pTerm->eOperator & WO_GT ); + testcase( pTerm->eOperator & WO_GE ); + pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; + pBtm = pTerm; + pTop = 0; + }else{ + assert( pTerm->eOperator & (WO_LT|WO_LE) ); + testcase( pTerm->eOperator & WO_LT ); + testcase( pTerm->eOperator & WO_LE ); + pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; + pTop = pTerm; + pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? + pNew->aLTerm[pNew->nLTerm-2] : 0; + } + if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ + /* Adjust nOut and rRun for STAT3 range values */ + WhereCost rDiv; + whereRangeScanEst(pParse, pProbe, pNew->u.btree.nEq, + pBtm, pTop, &rDiv); + pNew->nOut = saved_nOut>rDiv+10 ? saved_nOut - rDiv : 10; + } +#ifdef SQLITE_ENABLE_STAT3 + if( pNew->u.btree.nEq==1 && pProbe->nSample ){ + tRowcnt nOut = 0; + if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){ + testcase( pTerm->eOperator & WO_EQ ); + testcase( pTerm->eOperator & WO_ISNULL ); + rc = whereEqualScanEst(pParse, pProbe, pTerm->pExpr->pRight, &nOut); + }else if( (pTerm->eOperator & WO_IN) + && !ExprHasProperty(pTerm->pExpr, EP_xIsSelect) ){ + rc = whereInScanEst(pParse, pProbe, pTerm->pExpr->x.pList, &nOut); + } + if( rc==SQLITE_OK ) pNew->nOut = whereCost(nOut); + } +#endif + if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){ + /* Each row involves a step of the index, then a binary search of + ** the main table */ + pNew->rRun = whereCostAdd(pNew->rRun, rLogSize>27 ? rLogSize-17 : 10); + } + /* Step cost for each output row */ + pNew->rRun = whereCostAdd(pNew->rRun, pNew->nOut); + /* TBD: Adjust nOut for additional constraints */ + rc = whereLoopInsert(pBuilder, pNew); + if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 + && pNew->u.btree.nEq<(pProbe->nColumn + (pProbe->zName!=0)) + ){ + whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); + } + } + pNew->prereq = saved_prereq; + pNew->u.btree.nEq = saved_nEq; + pNew->wsFlags = saved_wsFlags; + pNew->nOut = saved_nOut; + pNew->nLTerm = saved_nLTerm; + return rc; +} + +/* +** Return True if it is possible that pIndex might be useful in +** implementing the ORDER BY clause in pBuilder. +** +** Return False if pBuilder does not contain an ORDER BY clause or +** if there is no way for pIndex to be useful in implementing that +** ORDER BY clause. +*/ +static int indexMightHelpWithOrderBy( + WhereLoopBuilder *pBuilder, + Index *pIndex, + int iCursor +){ + ExprList *pOB; + int ii, jj; + + if( pIndex->bUnordered ) return 0; + if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0; + for(ii=0; ii<pOB->nExpr; ii++){ + Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr); + if( pExpr->op!=TK_COLUMN ) return 0; + if( pExpr->iTable==iCursor ){ + for(jj=0; jj<pIndex->nColumn; jj++){ + if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; + } + } + } + return 0; +} + +/* +** Return a bitmask where 1s indicate that the corresponding column of +** the table is used by an index. Only the first 63 columns are considered. +*/ +static Bitmask columnsInIndex(Index *pIdx){ + Bitmask m = 0; + int j; + for(j=pIdx->nColumn-1; j>=0; j--){ + int x = pIdx->aiColumn[j]; + testcase( x==BMS-1 ); + testcase( x==BMS-2 ); + if( x<BMS-1 ) m |= MASKBIT(x); + } + return m; +} + + +/* +** Add all WhereLoop objects a single table of the join were the table +** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be +** a b-tree table, not a virtual table. +*/ +static int whereLoopAddBtree( + WhereLoopBuilder *pBuilder, /* WHERE clause information */ + Bitmask mExtra /* Extra prerequesites for using this table */ +){ + WhereInfo *pWInfo; /* WHERE analysis context */ + Index *pProbe; /* An index we are evaluating */ + Index sPk; /* A fake index object for the primary key */ + tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ + int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ + SrcList *pTabList; /* The FROM clause */ + struct SrcList_item *pSrc; /* The FROM clause btree term to add */ + WhereLoop *pNew; /* Template WhereLoop object */ + int rc = SQLITE_OK; /* Return code */ + int iSortIdx = 1; /* Index number */ + int b; /* A boolean value */ + WhereCost rSize; /* number of rows in the table */ + WhereCost rLogSize; /* Logarithm of the number of rows in the table */ + + pNew = pBuilder->pNew; + pWInfo = pBuilder->pWInfo; + pTabList = pWInfo->pTabList; + pSrc = pTabList->a + pNew->iTab; + assert( !IsVirtual(pSrc->pTab) ); + + if( pSrc->pIndex ){ + /* An INDEXED BY clause specifies a particular index to use */ + pProbe = pSrc->pIndex; + }else{ + /* There is no INDEXED BY clause. Create a fake Index object in local + ** variable sPk to represent the rowid primary key index. Make this + ** fake index the first in a chain of Index objects with all of the real + ** indices to follow */ + Index *pFirst; /* First of real indices on the table */ + memset(&sPk, 0, sizeof(Index)); + sPk.nColumn = 1; + sPk.aiColumn = &aiColumnPk; + sPk.aiRowEst = aiRowEstPk; + sPk.onError = OE_Replace; + sPk.pTable = pSrc->pTab; + aiRowEstPk[0] = pSrc->pTab->nRowEst; + aiRowEstPk[1] = 1; + pFirst = pSrc->pTab->pIndex; + if( pSrc->notIndexed==0 ){ + /* The real indices of the table are only considered if the + ** NOT INDEXED qualifier is omitted from the FROM clause */ + sPk.pNext = pFirst; + } + pProbe = &sPk; + } + rSize = whereCost(pSrc->pTab->nRowEst); + rLogSize = estLog(rSize); + + /* Automatic indexes */ + if( !pBuilder->pBest + && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 + && pSrc->pIndex==0 + && !pSrc->viaCoroutine + && !pSrc->notIndexed + && !pSrc->isCorrelated + ){ + /* Generate auto-index WhereLoops */ + WhereClause *pWC = pBuilder->pWC; + WhereTerm *pTerm; + WhereTerm *pWCEnd = pWC->a + pWC->nTerm; + for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){ + if( pTerm->prereqRight & pNew->maskSelf ) continue; + if( termCanDriveIndex(pTerm, pSrc, 0) ){ + pNew->u.btree.nEq = 1; + pNew->u.btree.pIndex = 0; + pNew->nLTerm = 1; + pNew->aLTerm[0] = pTerm; + /* TUNING: One-time cost for computing the automatic index is + ** approximately 6*N*log2(N) where N is the number of rows in + ** the table being indexed. */ + pNew->rSetup = rLogSize + rSize + 26; assert( 26==whereCost(6) ); + /* TUNING: Each index lookup yields 10 rows in the table */ + pNew->nOut = 33; assert( 33==whereCost(10) ); + pNew->rRun = whereCostAdd(rLogSize,pNew->nOut); + pNew->wsFlags = WHERE_TEMP_INDEX; + pNew->prereq = mExtra | pTerm->prereqRight; + rc = whereLoopInsert(pBuilder, pNew); + } + } + } + + /* Loop over all indices + */ + for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){ + pNew->u.btree.nEq = 0; + pNew->nLTerm = 0; + pNew->iSortIdx = 0; + pNew->rSetup = 0; + pNew->prereq = mExtra; + pNew->nOut = rSize; + pNew->u.btree.pIndex = pProbe; + b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); + /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ + assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); + if( pProbe->tnum<=0 ){ + /* Integer primary key index */ + pNew->wsFlags = WHERE_IPK; + + /* Full table scan */ + pNew->iSortIdx = b ? iSortIdx : 0; + /* TUNING: Cost of full table scan is 3*(N + log2(N)). + ** + The extra 3 factor is to encourage the use of indexed lookups + ** over full scans. A smaller constant 2 is used for covering + ** index scans so that a covering index scan will be favored over + ** a table scan. */ + pNew->rRun = whereCostAdd(rSize,rLogSize) + 16; + rc = whereLoopInsert(pBuilder, pNew); + if( rc ) break; + }else{ + Bitmask m = pSrc->colUsed & ~columnsInIndex(pProbe); + pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; + + /* Full scan via index */ + if( b + || ( m==0 + && pProbe->bUnordered==0 + && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 + && sqlite3GlobalConfig.bUseCis + && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan) + ) + ){ + pNew->iSortIdx = b ? iSortIdx : 0; + if( m==0 ){ + /* TUNING: Cost of a covering index scan is 2*(N + log2(N)). + ** + The extra 2 factor is to encourage the use of indexed lookups + ** over index scans. A table scan uses a factor of 3 so that + ** index scans are favored over table scans. + ** + If this covering index might also help satisfy the ORDER BY + ** clause, then the cost is fudged down slightly so that this + ** index is favored above other indices that have no hope of + ** helping with the ORDER BY. */ + pNew->rRun = 10 + whereCostAdd(rSize,rLogSize) - b; + }else{ + assert( b!=0 ); + /* TUNING: Cost of scanning a non-covering index is (N+1)*log2(N) + ** which we will simplify to just N*log2(N) */ + pNew->rRun = rSize + rLogSize; } - sqlite3DbFree(db, pInfo); + rc = whereLoopInsert(pBuilder, pNew); + if( rc ) break; } - if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){ - Index *pIdx = pWInfo->a[i].plan.u.pIdx; - if( pIdx ){ - sqlite3DbFree(db, pIdx->zColAff); - sqlite3DbFree(db, pIdx); + } + rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); + + /* If there was an INDEXED BY clause, then only that one index is + ** considered. */ + if( pSrc->pIndex ) break; + } + return rc; +} + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Add all WhereLoop objects for a table of the join identified by +** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. +*/ +static int whereLoopAddVirtual( + WhereLoopBuilder *pBuilder /* WHERE clause information */ +){ + WhereInfo *pWInfo; /* WHERE analysis context */ + Parse *pParse; /* The parsing context */ + WhereClause *pWC; /* The WHERE clause */ + struct SrcList_item *pSrc; /* The FROM clause term to search */ + Table *pTab; + sqlite3 *db; + sqlite3_index_info *pIdxInfo; + struct sqlite3_index_constraint *pIdxCons; + struct sqlite3_index_constraint_usage *pUsage; + WhereTerm *pTerm; + int i, j; + int iTerm, mxTerm; + int nConstraint; + int seenIn = 0; /* True if an IN operator is seen */ + int seenVar = 0; /* True if a non-constant constraint is seen */ + int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */ + WhereLoop *pNew; + int rc = SQLITE_OK; + + pWInfo = pBuilder->pWInfo; + pParse = pWInfo->pParse; + db = pParse->db; + pWC = pBuilder->pWC; + pNew = pBuilder->pNew; + pSrc = &pWInfo->pTabList->a[pNew->iTab]; + pTab = pSrc->pTab; + assert( IsVirtual(pTab) ); + pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy); + if( pIdxInfo==0 ) return SQLITE_NOMEM; + pNew->prereq = 0; + pNew->rSetup = 0; + pNew->wsFlags = WHERE_VIRTUALTABLE; + pNew->nLTerm = 0; + pNew->u.vtab.needFree = 0; + pUsage = pIdxInfo->aConstraintUsage; + nConstraint = pIdxInfo->nConstraint; + if( whereLoopResize(db, pNew, nConstraint) ){ + sqlite3DbFree(db, pIdxInfo); + return SQLITE_NOMEM; + } + + for(iPhase=0; iPhase<=3; iPhase++){ + if( !seenIn && (iPhase&1)!=0 ){ + iPhase++; + if( iPhase>3 ) break; + } + if( !seenVar && iPhase>1 ) break; + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + switch( iPhase ){ + case 0: /* Constants without IN operator */ + pIdxCons->usable = 0; + if( (pTerm->eOperator & WO_IN)!=0 ){ + seenIn = 1; + } + if( pTerm->prereqRight!=0 ){ + seenVar = 1; + }else if( (pTerm->eOperator & WO_IN)==0 ){ + pIdxCons->usable = 1; + } + break; + case 1: /* Constants with IN operators */ + assert( seenIn ); + pIdxCons->usable = (pTerm->prereqRight==0); + break; + case 2: /* Variables without IN */ + assert( seenVar ); + pIdxCons->usable = (pTerm->eOperator & WO_IN)==0; + break; + default: /* Variables with IN */ + assert( seenVar && seenIn ); + pIdxCons->usable = 1; + break; + } + } + memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); + if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr); + pIdxInfo->idxStr = 0; + pIdxInfo->idxNum = 0; + pIdxInfo->needToFreeIdxStr = 0; + pIdxInfo->orderByConsumed = 0; + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; + rc = vtabBestIndex(pParse, pTab, pIdxInfo); + if( rc ) goto whereLoopAddVtab_exit; + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + pNew->prereq = 0; + mxTerm = -1; + assert( pNew->nLSlot>=nConstraint ); + for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0; + pNew->u.vtab.omitMask = 0; + for(i=0; i<nConstraint; i++, pIdxCons++){ + if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){ + j = pIdxCons->iTermOffset; + if( iTerm>=nConstraint + || j<0 + || j>=pWC->nTerm + || pNew->aLTerm[iTerm]!=0 + ){ + rc = SQLITE_ERROR; + sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName); + goto whereLoopAddVtab_exit; + } + testcase( iTerm==nConstraint-1 ); + testcase( j==0 ); + testcase( j==pWC->nTerm-1 ); + pTerm = &pWC->a[j]; + pNew->prereq |= pTerm->prereqRight; + assert( iTerm<pNew->nLSlot ); + pNew->aLTerm[iTerm] = pTerm; + if( iTerm>mxTerm ) mxTerm = iTerm; + testcase( iTerm==15 ); + testcase( iTerm==16 ); + if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm; + if( (pTerm->eOperator & WO_IN)!=0 ){ + if( pUsage[i].omit==0 ){ + /* Do not attempt to use an IN constraint if the virtual table + ** says that the equivalent EQ constraint cannot be safely omitted. + ** If we do attempt to use such a constraint, some rows might be + ** repeated in the output. */ + break; + } + /* A virtual table that is constrained by an IN clause may not + ** consume the ORDER BY clause because (1) the order of IN terms + ** is not necessarily related to the order of output terms and + ** (2) Multiple outputs from a single IN value will not merge + ** together. */ + pIdxInfo->orderByConsumed = 0; } } } - whereClauseClear(pWInfo->pWC); - sqlite3DbFree(db, pWInfo); + if( i>=nConstraint ){ + pNew->nLTerm = mxTerm+1; + assert( pNew->nLTerm<=pNew->nLSlot ); + pNew->u.vtab.idxNum = pIdxInfo->idxNum; + pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; + pIdxInfo->needToFreeIdxStr = 0; + pNew->u.vtab.idxStr = pIdxInfo->idxStr; + pNew->u.vtab.isOrdered = (u8)((pIdxInfo->nOrderBy!=0) + && pIdxInfo->orderByConsumed); + pNew->rSetup = 0; + pNew->rRun = whereCostFromDouble(pIdxInfo->estimatedCost); + /* TUNING: Every virtual table query returns 25 rows */ + pNew->nOut = 46; assert( 46==whereCost(25) ); + whereLoopInsert(pBuilder, pNew); + if( pNew->u.vtab.needFree ){ + sqlite3_free(pNew->u.vtab.idxStr); + pNew->u.vtab.needFree = 0; + } + } + } + +whereLoopAddVtab_exit: + if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr); + sqlite3DbFree(db, pIdxInfo); + return rc; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +/* +** Add WhereLoop entries to handle OR terms. This works for either +** btrees or virtual tables. +*/ +static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){ + WhereInfo *pWInfo = pBuilder->pWInfo; + WhereClause *pWC; + WhereLoop *pNew; + WhereTerm *pTerm, *pWCEnd; + int rc = SQLITE_OK; + int iCur; + WhereClause tempWC; + WhereLoopBuilder sSubBuild; + WhereLoop sBest; + struct SrcList_item *pItem; + + pWC = pBuilder->pWC; + if( pWInfo->wctrlFlags & WHERE_AND_ONLY ) return SQLITE_OK; + pWCEnd = pWC->a + pWC->nTerm; + pNew = pBuilder->pNew; + + for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){ + if( (pTerm->eOperator & WO_OR)!=0 + && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 + ){ + WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; + WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; + WhereTerm *pOrTerm; + WhereCost rTotal = 0; + WhereCost nRow = 0; + Bitmask prereq = mExtra; + + whereLoopInit(&sBest); + pItem = pWInfo->pTabList->a + pNew->iTab; + iCur = pItem->iCursor; + sSubBuild = *pBuilder; + sSubBuild.pOrderBy = 0; + sSubBuild.pBest = &sBest; + + for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ + if( (pOrTerm->eOperator & WO_AND)!=0 ){ + sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; + }else if( pOrTerm->leftCursor==iCur ){ + tempWC.pWInfo = pWC->pWInfo; + tempWC.pOuter = pWC; + tempWC.op = TK_AND; + tempWC.nTerm = 1; + tempWC.a = pOrTerm; + sSubBuild.pWC = &tempWC; + }else{ + continue; + } + sBest.maskSelf = 0; + sBest.rSetup = 0; + sBest.rRun = 0; +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( IsVirtual(pItem->pTab) ){ + rc = whereLoopAddVirtual(&sSubBuild); + }else +#endif + { + rc = whereLoopAddBtree(&sSubBuild, mExtra); + } + /* sBest.maskSelf is always zero if an error occurs */ + assert( rc==SQLITE_OK || sBest.maskSelf==0 ); + if( sBest.maskSelf==0 ) break; + assert( sBest.rSetup==0 ); + rTotal = whereCostAdd(rTotal, sBest.rRun); + nRow = whereCostAdd(nRow, sBest.nOut); + prereq |= sBest.prereq; + } + assert( pNew->nLSlot>=1 ); + if( sBest.maskSelf ){ + pNew->nLTerm = 1; + pNew->aLTerm[0] = pTerm; + pNew->wsFlags = WHERE_MULTI_OR; + pNew->rSetup = 0; + /* TUNING: Multiple by 3.5 for the secondary table lookup */ + pNew->rRun = rTotal + 18; assert( 18==whereCost(7)-whereCost(2) ); + pNew->nOut = nRow; + pNew->prereq = prereq; + memset(&pNew->u, 0, sizeof(pNew->u)); + rc = whereLoopInsert(pBuilder, pNew); + } + whereLoopClear(pWInfo->pParse->db, &sBest); + } } + return rc; +} + +/* +** Add all WhereLoop objects for all tables +*/ +static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ + WhereInfo *pWInfo = pBuilder->pWInfo; + Bitmask mExtra = 0; + Bitmask mPrior = 0; + int iTab; + SrcList *pTabList = pWInfo->pTabList; + struct SrcList_item *pItem; + sqlite3 *db = pWInfo->pParse->db; + int nTabList = pWInfo->nLevel; + int rc = SQLITE_OK; + u8 priorJoinType = 0; + WhereLoop *pNew; + + /* Loop over the tables in the join, from left to right */ + pNew = pBuilder->pNew; + whereLoopInit(pNew); + for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){ + pNew->iTab = iTab; + pNew->maskSelf = getMask(&pWInfo->sMaskSet, pItem->iCursor); + if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){ + mExtra = mPrior; + } + priorJoinType = pItem->jointype; + if( IsVirtual(pItem->pTab) ){ + rc = whereLoopAddVirtual(pBuilder); + }else{ + rc = whereLoopAddBtree(pBuilder, mExtra); + } + if( rc==SQLITE_OK ){ + rc = whereLoopAddOr(pBuilder, mExtra); + } + mPrior |= pNew->maskSelf; + if( rc || db->mallocFailed ) break; + } + whereLoopClear(db, pNew); + return rc; +} + +/* +** Examine a WherePath (with the addition of the extra WhereLoop of the 5th +** parameters) to see if it outputs rows in the requested ORDER BY +** (or GROUP BY) without requiring a separate source operation. Return: +** +** 0: ORDER BY is not satisfied. Sorting required +** 1: ORDER BY is satisfied. Omit sorting +** -1: Unknown at this time +** +*/ +static int wherePathSatisfiesOrderBy( + WhereInfo *pWInfo, /* The WHERE clause */ + ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */ + WherePath *pPath, /* The WherePath to check */ + u16 wctrlFlags, /* Might contain WHERE_GROUPBY or WHERE_DISTINCTBY */ + u16 nLoop, /* Number of entries in pPath->aLoop[] */ + WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */ + Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */ +){ + u8 revSet; /* True if rev is known */ + u8 rev; /* Composite sort order */ + u8 revIdx; /* Index sort order */ + u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */ + u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */ + u8 isMatch; /* iColumn matches a term of the ORDER BY clause */ + u16 nColumn; /* Number of columns in pIndex */ + u16 nOrderBy; /* Number terms in the ORDER BY clause */ + int iLoop; /* Index of WhereLoop in pPath being processed */ + int i, j; /* Loop counters */ + int iCur; /* Cursor number for current WhereLoop */ + int iColumn; /* A column number within table iCur */ + WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */ + WhereTerm *pTerm; /* A single term of the WHERE clause */ + Expr *pOBExpr; /* An expression from the ORDER BY clause */ + CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */ + Index *pIndex; /* The index associated with pLoop */ + sqlite3 *db = pWInfo->pParse->db; /* Database connection */ + Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */ + Bitmask obDone; /* Mask of all ORDER BY terms */ + Bitmask orderDistinctMask; /* Mask of all well-ordered loops */ + Bitmask ready; /* Mask of inner loops */ + + /* + ** We say the WhereLoop is "one-row" if it generates no more than one + ** row of output. A WhereLoop is one-row if all of the following are true: + ** (a) All index columns match with WHERE_COLUMN_EQ. + ** (b) The index is unique + ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row. + ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags. + ** + ** We say the WhereLoop is "order-distinct" if the set of columns from + ** that WhereLoop that are in the ORDER BY clause are different for every + ** row of the WhereLoop. Every one-row WhereLoop is automatically + ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause + ** is not order-distinct. To be order-distinct is not quite the same as being + ** UNIQUE since a UNIQUE column or index can have multiple rows that + ** are NULL and NULL values are equivalent for the purpose of order-distinct. + ** To be order-distinct, the columns must be UNIQUE and NOT NULL. + ** + ** The rowid for a table is always UNIQUE and NOT NULL so whenever the + ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is + ** automatically order-distinct. + */ + + assert( pOrderBy!=0 ); + + /* Sortability of virtual tables is determined by the xBestIndex method + ** of the virtual table itself */ + if( pLast->wsFlags & WHERE_VIRTUALTABLE ){ + testcase( nLoop>0 ); /* True when outer loops are one-row and match + ** no ORDER BY terms */ + return pLast->u.vtab.isOrdered; + } + if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0; + + nOrderBy = pOrderBy->nExpr; + testcase( nOrderBy==BMS-1 ); + if( nOrderBy>BMS-1 ) return 0; /* Cannot optimize overly large ORDER BYs */ + isOrderDistinct = 1; + obDone = MASKBIT(nOrderBy)-1; + orderDistinctMask = 0; + ready = 0; + for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){ + if( iLoop>0 ) ready |= pLoop->maskSelf; + pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast; + assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ); + iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; + + /* Mark off any ORDER BY term X that is a column in the table of + ** the current loop for which there is term in the WHERE + ** clause of the form X IS NULL or X=? that reference only outer + ** loops. + */ + for(i=0; i<nOrderBy; i++){ + if( MASKBIT(i) & obSat ) continue; + pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr); + if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->iTable!=iCur ) continue; + pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, + ~ready, WO_EQ|WO_ISNULL, 0); + if( pTerm==0 ) continue; + if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){ + const char *z1, *z2; + pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); + if( !pColl ) pColl = db->pDfltColl; + z1 = pColl->zName; + pColl = sqlite3ExprCollSeq(pWInfo->pParse, pTerm->pExpr); + if( !pColl ) pColl = db->pDfltColl; + z2 = pColl->zName; + if( sqlite3StrICmp(z1, z2)!=0 ) continue; + } + obSat |= MASKBIT(i); + } + + if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){ + if( pLoop->wsFlags & WHERE_IPK ){ + pIndex = 0; + nColumn = 0; + }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){ + return 0; + }else{ + nColumn = pIndex->nColumn; + isOrderDistinct = pIndex->onError!=OE_None; + } + + /* Loop through all columns of the index and deal with the ones + ** that are not constrained by == or IN. + */ + rev = revSet = 0; + distinctColumns = 0; + for(j=0; j<=nColumn; j++){ + u8 bOnce; /* True to run the ORDER BY search loop */ + + /* Skip over == and IS NULL terms */ + if( j<pLoop->u.btree.nEq + && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0 + ){ + if( i & WO_ISNULL ){ + testcase( isOrderDistinct ); + isOrderDistinct = 0; + } + continue; + } + + /* Get the column number in the table (iColumn) and sort order + ** (revIdx) for the j-th column of the index. + */ + if( j<nColumn ){ + /* Normal index columns */ + iColumn = pIndex->aiColumn[j]; + revIdx = pIndex->aSortOrder[j]; + if( iColumn==pIndex->pTable->iPKey ) iColumn = -1; + }else{ + /* The ROWID column at the end */ + assert( j==nColumn ); + iColumn = -1; + revIdx = 0; + } + + /* An unconstrained column that might be NULL means that this + ** WhereLoop is not well-ordered + */ + if( isOrderDistinct + && iColumn>=0 + && j>=pLoop->u.btree.nEq + && pIndex->pTable->aCol[iColumn].notNull==0 + ){ + isOrderDistinct = 0; + } + + /* Find the ORDER BY term that corresponds to the j-th column + ** of the index and and mark that ORDER BY term off + */ + bOnce = 1; + isMatch = 0; + for(i=0; bOnce && i<nOrderBy; i++){ + if( MASKBIT(i) & obSat ) continue; + pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr); + testcase( wctrlFlags & WHERE_GROUPBY ); + testcase( wctrlFlags & WHERE_DISTINCTBY ); + if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0; + if( pOBExpr->op!=TK_COLUMN ) continue; + if( pOBExpr->iTable!=iCur ) continue; + if( pOBExpr->iColumn!=iColumn ) continue; + if( iColumn>=0 ){ + pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); + if( !pColl ) pColl = db->pDfltColl; + if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue; + } + isMatch = 1; + break; + } + if( isMatch ){ + if( iColumn<0 ){ + testcase( distinctColumns==0 ); + distinctColumns = 1; + } + obSat |= MASKBIT(i); + if( (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){ + /* Make sure the sort order is compatible in an ORDER BY clause. + ** Sort order is irrelevant for a GROUP BY clause. */ + if( revSet ){ + if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) return 0; + }else{ + rev = revIdx ^ pOrderBy->a[i].sortOrder; + if( rev ) *pRevMask |= MASKBIT(iLoop); + revSet = 1; + } + } + }else{ + /* No match found */ + if( j==0 || j<nColumn ){ + testcase( isOrderDistinct!=0 ); + isOrderDistinct = 0; + } + break; + } + } /* end Loop over all index columns */ + if( distinctColumns ){ + testcase( isOrderDistinct==0 ); + isOrderDistinct = 1; + } + } /* end-if not one-row */ + + /* Mark off any other ORDER BY terms that reference pLoop */ + if( isOrderDistinct ){ + orderDistinctMask |= pLoop->maskSelf; + for(i=0; i<nOrderBy; i++){ + Expr *p; + if( MASKBIT(i) & obSat ) continue; + p = pOrderBy->a[i].pExpr; + if( (exprTableUsage(&pWInfo->sMaskSet, p)&~orderDistinctMask)==0 ){ + obSat |= MASKBIT(i); + } + } + } + } /* End the loop over all WhereLoops from outer-most down to inner-most */ + if( obSat==obDone ) return 1; + if( !isOrderDistinct ) return 0; + return -1; +} + +#ifdef WHERETRACE_ENABLED +/* For debugging use only: */ +static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ + static char zName[65]; + int i; + for(i=0; i<nLoop; i++){ zName[i] = pPath->aLoop[i]->cId; } + if( pLast ) zName[i++] = pLast->cId; + zName[i] = 0; + return zName; } +#endif /* +** Given the list of WhereLoop objects on pWInfo->pLoops, this routine +** attempts to find the lowest cost path that visits each WhereLoop +** once. This path is then loaded into the pWInfo->a[].pWLoop fields. +** +** Assume that the total number of output rows that will need to be sorted +** will be nRowEst (in the 10*log2 representation). Or, ignore sorting +** costs if nRowEst==0. +** +** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation +** error occurs. +*/ +static int wherePathSolver(WhereInfo *pWInfo, WhereCost nRowEst){ + int mxChoice; /* Maximum number of simultaneous paths tracked */ + int nLoop; /* Number of terms in the join */ + Parse *pParse; /* Parsing context */ + sqlite3 *db; /* The database connection */ + int iLoop; /* Loop counter over the terms of the join */ + int ii, jj; /* Loop counters */ + WhereCost rCost; /* Cost of a path */ + WhereCost mxCost = 0; /* Maximum cost of a set of paths */ + WhereCost rSortCost; /* Cost to do a sort */ + int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ + WherePath *aFrom; /* All nFrom paths at the previous level */ + WherePath *aTo; /* The nTo best paths at the current level */ + WherePath *pFrom; /* An element of aFrom[] that we are working on */ + WherePath *pTo; /* An element of aTo[] that we are working on */ + WhereLoop *pWLoop; /* One of the WhereLoop objects */ + WhereLoop **pX; /* Used to divy up the pSpace memory */ + char *pSpace; /* Temporary memory used by this routine */ + + pParse = pWInfo->pParse; + db = pParse->db; + nLoop = pWInfo->nLevel; + /* TUNING: For simple queries, only the best path is tracked. + ** For 2-way joins, the 5 best paths are followed. + ** For joins of 3 or more tables, track the 10 best paths */ + mxChoice = (nLoop==1) ? 1 : (nLoop==2 ? 5 : 10); + assert( nLoop<=pWInfo->pTabList->nSrc ); + WHERETRACE(0x002, ("---- begin solver\n")); + + /* Allocate and initialize space for aTo and aFrom */ + ii = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; + pSpace = sqlite3DbMallocRaw(db, ii); + if( pSpace==0 ) return SQLITE_NOMEM; + aTo = (WherePath*)pSpace; + aFrom = aTo+mxChoice; + memset(aFrom, 0, sizeof(aFrom[0])); + pX = (WhereLoop**)(aFrom+mxChoice); + for(ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop){ + pFrom->aLoop = pX; + } + + /* Seed the search with a single WherePath containing zero WhereLoops. + ** + ** TUNING: Do not let the number of iterations go above 25. If the cost + ** of computing an automatic index is not paid back within the first 25 + ** rows, then do not use the automatic index. */ + aFrom[0].nRow = MIN(pParse->nQueryLoop, 46); assert( 46==whereCost(25) ); + nFrom = 1; + + /* Precompute the cost of sorting the final result set, if the caller + ** to sqlite3WhereBegin() was concerned about sorting */ + rSortCost = 0; + if( pWInfo->pOrderBy==0 || nRowEst==0 ){ + aFrom[0].isOrderedValid = 1; + }else{ + /* TUNING: Estimated cost of sorting is N*log2(N) where N is the + ** number of output rows. */ + rSortCost = nRowEst + estLog(nRowEst); + WHERETRACE(0x002,("---- sort cost=%-3d\n", rSortCost)); + } + + /* Compute successively longer WherePaths using the previous generation + ** of WherePaths as the basis for the next. Keep track of the mxChoice + ** best paths at each generation */ + for(iLoop=0; iLoop<nLoop; iLoop++){ + nTo = 0; + for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){ + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + Bitmask maskNew; + Bitmask revMask = 0; + u8 isOrderedValid = pFrom->isOrderedValid; + u8 isOrdered = pFrom->isOrdered; + if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue; + if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue; + /* At this point, pWLoop is a candidate to be the next loop. + ** Compute its cost */ + rCost = whereCostAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); + rCost = whereCostAdd(rCost, pFrom->rCost); + maskNew = pFrom->maskLoop | pWLoop->maskSelf; + if( !isOrderedValid ){ + switch( wherePathSatisfiesOrderBy(pWInfo, + pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, + iLoop, pWLoop, &revMask) ){ + case 1: /* Yes. pFrom+pWLoop does satisfy the ORDER BY clause */ + isOrdered = 1; + isOrderedValid = 1; + break; + case 0: /* No. pFrom+pWLoop will require a separate sort */ + isOrdered = 0; + isOrderedValid = 1; + rCost = whereCostAdd(rCost, rSortCost); + break; + default: /* Cannot tell yet. Try again on the next iteration */ + break; + } + }else{ + revMask = pFrom->revLoop; + } + /* Check to see if pWLoop should be added to the mxChoice best so far */ + for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){ + if( pTo->maskLoop==maskNew && pTo->isOrderedValid==isOrderedValid ){ + testcase( jj==nTo-1 ); + break; + } + } + if( jj>=nTo ){ + if( nTo>=mxChoice && rCost>=mxCost ){ +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf("Skip %s cost=%3d order=%c\n", + wherePathName(pFrom, iLoop, pWLoop), rCost, + isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?'); + } +#endif + continue; + } + /* Add a new Path to the aTo[] set */ + if( nTo<mxChoice ){ + /* Increase the size of the aTo set by one */ + jj = nTo++; + }else{ + /* New path replaces the prior worst to keep count below mxChoice */ + for(jj=nTo-1; aTo[jj].rCost<mxCost; jj--){ assert(jj>0); } + } + pTo = &aTo[jj]; +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf("New %s cost=%-3d order=%c\n", + wherePathName(pFrom, iLoop, pWLoop), rCost, + isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?'); + } +#endif + }else{ + if( pTo->rCost<=rCost ){ +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf( + "Skip %s cost=%-3d order=%c", + wherePathName(pFrom, iLoop, pWLoop), rCost, + isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?'); + sqlite3DebugPrintf(" vs %s cost=%-3d order=%c\n", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, + pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?'); + } +#endif + testcase( pTo->rCost==rCost ); + continue; + } + testcase( pTo->rCost==rCost+1 ); + /* A new and better score for a previously created equivalent path */ +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace&0x4 ){ + sqlite3DebugPrintf( + "Update %s cost=%-3d order=%c", + wherePathName(pFrom, iLoop, pWLoop), rCost, + isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?'); + sqlite3DebugPrintf(" was %s cost=%-3d order=%c\n", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, + pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?'); + } +#endif + } + /* pWLoop is a winner. Add it to the set of best so far */ + pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf; + pTo->revLoop = revMask; + pTo->nRow = pFrom->nRow + pWLoop->nOut; + pTo->rCost = rCost; + pTo->isOrderedValid = isOrderedValid; + pTo->isOrdered = isOrdered; + memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); + pTo->aLoop[iLoop] = pWLoop; + if( nTo>=mxChoice ){ + mxCost = aTo[0].rCost; + for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){ + if( pTo->rCost>mxCost ) mxCost = pTo->rCost; + } + } + } + } + +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace>=2 ){ + sqlite3DebugPrintf("---- after round %d ----\n", iLoop); + for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){ + sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c", + wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, + pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?'); + if( pTo->isOrderedValid && pTo->isOrdered ){ + sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); + }else{ + sqlite3DebugPrintf("\n"); + } + } + } +#endif + + /* Swap the roles of aFrom and aTo for the next generation */ + pFrom = aTo; + aTo = aFrom; + aFrom = pFrom; + nFrom = nTo; + } + + if( nFrom==0 ){ + sqlite3ErrorMsg(pParse, "no query solution"); + sqlite3DbFree(db, pSpace); + return SQLITE_ERROR; + } + + /* Find the lowest cost path. pFrom will be left pointing to that path */ + pFrom = aFrom; + assert( nFrom==1 ); +#if 0 /* The following is needed if nFrom is ever more than 1 */ + for(ii=1; ii<nFrom; ii++){ + if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii]; + } +#endif + assert( pWInfo->nLevel==nLoop ); + /* Load the lowest cost path into pWInfo */ + for(iLoop=0; iLoop<nLoop; iLoop++){ + WhereLevel *pLevel = pWInfo->a + iLoop; + pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop]; + pLevel->iFrom = pWLoop->iTab; + pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor; + } + if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0 + && pWInfo->pDistinct + && nRowEst + ){ + Bitmask notUsed; + int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pDistinct, pFrom, + WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used); + if( rc==1 ) pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; + } + if( pFrom->isOrdered ){ + if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){ + pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; + }else{ + pWInfo->bOBSat = 1; + pWInfo->revMask = pFrom->revLoop; + } + } + pWInfo->nRowOut = pFrom->nRow; + + /* Free temporary memory and return success */ + sqlite3DbFree(db, pSpace); + return SQLITE_OK; +} + +/* +** Most queries use only a single table (they are not joins) and have +** simple == constraints against indexed fields. This routine attempts +** to plan those simple cases using much less ceremony than the +** general-purpose query planner, and thereby yield faster sqlite3_prepare() +** times for the common case. +** +** Return non-zero on success, if this query can be handled by this +** no-frills query planner. Return zero if this query needs the +** general-purpose query planner. +*/ +static int whereShortCut(WhereLoopBuilder *pBuilder){ + WhereInfo *pWInfo; + struct SrcList_item *pItem; + WhereClause *pWC; + WhereTerm *pTerm; + WhereLoop *pLoop; + int iCur; + int j; + Table *pTab; + Index *pIdx; + + pWInfo = pBuilder->pWInfo; + if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0; + assert( pWInfo->pTabList->nSrc>=1 ); + pItem = pWInfo->pTabList->a; + pTab = pItem->pTab; + if( IsVirtual(pTab) ) return 0; + if( pItem->zIndex ) return 0; + iCur = pItem->iCursor; + pWC = &pWInfo->sWC; + pLoop = pBuilder->pNew; + pLoop->wsFlags = 0; + pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0); + if( pTerm ){ + pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; + pLoop->aLTerm[0] = pTerm; + pLoop->nLTerm = 1; + pLoop->u.btree.nEq = 1; + /* TUNING: Cost of a rowid lookup is 10 */ + pLoop->rRun = 33; /* 33==whereCost(10) */ + }else{ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->onError==OE_None ) continue; + for(j=0; j<pIdx->nColumn; j++){ + pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx); + if( pTerm==0 ) break; + whereLoopResize(pWInfo->pParse->db, pLoop, j); + pLoop->aLTerm[j] = pTerm; + } + if( j!=pIdx->nColumn ) continue; + pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; + if( (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){ + pLoop->wsFlags |= WHERE_IDX_ONLY; + } + pLoop->nLTerm = j; + pLoop->u.btree.nEq = j; + pLoop->u.btree.pIndex = pIdx; + /* TUNING: Cost of a unique index lookup is 15 */ + pLoop->rRun = 39; /* 39==whereCost(15) */ + break; + } + } + if( pLoop->wsFlags ){ + pLoop->nOut = (WhereCost)1; + pWInfo->a[0].pWLoop = pLoop; + pLoop->maskSelf = getMask(&pWInfo->sMaskSet, iCur); + pWInfo->a[0].iTabCur = iCur; + pWInfo->nRowOut = 1; + if( pWInfo->pOrderBy ) pWInfo->bOBSat = 1; + if( pWInfo->pDistinct ) pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; +#ifdef SQLITE_DEBUG + pLoop->cId = '0'; +#endif + return 1; + } + return 0; +} + +/* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine @@ -5040,15 +5551,6 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ ** pOrderBy is a pointer to the ORDER BY clause of a SELECT statement, ** if there is one. If there is no ORDER BY clause or if this routine ** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. -** -** If an index can be used so that the natural output order of the table -** scan is correct for the ORDER BY clause, then that index is used and -** the returned WhereInfo.nOBSat field is set to pOrderBy->nExpr. This -** is an optimization that prevents an unnecessary sort of the result set -** if an index appropriate for the ORDER BY clause already exists. -** -** If the where clause loops cannot be arranged to provide the correct -** output order, then WhereInfo.nOBSat is 0. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ @@ -5064,18 +5566,17 @@ WhereInfo *sqlite3WhereBegin( WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Bitmask notReady; /* Cursors that are not yet positioned */ - WhereBestIdx sWBI; /* Best index search context */ + WhereLoopBuilder sWLB; /* The WhereLoop builder */ WhereMaskSet *pMaskSet; /* The expression mask set */ WhereLevel *pLevel; /* A single level in pWInfo->a[] */ - int iFrom; /* First unused FROM clause element */ - int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */ int ii; /* Loop counter */ sqlite3 *db; /* Database connection */ + int rc; /* Return code */ /* Variable initialization */ - memset(&sWBI, 0, sizeof(sWBI)); - sWBI.pParse = pParse; + memset(&sWLB, 0, sizeof(sWLB)); + sWLB.pOrderBy = pOrderBy; /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -5102,11 +5603,7 @@ WhereInfo *sqlite3WhereBegin( */ db = pParse->db; nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); - pWInfo = sqlite3DbMallocZero(db, - nByteWInfo + - sizeof(WhereClause) + - sizeof(WhereMaskSet) - ); + pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereLoop)); if( db->mallocFailed ){ sqlite3DbFree(db, pWInfo); pWInfo = 0; @@ -5115,12 +5612,19 @@ WhereInfo *sqlite3WhereBegin( pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; + pWInfo->pOrderBy = pOrderBy; + pWInfo->pDistinct = pDistinct; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); - pWInfo->pWC = sWBI.pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo]; pWInfo->wctrlFlags = wctrlFlags; pWInfo->savedNQueryLoop = pParse->nQueryLoop; - pMaskSet = (WhereMaskSet*)&sWBI.pWC[1]; - sWBI.aLevel = pWInfo->a; + pMaskSet = &pWInfo->sMaskSet; + sWLB.pWInfo = pWInfo; + sWLB.pWC = &pWInfo->sWC; + sWLB.pNew = (WhereLoop*)&pWInfo->a[nTabList]; + whereLoopInit(sWLB.pNew); +#ifdef SQLITE_DEBUG + sWLB.pNew->cId = '*'; +#endif /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ @@ -5130,9 +5634,9 @@ WhereInfo *sqlite3WhereBegin( ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); - whereClauseInit(sWBI.pWC, pParse, pMaskSet, wctrlFlags); + whereClauseInit(&pWInfo->sWC, pWInfo); sqlite3ExprCodeConstants(pParse, pWhere); - whereSplit(sWBI.pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */ + whereSplit(&pWInfo->sWC, pWhere, TK_AND); /* IMP: R-15842-53296 */ /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. @@ -5142,6 +5646,13 @@ WhereInfo *sqlite3WhereBegin( pWhere = 0; } + /* Special case: No FROM clause + */ + if( nTabList==0 ){ + if( pOrderBy ) pWInfo->bOBSat = 1; + if( pDistinct ) pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + } + /* Assign a bit from the bitmask to every term in the FROM clause. ** ** When assigning bitmask values to FROM clause cursors, it must be @@ -5177,288 +5688,103 @@ WhereInfo *sqlite3WhereBegin( ** want to analyze these virtual terms, so start analyzing at the end ** and work forward so that the added virtual terms are never processed. */ - exprAnalyzeAll(pTabList, sWBI.pWC); + exprAnalyzeAll(pTabList, &pWInfo->sWC); if( db->mallocFailed ){ goto whereBeginError; } + /* If the ORDER BY (or GROUP BY) clause contains references to general + ** expressions, then we won't be able to satisfy it using indices, so + ** go ahead and disable it now. + */ + if( pOrderBy && pDistinct ){ + for(ii=0; ii<pOrderBy->nExpr; ii++){ + Expr *pExpr = sqlite3ExprSkipCollate(pOrderBy->a[ii].pExpr); + if( pExpr->op!=TK_COLUMN ){ + pWInfo->pOrderBy = pOrderBy = 0; + break; + }else if( pExpr->iColumn<0 ){ + break; + } + } + } + /* Check if the DISTINCT qualifier, if there is one, is redundant. ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT. */ - if( pDistinct && isDistinctRedundant(pParse, pTabList, sWBI.pWC, pDistinct) ){ - pDistinct = 0; - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + if( pDistinct ){ + if( isDistinctRedundant(pParse,pTabList,&pWInfo->sWC,pDistinct) ){ + pDistinct = 0; + pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + }else if( pOrderBy==0 ){ + pWInfo->wctrlFlags |= WHERE_DISTINCTBY; + pWInfo->pOrderBy = pDistinct; + } } - /* Chose the best index to use for each table in the FROM clause. - ** - ** This loop fills in the following fields: - ** - ** pWInfo->a[].pIdx The index to use for this level of the loop. - ** pWInfo->a[].wsFlags WHERE_xxx flags associated with pIdx - ** pWInfo->a[].nEq The number of == and IN constraints - ** pWInfo->a[].iFrom Which term of the FROM clause is being coded - ** pWInfo->a[].iTabCur The VDBE cursor for the database table - ** pWInfo->a[].iIdxCur The VDBE cursor for the index - ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term - ** - ** This loop also figures out the nesting order of tables in the FROM - ** clause. - */ - sWBI.notValid = ~(Bitmask)0; - sWBI.pOrderBy = pOrderBy; - sWBI.n = nTabList; - sWBI.pDistinct = pDistinct; - andFlags = ~0; - WHERETRACE(("*** Optimizer Start ***\n")); - for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){ - WhereCost bestPlan; /* Most efficient plan seen so far */ - Index *pIdx; /* Index for FROM table at pTabItem */ - int j; /* For looping over FROM tables */ - int bestJ = -1; /* The value of j */ - Bitmask m; /* Bitmask value for j or bestJ */ - int isOptimal; /* Iterator for optimal/non-optimal search */ - int ckOptimal; /* Do the optimal scan check */ - int nUnconstrained; /* Number tables without INDEXED BY */ - Bitmask notIndexed; /* Mask of tables that cannot use an index */ - - memset(&bestPlan, 0, sizeof(bestPlan)); - bestPlan.rCost = SQLITE_BIG_DBL; - WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i)); - - /* Loop through the remaining entries in the FROM clause to find the - ** next nested loop. The loop tests all FROM clause entries - ** either once or twice. - ** - ** The first test is always performed if there are two or more entries - ** remaining and never performed if there is only one FROM clause entry - ** to choose from. The first test looks for an "optimal" scan. In - ** this context an optimal scan is one that uses the same strategy - ** for the given FROM clause entry as would be selected if the entry - ** were used as the innermost nested loop. In other words, a table - ** is chosen such that the cost of running that table cannot be reduced - ** by waiting for other tables to run first. This "optimal" test works - ** by first assuming that the FROM clause is on the inner loop and finding - ** its query plan, then checking to see if that query plan uses any - ** other FROM clause terms that are sWBI.notValid. If no notValid terms - ** are used then the "optimal" query plan works. - ** - ** Note that the WhereCost.nRow parameter for an optimal scan might - ** not be as small as it would be if the table really were the innermost - ** join. The nRow value can be reduced by WHERE clause constraints - ** that do not use indices. But this nRow reduction only happens if the - ** table really is the innermost join. - ** - ** The second loop iteration is only performed if no optimal scan - ** strategies were found by the first iteration. This second iteration - ** is used to search for the lowest cost scan overall. - ** - ** Without the optimal scan step (the first iteration) a suboptimal - ** plan might be chosen for queries like this: - ** - ** CREATE TABLE t1(a, b); - ** CREATE TABLE t2(c, d); - ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; - ** - ** The best strategy is to iterate through table t1 first. However it - ** is not possible to determine this with a simple greedy algorithm. - ** Since the cost of a linear scan through table t2 is the same - ** as the cost of a linear scan through table t1, a simple greedy - ** algorithm may choose to use t2 for the outer loop, which is a much - ** costlier approach. - */ - nUnconstrained = 0; - notIndexed = 0; - - /* The optimal scan check only occurs if there are two or more tables - ** available to be reordered */ - if( iFrom==nTabList-1 ){ - ckOptimal = 0; /* Common case of just one table in the FROM clause */ - }else{ - ckOptimal = -1; - for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ - m = getMask(pMaskSet, sWBI.pSrc->iCursor); - if( (m & sWBI.notValid)==0 ){ - if( j==iFrom ) iFrom++; - continue; - } - if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; - if( ++ckOptimal ) break; - if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; + /* Construct the WhereLoop objects */ + WHERETRACE(0xffff,("*** Optimizer Start ***\n")); + if( nTabList!=1 || whereShortCut(&sWLB)==0 ){ + rc = whereLoopAddAll(&sWLB); + if( rc ) goto whereBeginError; + + /* Display all of the WhereLoop objects if wheretrace is enabled */ +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace ){ + WhereLoop *p; + int i = 0; + static char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" + "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; + for(p=pWInfo->pLoops; p; p=p->pNextLoop){ + p->cId = zLabel[(i++)%sizeof(zLabel)]; + whereLoopPrint(p, pTabList); } } - assert( ckOptimal==0 || ckOptimal==1 ); - - for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ - for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){ - if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ - /* This break and one like it in the ckOptimal computation loop - ** above prevent table reordering across LEFT and CROSS JOINs. - ** The LEFT JOIN case is necessary for correctness. The prohibition - ** against reordering across a CROSS JOIN is an SQLite feature that - ** allows the developer to control table reordering */ - break; - } - m = getMask(pMaskSet, sWBI.pSrc->iCursor); - if( (m & sWBI.notValid)==0 ){ - assert( j>iFrom ); - continue; - } - sWBI.notReady = (isOptimal ? m : sWBI.notValid); - if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; - - WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", - j, sWBI.pSrc->pTab->zName, isOptimal)); - assert( sWBI.pSrc->pTab ); -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(sWBI.pSrc->pTab) ){ - sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo; - bestVirtualIndex(&sWBI); - }else #endif - { - bestBtreeIndex(&sWBI); - } - assert( isOptimal || (sWBI.cost.used&sWBI.notValid)==0 ); - - /* If an INDEXED BY clause is present, then the plan must use that - ** index if it uses any index at all */ - assert( sWBI.pSrc->pIndex==0 - || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 - || sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex ); - - if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){ - notIndexed |= m; - } - if( isOptimal ){ - pWInfo->a[j].rOptCost = sWBI.cost.rCost; - }else if( ckOptimal ){ - /* If two or more tables have nearly the same outer loop cost, but - ** very different inner loop (optimal) cost, we want to choose - ** for the outer loop that table which benefits the least from - ** being in the inner loop. The following code scales the - ** outer loop cost estimate to accomplish that. */ - WHERETRACE((" scaling cost from %.1f to %.1f\n", - sWBI.cost.rCost, - sWBI.cost.rCost/pWInfo->a[j].rOptCost)); - sWBI.cost.rCost /= pWInfo->a[j].rOptCost; - } - - /* Conditions under which this table becomes the best so far: - ** - ** (1) The table must not depend on other tables that have not - ** yet run. (In other words, it must not depend on tables - ** in inner loops.) - ** - ** (2) (This rule was removed on 2012-11-09. The scaling of the - ** cost using the optimal scan cost made this rule obsolete.) - ** - ** (3) All tables have an INDEXED BY clause or this table lacks an - ** INDEXED BY clause or this table uses the specific - ** index specified by its INDEXED BY clause. This rule ensures - ** that a best-so-far is always selected even if an impossible - ** combination of INDEXED BY clauses are given. The error - ** will be detected and relayed back to the application later. - ** The NEVER() comes about because rule (2) above prevents - ** An indexable full-table-scan from reaching rule (3). - ** - ** (4) The plan cost must be lower than prior plans, where "cost" - ** is defined by the compareCost() function above. - */ - if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */ - && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */ - || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) - && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */ - ){ - WHERETRACE((" === table %d (%s) is best so far\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n", - j, sWBI.pSrc->pTab->zName, - sWBI.cost.rCost, sWBI.cost.plan.nRow, - sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); - bestPlan = sWBI.cost; - bestJ = j; - } - - /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that - ** table y (and not table z) is always the next inner loop inside - ** of table x. */ - if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; - } + + wherePathSolver(pWInfo, 0); + if( db->mallocFailed ) goto whereBeginError; + if( pWInfo->pOrderBy ){ + wherePathSolver(pWInfo, pWInfo->nRowOut+1); + if( db->mallocFailed ) goto whereBeginError; } - assert( bestJ>=0 ); - assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); - assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); - testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); - testcase( bestJ>iFrom && bestJ<nTabList-1 - && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); - WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" - " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", - bestJ, pTabList->a[bestJ].pTab->zName, - pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, - bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); - if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ - assert( pWInfo->eDistinct==0 ); - pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; + } + if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ + pWInfo->revMask = (Bitmask)(-1); + } + if( pParse->nErr || NEVER(db->mallocFailed) ){ + goto whereBeginError; + } +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace ){ + int ii; + sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); + if( pWInfo->bOBSat ){ + sqlite3DebugPrintf(" ORDERBY=0x%llx", pWInfo->revMask); } - andFlags &= bestPlan.plan.wsFlags; - pLevel->plan = bestPlan.plan; - pLevel->iTabCur = pTabList->a[bestJ].iCursor; - testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); - testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); - if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ - if( (wctrlFlags & WHERE_ONETABLE_ONLY) - && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0 - ){ - pLevel->iIdxCur = iIdxCur; - }else{ - pLevel->iIdxCur = pParse->nTab++; + switch( pWInfo->eDistinct ){ + case WHERE_DISTINCT_UNIQUE: { + sqlite3DebugPrintf(" DISTINCT=unique"); + break; } - }else{ - pLevel->iIdxCur = -1; - } - sWBI.notValid &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); - pLevel->iFrom = (u8)bestJ; - if( bestPlan.plan.nRow>=(double)1 ){ - pParse->nQueryLoop *= bestPlan.plan.nRow; - } - - /* Check that if the table scanned by this loop iteration had an - ** INDEXED BY clause attached to it, that the named index is being - ** used for the scan. If not, then query compilation has failed. - ** Return an error. - */ - pIdx = pTabList->a[bestJ].pIndex; - if( pIdx ){ - if( (bestPlan.plan.wsFlags & WHERE_INDEXED)==0 ){ - sqlite3ErrorMsg(pParse, "cannot use index: %s", pIdx->zName); - goto whereBeginError; - }else{ - /* If an INDEXED BY clause is used, the bestIndex() function is - ** guaranteed to find the index specified in the INDEXED BY clause - ** if it find an index at all. */ - assert( bestPlan.plan.u.pIdx==pIdx ); + case WHERE_DISTINCT_ORDERED: { + sqlite3DebugPrintf(" DISTINCT=ordered"); + break; + } + case WHERE_DISTINCT_UNORDERED: { + sqlite3DebugPrintf(" DISTINCT=unordered"); + break; } } + sqlite3DebugPrintf("\n"); + for(ii=0; ii<nTabList; ii++){ + whereLoopPrint(pWInfo->a[ii].pWLoop, pTabList); + } } - WHERETRACE(("*** Optimizer Finished ***\n")); - if( pParse->nErr || db->mallocFailed ){ - goto whereBeginError; - } - if( nTabList ){ - pLevel--; - pWInfo->nOBSat = pLevel->plan.nOBSat; - }else{ - pWInfo->nOBSat = 0; - } - - /* If the total query only selects a single row, then the ORDER BY - ** clause is irrelevant. - */ - if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){ - assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ); - pWInfo->nOBSat = pOrderBy->nExpr; - } +#endif + WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); + pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; /* If the caller is an UPDATE or DELETE statement that is requesting ** to use a one-pass algorithm, determine if this is appropriate. @@ -5466,9 +5792,10 @@ WhereInfo *sqlite3WhereBegin( ** the statement to update a single row. */ assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); - if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){ + if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 + && (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){ pWInfo->okOnePass = 1; - pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY; + pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY; } /* Open all tables in the pTabList and any indices selected for @@ -5476,21 +5803,21 @@ WhereInfo *sqlite3WhereBegin( */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; - pWInfo->nRowOut = (double)1; for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ struct SrcList_item *pTabItem; + WhereLoop *pLoop; pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; - pWInfo->nRowOut *= pLevel->plan.nRow; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + pLoop = pLevel->pWLoop; if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE - if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); @@ -5498,12 +5825,12 @@ WhereInfo *sqlite3WhereBegin( /* noop */ }else #endif - if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 + if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); - testcase( pTab->nCol==BMS-1 ); - testcase( pTab->nCol==BMS ); + testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 ); + testcase( !pWInfo->okOnePass && pTab->nCol==BMS ); if( !pWInfo->okOnePass && pTab->nCol<BMS ){ Bitmask b = pTabItem->colUsed; int n = 0; @@ -5516,14 +5843,15 @@ WhereInfo *sqlite3WhereBegin( sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } #ifndef SQLITE_OMIT_AUTOMATIC_INDEX - if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ - constructAutomaticIndex(pParse, sWBI.pWC, pTabItem, notReady, pLevel); + if( (pLoop->wsFlags & WHERE_TEMP_INDEX)!=0 ){ + constructAutomaticIndex(pParse, &pWInfo->sWC, pTabItem, notReady, pLevel); }else #endif - if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ - Index *pIx = pLevel->plan.u.pIdx; + if( pLoop->wsFlags & WHERE_INDEXED ){ + Index *pIx = pLoop->u.btree.pIndex; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx); - int iIndexCur = pLevel->iIdxCur; + /* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */ + int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb, @@ -5531,7 +5859,7 @@ WhereInfo *sqlite3WhereBegin( VdbeComment((v, "%s", pIx->zName)); } sqlite3CodeVerifySchema(pParse, iDb); - notReady &= ~getMask(sWBI.pWC->pMaskSet, pTabItem->iCursor); + notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); if( db->mallocFailed ) goto whereBeginError; @@ -5544,66 +5872,11 @@ WhereInfo *sqlite3WhereBegin( for(ii=0; ii<nTabList; ii++){ pLevel = &pWInfo->a[ii]; explainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags); - notReady = codeOneLoopStart(pWInfo, ii, wctrlFlags, notReady); + notReady = codeOneLoopStart(pWInfo, ii, notReady); pWInfo->iContinue = pLevel->addrCont; } -#ifdef SQLITE_TEST /* For testing and debugging use only */ - /* Record in the query plan information about the current table - ** and the index used to access it (if any). If the table itself - ** is not used, its name is just '{}'. If no index is used - ** the index is listed as "{}". If the primary key is used the - ** index name is '*'. - */ - for(ii=0; ii<nTabList; ii++){ - char *z; - int n; - int w; - struct SrcList_item *pTabItem; - - pLevel = &pWInfo->a[ii]; - w = pLevel->plan.wsFlags; - pTabItem = &pTabList->a[pLevel->iFrom]; - z = pTabItem->zAlias; - if( z==0 ) z = pTabItem->pTab->zName; - n = sqlite3Strlen30(z); - if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){ - if( (w & WHERE_IDX_ONLY)!=0 && (w & WHERE_COVER_SCAN)==0 ){ - memcpy(&sqlite3_query_plan[nQPlan], "{}", 2); - nQPlan += 2; - }else{ - memcpy(&sqlite3_query_plan[nQPlan], z, n); - nQPlan += n; - } - sqlite3_query_plan[nQPlan++] = ' '; - } - testcase( w & WHERE_ROWID_EQ ); - testcase( w & WHERE_ROWID_RANGE ); - if( w & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ - memcpy(&sqlite3_query_plan[nQPlan], "* ", 2); - nQPlan += 2; - }else if( (w & WHERE_INDEXED)!=0 && (w & WHERE_COVER_SCAN)==0 ){ - n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName); - if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){ - memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n); - nQPlan += n; - sqlite3_query_plan[nQPlan++] = ' '; - } - }else{ - memcpy(&sqlite3_query_plan[nQPlan], "{} ", 3); - nQPlan += 3; - } - } - while( nQPlan>0 && sqlite3_query_plan[nQPlan-1]==' ' ){ - sqlite3_query_plan[--nQPlan] = 0; - } - sqlite3_query_plan[nQPlan] = 0; - nQPlan = 0; -#endif /* SQLITE_TEST // Testing and debugging use only */ - - /* Record the continuation address in the WhereInfo structure. Then - ** clean up and return. - */ + /* Done. */ return pWInfo; /* Jump here if malloc fails */ @@ -5624,6 +5897,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ Vdbe *v = pParse->pVdbe; int i; WhereLevel *pLevel; + WhereLoop *pLoop; SrcList *pTabList = pWInfo->pTabList; sqlite3 *db = pParse->db; @@ -5632,12 +5906,13 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3ExprCacheClear(pParse); for(i=pWInfo->nLevel-1; i>=0; i--){ pLevel = &pWInfo->a[i]; + pLoop = pLevel->pWLoop; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2); sqlite3VdbeChangeP5(v, pLevel->p5); } - if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ + if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; int j; sqlite3VdbeResolveLabel(v, pLevel->addrNxt); @@ -5652,12 +5927,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ if( pLevel->iLeftJoin ){ int addr; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); - assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ); - if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){ + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 + || (pLoop->wsFlags & WHERE_INDEXED)!=0 ); + if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor); } - if( pLevel->iIdxCur>=0 ){ + if( pLoop->wsFlags & WHERE_INDEXED ){ sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } if( pLevel->op==OP_Return ){ @@ -5682,25 +5957,24 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); + pLoop = pLevel->pWLoop; if( (pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){ - int ws = pLevel->plan.wsFlags; + int ws = pLoop->wsFlags; if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } - if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){ + if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_TEMP_INDEX))==0 ){ sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } } - /* If this scan uses an index, make code substitutions to read data - ** from the index in preference to the table. Sometimes, this means - ** the table need never be read from. This is a performance boost, - ** as the vdbe level waits until the table is read before actually - ** seeking the table cursor to the record corresponding to the current - ** position in the index. + /* If this scan uses an index, make VDBE code substitutions to read data + ** from the index instead of from the table where possible. In some cases + ** this optimization prevents the table from ever being read, which can + ** yield a significant performance boost. ** ** Calls to the code generator in between sqlite3WhereBegin and ** sqlite3WhereEnd will have created code that references the table @@ -5708,12 +5982,12 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ ** that reference the table and converts them into opcodes that ** reference the index. */ - if( pLevel->plan.wsFlags & WHERE_INDEXED ){ - pIdx = pLevel->plan.u.pIdx; - }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ + if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){ + pIdx = pLoop->u.btree.pIndex; + }else if( pLoop->wsFlags & WHERE_MULTI_OR ){ pIdx = pLevel->u.pCovidx; } - if( pIdx && !db->mallocFailed){ + if( pIdx && !db->mallocFailed ){ int k, j, last; VdbeOp *pOp; @@ -5729,8 +6003,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ break; } } - assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 - || j<pIdx->nColumn ); + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || j<pIdx->nColumn ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; diff --git a/test/all.test b/test/all.test index cd2a43039..f6e722f2d 100644 --- a/test/all.test +++ b/test/all.test @@ -48,5 +48,3 @@ if {$::tcl_platform(platform)=="unix"} { } finish_test - - diff --git a/test/analyze3.test b/test/analyze3.test index f705bc666..c25d04422 100644 --- a/test/analyze3.test +++ b/test/analyze3.test @@ -97,10 +97,10 @@ do_test analyze3-1.1.1 { do_eqp_test analyze3-1.1.2 { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~179 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} do_eqp_test analyze3-1.1.3 { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~959 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}} do_test analyze3-1.1.4 { sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } @@ -146,10 +146,10 @@ do_test analyze3-1.2.1 { } {} do_eqp_test analyze3-1.2.2 { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 -} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~196 rows)}} +} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}} do_eqp_test analyze3-1.2.3 { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 -} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~968 rows)}} +} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}} do_test analyze3-1.2.4 { sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 } } {161 0 4760} @@ -193,10 +193,10 @@ do_test analyze3-1.3.1 { } {} do_eqp_test analyze3-1.3.2 { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 -} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~156 rows)}} +} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}} do_eqp_test analyze3-1.3.3 { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 -} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~989 rows)}} +} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}} do_test analyze3-1.3.4 { sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } @@ -248,10 +248,10 @@ do_test analyze3-2.1 { } {} do_eqp_test analyze3-2.2 { SELECT count(a) FROM t1 WHERE b LIKE 'a%' -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~31250 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?)}} do_eqp_test analyze3-2.3 { SELECT count(a) FROM t1 WHERE b LIKE '%a' -} {0 0 0 {SCAN TABLE t1 (~500000 rows)}} +} {0 0 0 {SCAN TABLE t1}} do_test analyze3-2.4 { sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' } @@ -330,7 +330,7 @@ do_test analyze3-3.2.5 { do_test analyze3-3.2.6 { sqlite3_bind_text $S 1 "abc" 3 sqlite3_expired $S -} {0} +} {1} do_test analyze3-3.2.7 { sqlite3_finalize $S } {SQLITE_OK} diff --git a/test/analyze4.test b/test/analyze4.test index 1fed564b8..974ed89a8 100644 --- a/test/analyze4.test +++ b/test/analyze4.test @@ -38,7 +38,7 @@ do_test analyze4-1.0 { # Should choose the t1a index since it is more specific than t1b. db eval {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=5 AND b IS NULL} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} # Verify that the t1b index shows that it does not narrow down the # search any at all. diff --git a/test/analyze5.test b/test/analyze5.test index 1041d709d..c400780ff 100644 --- a/test/analyze5.test +++ b/test/analyze5.test @@ -156,13 +156,14 @@ foreach {testid where index rows} { } { # Verify that the expected index is used with the expected row count - do_test analyze5-1.${testid}a { - set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] - set idx {} - regexp {INDEX (t1.) } $x all idx - regexp {~([0-9]+) rows} $x all nrow - list $idx $nrow - } [list $index $rows] + # No longer valid due to an EXPLAIN QUERY PLAN output format change + # do_test analyze5-1.${testid}a { + # set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + # set idx {} + # regexp {INDEX (t1.) } $x all idx + # regexp {~([0-9]+) rows} $x all nrow + # list $idx $nrow + # } [list $index $rows] # Verify that the same result is achieved regardless of whether or not # the index is used @@ -202,15 +203,14 @@ foreach {testid where index rows} { } { # Verify that the expected index is used with the expected row count -if {$testid==50299} {breakpoint; set sqlite_where_trace 1} - do_test analyze5-1.${testid}a { - set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] - set idx {} - regexp {INDEX (t1.) } $x all idx - regexp {~([0-9]+) rows} $x all nrow - list $idx $nrow - } [list $index $rows] -if {$testid==50299} exit + # No longer valid due to an EXPLAIN QUERY PLAN format change + # do_test analyze5-1.${testid}a { + # set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + # set idx {} + # regexp {INDEX (t1.) } $x all idx + # regexp {~([0-9]+) rows} $x all nrow + # list $idx $nrow + # } [list $index $rows] # Verify that the same result is achieved regardless of whether or not # the index is used diff --git a/test/analyze6.test b/test/analyze6.test index eaa9d731b..55e4347c6 100644 --- a/test/analyze6.test +++ b/test/analyze6.test @@ -61,14 +61,14 @@ do_test analyze6-1.0 { # do_test analyze6-1.1 { eqp {SELECT count(*) FROM ev, cat WHERE x=y} -} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} +} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}} # The same plan is chosen regardless of the order of the tables in the # FROM clause. # do_test analyze6-1.2 { eqp {SELECT count(*) FROM cat, ev WHERE x=y} -} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} +} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}} # Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30 @@ -82,26 +82,26 @@ do_test analyze6-2.1 { ANALYZE; } eqp {SELECT * FROM t201 WHERE z=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}} do_test analyze6-2.2 { eqp {SELECT * FROM t201 WHERE y=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}} do_test analyze6-2.3 { eqp {SELECT * FROM t201 WHERE x=5} -} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}} do_test analyze6-2.4 { execsql { INSERT INTO t201 VALUES(1,2,3); ANALYZE t201; } eqp {SELECT * FROM t201 WHERE z=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}} do_test analyze6-2.5 { eqp {SELECT * FROM t201 WHERE y=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}} do_test analyze6-2.6 { eqp {SELECT * FROM t201 WHERE x=5} -} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}} do_test analyze6-2.7 { execsql { INSERT INTO t201 VALUES(4,5,7); @@ -111,12 +111,12 @@ do_test analyze6-2.7 { ANALYZE t201; } eqp {SELECT * FROM t201 WHERE z=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}} do_test analyze6-2.8 { eqp {SELECT * FROM t201 WHERE y=5} -} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}} do_test analyze6-2.9 { eqp {SELECT * FROM t201 WHERE x=5} -} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}} finish_test diff --git a/test/analyze7.test b/test/analyze7.test index 46ec39e7b..ddfe398a9 100644 --- a/test/analyze7.test +++ b/test/analyze7.test @@ -37,13 +37,13 @@ do_test analyze7-1.0 { WHERE value BETWEEN 1 AND 256; EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123; } -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test analyze7-1.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test analyze7-1.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} # Run an analyze on one of the three indices. Verify that this # effects the row-count estimate on the one query that uses that @@ -53,20 +53,20 @@ do_test analyze7-2.0 { execsql {ANALYZE t1a;} db cache flush execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test analyze7-2.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test analyze7-2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} # Verify that since the query planner now things that t1a is more # selective than t1b, it prefers to use t1a. # do_test analyze7-2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} # Run an analysis on another of the three indices. Verify that this # new analysis works and does not disrupt the previous analysis. @@ -75,39 +75,39 @@ do_test analyze7-3.0 { execsql {ANALYZE t1cd;} db cache flush; execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test analyze7-3.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test analyze7-3.2.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} ifcapable stat3 { # If ENABLE_STAT3 is defined, SQLite comes up with a different estimated # row count for (c=2) than it does for (c=?). do_test analyze7-3.2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} - } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~57 rows)}} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} } else { # If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the # same as that for (c=?). do_test analyze7-3.2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} - } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} } do_test analyze7-3.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} ifcapable {!stat3} { do_test analyze7-3.4 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123} - } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test analyze7-3.5 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123} - } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} } do_test analyze7-3.6 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?)}} finish_test diff --git a/test/analyze8.test b/test/analyze8.test index f3e2710ab..94054e1c8 100644 --- a/test/analyze8.test +++ b/test/analyze8.test @@ -61,25 +61,25 @@ do_test 1.0 { # do_test 1.1 { eqp {SELECT * FROM t1 WHERE a=100 AND b=55} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test 1.2 { eqp {SELECT * FROM t1 WHERE a=99 AND b=55} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test 1.3 { eqp {SELECT * FROM t1 WHERE a=101 AND b=55} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test 1.4 { eqp {SELECT * FROM t1 WHERE a=100 AND b=56} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} do_test 1.5 { eqp {SELECT * FROM t1 WHERE a=99 AND b=56} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test 1.6 { eqp {SELECT * FROM t1 WHERE a=101 AND b=56} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test 2.1 { eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}} # There are many more values of c between 0 and 100000 than there are # between 800000 and 900000. So t1c is more selective for the latter @@ -87,17 +87,17 @@ do_test 2.1 { # do_test 3.1 { eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~6 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}} do_test 3.2 { eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 800000 AND 900000} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~4 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}} do_test 3.3 { eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~63 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} do_test 3.4 { eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 800000 AND 900000} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}} finish_test diff --git a/test/async5.test b/test/async5.test index aa484fccb..abac11f75 100644 --- a/test/async5.test +++ b/test/async5.test @@ -66,4 +66,3 @@ sqlite3async_control halt never sqlite3async_shutdown set sqlite3async_trace 0 finish_test - diff --git a/test/autoindex1.test b/test/autoindex1.test index 54ff82a2b..f8f1d9e7c 100644 --- a/test/autoindex1.test +++ b/test/autoindex1.test @@ -78,6 +78,9 @@ do_test autoindex1-202 { do_test autoindex1-210 { db eval { PRAGMA automatic_index=ON; + ANALYZE; + UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t1'; + ANALYZE sqlite_master; SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1; } } {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988} @@ -143,22 +146,25 @@ do_test autoindex1-401 { do_execsql_test autoindex1-500 { CREATE TABLE t501(a INTEGER PRIMARY KEY, b); CREATE TABLE t502(x INTEGER PRIMARY KEY, y); + INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t501',null,'1000000'); + INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t502',null,'1000'); + ANALYZE sqlite_master; EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=?); } { - 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)} + 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {EXECUTE LIST SUBQUERY 1} - 1 0 0 {SCAN TABLE t502 (~100000 rows)} + 1 0 0 {SCAN TABLE t502} } do_execsql_test autoindex1-501 { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } { - 0 0 0 {SCAN TABLE t501 (~500000 rows)} + 0 0 0 {SCAN TABLE t501} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} - 1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?) (~7 rows)} + 1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?)} } do_execsql_test autoindex1-502 { EXPLAIN QUERY PLAN @@ -166,9 +172,9 @@ do_execsql_test autoindex1-502 { WHERE t501.a=123 AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } { - 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} - 1 0 0 {SCAN TABLE t502 (~100000 rows)} + 1 0 0 {SCAN TABLE t502} } @@ -240,12 +246,12 @@ do_execsql_test autoindex1-600 { WHERE y.sheep_no IS NULL ORDER BY x.registering_flock; } { - 1 0 0 {SCAN TABLE sheep AS s (~1000000 rows)} - 1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?) (~2 rows)} + 1 0 0 {SCAN TABLE sheep AS s} + 1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?)} 1 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} - 2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?) (~1 rows)} - 0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index (~1000000 rows)} - 0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?) (~8 rows)} + 2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?)} + 0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index} + 0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?)} } @@ -253,7 +259,7 @@ do_execsql_test autoindex1-700 { CREATE TABLE t5(a, b, c); EXPLAIN QUERY PLAN SELECT a FROM t5 WHERE b=10 ORDER BY c; } { - 0 0 0 {SCAN TABLE t5 (~100000 rows)} + 0 0 0 {SCAN TABLE t5} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } diff --git a/test/backup4.test b/test/backup4.test index a1a773552..417df80e5 100644 --- a/test/backup4.test +++ b/test/backup4.test @@ -101,4 +101,3 @@ do_test 3.3 { do_test 3.4 { file size test.db2 } 0 finish_test - diff --git a/test/between.test b/test/between.test index 45436756a..df4c67995 100644 --- a/test/between.test +++ b/test/between.test @@ -48,14 +48,24 @@ do_test between-1.0 { # This procedure executes the SQL. Then it appends to the result the # "sort" or "nosort" keyword depending on whether or not any sorting -# is done. Then it appends the ::sqlite_query_plan variable. +# is done. Then it appends the names of the table and index used. # proc queryplan {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x - return [concat $data $::sqlite_query_plan] + set eqp [execsql "EXPLAIN QUERY PLAN $sql"] + # puts eqp=$eqp + foreach {a b c x} $eqp { + if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \ + $x all as tab idx]} { + lappend data $tab $idx + } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} { + lappend data $tab * + } + } + return $data } do_test between-1.1.1 { @@ -67,7 +77,7 @@ do_test between-1.1.2 { queryplan { SELECT * FROM t1 WHERE +w BETWEEN 5 AND 6 ORDER BY +w } -} {5 2 36 38 6 2 49 51 sort t1 {}} +} {5 2 36 38 6 2 49 51 sort t1 *} do_test between-1.2.1 { queryplan { SELECT * FROM t1 WHERE w BETWEEN 5 AND 65-y ORDER BY +w @@ -77,7 +87,7 @@ do_test between-1.2.2 { queryplan { SELECT * FROM t1 WHERE +w BETWEEN 5 AND 65-y ORDER BY +w } -} {5 2 36 38 6 2 49 51 sort t1 {}} +} {5 2 36 38 6 2 49 51 sort t1 *} do_test between-1.3.1 { queryplan { SELECT * FROM t1 WHERE w BETWEEN 41-y AND 6 ORDER BY +w @@ -87,12 +97,12 @@ do_test between-1.3.2 { queryplan { SELECT * FROM t1 WHERE +w BETWEEN 41-y AND 6 ORDER BY +w } -} {5 2 36 38 6 2 49 51 sort t1 {}} +} {5 2 36 38 6 2 49 51 sort t1 *} do_test between-1.4 { queryplan { SELECT * FROM t1 WHERE w BETWEEN 41-y AND 65-y ORDER BY +w } -} {5 2 36 38 6 2 49 51 sort t1 {}} +} {5 2 36 38 6 2 49 51 sort t1 *} do_test between-1.5.1 { queryplan { SELECT * FROM t1 WHERE 26 BETWEEN y AND z ORDER BY +w @@ -107,7 +117,7 @@ do_test between-1.5.3 { queryplan { SELECT * FROM t1 WHERE 26 BETWEEN y AND +z ORDER BY +w } -} {4 2 25 27 sort t1 {}} +} {4 2 25 27 sort t1 *} finish_test diff --git a/test/btreefault.test b/test/btreefault.test index 9b42240b3..61104c5a7 100644 --- a/test/btreefault.test +++ b/test/btreefault.test @@ -55,4 +55,3 @@ do_faultsim_test 1 -prep { } finish_test - diff --git a/test/capi3e.test b/test/capi3e.test index 21304cb8e..d7ab8d0bd 100644 --- a/test/capi3e.test +++ b/test/capi3e.test @@ -60,7 +60,7 @@ proc utf8 {str} { db close # here's the list of file names we're testing -set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db} +set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db} set i 0 foreach name $names { diff --git a/test/close.test b/test/close.test index 355a8865b..d5d6391ae 100644 --- a/test/close.test +++ b/test/close.test @@ -76,4 +76,3 @@ do_test 1.4.4 { } {SQLITE_OK} finish_test - diff --git a/test/collate2.test b/test/collate2.test index bf619231d..c758b0a6d 100644 --- a/test/collate2.test +++ b/test/collate2.test @@ -636,13 +636,15 @@ do_test collate2-4.2 { do_test collate2-4.3 { execsql { SELECT collate2t1.a FROM collate2t1, collate2t3 - WHERE collate2t1.b = collate2t3.b||''; + WHERE collate2t1.b = collate2t3.b||'' + ORDER BY +collate2t1.a DESC; } } {aa aA Aa AA} do_test collate2-4.4 { execsql { SELECT collate2t1.a FROM collate2t1, collate2t3 - WHERE collate2t3.b||'' = collate2t1.b; + WHERE collate2t3.b||'' = collate2t1.b + ORDER BY +collate2t1.a DESC; } } {aa aA Aa AA} diff --git a/test/contrib01.test b/test/contrib01.test new file mode 100644 index 000000000..43ea47c3e --- /dev/null +++ b/test/contrib01.test @@ -0,0 +1,90 @@ +# 2013-06-05 +# +# 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 contains test cases that were contributed on the sqlite-users +# mailing list on 2013-06-05 by Mi Chen at mi.chen@echostar.com. +# +# At the time it was contributed, this test failed on trunk, but +# worked on the NGQP. + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Build some test data +# +do_test contrib01-1.0 { + db eval { + CREATE TABLE T1 (B INTEGER NOT NULL, + C INTEGER NOT NULL, + D INTEGER NOT NULL, + E INTEGER NOT NULL, + F INTEGER NOT NULL, + G INTEGER NOT NULL, + H INTEGER NOT NULL, + PRIMARY KEY (B, C, D)); + + CREATE TABLE T2 (A INTEGER NOT NULL, + B INTEGER NOT NULL, + C INTEGER NOT NULL, + PRIMARY KEY (A, B, C)); + + INSERT INTO T2(A, B, C) VALUES(702118,16183,15527); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15560); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15561); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15563); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15564); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15566); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15567); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15569); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15612); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15613); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15638); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15681); + INSERT INTO T2(A, B, C) VALUES(702118,16183,15682); + + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15527,6,0,5,5,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15560,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15561,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15563,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15564,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15566,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15567,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15569,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15612,6,0,5,5,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15613,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15638,6,0,5,2,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15681,6,0,5,5,0); + INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15682,6,0,5,2,0); + } +} {} +do_test contrib01-1.1 { + db eval { + SELECT T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H, MAX(T1.C), '^' + FROM T1, T2 + WHERE T1.B = T2.B + AND T1.C = T2.C + GROUP BY T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H + ORDER BY +max(t1.c); + } +} {702118 16183 6 0 5 5 0 15681 ^ 702118 16183 6 0 5 2 0 15682 ^} +do_test contrib01-1.2 { + db eval { + SELECT T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H, MAX(T1.C), '^' + FROM T1, T2 + WHERE T1.B = T2.B + AND T1.C = T2.C + GROUP BY T2.A, T2.B, T1.F, T1.D, T1.E, T1.G, T1.H + ORDER BY +max(t1.c); + } +} {702118 16183 6 0 5 5 0 15681 ^ 702118 16183 6 0 5 2 0 15682 ^} + +finish_test diff --git a/test/corruptF.test b/test/corruptF.test index 33eef39bd..64c6eb890 100644 --- a/test/corruptF.test +++ b/test/corruptF.test @@ -147,4 +147,3 @@ for {set i 127} {$i >= 0} {incr i -1} { } finish_test - diff --git a/test/descidx1.test b/test/descidx1.test index c7fab340a..a223664ff 100644 --- a/test/descidx1.test +++ b/test/descidx1.test @@ -197,12 +197,12 @@ ifcapable bloblit { } {1.0 2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0} do_test descidx1-4.3 { execsql { - SELECT d FROM t2 WHERE a>=2; + SELECT d FROM t2 WHERE a>=2 ORDER BY a; } } {2.2 2.0 2.1 2.3 3.0 4.0 5.0 6.0} do_test descidx1-4.4 { execsql { - SELECT d FROM t2 WHERE a>2; + SELECT d FROM t2 WHERE a>2 ORDER BY a; } } {3.0 4.0 5.0 6.0} do_test descidx1-4.5 { diff --git a/test/distinct.test b/test/distinct.test index fcbe4e6cb..2b006d3e6 100644 --- a/test/distinct.test +++ b/test/distinct.test @@ -165,7 +165,7 @@ foreach {tn sql temptables res} { 3 "a, b, c FROM t1" {hash} {a b c A B C} 4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c} 5 "b FROM t1 WHERE a = 'a'" {} {b} - 6 "b FROM t1" {hash} {b B} + 6 "b FROM t1 ORDER BY +b COLLATE binary" {btree hash} {B b} 7 "a FROM t1" {} {A a} 8 "b COLLATE nocase FROM t1" {} {b} 9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {b} diff --git a/test/e_createtable.test b/test/e_createtable.test index 351a0f722..249b5b0e7 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -1368,13 +1368,13 @@ do_execsql_test 4.10.0 { } do_createtable_tests 4.10 { 1 "EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 5" - {0 0 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (b=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (b=?)}} 2 "EXPLAIN QUERY PLAN SELECT * FROM t2 ORDER BY b, c" - {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1 (~1000000 rows)}} + {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1}} 3 "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b=10 AND c>10" - {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?) (~2 rows)}} + {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?)}} } # EVIDENCE-OF: R-45493-35653 A CHECK constraint may be attached to a diff --git a/test/e_fkey.test b/test/e_fkey.test index 001ba6c38..976ed1632 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -974,15 +974,15 @@ do_execsql_test e_fkey-25.2 { EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?; } { - 0 0 0 {SCAN TABLE artist (~1000000 rows)} - 0 0 0 {SCAN TABLE track (~100000 rows)} + 0 0 0 {SCAN TABLE artist} + 0 0 0 {SCAN TABLE track} } do_execsql_test e_fkey-25.3 { PRAGMA foreign_keys = ON; EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; } { - 0 0 0 {SCAN TABLE artist (~1000000 rows)} - 0 0 0 {SCAN TABLE track (~100000 rows)} + 0 0 0 {SCAN TABLE artist} + 0 0 0 {SCAN TABLE track} } do_test e_fkey-25.4 { execsql { @@ -1099,15 +1099,15 @@ do_test e_fkey-27.2 { do_execsql_test e_fkey-27.3 { EXPLAIN QUERY PLAN UPDATE artist SET artistid = ?, artistname = ? } { - 0 0 0 {SCAN TABLE artist (~1000000 rows)} - 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} - 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} + 0 0 0 {SCAN TABLE artist} + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)} + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)} } do_execsql_test e_fkey-27.4 { EXPLAIN QUERY PLAN DELETE FROM artist } { - 0 0 0 {SCAN TABLE artist (~1000000 rows)} - 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} + 0 0 0 {SCAN TABLE artist} + 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)} } diff --git a/test/eqp.test b/test/eqp.test index 454f2afbd..59f78d9c6 100644 --- a/test/eqp.test +++ b/test/eqp.test @@ -43,37 +43,37 @@ do_execsql_test 1.1 { do_eqp_test 1.2 { SELECT * FROM t2, t1 WHERE t1.a=1 OR t1.b=2; } { - 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} - 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)} - 0 1 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)} + 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)} + 0 1 0 {SCAN TABLE t2} } do_eqp_test 1.3 { SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=1 OR t1.b=2; } { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} - 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} - 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)} + 0 0 0 {SCAN TABLE t2} + 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)} + 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)} } do_eqp_test 1.3 { SELECT a FROM t1 ORDER BY a } { - 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1} } do_eqp_test 1.4 { SELECT a FROM t1 ORDER BY +a } { - 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.5 { SELECT a FROM t1 WHERE a=4 } { - 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)} } do_eqp_test 1.6 { SELECT DISTINCT count(*) FROM t3 GROUP BY a; } { - 0 0 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 0 {SCAN TABLE t3} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } @@ -81,40 +81,40 @@ do_eqp_test 1.6 { do_eqp_test 1.7 { SELECT * FROM t3 JOIN (SELECT 1) } { - 0 0 1 {SCAN SUBQUERY 1 (~1 rows)} - 0 1 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 1 {SCAN SUBQUERY 1} + 0 1 0 {SCAN TABLE t3} } do_eqp_test 1.8 { SELECT * FROM t3 JOIN (SELECT 1 UNION SELECT 2) } { 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} - 0 0 1 {SCAN SUBQUERY 1 (~2 rows)} - 0 1 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 1 {SCAN SUBQUERY 1} + 0 1 0 {SCAN TABLE t3} } do_eqp_test 1.9 { SELECT * FROM t3 JOIN (SELECT 1 EXCEPT SELECT a FROM t3 LIMIT 17) } { - 3 0 0 {SCAN TABLE t3 (~1000000 rows)} + 3 0 0 {SCAN TABLE t3} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)} - 0 0 1 {SCAN SUBQUERY 1 (~17 rows)} - 0 1 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 1 {SCAN SUBQUERY 1} + 0 1 0 {SCAN TABLE t3} } do_eqp_test 1.10 { SELECT * FROM t3 JOIN (SELECT 1 INTERSECT SELECT a FROM t3 LIMIT 17) } { - 3 0 0 {SCAN TABLE t3 (~1000000 rows)} + 3 0 0 {SCAN TABLE t3} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)} - 0 0 1 {SCAN SUBQUERY 1 (~1 rows)} - 0 1 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 1 {SCAN SUBQUERY 1} + 0 1 0 {SCAN TABLE t3} } do_eqp_test 1.11 { SELECT * FROM t3 JOIN (SELECT 1 UNION ALL SELECT a FROM t3 LIMIT 17) } { - 3 0 0 {SCAN TABLE t3 (~1000000 rows)} + 3 0 0 {SCAN TABLE t3} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)} - 0 0 1 {SCAN SUBQUERY 1 (~17 rows)} - 0 1 0 {SCAN TABLE t3 (~1000000 rows)} + 0 0 1 {SCAN SUBQUERY 1} + 0 1 0 {SCAN TABLE t3} } #------------------------------------------------------------------------- @@ -129,48 +129,48 @@ do_execsql_test 2.1 { } det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.2 "SELECT DISTINCT min(x), max(x) FROM t2 GROUP BY x ORDER BY 1" { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.3 "SELECT DISTINCT * FROM t1" { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } det 2.2.4 "SELECT DISTINCT * FROM t1, t2" { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} - 0 1 1 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} + 0 1 1 {SCAN TABLE t2} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } det 2.2.5 "SELECT DISTINCT * FROM t1, t2 ORDER BY t1.x" { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} - 0 1 1 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} + 0 1 1 {SCAN TABLE t2} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.6 "SELECT DISTINCT t2.x FROM t1, t2 ORDER BY t2.x" { - 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} - 0 1 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1} + 0 1 0 {SCAN TABLE t1} } det 2.3.1 "SELECT max(x) FROM t2" { - 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} + 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1} } det 2.3.2 "SELECT min(x) FROM t2" { - 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} + 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1} } det 2.3.3 "SELECT min(x), max(x) FROM t2" { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} } det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" { - 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)} } @@ -181,39 +181,39 @@ det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" { do_eqp_test 3.1.1 { SELECT (SELECT x FROM t1 AS sub) FROM t1; } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} - 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 AS sub} } do_eqp_test 3.1.2 { SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub); } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} - 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 AS sub} } do_eqp_test 3.1.3 { SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub ORDER BY y); } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} - 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 AS sub} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 3.1.4 { SELECT * FROM t1 WHERE (SELECT x FROM t2 ORDER BY x); } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} - 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} } det 3.2.1 { SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) ORDER BY y LIMIT 5 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 0 0 0 {SCAN SUBQUERY 1 (~10 rows)} + 0 0 0 {SCAN SUBQUERY 1} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 3.2.2 { @@ -222,34 +222,34 @@ det 3.2.2 { (SELECT * FROM t2 ORDER BY x LIMIT 10) AS x2 ORDER BY x2.y LIMIT 5 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)} - 0 0 0 {SCAN SUBQUERY 1 AS x1 (~10 rows)} - 0 1 1 {SCAN SUBQUERY 2 AS x2 (~10 rows)} + 2 0 0 {SCAN TABLE t2 USING INDEX t2i1} + 0 0 0 {SCAN SUBQUERY 1 AS x1} + 0 1 1 {SCAN SUBQUERY 2 AS x2} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 3.3.1 { SELECT * FROM t1 WHERE y IN (SELECT y FROM t2) } { - 0 0 0 {SCAN TABLE t1 (~100000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE LIST SUBQUERY 1} - 1 0 0 {SCAN TABLE t2 (~1000000 rows)} + 1 0 0 {SCAN TABLE t2} } det 3.3.2 { SELECT * FROM t1 WHERE y IN (SELECT y FROM t2 WHERE t1.x!=t2.x) } { - 0 0 0 {SCAN TABLE t1 (~500000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} - 1 0 0 {SCAN TABLE t2 (~500000 rows)} + 1 0 0 {SCAN TABLE t2} } det 3.3.3 { SELECT * FROM t1 WHERE EXISTS (SELECT y FROM t2 WHERE t1.x!=t2.x) } { - 0 0 0 {SCAN TABLE t1 (~500000 rows)} + 0 0 0 {SCAN TABLE t1} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1} - 1 0 0 {SCAN TABLE t2 (~500000 rows)} + 1 0 0 {SCAN TABLE t2} } #------------------------------------------------------------------------- @@ -258,43 +258,43 @@ det 3.3.3 { do_eqp_test 4.1.1 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} + 2 0 0 {SCAN TABLE t2} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.1.2 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.1.3 { SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)} } do_eqp_test 4.1.4 { SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)} } do_eqp_test 4.1.5 { SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } @@ -302,35 +302,35 @@ do_eqp_test 4.1.5 { do_eqp_test 4.2.2 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 1 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2 USING INDEX t2i1} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.2.3 { SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)} } do_eqp_test 4.2.4 { SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)} } do_eqp_test 4.2.5 { SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } @@ -338,28 +338,28 @@ do_eqp_test 4.2.5 { do_eqp_test 4.3.1 { SELECT x FROM t1 UNION SELECT x FROM t2 } { - 1 0 0 {SCAN TABLE t1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.2 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 } { - 2 0 0 {SCAN TABLE t1 (~1000000 rows)} - 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 2 0 0 {SCAN TABLE t1} + 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} - 4 0 0 {SCAN TABLE t1 (~1000000 rows)} + 4 0 0 {SCAN TABLE t1} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.3 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1 } { - 2 0 0 {SCAN TABLE t1 (~1000000 rows)} + 2 0 0 {SCAN TABLE t1} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} - 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} + 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION)} - 4 0 0 {SCAN TABLE t1 (~1000000 rows)} + 4 0 0 {SCAN TABLE t1} 4 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 (UNION)} } @@ -371,127 +371,127 @@ do_eqp_test 4.3.3 { drop_all_tables # EVIDENCE-OF: R-64208-08323 sqlite> EXPLAIN QUERY PLAN SELECT a, b -# FROM t1 WHERE a=1; 0|0|0|SCAN TABLE t1 (~100000 rows) +# FROM t1 WHERE a=1; 0|0|0|SCAN TABLE t1 do_execsql_test 5.1.0 { CREATE TABLE t1(a, b) } det 5.1.1 "SELECT a, b FROM t1 WHERE a=1" { - 0 0 0 {SCAN TABLE t1 (~100000 rows)} + 0 0 0 {SCAN TABLE t1} } # EVIDENCE-OF: R-09022-44606 sqlite> CREATE INDEX i1 ON t1(a); # sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1; -# 0|0|0|SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows) +# 0|0|0|SEARCH TABLE t1 USING INDEX i1 (a=?) do_execsql_test 5.2.0 { CREATE INDEX i1 ON t1(a) } det 5.2.1 "SELECT a, b FROM t1 WHERE a=1" { - 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)} } # EVIDENCE-OF: R-62228-34103 sqlite> CREATE INDEX i2 ON t1(a, b); # sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1; -# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) +# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) do_execsql_test 5.3.0 { CREATE INDEX i2 ON t1(a, b) } det 5.3.1 "SELECT a, b FROM t1 WHERE a=1" { - 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)} } # EVIDENCE-OF: R-22253-05302 sqlite> EXPLAIN QUERY PLAN SELECT t1.*, # t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2; 0|0|0|SEARCH TABLE t1 -# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|1|SCAN TABLE t2 -# (~1000000 rows) +# USING COVERING INDEX i2 (a=? AND b>?) 0|1|1|SCAN TABLE t2 +# do_execsql_test 5.4.0 {CREATE TABLE t2(c, d)} det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" { - 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)} - 0 1 1 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)} + 0 1 1 {SCAN TABLE t2} } # EVIDENCE-OF: R-21040-07025 sqlite> EXPLAIN QUERY PLAN SELECT t1.*, # t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2; 0|0|1|SEARCH TABLE t1 -# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|0|SCAN TABLE t2 -# (~1000000 rows) +# USING COVERING INDEX i2 (a=? AND b>?) 0|1|0|SCAN TABLE t2 +# det 5.5 "SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2" { - 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)} - 0 1 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)} + 0 1 0 {SCAN TABLE t2} } # EVIDENCE-OF: R-39007-61103 sqlite> CREATE INDEX i3 ON t1(b); # sqlite> EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=1 OR b=2; -# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) -# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows) +# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) +# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?) do_execsql_test 5.5.0 {CREATE INDEX i3 ON t1(b)} det 5.6.1 "SELECT * FROM t1 WHERE a=1 OR b=2" { - 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} - 0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)} + 0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)} } # EVIDENCE-OF: R-33025-54904 sqlite> EXPLAIN QUERY PLAN SELECT c, d -# FROM t2 ORDER BY c; 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|USE TEMP +# FROM t2 ORDER BY c; 0|0|0|SCAN TABLE t2 0|0|0|USE TEMP # B-TREE FOR ORDER BY det 5.7 "SELECT c, d FROM t2 ORDER BY c" { - 0 0 0 {SCAN TABLE t2 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } # EVIDENCE-OF: R-38854-22809 sqlite> CREATE INDEX i4 ON t2(c); # sqlite> EXPLAIN QUERY PLAN SELECT c, d FROM t2 ORDER BY c; -# 0|0|0|SCAN TABLE t2 USING INDEX i4 (~1000000 rows) +# 0|0|0|SCAN TABLE t2 USING INDEX i4 do_execsql_test 5.8.0 {CREATE INDEX i4 ON t2(c)} det 5.8.1 "SELECT c, d FROM t2 ORDER BY c" { - 0 0 0 {SCAN TABLE t2 USING INDEX i4 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING INDEX i4} } # EVIDENCE-OF: R-29884-43993 sqlite> EXPLAIN QUERY PLAN SELECT # (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2; -# 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|EXECUTE SCALAR SUBQUERY 1 -# 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) +# 0|0|0|SCAN TABLE t2 0|0|0|EXECUTE SCALAR SUBQUERY 1 +# 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) # 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2 2|0|0|SEARCH TABLE t1 USING -# INDEX i3 (b=?) (~10 rows) +# INDEX i3 (b=?) det 5.9 { SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2 } { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} - 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} + 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} - 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)} + 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)} } # EVIDENCE-OF: R-17911-16445 sqlite> EXPLAIN QUERY PLAN SELECT # count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x; -# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) 0|0|0|SCAN -# SUBQUERY 1 (~1000000 rows) 0|0|0|USE TEMP B-TREE FOR GROUP BY +# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2 0|0|0|SCAN +# SUBQUERY 1 0|0|0|USE TEMP B-TREE FOR GROUP BY det 5.10 { SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x } { - 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} - 0 0 0 {SCAN SUBQUERY 1 (~100 rows)} + 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2} + 0 0 0 {SCAN SUBQUERY 1} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} } # EVIDENCE-OF: R-18544-33103 sqlite> EXPLAIN QUERY PLAN SELECT * FROM # (SELECT * FROM t2 WHERE c=1), t1; 0|0|0|SEARCH TABLE t2 USING INDEX i4 -# (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows) +# (c=?) 0|1|1|SCAN TABLE t1 det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" { - 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)} - 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} + 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?)} + 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2} } # EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM -# t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 (~1000000 rows) -# 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2 +# t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 +# 2|0|0|SCAN TABLE t2 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # USING TEMP B-TREE (UNION) det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" { - 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2} + 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } # EVIDENCE-OF: R-61538-24748 sqlite> EXPLAIN QUERY PLAN SELECT a FROM # t1 EXCEPT SELECT d FROM t2 ORDER BY 1; 1|0|0|SCAN TABLE t1 USING -# COVERING INDEX i2 (~1000000 rows) 2|0|0|SCAN TABLE t2 (~1000000 rows) +# COVERING INDEX i2 2|0|0|SCAN TABLE t2 # 2|0|0|USE TEMP B-TREE FOR ORDER BY 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # (EXCEPT) det 5.13 "SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1" { - 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} - 2 0 0 {SCAN TABLE t2 (~1000000 rows)} + 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2} + 2 0 0 {SCAN TABLE t2} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } @@ -531,8 +531,8 @@ proc do_peqp_test {tn sql res} { do_peqp_test 6.1 { SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1 } [string trimleft { -1 0 0 SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) -2 0 0 SCAN TABLE t2 (~1000000 rows) +1 0 0 SCAN TABLE t1 USING COVERING INDEX i2 +2 0 0 SCAN TABLE t2 2 0 0 USE TEMP B-TREE FOR ORDER BY 0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT) }] @@ -550,11 +550,11 @@ do_execsql_test 7.0 { } det 7.1 "SELECT count(*) FROM t1" { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 0 {SCAN TABLE t1} } det 7.2 "SELECT count(*) FROM t2" { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1} } do_execsql_test 7.3 { @@ -572,11 +572,11 @@ db close sqlite3 db test.db det 7.4 "SELECT count(*) FROM t1" { - 0 0 0 {SCAN TABLE t1 (~2 rows)} + 0 0 0 {SCAN TABLE t1} } det 7.5 "SELECT count(*) FROM t2" { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~3 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1} } diff --git a/test/exclusive.test b/test/exclusive.test index ffde89153..c000dfefa 100644 --- a/test/exclusive.test +++ b/test/exclusive.test @@ -506,4 +506,3 @@ do_execsql_test exclusive-6.5 { } {exclusive} finish_test - diff --git a/test/fallocate.test b/test/fallocate.test index 8a5fa3236..f523c2cc4 100644 --- a/test/fallocate.test +++ b/test/fallocate.test @@ -143,4 +143,3 @@ if {!$skipwaltests} { finish_test - diff --git a/test/filefmt.test b/test/filefmt.test index bc1af18e8..2df142443 100644 --- a/test/filefmt.test +++ b/test/filefmt.test @@ -248,4 +248,3 @@ do_test filefmt-4.4 { db2 close finish_test - diff --git a/test/fts3aa.test b/test/fts3aa.test index 5d79e9318..bde38af55 100644 --- a/test/fts3aa.test +++ b/test/fts3aa.test @@ -224,4 +224,3 @@ do_catchsql_test fts3aa-7.5 { finish_test - diff --git a/test/fts3ao.test b/test/fts3ao.test index 786667a7f..91693094d 100644 --- a/test/fts3ao.test +++ b/test/fts3ao.test @@ -220,4 +220,3 @@ do_execsql_test 5.2 { } {0 6} finish_test - diff --git a/test/fts3atoken.test b/test/fts3atoken.test index 9277bfb8e..b7722c7d0 100644 --- a/test/fts3atoken.test +++ b/test/fts3atoken.test @@ -193,5 +193,3 @@ do_test fts3token-internal { finish_test - - diff --git a/test/fts3auto.test b/test/fts3auto.test index 20b2812dc..20640d29a 100644 --- a/test/fts3auto.test +++ b/test/fts3auto.test @@ -707,4 +707,3 @@ foreach {tn create} { set sqlite_fts3_enable_parentheses $sfep finish_test - diff --git a/test/fts3aux1.test b/test/fts3aux1.test index ef17949fd..d17ac85df 100644 --- a/test/fts3aux1.test +++ b/test/fts3aux1.test @@ -105,10 +105,10 @@ db func rec rec # do_execsql_test 2.1.1.1 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:} } do_execsql_test 2.1.1.2 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid' -} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}} +} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}} # Now show that using "term='braid'" means the virtual table returns # only 1 row to SQLite, but "+term='braid'" means all 19 are returned. @@ -154,24 +154,24 @@ do_execsql_test 2.1.5 { SELECT * FROM terms WHERE term=NULL } {} do_execsql_test 2.2.1.1 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2:} } do_execsql_test 2.2.1.2 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} } do_execsql_test 2.2.1.3 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4:} } do_execsql_test 2.2.1.4 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} } do_execsql_test 2.2.1.5 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6:} } do_execsql_test 2.2.1.6 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain' -} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } +} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} } do_test 2.2.2.1 { set cnt 0 @@ -335,7 +335,7 @@ foreach {tn sort orderby} { 9 1 "ORDER BY occurrences DESC" } { - set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}] + set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}] if {$sort} { lappend res 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } set sql "SELECT * FROM terms $orderby" @@ -410,32 +410,32 @@ proc do_plansql_test {tn sql r} { do_plansql_test 4.2 { SELECT y FROM x2, terms WHERE y = term AND col = '*' } { - 0 0 0 {SCAN TABLE x2 (~1000000 rows)} - 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} + 0 0 0 {SCAN TABLE x2} + 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:} a b c d e f g h i j k l } do_plansql_test 4.3 { SELECT y FROM terms, x2 WHERE y = term AND col = '*' } { - 0 0 1 {SCAN TABLE x2 (~1000000 rows)} - 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} + 0 0 1 {SCAN TABLE x2} + 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:} a b c d e f g h i j k l } do_plansql_test 4.4 { SELECT y FROM x3, terms WHERE y = term AND col = '*' } { - 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} - 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} + 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} + 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?)} a b c d e f g h i j k l } do_plansql_test 4.5 { SELECT y FROM terms, x3 WHERE y = term AND occurrences>1 AND col = '*' } { - 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} - 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} + 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} + 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?)} a k l } @@ -519,4 +519,3 @@ do_test 8.2 { } {1 {SQL logic error or missing database}} finish_test - diff --git a/test/fts3corrupt.test b/test/fts3corrupt.test index ea4c9a9d3..cb50e3e46 100644 --- a/test/fts3corrupt.test +++ b/test/fts3corrupt.test @@ -166,4 +166,3 @@ do_test 5.3.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB finish_test - diff --git a/test/fts3defer2.test b/test/fts3defer2.test index 337359af6..608371f15 100644 --- a/test/fts3defer2.test +++ b/test/fts3defer2.test @@ -153,4 +153,3 @@ foreach {tn sql} { finish_test - diff --git a/test/fts3expr3.test b/test/fts3expr3.test index e3cc2619c..a8d731926 100644 --- a/test/fts3expr3.test +++ b/test/fts3expr3.test @@ -204,7 +204,3 @@ do_faultsim_test fts3expr3-fault-1 -faults oom-* -body { set sqlite_fts3_enable_parentheses 0 finish_test - - - - diff --git a/test/fts3malloc.test b/test/fts3malloc.test index 095f9a7a5..ed30fe24c 100644 --- a/test/fts3malloc.test +++ b/test/fts3malloc.test @@ -305,4 +305,3 @@ do_write_test fts3_malloc-5.3 ft_content { finish_test - diff --git a/test/fts3matchinfo.test b/test/fts3matchinfo.test index 3998c9a40..3da3a7742 100644 --- a/test/fts3matchinfo.test +++ b/test/fts3matchinfo.test @@ -427,4 +427,3 @@ do_execsql_test 8.3 { } finish_test - diff --git a/test/fts3prefix2.test b/test/fts3prefix2.test index e3da3b759..e033fb6bc 100644 --- a/test/fts3prefix2.test +++ b/test/fts3prefix2.test @@ -59,4 +59,3 @@ do_execsql_test 1.3 { } finish_test - diff --git a/test/fts3query.test b/test/fts3query.test index c8f8686bf..2d12351ae 100644 --- a/test/fts3query.test +++ b/test/fts3query.test @@ -118,26 +118,26 @@ do_test fts3query-4.1 { do_eqp_test fts3query-4.2 { SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date } { - 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} - 0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1} + 0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1:} } do_eqp_test fts3query-4.3 { SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date } { - 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} - 0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)} + 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1} + 0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1:} } do_eqp_test fts3query-4.4 { SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date } { - 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} - 0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1} + 0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?)} } do_eqp_test fts3query-4.5 { SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date } { - 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} - 0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1} + 0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?)} } @@ -210,4 +210,3 @@ do_select_tests 6.2 { finish_test - diff --git a/test/fts3shared.test b/test/fts3shared.test index a587a437b..0943c0510 100644 --- a/test/fts3shared.test +++ b/test/fts3shared.test @@ -174,4 +174,3 @@ dbW close dbR close sqlite3_enable_shared_cache $::enable_shared_cache finish_test - diff --git a/test/fts3snippet.test b/test/fts3snippet.test index b8646cdfa..f19db925b 100644 --- a/test/fts3snippet.test +++ b/test/fts3snippet.test @@ -432,10 +432,10 @@ foreach {DO_MALLOC_TEST enc} { {2 2 1 3 3 3 6 3 0 0 0 2 3 2} }] - # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the
- # "query by rowid" or "linear scan" strategies, then the snippet and
- # offsets both return an empty string, and the matchinfo function
- # returns a blob value zero bytes in size.
+ # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the + # "query by rowid" or "linear scan" strategies, then the snippet and + # offsets both return an empty string, and the matchinfo function + # returns a blob value zero bytes in size. # set r 1000000 ;# A rowid that exists in table ft do_select_test $T.10.0 { SELECT rowid FROM ft WHERE rowid = $r } $r diff --git a/test/fts3sort.test b/test/fts3sort.test index be7560432..218bf3e82 100644 --- a/test/fts3sort.test +++ b/test/fts3sort.test @@ -182,4 +182,3 @@ do_execsql_test 3.2 { finish_test - diff --git a/test/fts3tok1.test b/test/fts3tok1.test index 98e55a06c..4faed34d1 100644 --- a/test/fts3tok1.test +++ b/test/fts3tok1.test @@ -113,5 +113,3 @@ do_catchsql_test 2.1 { finish_test - - diff --git a/test/fts3tok_err.test b/test/fts3tok_err.test index aaa7272b7..df0d4beeb 100644 --- a/test/fts3tok_err.test +++ b/test/fts3tok_err.test @@ -45,5 +45,3 @@ do_faultsim_test fts3tok_err-2 -faults oom* -prep { finish_test - - diff --git a/test/fts4content.test b/test/fts4content.test index 302f14e51..6b2cd3cc8 100644 --- a/test/fts4content.test +++ b/test/fts4content.test @@ -623,4 +623,3 @@ do_execsql_test 10.7 { } finish_test - diff --git a/test/fuzzer1.test b/test/fuzzer1.test index 473d0e186..4ee573080 100644 --- a/test/fuzzer1.test +++ b/test/fuzzer1.test @@ -1728,36 +1728,41 @@ do_execsql_test 8.1 { do_execsql_test 8.2.1 { SELECT cFrom, cTo, word FROM x3_rules CROSS JOIN x3 - WHERE word MATCH 'a' AND cost=distance AND ruleset=2; + WHERE word MATCH 'a' AND cost=distance AND ruleset=2 + ORDER BY +cTo; } {a x x a y y a z z} do_execsql_test 8.2.2 { SELECT cFrom, cTo, word FROM x3 CROSS JOIN x3_rules - WHERE word MATCH 'a' AND cost=distance AND ruleset=2; + WHERE word MATCH 'a' AND cost=distance AND ruleset=2 + ORDER BY +cTo DESC } {a z z a y y a x x} do_execsql_test 8.2.3 { SELECT cFrom, cTo, word FROM x3_rules, x3 - WHERE word MATCH 'a' AND cost=distance AND ruleset=2; + WHERE word MATCH 'a' AND cost=distance AND ruleset=2 + ORDER BY +cTo DESC; } {a z z a y y a x x} do_execsql_test 8.2.4 { SELECT cFrom, cTo, word FROM x3, x3_rules - WHERE word MATCH 'a' AND cost=distance AND ruleset=2; + WHERE word MATCH 'a' AND cost=distance AND ruleset=2 + ORDER BY +cTo DESC; } {a z z a y y a x x} do_execsql_test 8.2.5 { CREATE INDEX i1 ON x3_rules(cost); SELECT cFrom, cTo, word FROM x3_rules, x3 - WHERE word MATCH 'a' AND cost=distance AND ruleset=2; + WHERE word MATCH 'a' AND cost=distance AND ruleset=2 + ORDER BY +cTo DESC; } {a z z a y y a x x} do_execsql_test 8.2.5 { - SELECT word FROM x3_rules, x3 WHERE word MATCH x3_rules.cFrom AND ruleset=2; + SELECT word FROM x3_rules, x3 WHERE word MATCH x3_rules.cFrom AND ruleset=2 } {a z y x a z y x a z y x} do_execsql_test 8.2.6 { diff --git a/test/incrblob3.test b/test/incrblob3.test index 4c49f1563..5f2e860d0 100644 --- a/test/incrblob3.test +++ b/test/incrblob3.test @@ -269,4 +269,3 @@ db close tvfs delete finish_test - diff --git a/test/incrblob4.test b/test/incrblob4.test index a96356b06..a92e37353 100644 --- a/test/incrblob4.test +++ b/test/incrblob4.test @@ -87,4 +87,3 @@ do_test 3.3 { } {} finish_test - diff --git a/test/incrblobfault.test b/test/incrblobfault.test index d125471c4..0c1d93d46 100644 --- a/test/incrblobfault.test +++ b/test/incrblobfault.test @@ -67,4 +67,3 @@ do_faultsim_test 3 -prep { } finish_test - diff --git a/test/incrvacuum3.test b/test/incrvacuum3.test index f01dc100b..e901bfe1f 100644 --- a/test/incrvacuum3.test +++ b/test/incrvacuum3.test @@ -151,4 +151,3 @@ foreach {T jrnl_mode} { } finish_test - diff --git a/test/indexedby.test b/test/indexedby.test index 7ccc4de24..5193da40c 100644 --- a/test/indexedby.test +++ b/test/indexedby.test @@ -42,15 +42,15 @@ proc EQP {sql} { # do_execsql_test indexedby-1.2 { EXPLAIN QUERY PLAN select * from t1 WHERE a = 10; -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} do_execsql_test indexedby-1.3 { EXPLAIN QUERY PLAN select * from t1 ; -} {0 0 0 {SCAN TABLE t1 (~1000000 rows)}} +} {0 0 0 {SCAN TABLE t1}} do_execsql_test indexedby-1.4 { EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10; } { - 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} - 0 1 0 {SCAN TABLE t1 (~1000000 rows)} + 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)} + 0 1 0 {SCAN TABLE t1} } # Parser tests. Test that an INDEXED BY or NOT INDEX clause can be @@ -85,21 +85,21 @@ do_test indexedby-2.7 { # do_execsql_test indexedby-3.1 { EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two' -} {0 0 0 {SCAN TABLE t1 (~10000 rows)}} +} {0 0 0 {SCAN TABLE t1}} do_execsql_test indexedby-3.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two' -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} do_execsql_test indexedby-3.3 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two' -} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} do_test indexedby-3.4 { catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' } -} {1 {cannot use index: i2}} +} {1 {no query solution}} do_test indexedby-3.5 { catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a } -} {1 {cannot use index: i2}} +} {1 {no query solution}} do_test indexedby-3.6 { catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' } } {0 {}} @@ -110,14 +110,14 @@ do_test indexedby-3.7 { do_execsql_test indexedby-3.8 { EXPLAIN QUERY PLAN SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e -} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1 (~1000000 rows)}} +} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1}} do_execsql_test indexedby-3.9 { EXPLAIN QUERY PLAN SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 -} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?) (~1 rows)}} +} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?)}} do_test indexedby-3.10 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 } -} {1 {cannot use index: sqlite_autoindex_t3_1}} +} {1 {no query solution}} do_test indexedby-3.11 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 } } {1 {no such index: sqlite_autoindex_t3_2}} @@ -127,25 +127,25 @@ do_test indexedby-3.11 { do_execsql_test indexedby-4.1 { EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c } { - 0 0 0 {SCAN TABLE t1 (~1000000 rows)} - 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} + 0 0 0 {SCAN TABLE t1} + 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)} } do_execsql_test indexedby-4.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c } { - 0 0 1 {SCAN TABLE t2 (~1000000 rows)} - 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} + 0 0 1 {SCAN TABLE t2} + 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)} } do_test indexedby-4.3 { catchsql { SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c } -} {1 {cannot use index: i1}} +} {1 {no query solution}} do_test indexedby-4.4 { catchsql { SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c } -} {1 {cannot use index: i3}} +} {1 {no query solution}} # Test embedding an INDEXED BY in a CREATE VIEW statement. This block # also tests that nothing bad happens if an index refered to by @@ -154,10 +154,10 @@ do_test indexedby-4.4 { do_execsql_test indexedby-5.1 { CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; EXPLAIN QUERY PLAN SELECT * FROM v2 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~250000 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}} do_execsql_test indexedby-5.2 { EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~25000 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}} do_test indexedby-5.3 { execsql { DROP INDEX i1 } catchsql { SELECT * FROM v2 } @@ -166,7 +166,7 @@ do_test indexedby-5.4 { # Recreate index i1 in such a way as it cannot be used by the view query. execsql { CREATE INDEX i1 ON t1(b) } catchsql { SELECT * FROM v2 } -} {1 {cannot use index: i1}} +} {1 {no query solution}} do_test indexedby-5.5 { # Drop and recreate index i1 again. This time, create it so that it can # be used by the query. @@ -178,54 +178,54 @@ do_test indexedby-5.5 { # do_execsql_test indexedby-6.1 { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid -} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} do_execsql_test indexedby-6.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid -} {0 0 0 {SCAN TABLE t1 USING INTEGER PRIMARY KEY (~100000 rows)}} +} {0 0 0 {SCAN TABLE t1}} # Test that "INDEXED BY" can be used in a DELETE statement. # do_execsql_test indexedby-7.1 { EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5 -} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} do_execsql_test indexedby-7.2 { EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5 -} {0 0 0 {SCAN TABLE t1 (~100000 rows)}} +} {0 0 0 {SCAN TABLE t1}} do_execsql_test indexedby-7.3 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 -} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} do_execsql_test indexedby-7.4 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} do_execsql_test indexedby-7.5 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} do_test indexedby-7.6 { catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5} -} {1 {cannot use index: i2}} +} {1 {no query solution}} # Test that "INDEXED BY" can be used in an UPDATE statement. # do_execsql_test indexedby-8.1 { EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5 -} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} do_execsql_test indexedby-8.2 { EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 -} {0 0 0 {SCAN TABLE t1 (~100000 rows)}} +} {0 0 0 {SCAN TABLE t1}} do_execsql_test indexedby-8.3 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 -} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} do_execsql_test indexedby-8.4 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} do_execsql_test indexedby-8.5 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10 -} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} +} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} do_test indexedby-8.6 { catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5} -} {1 {cannot use index: i2}} +} {1 {no query solution}} # Test that bug #3560 is fixed. # @@ -243,10 +243,10 @@ do_test indexedby-9.2 { joinme as j indexed by joinme_id_text_idx on ( m.id = j.id_int) } -} {1 {cannot use index: joinme_id_text_idx}} +} {1 {no query solution}} do_test indexedby-9.3 { catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx } -} {1 {cannot use index: joinme_id_text_idx}} +} {1 {no query solution}} # Make sure we can still create tables, indices, and columns whose name # is "indexed". diff --git a/test/intpkey.test b/test/intpkey.test index db3942128..7ed25e4a1 100644 --- a/test/intpkey.test +++ b/test/intpkey.test @@ -125,8 +125,11 @@ do_test intpkey-1.12.1 { } } {4 one two} do_test intpkey-1.12.2 { - set sqlite_query_plan -} {t1 *} + execsql { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a==4; + } +} {/SEARCH TABLE t1 /} # Try to insert a non-integer value into the primary key field. This # should result in a data type mismatch. diff --git a/test/io.test b/test/io.test index 11f9cc842..c5086c10e 100644 --- a/test/io.test +++ b/test/io.test @@ -641,4 +641,3 @@ foreach {tn sql} { sqlite3_simulate_device -char {} -sectorsize 0 finish_test - diff --git a/test/ioerr6.test b/test/ioerr6.test index 66c48ad2e..d1847b287 100644 --- a/test/ioerr6.test +++ b/test/ioerr6.test @@ -89,4 +89,3 @@ do_faultsim_test 3 -faults full* -prep { } finish_test - diff --git a/test/like.test b/test/like.test index 80ba41858..230dc74fe 100644 --- a/test/like.test +++ b/test/like.test @@ -156,14 +156,27 @@ ifcapable !like_opt { # This procedure executes the SQL. Then it appends to the result the # "sort" or "nosort" keyword (as in the cksort procedure above) then -# it appends the ::sqlite_query_plan variable. +# it appends the names of the table and index used. # proc queryplan {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x - return [concat $data $::sqlite_query_plan] + set eqp [execsql "EXPLAIN QUERY PLAN $sql"] + # puts eqp=$eqp + foreach {a b c x} $eqp { + if {[regexp { TABLE (\w+ AS )?(\w+) USING COVERING INDEX (\w+)\y} \ + $x all as tab idx]} { + lappend data {} $idx + } elseif {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \ + $x all as tab idx]} { + lappend data $tab $idx + } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} { + lappend data $tab * + } + } + return $data } # Perform tests on the like optimization. @@ -176,7 +189,7 @@ do_test like-3.1 { queryplan { SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1; } -} {ABC {ABC abc xyz} abc abcd sort t1 {}} +} {ABC {ABC abc xyz} abc abcd sort t1 *} do_test like-3.2 { set sqlite_like_count } {12} @@ -269,8 +282,8 @@ do_test like-3.12 { # do_test like-3.13 { set sqlite_like_count 0 + db eval {PRAGMA case_sensitive_like=off;} queryplan { - PRAGMA case_sensitive_like=off; SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1; } } {ABC {ABC abc xyz} abc abcd nosort {} i1} @@ -282,12 +295,14 @@ do_test like-3.14 { # do_test like-3.15 { set sqlite_like_count 0 - queryplan { + db eval { PRAGMA case_sensitive_like=on; DROP INDEX i1; + } + queryplan { SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1; } -} {abc abcd sort t1 {}} +} {abc abcd sort t1 *} do_test like-3.16 { set sqlite_like_count } 12 @@ -299,7 +314,7 @@ do_test like-3.17 { queryplan { SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1; } -} {abc abcd sort t1 {}} +} {abc abcd sort t1 *} do_test like-3.18 { set sqlite_like_count } 12 @@ -318,8 +333,8 @@ do_test like-3.20 { } 0 do_test like-3.21 { set sqlite_like_count 0 + db eval {PRAGMA case_sensitive_like=on;} queryplan { - PRAGMA case_sensitive_like=on; SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1; } } {abc abcd nosort {} i1} @@ -328,8 +343,8 @@ do_test like-3.22 { } 0 do_test like-3.23 { set sqlite_like_count 0 + db eval {PRAGMA case_sensitive_like=off;} queryplan { - PRAGMA case_sensitive_like=off; SELECT x FROM t1 WHERE x GLOB 'a[bc]d' ORDER BY 1; } } {abd acd nosort {} i1} @@ -809,60 +824,66 @@ do_test like-11.0 { } } {12} do_test like-11.1 { + db eval {PRAGMA case_sensitive_like=OFF;} queryplan { - PRAGMA case_sensitive_like=OFF; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd ABC ABCD nosort t11 *} do_test like-11.2 { + db eval {PRAGMA case_sensitive_like=ON;} queryplan { - PRAGMA case_sensitive_like=ON; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd nosort t11 *} do_test like-11.3 { - queryplan { + db eval { PRAGMA case_sensitive_like=OFF; CREATE INDEX t11b ON t11(b); + } + queryplan { SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11b} do_test like-11.4 { + db eval {PRAGMA case_sensitive_like=ON;} queryplan { - PRAGMA case_sensitive_like=ON; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd nosort t11 *} do_test like-11.5 { - queryplan { + db eval { PRAGMA case_sensitive_like=OFF; DROP INDEX t11b; CREATE INDEX t11bnc ON t11(b COLLATE nocase); + } + queryplan { SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.6 { + db eval {CREATE INDEX t11bb ON t11(b COLLATE binary);} queryplan { - CREATE INDEX t11bb ON t11(b COLLATE binary); SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.7 { + db eval {PRAGMA case_sensitive_like=ON;} queryplan { - PRAGMA case_sensitive_like=ON; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.8 { + db eval {PRAGMA case_sensitive_like=OFF;} queryplan { - PRAGMA case_sensitive_like=OFF; SELECT b FROM t11 WHERE b GLOB 'abc*' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.9 { - queryplan { + db eval { CREATE INDEX t11cnc ON t11(c COLLATE nocase); CREATE INDEX t11cb ON t11(c COLLATE binary); + } + queryplan { SELECT c FROM t11 WHERE c LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11cnc} diff --git a/test/lock7.test b/test/lock7.test index 49bc147a2..02f2c6c63 100644 --- a/test/lock7.test +++ b/test/lock7.test @@ -58,4 +58,3 @@ db1 close db2 close finish_test - diff --git a/test/misc7.test b/test/misc7.test index 72c1cd64f..96062a93b 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -269,17 +269,17 @@ ifcapable explain { CREATE TABLE abc(a PRIMARY KEY, b, c); EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid = 1; } { - 0 0 0 {SEARCH TABLE abc AS t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SEARCH TABLE abc AS t2 USING INTEGER PRIMARY KEY (rowid=?)} } do_execsql_test misc7-14.2 { EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE a = 1; } {0 0 0 - {SEARCH TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (a=?) (~1 rows)} + {SEARCH TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (a=?)} } do_execsql_test misc7-14.3 { EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 ORDER BY a; } {0 0 0 - {SCAN TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (~1000000 rows)} + {SCAN TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1} } } diff --git a/test/notify3.test b/test/notify3.test index 446f01078..4b5e8016b 100644 --- a/test/notify3.test +++ b/test/notify3.test @@ -150,4 +150,3 @@ catch { db2 close } sqlite3_enable_shared_cache $esc finish_test - diff --git a/test/orderby1.test b/test/orderby1.test index f459fc819..d4795671d 100644 --- a/test/orderby1.test +++ b/test/orderby1.test @@ -48,7 +48,7 @@ do_test 1.0 { } {} do_test 1.1a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} @@ -66,7 +66,7 @@ do_test 1.1b { # do_test 1.2a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} @@ -75,7 +75,7 @@ do_test 1.2a { do_test 1.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -85,13 +85,13 @@ do_test 1.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 1.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -101,55 +101,57 @@ db cache flush # do_test 1.4a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn } } {three-a three-c two-a two-b one-a one-c} do_test 1.4b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 1.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn } -} {~/ORDER BY/} ;# optimized out - +} {~/ORDER BY/} ;# ORDER BY suppressed due to uniqueness constraints do_test 1.5a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 1.5b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting do_test 1.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } -} {~/ORDER BY/} ;# optimized out +} {~/ORDER BY/} ;# ORDER BY suppressed due to uniqueness constraints do_test 1.6a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) + ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 1.6b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) + ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 1.6c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album CROSS JOIN track USING (aid) + ORDER BY title DESC, tn DESC } -} {~/ORDER BY/} ;# ORDER BY optimized-out +} {~/ORDER BY/} ;# ORDER BY # Reconstruct the test data to use indices rather than integer primary keys. @@ -183,7 +185,7 @@ do_test 2.0 { } {} do_test 2.1a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} @@ -192,28 +194,28 @@ do_test 2.1a { do_test 2.1b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } -} {~/ORDER BY/} ;# ORDER BY optimized out +} {/ORDER BY/} ;# ORDER BY required because of missing aid term in ORDER BY do_test 2.1c { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn } } {one-a one-c two-a two-b three-a three-c} do_test 2.1d { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn } -} {~/ORDER BY/} ;# ORDER BY optimized out +} {/ORDER BY/} ;# ORDER BY required in this case # The same query with ORDER BY clause optimization disabled via + operators # should give exactly the same answer. # do_test 2.2a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} @@ -222,7 +224,7 @@ do_test 2.2a { do_test 2.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -232,13 +234,13 @@ do_test 2.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 2.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -248,55 +250,55 @@ db cache flush # do_test 2.4a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn } } {three-a three-c two-a two-b one-a one-c} do_test 2.4b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 2.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn } -} {~/ORDER BY/} ;# optimized out +} {/ORDER BY/} ;# separate sorting pass due to mixed DESC/ASC do_test 2.5a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 2.5b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting do_test 2.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } -} {~/ORDER BY/} ;# optimized out +} {/ORDER BY/} ;# separate sorting pass due to mixed ASC/DESC do_test 2.6a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 2.6b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 2.6c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC } -} {~/ORDER BY/} ;# ORDER BY optimized out +} {/ORDER BY/} ;# ORDER BY required # Generate another test dataset, but this time using mixed ASC/DESC indices. @@ -348,7 +350,7 @@ do_test 3.1b { # do_test 3.2a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC } } {one-c one-a two-b two-a three-c three-a} @@ -357,7 +359,7 @@ do_test 3.2a { do_test 3.2b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC } } {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms @@ -367,13 +369,13 @@ do_test 3.3a { optimization_control db order-by-idx-join 0 db cache flush db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } } {one-c one-a two-b two-a three-c three-a} do_test 3.3b { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC } } {/ORDER BY/} ;# separate sorting pass due to disabled optimization optimization_control db all 1 @@ -383,38 +385,37 @@ db cache flush # do_test 3.4a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } } {one-a one-c two-a two-b three-a three-c} do_test 3.4b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn + SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn } } {one-a one-c two-a two-b three-a three-c} ;# verify same order after sorting do_test 3.4c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn + SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn } -} {~/ORDER BY/} ;# optimized out - +} {~/ORDER BY/} ;# ORDER BY suppressed by uniqueness constraints do_test 3.5a { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC } } {three-c three-a two-b two-a one-c one-a} do_test 3.5b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC } } {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting do_test 3.5c { db eval { EXPLAIN QUERY PLAN - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC + SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC } -} {~/ORDER BY/} ;# optimzed out +} {~/ORDER BY/} ;# ORDER BY suppressed by uniqueness constraints do_test 3.6a { @@ -424,7 +425,8 @@ do_test 3.6a { } {three-a three-c two-a two-b one-a one-c} do_test 3.6b { db eval { - SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn + SELECT name FROM album CROSS JOIN track USING (aid) + ORDER BY +title DESC, +tn } } {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting do_test 3.6c { @@ -434,5 +436,22 @@ do_test 3.6c { } } {~/ORDER BY/} ;# inverted ASC/DESC is optimized out +# Ticket 5ed1772895bf3deeab78c5e3519b1da9165c541b (2013-06-04) +# Incorrect ORDER BY on an indexed JOIN +# +do_test 4.0 { + db eval { + CREATE TABLE t41(a INT UNIQUE NOT NULL, b INT NOT NULL); + CREATE INDEX t41ba ON t41(b,a); + CREATE TABLE t42(x INT NOT NULL REFERENCES t41(a), y INT NOT NULL); + CREATE UNIQUE INDEX t42xy ON t42(x,y); + INSERT INTO t41 VALUES(1,1),(3,1); + INSERT INTO t42 VALUES(1,13),(1,15),(3,14),(3,16); + + SELECT b, y FROM t41 CROSS JOIN t42 ON x=a ORDER BY b, y; + } +} {1 13 1 14 1 15 1 16} + + finish_test diff --git a/test/orderby5.test b/test/orderby5.test new file mode 100644 index 000000000..5bae5db71 --- /dev/null +++ b/test/orderby5.test @@ -0,0 +1,97 @@ +# 2013-06-14 +# +# 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. The +# focus of this file is testing that the optimizations that disable +# ORDER BY clauses work correctly +# + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix orderby5 + +# Generate test data for a join. Verify that the join gets the +# correct answer. +# +do_execsql_test 1.1 { + CREATE TABLE t1(a,b,c); + CREATE INDEX t1bc ON t1(b,c); + + EXPLAIN QUERY PLAN + SELECT DISTINCT a, b, c FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.2.1 { + EXPLAIN QUERY PLAN + SELECT DISTINCT a, c, b FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.2.2 { + EXPLAIN QUERY PLAN + SELECT DISTINCT a, c, b FROM t1 WHERE a='xyz' COLLATE nocase; +} {/B-TREE/} +do_execsql_test 1.2.3 { + EXPLAIN QUERY PLAN + SELECT DISTINCT a COLLATE nocase, c, b FROM t1 WHERE a='xyz'; +} {/B-TREE/} +do_execsql_test 1.2.4 { + EXPLAIN QUERY PLAN + SELECT DISTINCT a COLLATE nocase, c, b FROM t1 WHERE a='xyz' COLLATE nocase; +} {~/B-TREE/} +do_execsql_test 1.3 { + EXPLAIN QUERY PLAN + SELECT DISTINCT b, a, c FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.4 { + EXPLAIN QUERY PLAN + SELECT DISTINCT b, c, a FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.5 { + EXPLAIN QUERY PLAN + SELECT DISTINCT c, a, b FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.6 { + EXPLAIN QUERY PLAN + SELECT DISTINCT c, b, a FROM t1 WHERE a=0; +} {~/B-TREE/} +do_execsql_test 1.7 { + EXPLAIN QUERY PLAN + SELECT DISTINCT c, b, a FROM t1 WHERE +a=0; +} {/B-TREE/} +do_execsql_test 2.1 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY a, b, c; +} {~/B-TREE/} +do_execsql_test 2.2 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE +a=0 ORDER BY a, b, c; +} {/B-TREE/} +do_execsql_test 2.3 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY b, a, c; +} {~/B-TREE/} +do_execsql_test 2.4 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY b, c, a; +} {~/B-TREE/} +do_execsql_test 2.5 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY a, c, b; +} {/B-TREE/} +do_execsql_test 2.6 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY c, a, b; +} {/B-TREE/} +do_execsql_test 2.7 { + EXPLAIN QUERY PLAN + SELECT * FROM t1 WHERE a=0 ORDER BY c, b, a; +} {/B-TREE/} + + +finish_test diff --git a/test/pager1.test b/test/pager1.test index 72e47805a..4a3b6fa2d 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -2815,4 +2815,3 @@ do_test 43.3 { } {0 1 0} finish_test - diff --git a/test/pagerfault.test b/test/pagerfault.test index 23754fa9d..796f531c3 100644 --- a/test/pagerfault.test +++ b/test/pagerfault.test @@ -1546,4 +1546,3 @@ sqlite3_shutdown sqlite3_config_uri 0 finish_test - diff --git a/test/pagerfault2.test b/test/pagerfault2.test index 6cdb99a3e..7f6c995ef 100644 --- a/test/pagerfault2.test +++ b/test/pagerfault2.test @@ -96,4 +96,3 @@ do_faultsim_test pagerfault2-2 -faults oom-transient -prep { sqlite3_memdebug_vfs_oom_test 1 finish_test - diff --git a/test/pagerfault3.test b/test/pagerfault3.test index 3d192e6d4..a4301cd17 100644 --- a/test/pagerfault3.test +++ b/test/pagerfault3.test @@ -61,4 +61,3 @@ do_faultsim_test pagerfault3-1 -faults ioerr-transient -prep { } finish_test - diff --git a/test/permutations.test b/test/permutations.test index d035a1afa..f0494870a 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -213,6 +213,82 @@ test_suite "nofaultsim" -prefix "" -description { unset -nocomplain ::G(valgrind) } +test_suite "queryplanner" -prefix "" -description { + Tests of the query planner and query optimizer +} -files { + alter2.test alter3.test alter4.test alter.test analyze3.test + analyze4.test analyze5.test analyze6.test analyze7.test analyze8.test + analyze.test attach2.test attach3.test attach4.test + attach.test autoinc.test autoindex1.test between.test cast.test + check.test closure01.test coalesce.test collate1.test collate2.test + collate3.test collate4.test collate5.test collate6.test collate7.test + collate8.test collate9.test collateA.test colmeta.test colname.test + conflict.test count.test coveridxscan.test createtab.test cse.test + date.test dbstatus2.test dbstatus.test default.test delete2.test + delete3.test delete.test descidx1.test descidx2.test descidx3.test + distinctagg.test distinct.test e_createtable.test e_delete.test + e_droptrigger.test e_dropview.test e_expr.test e_insert.test + eqp.test e_reindex.test e_resolve.test e_select2.test e_select.test + e_update.test exists.test expr.test fkey1.test fkey2.test fkey3.test + fkey4.test fkey5.test func2.test func3.test func.test + in3.test in4.test in5.test index2.test index3.test + index4.test index5.test indexedby.test index.test + insert2.test insert3.test insert4.test insert5.test insert.test + instr.test in.test intpkey.test join2.test join3.test join4.test + join5.test join6.test join.test like2.test like.test limit.test + minmax2.test minmax3.test minmax4.test minmax.test misc1.test misc2.test + misc3.test misc4.test misc5.test misc6.test misc7.test orderby1.test + orderby2.test orderby3.test orderby4.test randexpr1.test regexp1.test + reindex.test rowhash.test rowid.test schema2.test schema3.test + schema4.test schema5.test schema.test + select1.test select2.test select3.test select4.test select5.test + select6.test select7.test select8.test select9.test selectA.test + selectB.test selectC.test selectD.test selectE.test sidedelete.test + sort.test spellfix.test subquery2.test subquery.test subselect.test + substr.test tkt-02a8e81d44.test tkt1435.test tkt1443.test tkt1444.test + tkt1449.test tkt1473.test tkt1501.test tkt1512.test tkt1514.test + tkt1536.test tkt1537.test tkt1567.test tkt1644.test tkt1667.test + tkt1873.test tkt2141.test tkt2192.test tkt2213.test tkt2251.test + tkt2285.test tkt2332.test tkt2339.test tkt2391.test tkt2409.test + tkt2450.test tkt2565.test tkt2640.test tkt2643.test tkt2686.test + tkt-26ff0c2d1e.test tkt2767.test tkt2817.test tkt2820.test tkt2822.test + tkt2832.test tkt2854.test tkt2920.test tkt2927.test tkt2942.test + tkt-2a5629202f.test tkt-2d1a5c67d.test tkt-2ea2425d34.test tkt3080.test + tkt3093.test tkt3121.test tkt-31338dca7e.test tkt-313723c356.test + tkt3201.test tkt3292.test tkt3298.test tkt3334.test tkt3346.test + tkt3357.test tkt3419.test tkt3424.test tkt3442.test tkt3457.test + tkt3461.test tkt3493.test tkt3508.test tkt3522.test tkt3527.test + tkt3541.test tkt3554.test tkt3581.test tkt35xx.test tkt3630.test + tkt3718.test tkt3731.test tkt3757.test tkt3761.test tkt3762.test + tkt3773.test tkt3791.test tkt3793.test tkt3810.test tkt3824.test + tkt3832.test tkt3838.test tkt3841.test tkt-385a5b56b9.test tkt3871.test + tkt3879.test tkt-38cb5df375.test tkt3911.test tkt3918.test tkt3922.test + tkt3929.test tkt3935.test tkt3992.test tkt3997.test tkt-3998683a16.test + tkt-3a77c9714e.test tkt-3fe897352e.test tkt4018.test tkt-4a03edc4c8.test + tkt-4dd95f6943.test tkt-54844eea3f.test tkt-5d863f876e.test + tkt-5e10420e8d.test tkt-5ee23731f.test tkt-6bfb98dfc0.test + tkt-752e1646fc.test tkt-78e04e52ea.test tkt-7a31705a7e6.test + tkt-7bbfb7d442.test tkt-80ba201079.test tkt-80e031a00f.test + tkt-8454a207b9.test tkt-91e2e8ba6f.test tkt-94c04eaadb.test + tkt-9d68c883.test tkt-a7b7803e.test tkt-b1d3a2e531.test + tkt-b351d95f9.test tkt-b72787b1.test tkt-bd484a090c.test + tkt-bdc6bbbb38.test tkt-c48d99d690.test tkt-cbd054fa6b.test + tkt-d11f09d36e.test tkt-d635236375.test tkt-d82e3f3721.test + tkt-f3e5abed55.test tkt-f777251dc7a.test tkt-f7b4edec.test + tkt-f973c7ac31.test tkt-fa7bf5ec.test tkt-fc62af4523.test + tkt-fc7bd6358f.test trigger1.test trigger2.test trigger3.test + trigger4.test trigger5.test trigger6.test trigger7.test trigger8.test + trigger9.test triggerA.test triggerB.test triggerC.test triggerD.test + types2.test types3.test types.test unique.test unordered.test + update.test view.test vtab1.test vtab2.test vtab3.test vtab4.test + vtab5.test vtab6.test vtab7.test vtab8.test vtab9.test vtab_alter.test + vtabA.test vtabB.test vtabC.test vtabD.test vtabE.test + vtabF.test where2.test where3.test where4.test where5.test where6.test + where7.test where8m.test where8.test where9.test whereA.test whereB.test + whereC.test whereD.test whereE.test whereF.test wherelimit.test + where.test +} + lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the coverage related test suites: diff --git a/test/securedel2.test b/test/securedel2.test index b20f4f921..9badc560d 100644 --- a/test/securedel2.test +++ b/test/securedel2.test @@ -92,4 +92,3 @@ do_test 1.6.2 { } {0} finish_test - diff --git a/test/shared8.test b/test/shared8.test index 600e02bf7..73f0d4762 100644 --- a/test/shared8.test +++ b/test/shared8.test @@ -110,4 +110,3 @@ do_test 1.8 { foreach db {db1 db2 db3 db4} { catch { $db close } } sqlite3_enable_shared_cache $::enable_shared_cache finish_test - diff --git a/test/sharedlock.test b/test/sharedlock.test index 1e78eeafd..9dd7fe2bf 100644 --- a/test/sharedlock.test +++ b/test/sharedlock.test @@ -52,4 +52,3 @@ db2 close sqlite3_enable_shared_cache $::enable_shared_cache finish_test - diff --git a/test/subquery.test b/test/subquery.test index f601d3f80..da0a44cc5 100644 --- a/test/subquery.test +++ b/test/subquery.test @@ -241,8 +241,11 @@ do_test subquery-2.5.3.1 { } {10.0} do_test subquery-2.5.3.2 { # Verify that the t4i index was not used in the previous query - set ::sqlite_query_plan -} {t4 {}} + execsql { + EXPLAIN QUERY PLAN + SELECT * FROM t4 WHERE x IN (SELECT a FROM t3); + } +} {/SCAN TABLE t4 /} do_test subquery-2.5.4 { execsql { DROP TABLE t3; diff --git a/test/tester.tcl b/test/tester.tcl index 761a36e35..32dca4cb7 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -551,6 +551,9 @@ proc do_test {name cmd expected} { fail_test $name } else { if {[regexp {^~?/.*/$} $expected]} { + # "expected" is of the form "/PATTERN/" then the result if correct if + # regular expression PATTERN matches the result. "~/PATTERN/" means + # the regular expression must not match. if {[string index $expected 0]=="~"} { set re [string map {# {[-0-9.]+}} [string range $expected 2 end-1]] set ok [expr {![regexp $re $result]}] @@ -558,6 +561,16 @@ proc do_test {name cmd expected} { set re [string map {# {[-0-9.]+}} [string range $expected 1 end-1]] set ok [regexp $re $result] } + } elseif {[regexp {^~?\*.*\*$} $expected]} { + # "expected" is of the form "*GLOB*" then the result if correct if + # glob pattern GLOB matches the result. "~/GLOB/" means + # the glob must not match. + if {[string index $expected 0]=="~"} { + set e [string range $expected 1 end] + set ok [expr {![string match $e $result]}] + } else { + set ok [string match $expected $result] + } } else { set ok [expr {[string compare $result $expected]==0}] } @@ -793,9 +806,28 @@ proc finalize_testing {} { set nTest [incr_ntest] set nErr [set_test_counter errors] - puts "$nErr errors out of $nTest tests" - if {$nErr>0} { - puts "Failures on these tests: [set_test_counter fail_list]" + set nKnown 0 + if {[file readable known-problems.txt]} { + set fd [open known-problems.txt] + set content [read $fd] + close $fd + foreach x $content {set known_error($x) 1} + foreach x [set_test_counter fail_list] { + if {[info exists known_error($x)]} {incr nKnown} + } + } + if {$nKnown>0} { + puts "[expr {$nErr-$nKnown}] new errors and $nKnown known errors\ + out of $nTest tests" + } else { + puts "$nErr errors out of $nTest tests" + } + if {$nErr>$nKnown} { + puts -nonewline "Failures on these tests:" + foreach x [set_test_counter fail_list] { + if {![info exists known_error($x)]} {puts -nonewline " $x"} + } + puts "" } foreach warning [set_test_counter warn_list] { puts "Warning: $warning" diff --git a/test/tkt-2a5629202f.test b/test/tkt-2a5629202f.test index 037f100f6..8f09c310a 100644 --- a/test/tkt-2a5629202f.test +++ b/test/tkt-2a5629202f.test @@ -46,6 +46,12 @@ do_execsql_test 1.3 { SELECT coalesce(b, 'null') || '/' || c FROM t8 x ORDER BY x.b, x.c } {null/four null/three a/one b/two} +do_execsql_test 1.4 { + DROP INDEX i1; + CREATE UNIQUE INDEX i1 ON t8(b, c); + SELECT coalesce(b, 'null') || '/' || c FROM t8 x ORDER BY x.b, x.c +} {null/four null/three a/one b/two} + #------------------------------------------------------------------------- # @@ -68,4 +74,3 @@ do_test 2.4 { } {sort} finish_test - diff --git a/test/tkt-385a5b56b9.test b/test/tkt-385a5b56b9.test index 818486486..1338435ed 100644 --- a/test/tkt-385a5b56b9.test +++ b/test/tkt-385a5b56b9.test @@ -35,19 +35,19 @@ do_execsql_test 2.0 { } do_eqp_test 2.1 { SELECT DISTINCT x FROM t2 } { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2x (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2x} } do_eqp_test 2.2 { SELECT DISTINCT y FROM t2 } { - 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y (~1000000 rows)} + 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y} } do_eqp_test 2.3 { SELECT DISTINCT x, y FROM t2 WHERE y=10 } { - 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?) (~1 rows)} + 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?)} } do_eqp_test 2.4 { SELECT DISTINCT x, y FROM t2 WHERE x=10 } { - 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x=?) (~1 rows)} + 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x=?)} } finish_test diff --git a/test/tkt-3a77c9714e.test b/test/tkt-3a77c9714e.test index 6eaec16cd..b7d366f36 100644 --- a/test/tkt-3a77c9714e.test +++ b/test/tkt-3a77c9714e.test @@ -70,4 +70,3 @@ do_execsql_test 2.2 { finish_test - diff --git a/test/tkt-3fe897352e.test b/test/tkt-3fe897352e.test index deafe48e0..bc2b2033e 100644 --- a/test/tkt-3fe897352e.test +++ b/test/tkt-3fe897352e.test @@ -16,9 +16,9 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -
-# The following tests use hex_to_utf16be() and hex_to_utf16le() which
-# which are only available if SQLite is built with UTF16 support.
+ +# The following tests use hex_to_utf16be() and hex_to_utf16le() which +# which are only available if SQLite is built with UTF16 support. ifcapable {!utf16} { finish_test return diff --git a/test/tkt-78e04e52ea.test b/test/tkt-78e04e52ea.test index a664ceb9e..e906d3b7f 100644 --- a/test/tkt-78e04e52ea.test +++ b/test/tkt-78e04e52ea.test @@ -44,7 +44,7 @@ do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%'; } -} {0 0 0 {SCAN TABLE USING COVERING INDEX i1 (~500000 rows)}} +} {0 0 0 {SCAN TABLE USING COVERING INDEX i1}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; @@ -57,12 +57,12 @@ do_test tkt-78e04-2.1 { CREATE INDEX "" ON t2(x); EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=5; } -} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?) (~10 rows)}} +} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?)}} do_test tkt-78e04-2.2 { execsql { DROP INDEX ""; EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=2; } -} {0 0 0 {SCAN TABLE t2 (~100000 rows)}} +} {0 0 0 {SCAN TABLE t2}} finish_test diff --git a/test/tkt-7a31705a7e6.test b/test/tkt-7a31705a7e6.test index 64701220a..e3e402917 100644 --- a/test/tkt-7a31705a7e6.test +++ b/test/tkt-7a31705a7e6.test @@ -23,4 +23,3 @@ do_execsql_test tkt-7a31705a7e6-1.1 { CREATE TABLE t2x (b INTEGER PRIMARY KEY); SELECT t1.a FROM ((t1 JOIN t2 ON t1.a=t2.a) AS x JOIN t2x ON x.b=t2x.b) as y; } {} - diff --git a/test/tkt-7bbfb7d442.test b/test/tkt-7bbfb7d442.test index dcb9b1605..56d4caeb3 100644 --- a/test/tkt-7bbfb7d442.test +++ b/test/tkt-7bbfb7d442.test @@ -152,5 +152,3 @@ do_execsql_test 2.3 { finish_test - - diff --git a/test/tkt-c48d99d690.test b/test/tkt-c48d99d690.test index 9b4057991..6d1b9dbe6 100644 --- a/test/tkt-c48d99d690.test +++ b/test/tkt-c48d99d690.test @@ -23,4 +23,3 @@ do_test 1.1 { do_test 1.2 { execsql VACUUM } {} finish_test - diff --git a/test/tkt-d11f09d36e.test b/test/tkt-d11f09d36e.test index 70657706c..ffd3d2192 100644 --- a/test/tkt-d11f09d36e.test +++ b/test/tkt-d11f09d36e.test @@ -59,4 +59,3 @@ do_test tkt-d11f09d36e.5 { } {ok} finish_test - diff --git a/test/tkt-f3e5abed55.test b/test/tkt-f3e5abed55.test index b3f5d5656..3c793d4f4 100644 --- a/test/tkt-f3e5abed55.test +++ b/test/tkt-f3e5abed55.test @@ -114,4 +114,3 @@ if {[permutation]!="inmemory_journal"} { finish_test - diff --git a/test/tkt-f973c7ac31.test b/test/tkt-f973c7ac31.test index 882e86a0a..454309057 100644 --- a/test/tkt-f973c7ac31.test +++ b/test/tkt-f973c7ac31.test @@ -84,4 +84,3 @@ foreach {tn sql} { finish_test - diff --git a/test/tkt3442.test b/test/tkt3442.test index ae03949d5..ee9169ce2 100644 --- a/test/tkt3442.test +++ b/test/tkt3442.test @@ -49,10 +49,10 @@ proc EQP {sql} { ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } - } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}} do_test tkt3442-1.3 { EQP { SELECT node FROM listhash WHERE id="5000" LIMIT 1; } - } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}} } @@ -61,7 +61,7 @@ ifcapable explain { ifcapable explain { do_test tkt3442-1.4 { EQP { SELECT node FROM listhash WHERE id=5000 LIMIT 1; } - } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} + } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}} } do_test tkt3442-1.5 { catchsql { diff --git a/test/tkt3918.test b/test/tkt3918.test index c46ad8f6c..e20ee152f 100644 --- a/test/tkt3918.test +++ b/test/tkt3918.test @@ -57,4 +57,3 @@ do_test tkt3918.5 { } {} finish_test - diff --git a/test/tkt3929.test b/test/tkt3929.test index 3ed4d288c..db02bb841 100644 --- a/test/tkt3929.test +++ b/test/tkt3929.test @@ -50,4 +50,3 @@ do_test tkt3930-1.2 { integrity_check tkt3930-1.3 finish_test - diff --git a/test/unordered.test b/test/unordered.test index 4aa8310f8..d8f7aa6ad 100644 --- a/test/unordered.test +++ b/test/unordered.test @@ -40,28 +40,28 @@ foreach idxmode {ordered unordered} { sqlite3 db test.db foreach {tn sql r(ordered) r(unordered)} { 1 "SELECT * FROM t1 ORDER BY a" - {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} - {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + {0 0 0 {SCAN TABLE t1 USING INDEX i1}} + {0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} 2 "SELECT * FROM t1 WHERE a >?" - {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~32 rows)}} - {0 0 0 {SCAN TABLE t1 (~42 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}} + {0 0 0 {SCAN TABLE t1}} 3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid" - {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} - {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}} 4 "SELECT max(a) FROM t1" - {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} - {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1}} + {0 0 0 {SEARCH TABLE t1}} 5 "SELECT group_concat(b) FROM t1 GROUP BY a" - {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}} - {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} + {0 0 0 {SCAN TABLE t1 USING INDEX i1}} + {0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}} 6 "SELECT * FROM t1 WHERE a = ?" - {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} - {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} + {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 7 "SELECT count(*) FROM t1" - {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1(~128 rows)}} - {0 0 0 {SCAN TABLE t1 (~128 rows)}} + {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}} + {0 0 0 {SCAN TABLE t1}} } { do_eqp_test 1.$idxmode.$tn $sql $r($idxmode) } diff --git a/test/veryquick.test b/test/veryquick.test index ca82b22b9..c8d6ce8f8 100644 --- a/test/veryquick.test +++ b/test/veryquick.test @@ -16,4 +16,3 @@ source $testdir/permutations.test run_test_suite veryquick finish_test - diff --git a/test/vtab1.test b/test/vtab1.test index 1f17e536e..dfcec5ba9 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -618,8 +618,9 @@ do_test vtab1-5-6 { do_test vtab1-5-7 { filter $::echo_module } [list \ - xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ xFilter {SELECT rowid, * FROM 't1'} \ + xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ + xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \ ] execsql { @@ -1133,12 +1134,12 @@ do_test vtab1-14.015 { -do_test vtab1-14.1 { - execsql { DELETE FROM c } - set echo_module "" - execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) } - set echo_module -} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c' WHERE rowid = .} 1/} +#do_test vtab1-14.1 { +# execsql { DELETE FROM c } +# set echo_module "" +# execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) } +# set echo_module +#} {/.*xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c'} 1/} do_test vtab1-14.2 { set echo_module "" @@ -1152,11 +1153,11 @@ do_test vtab1-14.3 { set echo_module } [list xBestIndex {SELECT rowid, * FROM 'c' WHERE a = ?} xFilter {SELECT rowid, * FROM 'c' WHERE a = ?} 1] -do_test vtab1-14.4 { - set echo_module "" - execsql { SELECT * FROM echo_c WHERE a IN (1, 2) } - set echo_module -} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/} +#do_test vtab1-14.4 { +# set echo_module "" +# execsql { SELECT * FROM echo_c WHERE a IN (1, 2) } +# set echo_module +#} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/} do_test vtab1-15.1 { execsql { diff --git a/test/vtab6.test b/test/vtab6.test index 96e45bf54..10bf286ab 100644 --- a/test/vtab6.test +++ b/test/vtab6.test @@ -561,12 +561,12 @@ do_test vtab6-11.4.1 { catchsql { SELECT a, b, c FROM ab NATURAL JOIN bc; } -} {1 {table bc: xBestIndex returned an invalid plan}} +} {1 {table ab: xBestIndex returned an invalid plan}} do_test vtab6-11.4.2 { catchsql { SELECT a, b, c FROM bc NATURAL JOIN ab; } -} {1 {table ab: xBestIndex returned an invalid plan}} +} {1 {table bc: xBestIndex returned an invalid plan}} unset ::echo_module_ignore_usable diff --git a/test/wal8.test b/test/wal8.test index 339953895..0682fce35 100644 --- a/test/wal8.test +++ b/test/wal8.test @@ -88,4 +88,3 @@ do_execsql_test 3.1 { } {t1} finish_test - diff --git a/test/walcksum.test b/test/walcksum.test index 08278dd05..3005a758a 100644 --- a/test/walcksum.test +++ b/test/walcksum.test @@ -390,4 +390,3 @@ foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} { } finish_test - diff --git a/test/walcrash.test b/test/walcrash.test index adc4841dd..2a647188b 100644 --- a/test/walcrash.test +++ b/test/walcrash.test @@ -293,4 +293,3 @@ for {set i 1} {$i < $REPEATS} {incr i} { } finish_test - diff --git a/test/walcrash2.test b/test/walcrash2.test index 7116281c2..9c93bcdef 100644 --- a/test/walcrash2.test +++ b/test/walcrash2.test @@ -96,4 +96,3 @@ do_test walcrash2-1.3 { catch { db2 close } finish_test - diff --git a/test/walcrash3.test b/test/walcrash3.test index c2c9a6d51..71dfb4555 100644 --- a/test/walcrash3.test +++ b/test/walcrash3.test @@ -126,4 +126,3 @@ for {set i 2} {$i<10000 && [set_test_counter errors]==$nInitialErr} {incr i} { } finish_test - diff --git a/test/walro.test b/test/walro.test index 465ce838c..f1ed275a1 100644 --- a/test/walro.test +++ b/test/walro.test @@ -291,5 +291,3 @@ do_multiclient_test tn { } finish_test - - diff --git a/test/walshared.test b/test/walshared.test index 73a3fb8bc..fbbdeb4de 100644 --- a/test/walshared.test +++ b/test/walshared.test @@ -60,4 +60,3 @@ do_test walshared-1.4 { sqlite3_enable_shared_cache $::enable_shared_cache finish_test - diff --git a/test/where.test b/test/where.test index 2dbc28341..42170ceac 100644 --- a/test/where.test +++ b/test/where.test @@ -65,9 +65,9 @@ proc count sql { do_test where-1.1.1 { count {SELECT x, y, w FROM t1 WHERE w=10} } {3 121 10 3} -do_test where-1.1.2 { - set sqlite_query_plan -} {t1 i1w} +do_eqp_test where-1.1.2 { + SELECT x, y, w FROM t1 WHERE w=10 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.1.3 { db status step } {0} @@ -77,15 +77,15 @@ do_test where-1.1.4 { do_test where-1.1.5 { db status step } {99} -do_test where-1.1.6 { - set sqlite_query_plan -} {t1 {}} +do_eqp_test where-1.1.6 { + SELECT x, y, w FROM t1 WHERE +w=10 +} {*SCAN TABLE t1*} do_test where-1.1.7 { count {SELECT x, y, w AS abc FROM t1 WHERE abc=10} } {3 121 10 3} -do_test where-1.1.8 { - set sqlite_query_plan -} {t1 i1w} +do_eqp_test where-1.1.8 { + SELECT x, y, w AS abc FROM t1 WHERE abc=10 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.1.9 { db status step } {0} @@ -104,21 +104,21 @@ do_test where-1.3.2 { do_test where-1.4.1 { count {SELECT w, x, y FROM t1 WHERE 11=w AND x>2} } {11 3 144 3} -do_test where-1.4.2 { - set sqlite_query_plan -} {t1 i1w} +do_eqp_test where-1.4.2 { + SELECT w, x, y FROM t1 WHERE 11=w AND x>2 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.4.3 { count {SELECT w AS a, x AS b, y FROM t1 WHERE 11=a AND b>2} } {11 3 144 3} -do_test where-1.4.4 { - set sqlite_query_plan -} {t1 i1w} +do_eqp_test where-1.4.4 { + SELECT w AS a, x AS b, y FROM t1 WHERE 11=a AND b>2 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.5 { count {SELECT x, y FROM t1 WHERE y<200 AND w=11 AND x>2} } {3 144 3} -do_test where-1.5.2 { - set sqlite_query_plan -} {t1 i1w} +do_eqp_test where-1.5.2 { + SELECT x, y FROM t1 WHERE y<200 AND w=11 AND x>2 +} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*} do_test where-1.6 { count {SELECT x, y FROM t1 WHERE y<200 AND x>2 AND w=11} } {3 144 3} @@ -128,13 +128,12 @@ do_test where-1.7 { do_test where-1.8 { count {SELECT x, y FROM t1 WHERE w>10 AND y=144 AND x=3} } {3 144 3} -do_test where-1.8.2 { - set sqlite_query_plan -} {t1 i1xy} -do_test where-1.8.3 { - count {SELECT x, y FROM t1 WHERE y=144 AND x=3} - set sqlite_query_plan -} {{} i1xy} +do_eqp_test where-1.8.2 { + SELECT x, y FROM t1 WHERE w>10 AND y=144 AND x=3 +} {*SEARCH TABLE t1 USING INDEX i1xy (x=? AND y=?)*} +do_eqp_test where-1.8.3 { + SELECT x, y FROM t1 WHERE y=144 AND x=3 +} {*SEARCH TABLE t1 USING COVERING INDEX i1xy (x=? AND y=?)*} do_test where-1.9 { count {SELECT x, y FROM t1 WHERE y=144 AND w>10 AND x=3} } {3 144 3} @@ -605,7 +604,7 @@ do_test where-6.9.7 { cksort { SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY c,a LIMIT 3 } -} {1 100 4 sort} +} {1 100 4 nosort} do_test where-6.9.8 { cksort { SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c ASC LIMIT 3 diff --git a/test/where2.test b/test/where2.test index e8c2f3613..e57192d85 100644 --- a/test/where2.test +++ b/test/where2.test @@ -66,14 +66,24 @@ proc cksort {sql} { # This procedure executes the SQL. Then it appends to the result the # "sort" or "nosort" keyword (as in the cksort procedure above) then -# it appends the ::sqlite_query_plan variable. +# it appends the name of the table and index used. # proc queryplan {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] if {$::sqlite_sort_count} {set x sort} {set x nosort} lappend data $x - return [concat $data $::sqlite_query_plan] + set eqp [execsql "EXPLAIN QUERY PLAN $sql"] + # puts eqp=$eqp + foreach {a b c x} $eqp { + if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \ + $x all as tab idx]} { + lappend data $tab $idx + } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} { + lappend data $tab * + } + } + return $data } @@ -273,12 +283,12 @@ do_test where2-6.3 { queryplan { SELECT * FROM t1 WHERE w=99 OR w=100 OR 6=+w ORDER BY +w } -} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 {}} +} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 *} do_test where2-6.4 { queryplan { SELECT * FROM t1 WHERE w=99 OR +w=100 OR 6=w ORDER BY +w } -} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 {}} +} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 *} set ::idx {} ifcapable subquery {set ::idx i1zyx} @@ -314,7 +324,7 @@ do_test where2-6.7 { -- SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=b; } -} {123 0123 nosort t2249b {} t2249a {}} +} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.9 { queryplan { -- The + operator removes affinity from the rhs. No conversions @@ -322,13 +332,13 @@ do_test where2-6.9 { -- SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b; } -} {nosort t2249b {} {} sqlite_autoindex_t2249a_1} +} {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.9.2 { # The same thing but with the expression flipped around. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE +b=a } -} {nosort t2249b {} {} sqlite_autoindex_t2249a_1} +} {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.10 { queryplan { -- Use + on both sides of the comparison to disable indices @@ -336,32 +346,32 @@ do_test where2-6.10 { -- SELECT * FROM t2249b CROSS JOIN t2249a WHERE +a=+b; } -} {nosort t2249b {} t2249a {}} +} {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.11 { # This will not attempt the OR optimization because of the a=b # comparison. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=b OR a='hello'; } -} {123 0123 nosort t2249b {} t2249a {}} +} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.11.2 { # Permutations of the expression terms. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE b=a OR a='hello'; } -} {123 0123 nosort t2249b {} t2249a {}} +} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.11.3 { # Permutations of the expression terms. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE 'hello'=a OR b=a; } -} {123 0123 nosort t2249b {} t2249a {}} +} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.11.4 { # Permutations of the expression terms. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR b=a; } -} {123 0123 nosort t2249b {} t2249a {}} +} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1} ifcapable explain&&subquery { # These tests are not run if subquery support is not included in the # build. This is because these tests test the "a = 1 OR a = 2" to @@ -375,7 +385,7 @@ ifcapable explain&&subquery { queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b OR a='hello'; } - } {nosort t2249b {} {} sqlite_autoindex_t2249a_1} + } {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.12.2 { # In this case, the +b disables the affinity conflict and allows # the OR optimization to be used again. The result is now an empty @@ -383,7 +393,7 @@ ifcapable explain&&subquery { queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR +b=a; } - } {nosort t2249b {} {} sqlite_autoindex_t2249a_1} + } {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.12.3 { # In this case, the +b disables the affinity conflict and allows # the OR optimization to be used again. The result is now an empty @@ -391,14 +401,14 @@ ifcapable explain&&subquery { queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE +b=a OR a='hello'; } - } {nosort t2249b {} {} sqlite_autoindex_t2249a_1} + } {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} do_test where2-6.13 { # The addition of +a on the second term disabled the OR optimization. # But we should still get the same empty-set result as in where2-6.9. queryplan { SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=+b OR +a='hello'; } - } {nosort t2249b {} t2249a {}} + } {nosort t2249b * t2249a sqlite_autoindex_t2249a_1} } # Variations on the order of terms in a WHERE clause in order @@ -407,7 +417,7 @@ do_test where2-6.20 { queryplan { SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a } -} {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1} +} {0123 0123 nosort x sqlite_autoindex_t2249a_1 y sqlite_autoindex_t2249a_1} ifcapable explain&&subquery { # These tests are not run if subquery support is not included in the # build. This is because these tests test the "a = 1 OR a = 2" to @@ -418,17 +428,17 @@ ifcapable explain&&subquery { queryplan { SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a OR y.a='hello' } - } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1} + } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y sqlite_autoindex_t2249a_1} do_test where2-6.22 { queryplan { SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE y.a=x.a OR y.a='hello' } - } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1} + } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y sqlite_autoindex_t2249a_1} do_test where2-6.23 { queryplan { SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE y.a='hello' OR x.a=y.a } - } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1} + } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y sqlite_autoindex_t2249a_1} } # Unique queries (queries that are guaranteed to return only a single diff --git a/test/where3.test b/test/where3.test index e08f9051e..a5137173f 100644 --- a/test/where3.test +++ b/test/where3.test @@ -103,12 +103,22 @@ ifcapable explain { } # This procedure executes the SQL. Then it appends -# the ::sqlite_query_plan variable. +# the names of the table and index used # proc queryplan {sql} { set ::sqlite_sort_count 0 set data [execsql $sql] - return [concat $data $::sqlite_query_plan] + set eqp [execsql "EXPLAIN QUERY PLAN $sql"] + # puts eqp=$eqp + foreach {a b c x} $eqp { + if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \ + $x all as tab idx]} { + lappend data $tab $idx + } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} { + lappend data $tab * + } + } + return $data } @@ -144,73 +154,73 @@ do_test where3-2.1 { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND bpk=ax } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.1.1 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk WHERE cpk=bx AND bpk=ax } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.1.2 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk WHERE bx=cpk AND bpk=ax } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.1.3 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON cx=dpk WHERE bx=cpk AND ax=bpk } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.1.4 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE bx=cpk AND ax=bpk } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.1.5 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND ax=bpk } -} {tA {} tB * tC * tD *} +} {tA * tB * tC * tD *} do_test where3-2.2 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND apk=bx } -} {tB {} tA * tC * tD *} +} {tB * tA * tC * tD *} do_test where3-2.3 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND apk=bx } -} {tB {} tA * tC * tD *} +} {tB * tA * tC * tD *} do_test where3-2.4 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE apk=cx AND bpk=ax } -} {tC {} tA * tB * tD *} +} {tC * tA * tB * tD *} do_test where3-2.5 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=ax AND bpk=cx } -} {tA {} tC * tB * tD *} +} {tA * tC * tB * tD *} do_test where3-2.6 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE bpk=cx AND apk=bx } -} {tC {} tB * tA * tD *} +} {tC * tB * tA * tD *} do_test where3-2.7 { queryplan { SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx WHERE cpk=bx AND apk=cx } -} {tB {} tC * tA * tD *} +} {tB * tC * tA * tD *} # Ticket [13f033c865f878953] # If the outer loop must be a full table scan, do not let ANALYZE trick @@ -226,17 +236,18 @@ do_execsql_test where3-3.0 { ANALYZE; explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y; } { - 0 0 0 {SCAN TABLE t302 (~1 rows)} - 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SCAN TABLE t302} + 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)} } do_execsql_test where3-3.1 { explain query plan SELECT * FROM t301, t302 WHERE t302.x=5 AND t301.a=t302.y; } { - 0 0 1 {SCAN TABLE t302 (~1 rows)} - 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 1 {SCAN TABLE t302} + 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)} } +if 0 { # Query planner no longer does this # Verify that when there are multiple tables in a join which must be # full table scans that the query planner attempts put the table with # the fewest number of output rows as the outer loop. @@ -248,26 +259,27 @@ do_execsql_test where3-4.0 { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*'; } { - 0 0 2 {SCAN TABLE t402 (~500000 rows)} - 0 1 0 {SCAN TABLE t400 (~1000000 rows)} - 0 2 1 {SCAN TABLE t401 (~1000000 rows)} + 0 0 2 {SCAN TABLE t402} + 0 1 0 {SCAN TABLE t400} + 0 2 1 {SCAN TABLE t401} } do_execsql_test where3-4.1 { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*'; } { - 0 0 1 {SCAN TABLE t401 (~500000 rows)} - 0 1 0 {SCAN TABLE t400 (~1000000 rows)} - 0 2 2 {SCAN TABLE t402 (~1000000 rows)} + 0 0 1 {SCAN TABLE t401} + 0 1 0 {SCAN TABLE t400} + 0 2 2 {SCAN TABLE t402} } do_execsql_test where3-4.2 { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*'; } { - 0 0 0 {SCAN TABLE t400 (~500000 rows)} - 0 1 1 {SCAN TABLE t401 (~1000000 rows)} - 0 2 2 {SCAN TABLE t402 (~1000000 rows)} + 0 0 0 {SCAN TABLE t400} + 0 1 1 {SCAN TABLE t401} + 0 2 2 {SCAN TABLE t402} } +} ;# endif # Verify that a performance regression encountered by firefox # has been fixed. @@ -298,8 +310,8 @@ do_execsql_test where3-5.0 { AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { - 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} - 0 1 1 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)} + 0 1 1 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.1 { @@ -311,8 +323,8 @@ do_execsql_test where3-5.1 { AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { - 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} - 0 1 1 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)} + 0 1 1 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.2 { @@ -324,8 +336,8 @@ do_execsql_test where3-5.2 { AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { - 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} - 0 1 0 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)} + 0 1 0 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.3 { @@ -337,8 +349,8 @@ do_execsql_test where3-5.3 { AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { - 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} - 0 1 0 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} + 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)} + 0 1 0 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } diff --git a/test/where7.test b/test/where7.test index b6cd7ccbb..5032c698b 100644 --- a/test/where7.test +++ b/test/where7.test @@ -23303,7 +23303,7 @@ do_test where7-2.1001.2 { # # The test case that follows is code from an actual # application with identifiers change and unused columns -# remove. +# removed. # do_execsql_test where7-3.1 { CREATE TABLE t301 ( @@ -23332,16 +23332,16 @@ do_execsql_test where7-3.1 { EXPLAIN QUERY PLAN SELECT t302.c1 - FROM t302 JOIN t301 ON t302.c8 = t301.c8 + FROM t302 JOIN t301 ON t302.c8 = +t301.c8 WHERE t302.c2 = 19571 AND t302.c3 > 1287603136 AND (t301.c4 = 1407449685622784 OR t301.c8 = 1407424651264000) ORDER BY t302.c5 LIMIT 200; } { - 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)} - 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} - 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)} + 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?)} + 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)} + 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } diff --git a/test/where8.test b/test/where8.test index 9b6014e70..6890e3ac5 100644 --- a/test/where8.test +++ b/test/where8.test @@ -268,7 +268,7 @@ do_test where8-3.12 { execsql_status { SELECT a, d FROM t1, t2 WHERE (a=d OR b=e) AND +a<5 ORDER BY a } -} {1 1 2 2 3 3 4 2 4 4 0 0} +} {1 1 2 2 3 3 4 2 4 4 9 0} do_test where8-3.13 { execsql_status { SELECT a, d FROM t1, t2 WHERE (a=d OR b=e) AND +a<5 diff --git a/test/where9.test b/test/where9.test index 1e94fdff7..5b000d967 100644 --- a/test/where9.test +++ b/test/where9.test @@ -362,9 +362,9 @@ ifcapable explain { SELECT t2.a FROM t1, t2 WHERE t1.a=80 AND ((t1.c=t2.c AND t1.d=t2.d) OR t1.f=t2.f) } { - 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} - 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)} + 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?)} } do_execsql_test where9-3.2 { EXPLAIN QUERY PLAN @@ -372,9 +372,9 @@ ifcapable explain { FROM t1 LEFT JOIN t2 ON (t1.c+1=t2.c AND t1.d=t2.d) OR (t1.f||'x')=t2.f WHERE t1.a=80 } { - 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} - 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} - 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} + 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)} + 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?)} + 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?)} } } @@ -420,7 +420,7 @@ do_test where9-4.5 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {cannot use index: t1b}} +} {1 {no query solution}} do_test where9-4.6 { count_steps { SELECT a FROM t1 NOT INDEXED @@ -436,7 +436,7 @@ do_test where9-4.7 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {cannot use index: t1c}} +} {1 {no query solution}} do_test where9-4.8 { catchsql { SELECT a FROM t1 INDEXED BY t1d @@ -444,7 +444,7 @@ do_test where9-4.8 { AND (c=31031 OR d IS NULL) ORDER BY +a } -} {1 {cannot use index: t1d}} +} {1 {no query solution}} ifcapable explain { # The (c=31031 OR d IS NULL) clause is preferred over b>1000 because @@ -453,8 +453,8 @@ ifcapable explain { do_execsql_test where9-5.1 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~3 rows)} - 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~3 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?)} } # In contrast, b=1000 is preferred over any OR-clause. @@ -462,7 +462,7 @@ ifcapable explain { do_execsql_test where9-5.2 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b=1000 AND (c=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~5 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)} } # Likewise, inequalities in an AND are preferred over inequalities in @@ -471,7 +471,7 @@ ifcapable explain { do_execsql_test where9-5.3 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c>=31031 OR d IS NULL) } { - 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~125000 rows)} + 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?)} } } @@ -768,20 +768,57 @@ do_test where9-6.7.4 { do_test where9-6.8.1 { catchsql { DELETE FROM t1 INDEXED BY t1b - WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL) OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } -} {1 {cannot use index: t1b}} +} {1 {no query solution}} do_test where9-6.8.2 { catchsql { UPDATE t1 INDEXED BY t1b SET a=a+100 - WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL) OR (b NOT NULL AND c IS NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) } -} {1 {cannot use index: t1b}} - +} {1 {no query solution}} +ifcapable stat3 { + # When STAT3 is enabled, the "b NOT NULL" terms get translated + # into b>NULL, which can be satified by the index t1b. It is a very + # expensive way to do the query, but it works, and so a solution is possible. + do_test where9-6.8.3-stat3 { + catchsql { + UPDATE t1 INDEXED BY t1b SET a=a+100 + WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + OR (b NOT NULL AND c IS NULL AND d NOT NULL) + OR (b NOT NULL AND c NOT NULL AND d IS NULL) + } + } {0 {}} + do_test where9-6.8.4-stat3 { + catchsql { + DELETE FROM t1 INDEXED BY t1b + WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + OR (b NOT NULL AND c IS NULL AND d NOT NULL) + OR (b NOT NULL AND c NOT NULL AND d IS NULL) + } + } {0 {}} +} else { + do_test where9-6.8.3 { + catchsql { + UPDATE t1 INDEXED BY t1b SET a=a+100 + WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + OR (b NOT NULL AND c IS NULL AND d NOT NULL) + OR (b NOT NULL AND c NOT NULL AND d IS NULL) + } + } {1 {no query solution}} + do_test where9-6.8.4 { + catchsql { + DELETE FROM t1 INDEXED BY t1b + WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) + OR (b NOT NULL AND c IS NULL AND d NOT NULL) + OR (b NOT NULL AND c NOT NULL AND d IS NULL) + } + } {1 {no query solution}} +} ############################################################################ # Test cases where terms inside an OR series are combined with AND terms # external to the OR clause. In other words, cases where diff --git a/test/whereC.test b/test/whereC.test index 9fa1bbaf9..bd7fe9ad8 100644 --- a/test/whereC.test +++ b/test/whereC.test @@ -67,4 +67,3 @@ foreach {tn sql res} { finish_test - diff --git a/test/whereD.test b/test/whereD.test index 9ac5a6809..eb0f86402 100644 --- a/test/whereD.test +++ b/test/whereD.test @@ -186,4 +186,37 @@ do_test 4.3 { } } {1 2 3 3 6 9 4 5 6 {} {} {}} +# Ticket [bc1aea7b725f276177] +# Incorrect result on LEFT JOIN with OR constraints and an ORDER BY clause. +# +do_execsql_test 4.4 { + CREATE TABLE t44(a INTEGER, b INTEGER); + INSERT INTO t44 VALUES(1,2); + INSERT INTO t44 VALUES(3,4); + SELECT * + FROM t44 AS x + LEFT JOIN (SELECT a AS c, b AS d FROM t44) AS y ON a=c + WHERE d=4 OR d IS NULL; +} {3 4 3 4} +do_execsql_test 4.5 { + SELECT * + FROM t44 AS x + LEFT JOIN (SELECT a AS c, b AS d FROM t44) AS y ON a=c + WHERE d=4 OR d IS NULL + ORDER BY a; +} {3 4 3 4} +do_execsql_test 4.6 { + CREATE TABLE t46(c INTEGER, d INTEGER); + INSERT INTO t46 SELECT a, b FROM t44; + SELECT * FROM t44 LEFT JOIN t46 ON a=c + WHERE d=4 OR d IS NULL; +} {3 4 3 4} +do_execsql_test 4.7 { + SELECT * FROM t44 LEFT JOIN t46 ON a=c + WHERE d=4 OR d IS NULL + ORDER BY a; +} {3 4 3 4} + + + finish_test diff --git a/test/whereE.test b/test/whereE.test index e686a4628..a6b8f481b 100644 --- a/test/whereE.test +++ b/test/whereE.test @@ -47,16 +47,16 @@ do_execsql_test 1.1 { CREATE UNIQUE INDEX t2zx ON t2(z,x); EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; -} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/} do_execsql_test 1.2 { EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; -} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/} do_execsql_test 1.3 { ANALYZE; EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x; -} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/} do_execsql_test 1.4 { EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x; -} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/} +} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/} finish_test diff --git a/test/whereF.test b/test/whereF.test index 57bdbee05..c98312a26 100644 --- a/test/whereF.test +++ b/test/whereF.test @@ -46,7 +46,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -set testprefix x +set testprefix whereF do_execsql_test 1.0 { PRAGMA automatic_index = 0; @@ -63,7 +63,7 @@ foreach {tn sql} { } { do_test 1.$tn { db eval "EXPLAIN QUERY PLAN $sql" - } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} + } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/} } do_execsql_test 2.0 { @@ -84,7 +84,7 @@ foreach {tn sql} { } { do_test 2.$tn { db eval "EXPLAIN QUERY PLAN $sql" - } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} + } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/} } do_execsql_test 3.0 { @@ -109,7 +109,7 @@ foreach {tn sql} { } { do_test 3.$tn { db eval "EXPLAIN QUERY PLAN $sql" - } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/} + } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/} } finish_test diff --git a/tool/wherecosttest.c b/tool/wherecosttest.c new file mode 100644 index 000000000..39d4a7a36 --- /dev/null +++ b/tool/wherecosttest.c @@ -0,0 +1,111 @@ +/* +** 2013-06-10 +** +** 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 contains a simple command-line utility for converting from +** integers and WhereCost values and back again and for doing simple +** arithmetic operations (multiple and add) on WhereCost values. +** +** Usage: +** +** ./wherecosttest ARGS +** +** Arguments: +** +** 'x' Multiple the top two elements of the stack +** '+' Add the top two elements of the stack +** NUM Convert NUM from integer to WhereCost and push onto the stack +** ^NUM Interpret NUM as a WhereCost and push onto stack. +** +** Examples: +** +** To convert 123 from WhereCost to integer: +** +** ./wherecosttest ^123 +** +** To convert 123456 from integer to WhereCost: +** +** ./wherecosttest 123456 +** +*/ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +typedef unsigned short int WhereCost; /* 10 times log2() */ + +WhereCost whereCostMultiply(WhereCost a, WhereCost b){ return a+b; } +WhereCost whereCostAdd(WhereCost a, WhereCost b){ + static const unsigned char x[] = { + 10, 10, /* 0,1 */ + 9, 9, /* 2,3 */ + 8, 8, /* 4,5 */ + 7, 7, 7, /* 6,7,8 */ + 6, 6, 6, /* 9,10,11 */ + 5, 5, 5, /* 12-14 */ + 4, 4, 4, 4, /* 15-18 */ + 3, 3, 3, 3, 3, 3, /* 19-24 */ + 2, 2, 2, 2, 2, 2, 2, /* 25-31 */ + }; + if( a<b ){ WhereCost t = a; a = b; b = t; } + if( a>b+49 ) return a; + if( a>b+31 ) return a+1; + return a+x[a-b]; +} +WhereCost whereCostFromInteger(int x){ + static WhereCost a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; + WhereCost y = 40; + if( x<8 ){ + if( x<2 ) return 0; + while( x<8 ){ y -= 10; x <<= 1; } + }else{ + while( x>255 ){ y += 40; x >>= 4; } + while( x>15 ){ y += 10; x >>= 1; } + } + return a[x&7] + y - 10; +} +static unsigned long int whereCostToInt(WhereCost x){ + unsigned long int n; + if( x<10 ) return 1; + n = x%10; + x /= 10; + if( n>=5 ) n -= 2; + else if( n>=1 ) n -= 1; + if( x>=3 ) return (n+8)<<(x-3); + return (n+8)>>(3-x); +} + +int main(int argc, char **argv){ + int i; + int n = 0; + WhereCost a[100]; + for(i=1; i<argc; i++){ + const char *z = argv[i]; + if( z[0]=='+' ){ + if( n>=2 ){ + a[n-2] = whereCostAdd(a[n-2],a[n-1]); + n--; + } + }else if( z[0]=='x' ){ + if( n>=2 ){ + a[n-2] = whereCostMultiply(a[n-2],a[n-1]); + n--; + } + }else if( z[0]=='^' ){ + a[n++] = atoi(z+1); + }else{ + a[n++] = whereCostFromInteger(atoi(z)); + } + } + for(i=n-1; i>=0; i--){ + printf("%d (%lu)\n", a[i], whereCostToInt(a[i])); + } + return 0; +} |