From: drh Date: Wed, 5 Jul 2017 16:20:49 +0000 (+0000) Subject: Make use of covering indexes in the OR optimization. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b792d9ef4c0247841d6891741e526c88f1659b31;p=thirdparty%2Fsqlite.git Make use of covering indexes in the OR optimization. FossilOrigin-Name: fcbd6abdb1a4cf622ff7e85625b9c2a9bbae92410359872924b7fc1e35046a75 --- diff --git a/manifest b/manifest index d0b3c3e3d1..8e31fba7b8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\scount-of-view\soptimization\swhen\scompiled\susing\s\nSQLITE_COUNTOFVIEW_OPTIMIZATION. -D 2017-07-05T14:54:24.911 +C Make\suse\sof\scovering\sindexes\sin\sthe\sOR\soptimization. +D 2017-07-05T16:20:49.023 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 00d12636df7a5b08af09116bcd6c7bfd49b8b3b4 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -230,7 +230,7 @@ F src/printf.c 8ae1fa9d30c1200a9268a390ba9e9cea9197b27a F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 41aa91af56d960e9414ce1d7c17cfb68e0d1c6cb F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e -F src/select.c 296bf898a2d845ca9b58bf936e42b1122eab3cfab03848da0f684150548ff8a2 +F src/select.c 12b01d61078da5aeaa6bae2bcb1bd5550c86a761dea52b4ce833d4e273e44e96 F src/shell.c 84a1593bd86aaa14f4da8a8f9b16fbc239d262aa F src/sqlite.h.in 278602140d49575e8708e643161f4263e428a02a F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad @@ -293,11 +293,11 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e F src/vacuum.c 9460b9de7b2d4e34b0d374894aa6c8a0632be8ec -F src/vdbe.c 86ae6f4774410868af41bd839b72b7081ff03e78 +F src/vdbe.c 1e2abdaedcbd3697994c4f244ae388b2afbe1e1023537a78abdf0bbf70ef23a7 F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3 -F src/vdbeInt.h 9cbaa84f53ddd2d09a0cf61a94337a3a035d08a0 +F src/vdbeInt.h d96370101d9109cc476b42951272bd660336a638a6b8ef0d72c0911d6cdffbfd F src/vdbeapi.c 583d56b129dd27f12bed518270de9ebe521e6a75 -F src/vdbeaux.c 413dc496248ac18eb0c19e35e86bb1ffd47b8907 +F src/vdbeaux.c c562ac4e1d5803287f5ad2718460eb7e1f22b8356b22603f67f6b849056465e1 F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90 F src/vdbemem.c c0dc81285b7571b0a31c40f17846fe2397ec1cd9 F src/vdbesort.c 919717d7599fa31d343ec28bffd0f9e91a4ff5f6 @@ -307,7 +307,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb F src/wal.c 878c8e1a51cb2ec45c395d26b7d5cd9e1a098e4a F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 -F src/where.c 5b519760ffaeb8ea70c34ba6bd8344d768338c6a7ba907b01746cd92adde04ea +F src/where.c 4ba1381573d45cb11b09a0fbddd9f702a295a546986d5da4bf747f709aa4ac30 F src/whereInt.h 1d1fd0b3b9b56e08f5d3583c70a2c785a3c43941 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -1172,7 +1172,7 @@ F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2 F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a -F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5 +F test/whereD.test ac6926e3f518d5b75b61e81c2b7c327565e07a4bd919b6c5c3a003ca0cf19021 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3 @@ -1250,8 +1250,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 802b82f342328762e3995825aed1b22e61361ef24b673cd5d66b55756ce2a461 -Q +d1ba200234f40b84327c7fc28c2584ed069da80e97578df71114d1a9ba9c559c -R d1ab17963b33badcbff62fa608fc3eef +P b7ae4b879fc086e9543493843377ae90ceff1fe49c97b4c23367012034c3c9d5 +Q +9de3d7123007636aa97da1c70bc34344b0391078 +R f237a48695c9cf0b1b130ff48b449b88 U drh -Z 7c072e36d0f6a6f55c9a69a4c236ee0f +Z 975105f0956961b19cc95f2a1afc7d0c diff --git a/manifest.uuid b/manifest.uuid index 6b7c324175..387d721e48 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b7ae4b879fc086e9543493843377ae90ceff1fe49c97b4c23367012034c3c9d5 \ No newline at end of file +fcbd6abdb1a4cf622ff7e85625b9c2a9bbae92410359872924b7fc1e35046a75 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 8d31da3ddc..58d21758a4 100644 --- a/src/select.c +++ b/src/select.c @@ -2881,10 +2881,11 @@ static int multiSelectOrderBy( ** to the right and the left are evaluated, they use the correct ** collation. */ - aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy); + aPermute = sqlite3DbMallocRaw(db, sizeof(int)*(nOrderBy + 1)); if( aPermute ){ struct ExprList_item *pItem; - for(i=0, pItem=pOrderBy->a; ia; i<=nOrderBy; i++, pItem++){ assert( pItem->u.x.iOrderByCol>0 && pItem->u.x.iOrderByCol<=p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; diff --git a/src/vdbe.c b/src/vdbe.c index f2de90d14c..750f61f578 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1980,11 +1980,14 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** The permutation is only valid until the next OP_Compare that has ** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should ** occur immediately prior to the OP_Compare. +** +** The first integer in the P4 integer array is the length of the array +** and does not become part of the permutation. */ case OP_Permutation: { assert( pOp->p4type==P4_INTARRAY ); assert( pOp->p4.ai ); - aPermute = pOp->p4.ai; + aPermute = pOp->p4.ai + 1; break; } @@ -2290,12 +2293,16 @@ case OP_Column: { u16 fx; /* pDest->flags value */ Mem *pReg; /* PseudoTable input register */ + pC = p->apCsr[pOp->p1]; p2 = pOp->p2; + + /* If the cursor cache is stale, bring it up-to-date */ + rc = sqlite3VdbeCursorMoveto(&pC, &p2); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pOp->p1>=0 && pOp->p1nCursor ); - pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( p2nField ); aOffset = pC->aOffset; @@ -2306,8 +2313,6 @@ case OP_Column: { assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */ assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ - /* If the cursor cache is stale, bring it up-to-date */ - rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; if( pC->cacheStatus!=p->cacheCtr ){ if( pC->nullRow ){ @@ -3725,7 +3730,7 @@ case OP_SeekGT: { /* jump, in3 */ break; } -/* Opcode: Seek P1 P2 * * * +/* Opcode: Seek P1 P2 P3 P4 * ** Synopsis: intkey=r[P2] ** ** P1 is an open table cursor and P2 is a rowid integer. Arrange @@ -3734,6 +3739,13 @@ case OP_SeekGT: { /* jump, in3 */ ** This is actually a deferred seek. Nothing actually happens until ** the cursor is used to read a record. That way, if no reads ** occur, no unnecessary I/O happens. +** +** P4 may contain an array of integers (type P4_INTARRAY) containing +** one entry for each column in the table P1 is open on. If so, then +** parameter P3 is a cursor open on a database index. If array entry +** a[i] is non-zero, then reading column (a[i]-1) from cursor P3 is +** equivalent to performing the deferred seek and then reading column i +** from P1. */ case OP_Seek: { /* in2 */ VdbeCursor *pC; @@ -3747,6 +3759,9 @@ case OP_Seek: { /* in2 */ pIn2 = &aMem[pOp->p2]; pC->movetoTarget = sqlite3VdbeIntValue(pIn2); pC->deferredMoveto = 1; + assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 ); + pC->aAltMap = pOp->p4.ai; + pC->pAltCursor = p->apCsr[pOp->p3]; break; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index d3955af31e..b3ebccabab 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -60,6 +60,7 @@ typedef struct AuxData AuxData; ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. */ +typedef struct VdbeCursor VdbeCursor; struct VdbeCursor { BtCursor *pCursor; /* The cursor structure of the backend */ Btree *pBt; /* Separate file holding temporary table */ @@ -83,6 +84,11 @@ struct VdbeCursor { i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ + VdbeCursor *pAltCursor; /* Associated index cursor from which to read */ + int *aAltMap; /* Mapping from table to index column numbers */ +#ifdef SQLITE_ENABLE_COLUMN_USED_MASK + u64 maskUsed; /* Mask of columns used by this cursor */ +#endif /* Cached information about the header for the data record that the ** cursor is currently pointing to. Only valid if cacheStatus matches @@ -104,7 +110,6 @@ struct VdbeCursor { ** static element declared in the structure. nField total array slots for ** aType[] and nField+1 array slots for aOffset[] */ }; -typedef struct VdbeCursor VdbeCursor; /* ** When a sub-program is executed (OP_Program), a structure of this type @@ -393,7 +398,7 @@ struct Vdbe { */ void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); -int sqlite3VdbeCursorMoveto(VdbeCursor*); +int sqlite3VdbeCursorMoveto(VdbeCursor**, int*); int sqlite3VdbeCursorRestore(VdbeCursor*); #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) void sqlite3VdbePrintOp(FILE*, int, Op*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 9c5d9acca9..f1262489e6 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1123,7 +1123,21 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ } #endif case P4_INTARRAY: { - sqlite3_snprintf(nTemp, zTemp, "intarray"); + int i, j; + int *ai = pOp->p4.ai; + int n = ai[0]; /* The first element of an INTARRAY is always the + ** count of the number of elements to follow */ + zTemp[0] = '['; + for(i=j=1; i1 ) zTemp[j++] = ','; + sqlite3_snprintf(nTemp-j, zTemp+j, "%d", ai[i]); + j += sqlite3Strlen30(zTemp+j); + } + if( ideferredMoveto ){ + int iMap; + if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){ + *pp = p->pAltCursor; + *piCol = iMap - 1; + return SQLITE_OK; + } return handleDeferredMoveto(p); } if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){ diff --git a/src/where.c b/src/where.c index eff53bc9ba..0df25fc87a 100644 --- a/src/where.c +++ b/src/where.c @@ -3248,6 +3248,55 @@ static void whereLikeOptimizationStringFixup( } } +/* +** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains +** a rowid value just read from cursor iIdxCur, open on index pIdx. This +** function generates code to do a deferred seek of cursor iCur to the +** rowid stored in register iRowid. +** +** Normally, this is just: +** +** OP_Seek $iCur $iRowid +** +** However, if the scan currently being coded is a branch of an OR-loop and +** the statement currently being coded is a SELECT, then P3 of the OP_Seek +** is set to iIdxCur and P4 is set to point to an array of integers +** containing one entry for each column of the table cursor iCur is open +** on. For each table column, if the column is the i'th column of the +** index, then the corresponding array entry is set to (i+1). If the column +** does not appear in the index at all, the array entry is set to 0. +*/ +static void codeDeferredSeek( + WhereInfo *pWInfo, /* Where clause context */ + Index *pIdx, /* Index scan is using */ + int iCur, /* Cursor for IPK b-tree */ + int iRowid, /* Register containing rowid to seek to */ + int iIdxCur /* Index cursor */ +){ + Parse *pParse = pWInfo->pParse; /* Parse context */ + Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */ + + assert( iIdxCur>0 ); + assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 ); + + sqlite3VdbeAddOp3(v, OP_Seek, iCur, iRowid, iIdxCur); + if( (pWInfo->wctrlFlags & WHERE_FORCE_TABLE) + && sqlite3ParseToplevel(pParse)->writeMask==0 + ){ + int i; + Table *pTab = pIdx->pTable; + int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1)); + if( ai ){ + ai[0] = pTab->nCol; + for(i=0; inColumn-1; i++){ + assert( pIdx->aiColumn[i]nCol ); + if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1; + } + sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY); + } + } +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -3726,7 +3775,7 @@ static Bitmask codeOneLoopStart( iRowidReg = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); - sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ + codeDeferredSeek(pWInfo, pIdx, iCur, iRowidReg, iIdxCur); }else if( iCur!=iIdxCur ){ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol); diff --git a/test/whereD.test b/test/whereD.test index db993040b0..82c1cb7187 100644 --- a/test/whereD.test +++ b/test/whereD.test @@ -156,7 +156,7 @@ do_searchcount_test 3.4.4 { do_searchcount_test 3.5.1 { SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR rowid=4 -} {1 one 2 two search 2} +} {1 one 2 two search 1} do_searchcount_test 3.5.2 { SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR rowid=4 } {1 i 2 ii search 2} @@ -271,5 +271,76 @@ do_execsql_test 5.3 { c16=1 or c17=1; } {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {} {}} +#------------------------------------------------------------------------- +#------------------------------------------------------------------------- +do_execsql_test 6.1 { + CREATE TABLE x1(a, b, c, d, e); + CREATE INDEX x1a ON x1(a); + CREATE INDEX x1bc ON x1(b, c); + CREATE INDEX x1cd ON x1(c, d); + + INSERT INTO x1 VALUES(1, 2, 3, 4, 'A'); + INSERT INTO x1 VALUES(5, 6, 7, 8, 'B'); + INSERT INTO x1 VALUES(9, 10, 11, 12, 'C'); + INSERT INTO x1 VALUES(13, 14, 15, 16, 'D'); +} + +do_searchcount_test 6.2.1 { + SELECT e FROM x1 WHERE b=2 OR c=7; +} {A B search 6} +do_searchcount_test 6.2.2 { + SELECT c FROM x1 WHERE b=2 OR c=7; +} {3 7 search 4} + +do_searchcount_test 6.3.1 { + SELECT e FROM x1 WHERE a=1 OR b=10; +} {A C search 6} +do_searchcount_test 6.3.2 { + SELECT c FROM x1 WHERE a=1 OR b=10; +} {3 11 search 5} +do_searchcount_test 6.3.3 { + SELECT rowid FROM x1 WHERE a=1 OR b=10; +} {1 3 search 4} + +do_searchcount_test 6.4.1 { + SELECT a FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12 +} {1 9 search 6} +do_searchcount_test 6.4.2 { + SELECT b, c FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12 +} {2 3 10 11 search 5} +do_searchcount_test 6.4.3 { + SELECT rowid, c FROM x1 WHERE b BETWEEN 1 AND 4 OR c BETWEEN 8 AND 12 +} {1 3 3 11 search 4} + +db eval { + WITH RECURSIVE c(x) AS (VALUES(1000) UNION ALL SELECT x+1 FROM c WHERE x<2000) + INSERT INTO x1(rowid,a,b,c,d,e) SELECT x,x,x,x,x,x FROM c; +} + +do_searchcount_test 6.5.1 { + SELECT a FROM x1 WHERE rowid = 2 OR c=11 +} {5 9 search 3} +do_searchcount_test 6.5.2 { + SELECT d FROM x1 WHERE rowid = 2 OR c=11 +} {8 12 search 2} +do_searchcount_test 6.5.3 { + SELECT d FROM x1 WHERE c=11 OR rowid = 2 +} {12 8 search 2} +do_searchcount_test 6.5.4 { + SELECT a FROM x1 WHERE c=11 OR rowid = 2 +} {9 5 search 3} + +do_searchcount_test 6.6.1 { + SELECT rowid FROM x1 WHERE a=1 OR b=6 OR c=11 +} {1 2 3 search 6} +do_searchcount_test 6.6.2 { + SELECT c FROM x1 WHERE a=1 OR b=6 OR c=11 +} {3 7 11 search 7} +do_searchcount_test 6.6.3 { + SELECT c FROM x1 WHERE c=11 OR a=1 OR b=6 +} {11 3 7 search 7} +do_searchcount_test 6.6.4 { + SELECT c FROM x1 WHERE b=6 OR c=11 OR a=1 +} {7 11 3 search 7} finish_test