From: drh Date: Tue, 28 Jan 2014 00:49:55 +0000 (+0000) Subject: Provide hints to the btree layer Next and Previous primitives to let them X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Fbranch-3.8.2;p=thirdparty%2Fsqlite.git Provide hints to the btree layer Next and Previous primitives to let them know if they can be no-ops if the underlying index is unique. FossilOrigin-Name: a2c347faf91d82cf8734621bf378a6dd68b00957 --- diff --git a/manifest b/manifest index 969f59f8d9..a6cc0a1a6e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\sfixes\sfrom\strunk.\s\sCherrypick\sof\s[c43b59dac1],\s[a221aa82bb],\s[e1eba1fb09],\sand\s[1e131094b5]. -D 2014-01-15T00:24:22.522 +C Provide\shints\sto\sthe\sbtree\slayer\sNext\sand\sPrevious\sprimitives\sto\slet\sthem\s\nknow\sif\sthey\scan\sbe\sno-ops\sif\sthe\sunderlying\sindex\sis\sunique. +D 2014-01-28T00:49:55.352 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in e1a9b4258bbde53f5636f4e238c65b7e11459e2b F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -166,7 +166,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 1809a7caa2504233bdddd12f5018422421789537 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c fe6c95c57fd14ed4b3329f4b5bb08132fe7c322d +F src/btree.c c695c10622607d483ce435506420c99443a6b22c F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9 F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0 F src/build.c 1965c7b596ca6295b7ca59541ad186c57564b8f7 @@ -280,7 +280,7 @@ F src/update.c d1c2477dcf14d90999d1935af4efb4806553250b F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c 76ed0519296e3f62e97e57dab1999e34184c8e49 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c 54894fde8dc806d259e015ac7c9680145e725835 +F src/vdbe.c 73d27c705888814069f311aa5f7ca0281a98b628 F src/vdbe.h c06f0813f853566457ce9cfb1a4a4bc39a5da644 F src/vdbeInt.h 05fbda0e061dbc4aaa2709a8cccf3515c245b263 F src/vdbeapi.c 93a22a9ba2abe292d5c2cf304d7eb2e894dde0ed @@ -293,8 +293,8 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74 -F src/where.c 3b589eee28271c4a7cbdb11f0312e1dc2013c255 -F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358 +F src/where.c 549067bd1c78072d86a3fc48d86d549bb5b29b8d +F src/whereInt.h 921f935af8b684ffb49705610bda7284db1db138 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 @@ -1145,11 +1145,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P ac5852d6403c9c9628ca0aa7be135c702f000698 -Q +1e131094b522103a0829f72193b067b04e42ce82 -Q +a221aa82bb5496885fd0bf76e4601443799511de -Q +c43b59dac1fbb67ec3a9d921005543046ad416ce -Q +e1eba1fb09d7db49d77928bd115b27b8002ae640 -R 9a2615403bfd61ea9575eeff6c57728d -U mistachkin -Z 9a636fd2c05ad5aa90446f6df2e80da4 +P c697d2f83c2d8ea0a100b84b0debb6a322c3a876 +R 98f94a08f2da89f2a3296169c5e6a07d +U drh +Z b8a82ebf9ab9cd1246e1fd5c1b9c1901 diff --git a/manifest.uuid b/manifest.uuid index a70c5b7ce1..c41e5a4268 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c697d2f83c2d8ea0a100b84b0debb6a322c3a876 \ No newline at end of file +a2c347faf91d82cf8734621bf378a6dd68b00957 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 30fdf4256f..da01a7ae8c 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4817,6 +4817,15 @@ int sqlite3BtreeEof(BtCursor *pCur){ ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; @@ -4825,6 +4834,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ rc = restoreCursorPosition(pCur); @@ -4903,6 +4913,15 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ ** successful then set *pRes=0. If the cursor ** was already pointing to the first entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; @@ -4910,6 +4929,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->atLast = 0; if( pCur->eState!=CURSOR_VALID ){ @@ -7150,7 +7170,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ - int notUsed; + int notUsed = 0; rc = sqlite3BtreePrevious(pCur, ¬Used); if( rc ) return rc; } diff --git a/src/vdbe.c b/src/vdbe.c index 8ae4ce484c..9ce8422344 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3580,6 +3580,7 @@ case OP_SeekGt: { /* jump, in3 */ #endif if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); if( res<0 || (res==0 && oc==OP_SeekGt) ){ + res = 0; rc = sqlite3BtreeNext(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; pC->rowidIsValid = 0; @@ -3589,6 +3590,7 @@ case OP_SeekGt: { /* jump, in3 */ }else{ assert( oc==OP_SeekLt || oc==OP_SeekLe ); if( res>0 || (res==0 && oc==OP_SeekLt) ){ + res = 0; rc = sqlite3BtreePrevious(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; pC->rowidIsValid = 0; @@ -4442,7 +4444,7 @@ case OP_Rewind: { /* jump */ break; } -/* Opcode: Next P1 P2 * * P5 +/* Opcode: Next P1 P2 P3 * P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through @@ -4452,6 +4454,11 @@ case OP_Rewind: { /* jump */ ** The P1 cursor must be for a real table, not a pseudo-table. P1 must have ** been opened prior to this opcode or the program will segfault. ** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreeNext(). ** @@ -4460,12 +4467,12 @@ case OP_Rewind: { /* jump */ ** ** See also: Prev, NextIfOpen */ -/* Opcode: NextIfOpen P1 P2 * * P5 +/* Opcode: NextIfOpen P1 P2 P3 * P5 ** ** This opcode works just like OP_Next except that if cursor P1 is not ** open it behaves a no-op. */ -/* Opcode: Prev P1 P2 * * P5 +/* Opcode: Prev P1 P2 P3 * P5 ** ** Back up cursor P1 so that it points to the previous key/data pair in its ** table or index. If there is no previous key/value pairs then fall through @@ -4475,13 +4482,18 @@ case OP_Rewind: { /* jump */ ** The P1 cursor must be for a real table, not a pseudo-table. If P1 is ** not open then the behavior is undefined. ** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreePrevious(). ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ -/* Opcode: PrevIfOpen P1 P2 * * P5 +/* Opcode: PrevIfOpen P1 P2 P3 * P5 ** ** This opcode works just like OP_Prev except that if cursor P1 is not ** open it behaves a no-op. @@ -4503,9 +4515,12 @@ case OP_Next: /* jump */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p5aCounter) ); pC = p->apCsr[pOp->p1]; + res = pOp->p3; assert( pC!=0 ); assert( pC->deferredMoveto==0 ); assert( pC->pCursor ); + assert( res==0 || (res==1 && pC->isTable==0) ); + testcase( res==1 ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext ); diff --git a/src/where.c b/src/where.c index cfbf769c67..34bbf7ee47 100644 --- a/src/where.c +++ b/src/where.c @@ -3182,6 +3182,8 @@ static Bitmask codeOneLoopStart( pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; + assert( (WHERE_UNQ_WANTED>>16)==1 ); + pLevel->p3 = (pLoop->wsFlags>>16)&1; if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; }else{ @@ -3978,12 +3980,13 @@ static int whereLoopAddBtreeIndex( || nInMul==0 ); pNew->wsFlags |= WHERE_COLUMN_EQ; - if( iCol<0 - || (pProbe->onError!=OE_None && nInMul==0 - && pNew->u.btree.nEq==pProbe->nKeyCol-1) - ){ + if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1)){ assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 ); - pNew->wsFlags |= WHERE_ONEROW; + if( iCol>=0 && pProbe->onError==OE_None ){ + pNew->wsFlags |= WHERE_UNQ_WANTED; + }else{ + pNew->wsFlags |= WHERE_ONEROW; + } } pNew->u.btree.nEq++; pNew->nOut = nRowEst + nInMul; @@ -5773,7 +5776,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ pLoop = pLevel->pWLoop; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ - sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2); + sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); } if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ diff --git a/src/whereInt.h b/src/whereInt.h index 56646c55e6..23f929c461 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -70,7 +70,7 @@ struct WhereLevel { int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ + u8 op, p3, p5; /* Opcode, P3 & 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 { @@ -457,3 +457,4 @@ struct WhereInfo { #define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */ #define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */ #define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */ +#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/