diff --git a/manifest b/manifest index e6d729925d..0187e05510 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\scompiler\swarning\sand\san\safter-OOM\smemory\sleak. -D 2014-03-28T19:47:19.372 +C Reopen\sthe\sorderby-planning\sbranch\sand\smerge\sin\sthe\slatest\strunk\senhancements\nand\sfixes. +D 2014-04-24T15:06:25.003 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -157,23 +157,23 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 5d99edbac5bc416032772b723ee30182ee6e5de0 +F src/alter.c b00900877f766f116f9e16116f1ccacdc21d82f1 F src/analyze.c 663e0b291d27eb03c9fd6b421e2d61ba348a2389 F src/attach.c 3801129015ef59d76bf23c95ef9b0069d18a0c52 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c a59a199f21338ae1847d69f5db87c3e8ef1b1578 -F src/btree.h 232836cb51753f2e96aa8ce0f052c6df850f76ba -F src/btreeInt.h 0be66063468a520e4d66b80c7a1dc26d04ee6ea4 -F src/build.c 0d50ef95aad63f4c4fc47f3fa2670d4557c45db0 +F src/btree.c 6c9b51abd404ce5b78b173b6f2248e8cb824758c +F src/btree.h d79306df4ed9181b48916737fe8871a4392c4594 +F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3 +F src/build.c 5bfeea8f302ec2926c9eea321a61daea92a29fa4 F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a F src/date.c 593c744b2623971e45affd0bde347631bdfa4625 -F src/delete.c cdd57149543bb28304d8f717c243f2a86b1fc280 -F src/expr.c da2b3cb41081af6b56e95e7c9e95949564ce2e21 +F src/delete.c bcf8f72126cea80fc3d5bc5494cf19b3f8935aaf +F src/expr.c 4f9e497c66e2f25a4d139357a778c84d5713207c F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 5269ef07b100763134f71b889327c333bd0989cf F src/func.c 2945bb2c4cdc0ac43733046285a4434310be1811 @@ -186,13 +186,13 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303 -F src/main.c 691b25754bef596108fe60ff1bcbe8445369c9db +F src/main.c 0a8cfb6b2899649880e5874ac9bfd1c6f7c6a4a3 F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b F src/mem2.c dce31758da87ec2cfa52ba4c5df1aed6e07d8e8f F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 -F src/mem5.c aeb019f271ea53de83d651ec526877e6ba863450 +F src/mem5.c 74670012946c4adc8a6ad84d03acc80959c3e529 F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785 F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea @@ -205,24 +205,24 @@ F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_unix.c ae4b5240af4619d711301d7992396e182585269f F src/os_win.c e71678ac927d0a0fb11d993db20a9748eabf808e -F src/pager.c 97a8908bf4e6e7c3adea09d3597cfa48ae33ab4e +F src/pager.c ab62a24218d87dda1be641f6c5ad291bff78fd94 F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428 F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0 F src/pcache.c d8eafac28290d4bb80332005435db44991d07fc2 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c 102e6f5a2fbc646154463eb856d1fd716867b64c -F src/pragma.c 10f169b9650f0930a7a6df67e1387a4c2c449f38 +F src/pragma.c 21ece94d4f3e76e8e150deecafb9c7abd398ec67 F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337 F src/printf.c e5a0005f8b3de21f85da6a709d2fbee76775bf4b F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 273d5f47c4e2c05b2d3d2bffeda939551ab59e66 -F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 20055cf917222e660c4222fea306bd13a0623caa -F src/shell.c cee9f46f2688a261601b1fd3d7f4b3cddf9b5cdf -F src/sqlite.h.in a2ef671f92747a5a1c8a47bad5c585a8dd9eca80 +F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be +F src/select.c 1a229278810ac02b9cb58687c3aa8cdc304d4dcf +F src/shell.c 2afe7a7154e97be0c74c5feacf09626bda8493be +F src/sqlite.h.in bde98816e1ba0c9ffef50afe7b32f4e5a8f54fe0 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h cc9582a91b2910404ccda7b7e198815ea0f75948 +F src/sqliteInt.h 03e2f60ccb0745fa2d3a072cb4f75fa29251d2ee F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -239,7 +239,7 @@ F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e -F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 +F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f F src/test_config.c 0336e0bdbe541b4af89d7e3dd0656e8e6b51e585 F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc @@ -278,12 +278,12 @@ F src/update.c 5b3e74a03b3811e586b4f2b4cbd7c49f01c93115 F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c 02f2de0b2f3b198438e3e64a2ceba9407bb8348b +F src/vdbe.c 59ad2f4c62178dcce4acef516e00290a7a58dd39 F src/vdbe.h 394464909ed682334aa3d5831aae0c2fe2abef94 F src/vdbeInt.h e6d83e5bfd62fc6685ba1ed6153f7099f82de9f7 F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4 -F src/vdbeaux.c 1153175fb57a8454e1c8cf79b59b7bf92b26779d -F src/vdbeblob.c 15377abfb59251bccedd5a9c7d014a895f0c04aa +F src/vdbeaux.c e493f38758c4b8f4ca2007cf6a700bd405d192f3 +F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447 F src/vdbesort.c ad0f9f717a73b870c12c0a0f47781b8b042a5348 F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 @@ -291,14 +291,14 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45 -F src/where.c 7d539cedb1c6a6d6b5d2075b8fea3a48db4838eb -F src/whereInt.h 2564055b440e44ebec8b47f237bbccae6719b7af +F src/where.c 3b127bdc24b7aa84ffa69729170be11555cd7733 +F src/whereInt.h 929c1349b5355fd44f22cee5c14d72b3329c58a6 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 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 -F test/alter.test e88dfa77e020c2b48e52a8020c70171ab828e079 +F test/alter.test 547dc2d292644301ac9a7dda22b319b74f9c08d2 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d F test/alter4.test d6c011fa0d6227abba762498cafbb607c9609e93 @@ -574,7 +574,7 @@ F test/fts4merge4.test c19c85ca1faa7b6d536832b49c12e1867235f584 F test/fts4noti.test aed33ba44808852dcb24bf70fa132e7bf530f057 F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test a21814945d32137412b553d98ad2107f9b2173a9 +F test/func.test c2cbfc23d554c5bf8678d0fb271aa4f8ef94839c F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f @@ -610,7 +610,7 @@ F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index3.test 55a90cff99834305e8141df7afaef39674b57062 F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6 F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33 -F test/index6.test 936979c3a1e87b81feaed2d00505665bf142d764 +F test/index6.test a0a2d286ffa6d35813f5003fdb7be124825b4422 F test/index7.test a3baf9a625bda7fd49471e99aeae04095fbfeecf F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d @@ -896,6 +896,7 @@ F test/tkt-a8a0d2996a.test eb597379dbcefa24765763d7f682c00cb5924fa9 F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 +F test/tkt-b75a9ca6b0.test 97cc2d5eeaf82799eb42138c0a1ff64370238ce4 F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898 F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447 @@ -1050,7 +1051,7 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772 -F test/wal.test 40073e54359d43354925b2b700b7eebd5e207285 +F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc F test/wal2.test a8e3963abf6b232cf0b852b09b53665ef34007af F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0 F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c @@ -1089,10 +1090,11 @@ F test/where9.test 4f3eab951353a3ae164befc521c777dfa903e46c F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a -F test/whereD.test 6c2feb79ef1f68381b07f39017fe5f9b96da8d62 +F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 -F test/whereG.test eb3a46b3eaf38e25e3013433b2db8a25a866c215 +F test/whereG.test 2533b72ed4a31fd1687230a499b557b911525344 +F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c @@ -1159,7 +1161,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 3047a25f1c41e83f0b4772f7c36fbfec0f12dc7e -R 911f6d7d99d9350a186d106d9b5cf527 +P 58f7ca29303c280229f86d01f418e1de5f5aebba 65d2544af9adc1e2f1d193e57f8be0422fb0d5eb +R b46cdf624d79e68eb1974021b4d3f992 U drh -Z 55f6b91a865b632e99503eb67df5d777 +Z d3c484ede684435a1175d26a414ba92a diff --git a/manifest.uuid b/manifest.uuid index cf96f01707..4866c44be0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58f7ca29303c280229f86d01f418e1de5f5aebba \ No newline at end of file +6077ddcd93318e24b9756adaaf293ba9fb3cedf7 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 1a83e570db..64204b7b24 100644 --- a/src/alter.c +++ b/src/alter.c @@ -116,6 +116,7 @@ static void renameParentFunc( int token; /* Type of token */ UNUSED_PARAMETER(NotUsed); + if( zInput==0 || zOld==0 ) return; for(z=zInput; *z; z=z+n){ n = sqlite3GetToken(z, &token); if( token==TK_REFERENCES ){ diff --git a/src/btree.c b/src/btree.c index c3055836c9..2bf026b840 100644 --- a/src/btree.c +++ b/src/btree.c @@ -446,16 +446,11 @@ static int cursorHoldsMutex(BtCursor *p){ } #endif - -#ifndef SQLITE_OMIT_INCRBLOB /* -** Invalidate the overflow page-list cache for cursor pCur, if any. +** Invalidate the overflow cache of the cursor passed as the first argument. +** on the shared btree structure pBt. */ -static void invalidateOverflowCache(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - sqlite3_free(pCur->aOverflow); - pCur->aOverflow = 0; -} +#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl) /* ** Invalidate the overflow page-list cache for all cursors opened @@ -469,6 +464,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){ } } +#ifndef SQLITE_OMIT_INCRBLOB /* ** This function is called before modifying the contents of a table ** to invalidate any incrblob cursors that are open on the @@ -491,16 +487,14 @@ static void invalidateIncrblobCursors( BtShared *pBt = pBtree->pBt; assert( sqlite3BtreeHoldsMutex(pBtree) ); for(p=pBt->pCursor; p; p=p->pNext){ - if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ + if( (p->curFlags & BTCF_Incrblob)!=0 && (isClearTable || p->info.nKey==iRow) ){ p->eState = CURSOR_INVALID; } } } #else - /* Stub functions when INCRBLOB is omitted */ - #define invalidateOverflowCache(x) - #define invalidateAllOverflowCache(x) + /* Stub function when INCRBLOB is omitted */ #define invalidateIncrblobCursors(x,y,z) #endif /* SQLITE_OMIT_INCRBLOB */ @@ -2563,7 +2557,8 @@ static int countValidCursors(BtShared *pBt, int wrOnly){ BtCursor *pCur; int r = 0; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( (wrOnly==0 || pCur->wrFlag) && pCur->eState!=CURSOR_FAULT ) r++; + if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0) + && pCur->eState!=CURSOR_FAULT ) r++; } return r; } @@ -3638,7 +3633,8 @@ static int btreeCursor( pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; - pCur->wrFlag = (u8)wrFlag; + assert( wrFlag==0 || wrFlag==BTCF_WriteFlag ); + pCur->curFlags = wrFlag; pCur->pNext = pBt->pCursor; if( pCur->pNext ){ pCur->pNext->pPrev = pCur; @@ -3708,7 +3704,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ releasePage(pCur->apPage[i]); } unlockBtreeIfUnused(pBt); - invalidateOverflowCache(pCur); + sqlite3DbFree(pBtree->db, pCur->aOverflow); /* sqlite3_free(pCur); */ sqlite3BtreeLeave(pBtree); } @@ -3747,7 +3743,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ if( pCur->info.nSize==0 ){ int iPage = pCur->iPage; btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); - pCur->validNKey = 1; + pCur->curFlags |= BTCF_ValidNKey; }else{ assertCellInfo(pCur); } @@ -3757,8 +3753,8 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){ #define getCellInfo(pCur) \ if( pCur->info.nSize==0 ){ \ int iPage = pCur->iPage; \ - btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ - pCur->validNKey = 1; \ + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ + pCur->curFlags |= BTCF_ValidNKey; \ }else{ \ assertCellInfo(pCur); \ } @@ -3929,10 +3925,12 @@ static int copyPayload( /* ** This function is used to read or overwrite payload information -** for the entry that the pCur cursor is pointing to. If the eOp -** parameter is 0, this is a read operation (data copied into -** buffer pBuf). If it is non-zero, a write (data copied from -** buffer pBuf). +** for the entry that the pCur cursor is pointing to. The eOp +** argument is interpreted as follows: +** +** 0: The operation is a read. Populate the overflow cache. +** 1: The operation is a write. Populate the overflow cache. +** 2: The operation is a read. Do not populate the overflow cache. ** ** A total of "amt" bytes are read or written beginning at "offset". ** Data is read to or from the buffer pBuf. @@ -3940,11 +3938,11 @@ static int copyPayload( ** The content being read or written might appear on the main page ** or be scattered out on multiple overflow pages. ** -** If the BtCursor.isIncrblobHandle flag is set, and the current -** cursor entry uses one or more overflow pages, this function -** allocates space for and lazily popluates the overflow page-list -** cache array (BtCursor.aOverflow). Subsequent calls use this -** cache to make seeking to the supplied offset more efficient. +** If the current cursor entry uses one or more overflow pages and the +** eOp argument is not 2, this function may allocate space for and lazily +** popluates the overflow page-list cache array (BtCursor.aOverflow). +** Subsequent calls use this cache to make seeking to the supplied offset +** more efficient. ** ** Once an overflow page-list cache has been allocated, it may be ** invalidated if some other cursor writes to the same table, or if @@ -3968,15 +3966,22 @@ static int accessPayload( int iIdx = 0; MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */ BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */ +#ifdef SQLITE_DIRECT_OVERFLOW_READ + int bEnd; /* True if reading to end of data */ +#endif assert( pPage ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->aiIdx[pCur->iPage]nCell ); assert( cursorHoldsMutex(pCur) ); + assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */ getCellInfo(pCur); aPayload = pCur->info.pCell + pCur->info.nHeader; nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); +#ifdef SQLITE_DIRECT_OVERFLOW_READ + bEnd = (offset+amt==nKey+pCur->info.nData); +#endif if( NEVER(offset+amt > nKey+pCur->info.nData) || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] @@ -3991,7 +3996,7 @@ static int accessPayload( if( a+offset>pCur->info.nLocal ){ a = pCur->info.nLocal - offset; } - rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage); + rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage); offset = 0; pBuf += a; amt -= a; @@ -4005,21 +4010,30 @@ static int accessPayload( nextPage = get4byte(&aPayload[pCur->info.nLocal]); -#ifndef SQLITE_OMIT_INCRBLOB - /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] - ** has not been allocated, allocate it now. The array is sized at - ** one entry for each overflow page in the overflow chain. The - ** page number of the first overflow page is stored in aOverflow[0], - ** etc. A value of 0 in the aOverflow[] array means "not yet known" - ** (the cache is lazily populated). + /* If the BtCursor.aOverflow[] has not been allocated, allocate it now. + ** Except, do not allocate aOverflow[] for eOp==2. + ** + ** The aOverflow[] array is sized at one entry for each overflow page + ** in the overflow chain. The page number of the first overflow page is + ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array + ** means "not yet known" (the cache is lazily populated). */ - if( pCur->isIncrblobHandle && !pCur->aOverflow ){ + if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; - pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl); - /* nOvfl is always positive. If it were zero, fetchPayload would have - ** been used instead of this routine. */ - if( ALWAYS(nOvfl) && !pCur->aOverflow ){ - rc = SQLITE_NOMEM; + if( nOvfl>pCur->nOvflAlloc ){ + Pgno *aNew = (Pgno*)sqlite3DbRealloc( + pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno) + ); + if( aNew==0 ){ + rc = SQLITE_NOMEM; + }else{ + pCur->nOvflAlloc = nOvfl*2; + pCur->aOverflow = aNew; + } + } + if( rc==SQLITE_OK ){ + memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); + pCur->curFlags |= BTCF_ValidOvfl; } } @@ -4027,22 +4041,19 @@ static int accessPayload( ** entry for the first required overflow page is valid, skip ** directly to it. */ - if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){ + if( (pCur->curFlags & BTCF_ValidOvfl)!=0 && pCur->aOverflow[offset/ovflSize] ){ iIdx = (offset/ovflSize); nextPage = pCur->aOverflow[iIdx]; offset = (offset%ovflSize); } -#endif for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){ -#ifndef SQLITE_OMIT_INCRBLOB /* If required, populate the overflow page-list cache. */ - if( pCur->aOverflow ){ + if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){ assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage); pCur->aOverflow[iIdx] = nextPage; } -#endif if( offset>=ovflSize ){ /* The only reason to read this page is to obtain the page @@ -4050,13 +4061,17 @@ static int accessPayload( ** data is not required. So first try to lookup the overflow ** page-list cache, if any, then fall back to the getOverflowPage() ** function. + ** + ** Note that the aOverflow[] array must be allocated because eOp!=2 + ** here. If eOp==2, then offset==0 and this branch is never taken. */ -#ifndef SQLITE_OMIT_INCRBLOB - if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){ + assert( eOp!=2 ); + assert( pCur->curFlags & BTCF_ValidOvfl ); + if( pCur->aOverflow[iIdx+1] ){ nextPage = pCur->aOverflow[iIdx+1]; - } else -#endif + }else{ rc = getOverflowPage(pBt, nextPage, 0, &nextPage); + } offset -= ovflSize; }else{ /* Need to read this page properly. It contains some of the @@ -4078,13 +4093,15 @@ static int accessPayload( ** 3) the database is file-backed, and ** 4) there is no open write-transaction, and ** 5) the database is not a WAL database, + ** 6) all data from the page is being read. ** ** then data can be read directly from the database file into the ** output buffer, bypassing the page-cache altogether. This speeds ** up loading large records that span many overflow pages. */ - if( eOp==0 /* (1) */ + if( (eOp&0x01)==0 /* (1) */ && offset==0 /* (2) */ + && (bEnd || a==ovflSize) /* (6) */ && pBt->inTransaction==TRANS_READ /* (4) */ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */ && pBt->pPage1->aData[19]==0x01 /* (5) */ @@ -4101,12 +4118,12 @@ static int accessPayload( { DbPage *pDbPage; rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage, - (eOp==0 ? PAGER_GET_READONLY : 0) + ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0) ); if( rc==SQLITE_OK ){ aPayload = sqlite3PagerGetData(pDbPage); nextPage = get4byte(aPayload); - rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); + rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage); sqlite3PagerUnref(pDbPage); offset = 0; } @@ -4257,14 +4274,14 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ return SQLITE_CORRUPT_BKPT; } rc = getAndInitPage(pBt, newPgno, &pNewPage, - pCur->wrFlag==0 ? PAGER_GET_READONLY : 0); + (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0); if( rc ) return rc; pCur->apPage[i+1] = pNewPage; pCur->aiIdx[i+1] = 0; pCur->iPage++; pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ return SQLITE_CORRUPT_BKPT; } @@ -4322,7 +4339,7 @@ static void moveToParent(BtCursor *pCur){ releasePage(pCur->apPage[pCur->iPage]); pCur->iPage--; pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); } /* @@ -4369,7 +4386,7 @@ static int moveToRoot(BtCursor *pCur){ return SQLITE_OK; }else{ rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0], - pCur->wrFlag==0 ? PAGER_GET_READONLY : 0); + (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; @@ -4396,8 +4413,7 @@ static int moveToRoot(BtCursor *pCur){ pCur->aiIdx[0] = 0; pCur->info.nSize = 0; - pCur->atLast = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl); if( pRoot->nCell>0 ){ pCur->eState = CURSOR_VALID; @@ -4460,7 +4476,7 @@ static int moveToRightmost(BtCursor *pCur){ if( rc==SQLITE_OK ){ pCur->aiIdx[pCur->iPage] = pPage->nCell-1; pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~BTCF_ValidNKey; } return rc; } @@ -4499,7 +4515,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); /* If the cursor already points to the last entry, this is a no-op. */ - if( CURSOR_VALID==pCur->eState && pCur->atLast ){ + if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ #ifdef SQLITE_DEBUG /* This block serves to assert() that the cursor really does point ** to the last entry in the b-tree. */ @@ -4522,7 +4538,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ assert( pCur->eState==CURSOR_VALID ); *pRes = 0; rc = moveToRightmost(pCur); - pCur->atLast = rc==SQLITE_OK ?1:0; + if( rc==SQLITE_OK ){ + pCur->curFlags |= BTCF_AtLast; + }else{ + pCur->curFlags &= ~BTCF_AtLast; + } + } } return rc; @@ -4573,14 +4594,14 @@ int sqlite3BtreeMovetoUnpacked( /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ - if( pCur->eState==CURSOR_VALID && pCur->validNKey + if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 && pCur->apPage[0]->intKey ){ if( pCur->info.nKey==intKey ){ *pRes = 0; return SQLITE_OK; } - if( pCur->atLast && pCur->info.nKeycurFlags & BTCF_AtLast)!=0 && pCur->info.nKeyupr ){ c = +1; break; } }else{ assert( nCellKey==intKey ); - pCur->validNKey = 1; + pCur->curFlags |= BTCF_ValidNKey; pCur->info.nKey = nCellKey; pCur->aiIdx[pCur->iPage] = (u16)idx; if( !pPage->leaf ){ @@ -4704,7 +4725,7 @@ int sqlite3BtreeMovetoUnpacked( goto moveto_finish; } pCur->aiIdx[pCur->iPage] = (u16)idx; - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); + rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2); if( rc ){ sqlite3_free(pCellKey); goto moveto_finish; @@ -4751,7 +4772,7 @@ moveto_next_layer: } moveto_finish: pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); return rc; } @@ -4796,6 +4817,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ + invalidateOverflowCache(pCur); rc = restoreCursorPosition(pCur); if( rc!=SQLITE_OK ){ *pRes = 0; @@ -4829,7 +4851,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ testcase( idx>pPage->nCell ); pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( idx>=pPage->nCell ){ if( !pPage->leaf ){ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8])); @@ -4890,7 +4912,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ assert( pRes!=0 ); assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); - pCur->atLast = 0; + pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl); if( pCur->eState!=CURSOR_VALID ){ if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){ rc = btreeRestoreCursorPosition(pCur); @@ -4935,7 +4957,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ moveToParent(pCur); } pCur->info.nSize = 0; - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); pCur->aiIdx[pCur->iPage]--; pPage = pCur->apPage[pCur->iPage]; @@ -6960,7 +6982,7 @@ int sqlite3BtreeInsert( } assert( cursorHoldsMutex(pCur) ); - assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE + assert( (pCur->curFlags & BTCF_WriteFlag)!=0 && pBt->inTransaction==TRANS_WRITE && (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); @@ -6993,7 +7015,7 @@ int sqlite3BtreeInsert( /* If the cursor is currently on the last row and we are appending a ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto() ** call */ - if( pCur->validNKey && nKey>0 && pCur->info.nKey==nKey-1 ){ + if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 && pCur->info.nKey==nKey-1 ){ loc = -1; } } @@ -7046,7 +7068,7 @@ int sqlite3BtreeInsert( /* If no error has occurred and pPage has an overflow cell, call balance() ** to redistribute the cells within the tree. Since balance() may move - ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey + ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey ** variables. ** ** Previous versions of SQLite called moveToRoot() to move the cursor @@ -7066,7 +7088,7 @@ int sqlite3BtreeInsert( */ pCur->info.nSize = 0; if( rc==SQLITE_OK && pPage->nOverflow ){ - pCur->validNKey = 0; + pCur->curFlags &= ~(BTCF_ValidNKey); rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() @@ -7098,7 +7120,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); - assert( pCur->wrFlag ); + assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); @@ -8411,7 +8433,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ int rc; assert( cursorHoldsMutex(pCsr) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); - assert( pCsr->isIncrblobHandle ); + assert( pCsr->curFlags & BTCF_Incrblob ); rc = restoreCursorPosition(pCsr); if( rc!=SQLITE_OK ){ @@ -8440,7 +8462,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ ** (d) there are no conflicting read-locks, and ** (e) the cursor points at a valid row of an intKey table. */ - if( !pCsr->wrFlag ){ + if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){ return SQLITE_READONLY; } assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0 @@ -8453,20 +8475,10 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ } /* -** Set a flag on this cursor to cache the locations of pages from the -** overflow list for the current row. This is used by cursors opened -** for incremental blob IO only. -** -** This function sets a flag only. The actual page location cache -** (stored in BtCursor.aOverflow[]) is allocated and used by function -** accessPayload() (the worker function for sqlite3BtreeData() and -** sqlite3BtreePutData()). +** Mark this cursor as an incremental blob cursor. */ -void sqlite3BtreeCacheOverflow(BtCursor *pCur){ - assert( cursorHoldsMutex(pCur) ); - assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - invalidateOverflowCache(pCur); - pCur->isIncrblobHandle = 1; +void sqlite3BtreeIncrblobCursor(BtCursor *pCur){ + pCur->curFlags |= BTCF_Incrblob; } #endif diff --git a/src/btree.h b/src/btree.h index a62c3c60d4..e452a23f7a 100644 --- a/src/btree.h +++ b/src/btree.h @@ -190,7 +190,7 @@ char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); struct Pager *sqlite3BtreePager(Btree*); int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); -void sqlite3BtreeCacheOverflow(BtCursor *); +void sqlite3BtreeIncrblobCursor(BtCursor *); void sqlite3BtreeClearCursor(BtCursor *); int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); diff --git a/src/btreeInt.h b/src/btreeInt.h index 87b4181f82..d1cdd46983 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -496,27 +496,30 @@ struct BtCursor { BtShared *pBt; /* The BtShared this cursor points to */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ -#ifndef SQLITE_OMIT_INCRBLOB Pgno *aOverflow; /* Cache of overflow page locations */ -#endif - Pgno pgnoRoot; /* The root page of this tree */ CellInfo info; /* A parse of the cell we are pointing at */ - i64 nKey; /* Size of pKey, or last integer key */ - void *pKey; /* Saved key that was cursor's last known position */ + i64 nKey; /* Size of pKey, or last integer key */ + void *pKey; /* Saved key that was cursor last known position */ + Pgno pgnoRoot; /* The root page of this tree */ + int nOvflAlloc; /* Allocated size of aOverflow[] array */ int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ - u8 wrFlag; /* True if writable */ - u8 atLast; /* Cursor pointing to the last entry */ - u8 validNKey; /* True if info.nKey is valid */ + u8 curFlags; /* zero or more BTCF_* flags defined below */ u8 eState; /* One of the CURSOR_XXX constants (see below) */ -#ifndef SQLITE_OMIT_INCRBLOB - u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ -#endif u8 hints; /* As configured by CursorSetHints() */ i16 iPage; /* Index of current page in apPage */ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ }; +/* +** Legal values for BtCursor.curFlags +*/ +#define BTCF_WriteFlag 0x01 /* True if a write cursor */ +#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ +#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ +#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */ +#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ + /* ** Potential values for BtCursor.eState. ** diff --git a/src/build.c b/src/build.c index 10077e5018..4d4155ba90 100644 --- a/src/build.c +++ b/src/build.c @@ -2680,7 +2680,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); - sqlite3VdbeResolveLabel(v, iPartIdxLabel); + sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); diff --git a/src/delete.c b/src/delete.c index 79e83cae52..c74d8eab7d 100644 --- a/src/delete.c +++ b/src/delete.c @@ -739,7 +739,7 @@ void sqlite3GenerateRowIndexDelete( &iPartIdxLabel, pPrior, r1); sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); - sqlite3VdbeResolveLabel(v, iPartIdxLabel); + sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); pPrior = pIdx; } } @@ -758,10 +758,11 @@ void sqlite3GenerateRowIndexDelete( ** ** If *piPartIdxLabel is not NULL, fill it in with a label and jump ** to that label if pIdx is a partial index that should be skipped. +** The label should be resolved using sqlite3ResolvePartIdxLabel(). ** A partial index should be skipped if its WHERE clause evaluates ** to false or null. If pIdx is not a partial index, *piPartIdxLabel ** will be set to zero which is an empty label that is ignored by -** sqlite3VdbeResolveLabel(). +** sqlite3ResolvePartIdxLabel(). ** ** The pPrior and regPrior parameters are used to implement a cache to ** avoid unnecessary register loads. If pPrior is not NULL, then it is @@ -794,6 +795,7 @@ int sqlite3GenerateIndexKey( if( pIdx->pPartIdxWhere ){ *piPartIdxLabel = sqlite3VdbeMakeLabel(v); pParse->iPartIdxTab = iDataCur; + sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, SQLITE_JUMPIFNULL); }else{ @@ -821,3 +823,15 @@ int sqlite3GenerateIndexKey( sqlite3ReleaseTempRange(pParse, regBase, nCol); return regBase; } + +/* +** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label +** because it was a partial index, then this routine should be called to +** resolve that label. +*/ +void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){ + if( iLabel ){ + sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel); + sqlite3ExprCachePop(pParse); + } +} diff --git a/src/expr.c b/src/expr.c index b070621ebd..08c121e386 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1883,7 +1883,7 @@ int sqlite3CodeSubselect( if( testAddr>=0 ){ sqlite3VdbeJumpHere(v, testAddr); } - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); return rReg; } @@ -2018,7 +2018,7 @@ static void sqlite3ExprCodeIN( } } sqlite3ReleaseTempReg(pParse, r1); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); VdbeComment((v, "end IN expr")); } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -2201,15 +2201,14 @@ void sqlite3ExprCachePush(Parse *pParse){ /* ** Remove from the column cache any entries that were added since the -** the previous N Push operations. In other words, restore the cache -** to the state it was in N Pushes ago. +** the previous sqlite3ExprCachePush operation. In other words, restore +** the cache to the state it was in prior the most recent Push. */ -void sqlite3ExprCachePop(Parse *pParse, int N){ +void sqlite3ExprCachePop(Parse *pParse){ int i; struct yColCache *p; - assert( N>0 ); - assert( pParse->iCacheLevel>=N ); - pParse->iCacheLevel -= N; + assert( pParse->iCacheLevel>=1 ); + pParse->iCacheLevel--; #ifdef SQLITE_DEBUG if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ printf("POP to %d\n", pParse->iCacheLevel); @@ -2687,7 +2686,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3ExprCacheRemove(pParse, target, 1); sqlite3ExprCachePush(pParse); sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); } sqlite3VdbeResolveLabel(v, endCoalesce); break; @@ -2741,7 +2740,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCodeExprList(pParse, pFarg, r1, SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR); - sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ + sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */ }else{ r1 = 0; } @@ -2961,13 +2960,13 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target); sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); sqlite3VdbeResolveLabel(v, nextCase); } if( (nExpr&1)!=0 ){ sqlite3ExprCachePush(pParse); sqlite3ExprCode(pParse, pEList->a[nExpr-1].pExpr, target); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, target); } @@ -3546,7 +3545,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); break; } case TK_OR: { @@ -3554,7 +3553,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); sqlite3ExprCachePush(pParse); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); break; } case TK_NOT: { @@ -3700,7 +3699,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); break; } case TK_OR: { @@ -3710,7 +3709,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3ExprCachePush(pParse); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); sqlite3VdbeResolveLabel(v, d2); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); break; } case TK_NOT: { diff --git a/src/main.c b/src/main.c index 9e83d4963a..65521f4466 100644 --- a/src/main.c +++ b/src/main.c @@ -3205,6 +3205,22 @@ int sqlite3_test_control(int op, ...){ break; } + /* + ** sqlite3_test_control(SQLITE_TESTCTRL_BYTEORDER); + ** + ** The integer returned reveals the byte-order of the computer on which + ** SQLite is running: + ** + ** 1 big-endian, determined at run-time + ** 10 little-endian, determined at run-time + ** 432101 big-endian, determined at compile-time + ** 123410 little-endian, determined at compile-time + */ + case SQLITE_TESTCTRL_BYTEORDER: { + rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN; + break; + } + /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) ** ** Set the nReserve size to N for the main database on the database diff --git a/src/mem5.c b/src/mem5.c index 5d75611a32..67615bb964 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -248,7 +248,7 @@ static void *memsys5MallocUnsafe(int nByte){ ** block. If not, then split a block of the next larger power of ** two in order to create a new free block of size iLogsize. */ - for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){} + for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){} if( iBin>LOGMAX ){ testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte); diff --git a/src/pager.c b/src/pager.c index c6485a4d45..b09d6cb930 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1624,12 +1624,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ if( !zMaster || pPager->journalMode==PAGER_JOURNALMODE_MEMORY - || pPager->journalMode==PAGER_JOURNALMODE_OFF + || !isOpen(pPager->jfd) ){ return SQLITE_OK; } pPager->setMaster = 1; - assert( isOpen(pPager->jfd) ); assert( pPager->journalHdr <= pPager->journalOff ); /* Calculate the length in bytes and the checksum of zMaster */ diff --git a/src/pragma.c b/src/pragma.c index 20da9a6892..66d0c3328e 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1928,7 +1928,7 @@ void sqlite3Pragma( sqlite3VdbeAddOp0(v, OP_Halt); sqlite3VdbeJumpHere(v, jmp4); sqlite3VdbeJumpHere(v, jmp2); - sqlite3VdbeResolveLabel(v, jmp3); + sqlite3ResolvePartIdxLabel(pParse, jmp3); } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); diff --git a/src/rowset.c b/src/rowset.c index 5761f98550..ba2e056bd4 100644 --- a/src/rowset.c +++ b/src/rowset.c @@ -112,8 +112,8 @@ struct RowSet { struct RowSetEntry *pFresh; /* Source of new entry objects */ struct RowSetEntry *pForest; /* List of binary trees of entries */ u16 nFresh; /* Number of objects on pFresh */ - u8 rsFlags; /* Various flags */ - u8 iBatch; /* Current insert batch */ + u16 rsFlags; /* Various flags */ + int iBatch; /* Current insert batch */ }; /* @@ -447,7 +447,7 @@ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ ** on pRowSet->pEntry, then sort those entires into the forest at ** pRowSet->pForest so that they can be tested. */ -int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){ +int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){ struct RowSetEntry *p, *pTree; /* This routine is never called after sqlite3RowSetNext() */ diff --git a/src/select.c b/src/select.c index 7480eeb94b..d71ac94121 100644 --- a/src/select.c +++ b/src/select.c @@ -4751,18 +4751,6 @@ int sqlite3Select( } #endif - /* If there is both a GROUP BY and an ORDER BY clause and they are - ** identical, then disable the ORDER BY clause since the GROUP BY - ** will cause elements to come out in the correct order. This is - ** an optimization - the correct answer should result regardless. - ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER - ** to disable this optimization for testing purposes. - */ - if( sqlite3ExprListCompare(p->pGroupBy, sSort.pOrderBy, -1)==0 - && OptimizationEnabled(db, SQLITE_GroupByOrder) ){ - sSort.pOrderBy = 0; - } - /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query ** can be rewritten as a GROUP BY. In other words, this: @@ -4892,6 +4880,7 @@ int sqlite3Select( int addrEnd; /* End of processing for this SELECT */ int sortPTab = 0; /* Pseudotable used to decode sorting results */ int sortOut = 0; /* Output register from the sorter */ + int orderByGrp = 0; /* True if the GROUP BY and ORDER BY are the same */ /* Remove any and all aliases between the result set and the ** GROUP BY clause. @@ -4911,6 +4900,18 @@ int sqlite3Select( p->nSelectRow = 1; } + + /* If there is both a GROUP BY and an ORDER BY clause and they are + ** identical, then it may be possible to disable the ORDER BY clause + ** on the grounds that the GROUP BY will cause elements to come out + ** in the correct order. It also may not - the GROUP BY may use a + ** database index that causes rows to be grouped together as required + ** but not actually sorted. Either way, record the fact that the + ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp + ** variable. */ + if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ + orderByGrp = 1; + } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); @@ -4991,7 +4992,8 @@ int sqlite3Select( */ sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, - WHERE_GROUPBY, 0); + WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0 + ); if( pWInfo==0 ) goto select_end; if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){ /* The optimizer is able to deliver rows in group by order so @@ -5055,6 +5057,21 @@ int sqlite3Select( VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); sAggInfo.useSortingIdx = 1; sqlite3ExprCacheClear(pParse); + + } + + /* If the index or temporary table used by the GROUP BY sort + ** will naturally deliver rows in the order required by the ORDER BY + ** clause, cancel the ephemeral table open coded earlier. + ** + ** This is an optimization - the correct answer should result regardless. + ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to + ** disable this optimization for testing purposes. */ + if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder) + && (groupBySort || sqlite3WhereIsSorted(pWInfo)) + ){ + sSort.pOrderBy = 0; + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex); } /* Evaluate the current GROUP BY terms and store in b0, b1, b2... diff --git a/src/shell.c b/src/shell.c index 1313112709..61965d2d39 100644 --- a/src/shell.c +++ b/src/shell.c @@ -2130,10 +2130,12 @@ static void tryToClone(struct callback_data *p, const char *zNewDb){ fprintf(stderr, "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ + sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); tryToCloneSchema(p, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); + sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } sqlite3_close(newDb); } @@ -3025,6 +3027,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, + { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, }; int testctrl = -1; int rc = 0; @@ -3065,9 +3068,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){ break; /* sqlite3_test_control(int) */ - case SQLITE_TESTCTRL_PRNG_SAVE: - case SQLITE_TESTCTRL_PRNG_RESTORE: + case SQLITE_TESTCTRL_PRNG_SAVE: + case SQLITE_TESTCTRL_PRNG_RESTORE: case SQLITE_TESTCTRL_PRNG_RESET: + case SQLITE_TESTCTRL_BYTEORDER: if( nArg==2 ){ rc = sqlite3_test_control(testctrl); fprintf(p->out, "%d (0x%08x)\n", rc, rc); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 5d2c87552c..e8dddc468c 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -6118,7 +6118,8 @@ int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 -#define SQLITE_TESTCTRL_LAST 21 +#define SQLITE_TESTCTRL_BYTEORDER 22 +#define SQLITE_TESTCTRL_LAST 22 /* ** CAPI3REF: SQLite Runtime Status diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0f67748fc8..8e81b7a970 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -546,22 +546,39 @@ typedef INT16_TYPE LogEst; /* ** Macros to determine whether the machine is big or little endian, -** evaluated at runtime. +** and whether or not that determination is run-time or compile-time. +** +** For best performance, an attempt is made to guess at the byte-order +** using C-preprocessor macros. If that is unsuccessful, or if +** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined +** at run-time. */ #ifdef SQLITE_AMALGAMATION const int sqlite3one = 1; #else extern const int sqlite3one; #endif -#if defined(i386) || defined(__i386__) || defined(_M_IX86)\ - || defined(__x86_64) || defined(__x86_64__) +#if (defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ + defined(__arm__)) && !defined(SQLITE_RUNTIME_BYTEORDER) +# define SQLITE_BYTEORDER 1234 # define SQLITE_BIGENDIAN 0 # define SQLITE_LITTLEENDIAN 1 # define SQLITE_UTF16NATIVE SQLITE_UTF16LE -#else +#endif +#if (defined(sparc) || defined(__ppc__)) \ + && !defined(SQLITE_RUNTIME_BYTEORDER) +# define SQLITE_BYTEORDER 4321 +# define SQLITE_BIGENDIAN 1 +# define SQLITE_LITTLEENDIAN 0 +# define SQLITE_UTF16NATIVE SQLITE_UTF16BE +#endif +#if !defined(SQLITE_BYTEORDER) +# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */ # define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) # define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) -# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) +# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) #endif /* @@ -2108,6 +2125,7 @@ struct SrcList { #define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */ +#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */ /* Allowed return values from sqlite3WhereIsDistinct() */ @@ -3019,7 +3037,7 @@ int sqlite3BitvecBuiltinTest(int,int*); RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int); void sqlite3RowSetClear(RowSet*); void sqlite3RowSetInsert(RowSet*, i64); -int sqlite3RowSetTest(RowSet*, u8 iBatch, i64); +int sqlite3RowSetTest(RowSet*, int iBatch, i64); int sqlite3RowSetNext(RowSet*, i64*); void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int); @@ -3075,6 +3093,7 @@ void sqlite3WhereEnd(WhereInfo*); u64 sqlite3WhereOutputRowCount(WhereInfo*); int sqlite3WhereIsDistinct(WhereInfo*); int sqlite3WhereIsOrdered(WhereInfo*); +int sqlite3WhereIsSorted(WhereInfo*); int sqlite3WhereContinueLabel(WhereInfo*); int sqlite3WhereBreakLabel(WhereInfo*); int sqlite3WhereOkOnePass(WhereInfo*, int*); @@ -3083,7 +3102,7 @@ void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); -void sqlite3ExprCachePop(Parse*, int); +void sqlite3ExprCachePop(Parse*); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); @@ -3135,6 +3154,7 @@ int sqlite3IsRowid(const char*); void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); +void sqlite3ResolvePartIdxLabel(Parse*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, u8,u8,int,int*); void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int); diff --git a/src/test_btree.c b/src/test_btree.c index db72889b2a..dfe77051b6 100644 --- a/src/test_btree.c +++ b/src/test_btree.c @@ -51,7 +51,7 @@ void sqlite3BtreeCursorList(Btree *p){ BtShared *pBt = p->pBt; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ MemPage *pPage = pCur->apPage[pCur->iPage]; - char *zMode = pCur->wrFlag ? "rw" : "ro"; + char *zMode = (pCur->curFlags & BTCF_WriteFlag) ? "rw" : "ro"; sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n", pCur, pCur->pgnoRoot, zMode, pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage], diff --git a/src/vdbe.c b/src/vdbe.c index de02d1f638..b86cc95a98 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5245,9 +5245,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ assert( pOp->p4type==P4_INT32 ); assert( iSet==-1 || iSet>=0 ); if( iSet ){ - exists = sqlite3RowSetTest(pIn1->u.pRowSet, - (u8)(iSet>=0 ? iSet & 0xf : 0xff), - pIn3->u.i); + exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i); VdbeBranchTaken(exists!=0,2); if( exists ){ pc = pOp->p2 - 1; @@ -5947,7 +5945,7 @@ case OP_VOpen: { #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * -** Synopsis: iPlan=r[P3] zPlan='P4' +** Synopsis: iplan=r[P3] zplan='P4' ** ** P1 is a cursor opened using VOpen. P2 is an address to jump to if ** the filtered result set is empty. diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 0ce21378d5..0a6b536720 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -276,7 +276,7 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){ int j = -1-x; assert( v->magic==VDBE_MAGIC_INIT ); assert( jnLabel ); - if( j>=0 && p->aLabel ){ + if( ALWAYS(j>=0) && p->aLabel ){ p->aLabel[j] = v->nOp; } p->iFixedOp = v->nOp - 1; diff --git a/src/vdbeblob.c b/src/vdbeblob.c index cf7495e5b4..083f3f413c 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -77,9 +77,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ p->iOffset = pC->aType[p->iCol + pC->nField]; p->nByte = sqlite3VdbeSerialTypeLen(type); p->pCsr = pC->pCursor; - sqlite3BtreeEnterCursor(p->pCsr); - sqlite3BtreeCacheOverflow(p->pCsr); - sqlite3BtreeLeaveCursor(p->pCsr); + sqlite3BtreeIncrblobCursor(p->pCsr); } } diff --git a/src/where.c b/src/where.c index 93ee8c59c3..9bde27e52f 100644 --- a/src/where.c +++ b/src/where.c @@ -2841,7 +2841,7 @@ static Bitmask codeOneLoopStart( pLevel->p1 = iCur; pLevel->p2 = sqlite3VdbeCurrentAddr(v); sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); - sqlite3ExprCachePop(pParse, 1); + sqlite3ExprCachePop(pParse); }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -3711,6 +3711,137 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ } } +/* +** Return TRUE if both of the following are true: +** +** (1) X has the same or lower cost that Y +** (2) X is a proper subset of Y +** +** By "proper subset" we mean that X uses fewer WHERE clause terms +** than Y and that every WHERE clause term used by X is also used +** by Y. +** +** If X is a proper subset of Y then Y is a better choice and ought +** to have a lower cost. This routine returns TRUE when that cost +** relationship is inverted and needs to be adjusted. +*/ +static int whereLoopCheaperProperSubset( + const WhereLoop *pX, /* First WhereLoop to compare */ + const WhereLoop *pY /* Compare against this WhereLoop */ +){ + int i, j; + if( pX->nLTerm >= pY->nLTerm ) return 0; /* X is not a subset of Y */ + if( pX->rRun >= pY->rRun ){ + if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */ + if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */ + } + for(j=0, i=pX->nLTerm-1; i>=0; i--){ + for(j=pY->nLTerm-1; j>=0; j--){ + if( pY->aLTerm[j]==pX->aLTerm[i] ) break; + } + if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ + } + return 1; /* All conditions meet */ +} + +/* +** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so +** that: +** +** (1) pTemplate costs less than any other WhereLoops that are a proper +** subset of pTemplate +** +** (2) pTemplate costs more than any other WhereLoops for which pTemplate +** is a proper subset. +** +** To say "WhereLoop X is a proper subset of Y" means that X uses fewer +** WHERE clause terms than Y and that every WHERE clause term used by X is +** also used by Y. +*/ +static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ + if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return; + for(; p; p=p->pNextLoop){ + if( p->iTab!=pTemplate->iTab ) continue; + if( (p->wsFlags & WHERE_INDEXED)==0 ) continue; + if( whereLoopCheaperProperSubset(p, pTemplate) ){ + /* Adjust pTemplate cost downward so that it is cheaper than its + ** subset p */ + pTemplate->rRun = p->rRun; + pTemplate->nOut = p->nOut - 1; + }else if( whereLoopCheaperProperSubset(pTemplate, p) ){ + /* Adjust pTemplate cost upward so that it is costlier than p since + ** pTemplate is a proper subset of p */ + pTemplate->rRun = p->rRun; + pTemplate->nOut = p->nOut + 1; + } + } +} + +/* +** Search the list of WhereLoops in *ppPrev looking for one that can be +** supplanted by pTemplate. +** +** Return NULL if the WhereLoop list contains an entry that can supplant +** pTemplate, in other words if pTemplate does not belong on the list. +** +** If pX is a WhereLoop that pTemplate can supplant, then return the +** link that points to pX. +** +** If pTemplate cannot supplant any existing element of the list but needs +** to be added to the list, then return a pointer to the tail of the list. +*/ +static WhereLoop **whereLoopFindLesser( + WhereLoop **ppPrev, + const WhereLoop *pTemplate +){ + WhereLoop *p; + for(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 existing WhereLoop p is better than pTemplate, pTemplate can be + ** discarded. WhereLoop p is better if: + ** (1) p has no more dependencies than pTemplate, and + ** (2) p has an equal or lower cost than pTemplate + */ + if( (p->prereq & pTemplate->prereq)==p->prereq /* (1) */ + && p->rSetup<=pTemplate->rSetup /* (2a) */ + && p->rRun<=pTemplate->rRun /* (2b) */ + && p->nOut<=pTemplate->nOut /* (2c) */ + ){ + return 0; /* Discard pTemplate */ + } + + /* If pTemplate is always better than p, then cause p to be overwritten + ** with pTemplate. pTemplate is better than p if: + ** (1) pTemplate has no more dependences than p, and + ** (2) pTemplate has an equal or lower cost than p. + */ + if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ + && p->rRun>=pTemplate->rRun /* (2a) */ + && p->nOut>=pTemplate->nOut /* (2b) */ + ){ + assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */ + break; /* Cause p to be overwritten by pTemplate */ + } + } + return ppPrev; +} + /* ** Insert or replace a WhereLoop entry using the template supplied. ** @@ -3720,25 +3851,23 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ ** fewer dependencies than the template. Otherwise a new WhereLoop is ** added based on the template. ** -** If pBuilder->pOrSet is not NULL then we only care about only the +** If pBuilder->pOrSet is not NULL then we care about only the ** prerequisites and rRun and nOut costs of the N best loops. That ** information is gathered in the pBuilder->pOrSet object. This special ** processing mode is used only for OR clause processing. ** ** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we ** still might overwrite similar loops with the new template if the -** template is better. Loops may be overwritten if the following +** new 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; + WhereLoop **ppPrev, *p; WhereInfo *pWInfo = pBuilder->pWInfo; sqlite3 *db = pWInfo->pParse->db; @@ -3761,64 +3890,23 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ return SQLITE_OK; } - /* Search for an existing WhereLoop to overwrite, or which takes - ** priority over pTemplate. + /* Look for an existing WhereLoop to replace with 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 ); + whereLoopAdjustCost(pWInfo->pLoops, pTemplate); + ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate); - /* 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 - && p->nOut<=pTemplate->nOut - ){ - /* This branch taken when p is equal or better than pTemplate in - ** all of (1) dependencies (2) setup-cost, (3) run-cost, and - ** (4) number of output rows. */ - assert( p->rSetup==pTemplate->rSetup ); - if( p->prereq==pTemplate->prereq - && p->nLTermnLTerm - && (p->wsFlags & pTemplate->wsFlags & WHERE_INDEXED)!=0 - && (p->u.btree.pIndex==pTemplate->u.btree.pIndex - || pTemplate->rRun+p->nLTerm<=p->rRun+pTemplate->nLTerm) - ){ - /* 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 - && p->nOut>=pTemplate->nOut - ){ - /* Overwrite an existing WhereLoop with a better one: one that is - ** better at one of (1) dependencies, (2) setup-cost, (3) run-cost - ** or (4) number of output rows, and is no worse in any of those - ** categories. */ - assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */ - pNext = p->pNextLoop; - break; + if( ppPrev==0 ){ + /* There already exists a WhereLoop on the list that is better + ** than pTemplate, so just ignore pTemplate */ +#if WHERETRACE_ENABLED /* 0x8 */ + if( sqlite3WhereTrace & 0x8 ){ + sqlite3DebugPrintf("ins-noop: "); + whereLoopPrint(pTemplate, pBuilder->pWC); } +#endif + return SQLITE_OK; + }else{ + p = *ppPrev; } /* If we reach this point it means that either p[] should be overwritten @@ -3836,13 +3924,33 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ } #endif if( p==0 ){ - p = sqlite3DbMallocRaw(db, sizeof(WhereLoop)); + /* Allocate a new WhereLoop to add to the end of the list */ + *ppPrev = p = sqlite3DbMallocRaw(db, sizeof(WhereLoop)); if( p==0 ) return SQLITE_NOMEM; whereLoopInit(p); + p->pNextLoop = 0; + }else{ + /* We will be overwriting WhereLoop p[]. But before we do, first + ** go through the rest of the list and delete any other entries besides + ** p[] that are also supplated by pTemplate */ + WhereLoop **ppTail = &p->pNextLoop; + WhereLoop *pToDel; + while( *ppTail ){ + ppTail = whereLoopFindLesser(ppTail, pTemplate); + if( NEVER(ppTail==0) ) break; + pToDel = *ppTail; + if( pToDel==0 ) break; + *ppTail = pToDel->pNextLoop; +#if WHERETRACE_ENABLED /* 0x8 */ + if( sqlite3WhereTrace & 0x8 ){ + sqlite3DebugPrintf("ins-del: "); + whereLoopPrint(pToDel, pBuilder->pWC); + } +#endif + whereLoopDelete(db, pToDel); + } } 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 ){ @@ -3850,16 +3958,6 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ } } return SQLITE_OK; - - /* Jump here if the insert is a no-op */ -whereLoopInsert_noop: -#if WHERETRACE_ENABLED /* 0x8 */ - if( sqlite3WhereTrace & 0x8 ){ - sqlite3DebugPrintf("ins-noop: "); - whereLoopPrint(pTemplate, pBuilder->pWC); - } -#endif - return SQLITE_OK; } /* @@ -4698,7 +4796,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ ** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as ** strict. With GROUP BY and DISTINCT the only requirement is that ** equivalent rows appear immediately adjacent to one another. GROUP BY -** and DISTINT do not require rows to appear in any particular order as long +** and DISTINCT do not require rows to appear in any particular order as long ** as equivelent rows are grouped together. Thus for GROUP BY and DISTINCT ** the pOrderBy terms can be matched in any order. With ORDER BY, the ** pOrderBy terms must be matched in strict left-to-right order. @@ -4867,7 +4965,7 @@ static i8 wherePathSatisfiesOrderBy( } /* Find the ORDER BY term that corresponds to the j-th column - ** of the index and and mark that ORDER BY term off + ** of the index and mark that ORDER BY term off */ bOnce = 1; isMatch = 0; @@ -4947,6 +5045,36 @@ static i8 wherePathSatisfiesOrderBy( return -1; } + +/* +** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(), +** the planner assumes that the specified pOrderBy list is actually a GROUP +** BY clause - and so any order that groups rows as required satisfies the +** request. +** +** Normally, in this case it is not possible for the caller to determine +** whether or not the rows are really being delivered in sorted order, or +** just in some other order that provides the required grouping. However, +** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then +** this function may be called on the returned WhereInfo object. It returns +** true if the rows really will be sorted in the specified order, or false +** otherwise. +** +** For example, assuming: +** +** CREATE INDEX i1 ON t1(x, Y); +** +** then +** +** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1 +** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0 +*/ +int sqlite3WhereIsSorted(WhereInfo *pWInfo){ + assert( pWInfo->wctrlFlags & WHERE_GROUPBY ); + assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); + return pWInfo->sorted; +} + #ifdef WHERETRACE_ENABLED /* For debugging use only: */ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ @@ -4959,7 +5087,6 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ } #endif - /* ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine ** attempts to find the lowest cost path that visits each WhereLoop @@ -5240,7 +5367,19 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0; pWInfo->revMask = pFrom->revLoop; } + if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) + && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr + ){ + Bitmask notUsed = 0; + int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, + pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used + ); + assert( pWInfo->sorted==0 ); + pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr); + } } + + pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ @@ -5924,7 +6063,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ for(; kp1!=pLevel->iTabCur ) continue; if( pOp->opcode==OP_Column ){ - pOp->opcode = OP_SCopy; + pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + pTabItem->regResult; pOp->p2 = pOp->p3; pOp->p3 = 0; diff --git a/src/whereInt.h b/src/whereInt.h index 419bb81c0e..72e7530db9 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -398,6 +398,7 @@ struct WhereInfo { LogEst nRowOut; /* Estimated number of output rows */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */ + u8 sorted; /* True if really sorted (not just grouped) */ 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 */ diff --git a/test/alter.test b/test/alter.test index 557162f837..ddf1698824 100644 --- a/test/alter.test +++ b/test/alter.test @@ -875,4 +875,42 @@ do_execsql_test alter-16.2 { SELECT * FROM t16a_rn ORDER BY a; } {abc 1.25 99 xyzzy cba 5.5 98 fizzle} +#------------------------------------------------------------------------- +# Verify that NULL values into the internal-use-only sqlite_rename_*() +# functions do not cause problems. +# +do_execsql_test alter-17.1 { + SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)','abc'); +} {{CREATE TABLE "abc"(a,b,c)}} +do_execsql_test alter-17.2 { + SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)',NULL); +} {{CREATE TABLE "(NULL)"(a,b,c)}} +do_execsql_test alter-17.3 { + SELECT sqlite_rename_table(NULL,'abc'); +} {{}} +do_execsql_test alter-17.4 { + SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN','abc'); +} {{CREATE TRIGGER r1 ON "abc" WHEN}} +do_execsql_test alter-17.5 { + SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN',NULL); +} {{CREATE TRIGGER r1 ON "(NULL)" WHEN}} +do_execsql_test alter-17.6 { + SELECT sqlite_rename_trigger(NULL,'abc'); +} {{}} +do_execsql_test alter-17.7 { + SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + 'xyzzy','lmnop'); +} {{CREATE TABLE t1(a REFERENCES "lmnop")}} +do_execsql_test alter-17.8 { + SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + 'xyzzy',NULL); +} {{CREATE TABLE t1(a REFERENCES "(NULL)")}} +do_execsql_test alter-17.9 { + SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + NULL, 'lmnop'); +} {{}} +do_execsql_test alter-17.10 { + SELECT sqlite_rename_parent(NULL,'abc','xyz'); +} {{}} + finish_test diff --git a/test/func.test b/test/func.test index 0fafd1828d..e3cec32153 100644 --- a/test/func.test +++ b/test/func.test @@ -1301,11 +1301,13 @@ do_test func-29.3 { db eval {SELECT typeof(+x) FROM t29 ORDER BY id} } {integer null real blob text} if {[permutation] != "mmap"} { - do_test func-29.4 { - set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] - if {$x>100} {set x many} - set x - } {many} + ifcapable !direct_read { + do_test func-29.4 { + set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] + if {$x>100} {set x many} + set x + } {many} + } } do_test func-29.5 { db close diff --git a/test/index6.test b/test/index6.test index 48706be3cc..3451e5c1df 100644 --- a/test/index6.test +++ b/test/index6.test @@ -248,4 +248,23 @@ do_execsql_test index6-5.0 { SELECT stat+0 FROM sqlite_stat1 WHERE idx='t3b'; } {6 6} +# Test case for ticket [2ea3e9fe6379fc3f6ce7e090ce483c1a3a80d6c9] from +# 2014-04-13: Partial index causes assertion fault on UPDATE OR REPLACE. +# +do_execsql_test index6-6.0 { + CREATE TABLE t6(a,b); + CREATE UNIQUE INDEX t6ab ON t1(a,b); + CREATE INDEX t6b ON t6(b) WHERE b=1; + INSERT INTO t6(a,b) VALUES(123,456); + SELECT * FROM t6; +} {123 456} +do_execsql_test index6-6.1 { + UPDATE OR REPLACE t6 SET b=789; + SELECT * FROM t6; +} {123 789} +do_execsql_test index6-6.2 { + PRAGMA integrity_check; +} {ok} + + finish_test diff --git a/test/tkt-b75a9ca6b0.test b/test/tkt-b75a9ca6b0.test new file mode 100644 index 0000000000..0c81a534da --- /dev/null +++ b/test/tkt-b75a9ca6b0.test @@ -0,0 +1,77 @@ +# 2014-04-21 +# +# 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. +# +#************************************************************************* +# +# Test that ticket [b75a9ca6b0] has been fixed. +# +# Ticket [b75a9ca6b0] concerns queries that have both a GROUP BY +# and an ORDER BY. This code verifies that SQLite is able to +# optimize out the ORDER BY in some circumstances, but retains the +# ORDER BY when necessary. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix tkt-b75a9ca6b0 + +do_execsql_test 1 { + CREATE TABLE t1 (x, y); + INSERT INTO t1 VALUES (1, 3); + INSERT INTO t1 VALUES (2, 2); + INSERT INTO t1 VALUES (3, 1); +} + +do_execsql_test 1.1 { + CREATE INDEX i1 ON t1(x, y); +} + +set idxscan {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}} +set tblscan {0 0 0 {SCAN TABLE t1}} +set grpsort {0 0 0 {USE TEMP B-TREE FOR GROUP BY}} +set sort {0 0 0 {USE TEMP B-TREE FOR ORDER BY}} + +foreach {tn q res eqp} [subst -nocommands { + 1 "SELECT * FROM t1 GROUP BY x, y ORDER BY x,y" + {1 3 2 2 3 1} {$idxscan} + + 2 "SELECT * FROM t1 GROUP BY x, y ORDER BY x" + {1 3 2 2 3 1} {$idxscan $sort} + + 3 "SELECT * FROM t1 GROUP BY y, x ORDER BY y, x" + {3 1 2 2 1 3} {$idxscan $sort} + + 4 "SELECT * FROM t1 GROUP BY x ORDER BY x" + {1 3 2 2 3 1} {$idxscan} + + 5 "SELECT * FROM t1 GROUP BY y ORDER BY y" + {3 1 2 2 1 3} {$tblscan $grpsort} + + 6 "SELECT * FROM t1 GROUP BY y ORDER BY x" + {1 3 2 2 3 1} {$tblscan $grpsort $sort} + + 7 "SELECT * FROM t1 GROUP BY x, y ORDER BY x, y DESC" + {1 3 2 2 3 1} {$idxscan $sort} + + 8 "SELECT * FROM t1 GROUP BY x, y ORDER BY x DESC, y DESC" + {3 1 2 2 1 3} {$idxscan $sort} + + 9 "SELECT * FROM t1 GROUP BY x, y ORDER BY x ASC, y ASC" + {1 3 2 2 3 1} {$idxscan} + + 10 "SELECT * FROM t1 GROUP BY x, y ORDER BY x COLLATE nocase, y" + {1 3 2 2 3 1} {$idxscan $sort} + +}] { + do_execsql_test 1.$tn.1 $q $res + do_eqp_test 1.$tn.2 $q $eqp +} + + +finish_test diff --git a/test/wal.test b/test/wal.test index 3a69de54f5..675be73791 100644 --- a/test/wal.test +++ b/test/wal.test @@ -1574,4 +1574,17 @@ sqlite3_shutdown test_sqlite3_log sqlite3_initialize +# Make sure PRAGMA journal_mode=WAL works with ATTACHED databases in +# all journal modes. +# +foreach mode {OFF MEMORY PERSIST DELETE TRUNCATE WAL} { + delete_file test.db test2.db + sqlite3 db test.db + do_test wal-25.$mode { + db eval "PRAGMA journal_mode=$mode" + db eval {ATTACH 'test2.db' AS t2; PRAGMA journal_mode=WAL;} + } {wal} + db close +} + finish_test diff --git a/test/whereD.test b/test/whereD.test index eb0f864025..db993040b0 100644 --- a/test/whereD.test +++ b/test/whereD.test @@ -217,6 +217,59 @@ do_execsql_test 4.7 { ORDER BY a; } {3 4 3 4} +# Verify fix of a bug reported on the mailing list by Peter Reid +# +do_execsql_test 5.1 { + DROP TABLE IF EXISTS t; + CREATE TABLE t(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17); + CREATE INDEX tc0 ON t(c0); + CREATE INDEX tc1 ON t(c1); + CREATE INDEX tc2 ON t(c2); + CREATE INDEX tc3 ON t(c3); + CREATE INDEX tc4 ON t(c4); + CREATE INDEX tc5 ON t(c5); + CREATE INDEX tc6 ON t(c6); + CREATE INDEX tc7 ON t(c7); + CREATE INDEX tc8 ON t(c8); + CREATE INDEX tc9 ON t(c9); + CREATE INDEX tc10 ON t(c10); + CREATE INDEX tc11 ON t(c11); + CREATE INDEX tc12 ON t(c12); + CREATE INDEX tc13 ON t(c13); + CREATE INDEX tc14 ON t(c14); + CREATE INDEX tc15 ON t(c15); + CREATE INDEX tc16 ON t(c16); + CREATE INDEX tc17 ON t(c17); + + INSERT INTO t(c0, c16) VALUES (1,1); + + SELECT * FROM t WHERE + c0=1 or c1=1 or c2=1 or c3=1 or + c4=1 or c5=1 or c6=1 or c7=1 or + c8=1 or c9=1 or c10=1 or c11=1 or + c12=1 or c13=1 or c14=1 or c15=1 or + c16=1 or c17=1; +} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {}} +do_execsql_test 5.2 { + DELETE FROM t; + INSERT INTO t(c0,c17) VALUES(1,1); + SELECT * FROM t WHERE + c0=1 or c1=1 or c2=1 or c3=1 or + c4=1 or c5=1 or c6=1 or c7=1 or + c8=1 or c9=1 or c10=1 or c11=1 or + c12=1 or c13=1 or c14=1 or c15=1 or + c16=1 or c17=1; +} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1} +do_execsql_test 5.3 { + DELETE FROM t; + INSERT INTO t(c0,c15) VALUES(1,1); + SELECT * FROM t WHERE + c0=1 or c1=1 or c2=1 or c3=1 or + c4=1 or c5=1 or c6=1 or c7=1 or + c8=1 or c9=1 or c10=1 or c11=1 or + c12=1 or c13=1 or c14=1 or c15=1 or + c16=1 or c17=1; +} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {} {}} finish_test diff --git a/test/whereG.test b/test/whereG.test index 490fffe64e..17d5653223 100644 --- a/test/whereG.test +++ b/test/whereG.test @@ -166,5 +166,18 @@ do_eqp_test whereG-3.4 { SELECT * FROM a, b WHERE a2=5 AND a1=b1; } {/.*SCAN TABLE a.*SEARCH TABLE b USING INDEX .*b_1 .b1=..*/} +# Ticket [1e64dd782a126f48d78c43a664844a41d0e6334e]: +# Incorrect result in a nested GROUP BY/DISTINCT due to the use of an OP_SCopy +# where an OP_Copy was needed. +# +do_execsql_test whereG-4.0 { + CREATE TABLE t4(x); + INSERT INTO t4 VALUES('right'),('wrong'); + SELECT DISTINCT x + FROM (SELECT x FROM t4 GROUP BY x) + WHERE x='right' + ORDER BY x; +} {right} + finish_test diff --git a/test/whereH.test b/test/whereH.test new file mode 100644 index 0000000000..c252fe1ef7 --- /dev/null +++ b/test/whereH.test @@ -0,0 +1,139 @@ +# 2014-03-31 +# +# 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. +# +#*********************************************************************** +# +# Test cases for query planning decisions where one candidate index +# covers a proper superset of the WHERE clause terms of another +# candidate index. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test whereH-1.1 { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1abc ON t1(a,b,c); + CREATE INDEX t1bc ON t1(b,c); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c; +} {/INDEX t1abc /} +do_execsql_test whereH-1.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-2.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1bc ON t1(b,c); + CREATE INDEX t1abc ON t1(a,b,c); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c; +} {/INDEX t1abc /} +do_execsql_test whereH-2.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-3.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1cd ON t1(c,d); + CREATE INDEX t1bcd ON t1(b,c,d); + CREATE INDEX t1abcd ON t1(a,b,c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-3.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-4.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1cd ON t1(c,d); + CREATE INDEX t1abcd ON t1(a,b,c,d); + CREATE INDEX t1bcd ON t1(b,c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-4.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-5.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1bcd ON t1(b,c,d); + CREATE INDEX t1cd ON t1(c,d); + CREATE INDEX t1abcd ON t1(a,b,c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-5.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-6.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1bcd ON t1(b,c,d); + CREATE INDEX t1abcd ON t1(a,b,c,d); + CREATE INDEX t1cd ON t1(c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-6.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-7.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1abcd ON t1(a,b,c,d); + CREATE INDEX t1bcd ON t1(b,c,d); + CREATE INDEX t1cd ON t1(c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-7.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + +do_execsql_test whereH-8.1 { + DROP TABLE t1; + CREATE TABLE t1(a,b,c,d,e); + CREATE INDEX t1abcd ON t1(a,b,c,d); + CREATE INDEX t1cd ON t1(c,d); + CREATE INDEX t1bcd ON t1(b,c,d); + + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {/INDEX t1abcd /} +do_execsql_test whereH-8.2 { + EXPLAIN QUERY PLAN + SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d; +} {~/TEMP B-TREE FOR ORDER BY/} + + + +finish_test