From: dan Date: Fri, 29 Jan 2016 19:29:45 +0000 (+0000) Subject: Experimental attempt to make better use of covering indexes within OR queries. X-Git-Tag: version-3.11.0~81^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=de892d96604b09cf959cd317243a2176b3d9531a;p=thirdparty%2Fsqlite.git Experimental attempt to make better use of covering indexes within OR queries. FossilOrigin-Name: a323ac3a9d42bd5cb38d724c7e1180584b91054c --- diff --git a/manifest b/manifest index abb80cb617..4025b2f01a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sunnecessary\sWHERE\sclause\sterm\stests\swhen\scoding\sa\sjoin\swhere\sone\sof\sthe\stables\scontains\sa\sOR\sconstraint. -D 2016-01-29T18:11:04.923 +C Experimental\sattempt\sto\smake\sbetter\suse\sof\scovering\sindexes\swithin\sOR\squeries. +D 2016-01-29T19:29:45.658 F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 72b7858f02017611c3ac1ddc965251017fed0845 @@ -412,11 +412,11 @@ F src/update.c 17332f9fe818cbc0444c36a811800af8498af4c3 F src/utf.c 32d7f82aa921322f3e1c956f4b58f019ebd2c6b3 F src/util.c 72d40df0a52d3f30b462a15f0e094fcbade6dc82 F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c 3ffbcc413bf793e3f0b95b79ef2f4bd449a5b5a3 +F src/vdbe.c 2e0c42bba0aee55b06b636feb5ad41d77c75abe3 F src/vdbe.h 7a733ea8aac1b77305a67698e784fa3484ee3337 -F src/vdbeInt.h 42eefa4f9e7432b9968d321b44e48821ec13b189 +F src/vdbeInt.h 91f22c4d54a2540fa0da64f04dd9d96969c0f0e0 F src/vdbeapi.c ffae8f5af4570fbd548504e815e9fb7227f0822e -F src/vdbeaux.c fc8926c4232cd5c982f36197b1443212f265d927 +F src/vdbeaux.c 657402ba7d253963e3fca323c6ad9f7b29a9efb9 F src/vdbeblob.c 37c3d11a753e403698c69e17383d282e1ae73e75 F src/vdbemem.c b9181e77eca2a095929d46250daf85c8d2621fc0 F src/vdbesort.c ef3c6d1f1a7d44cf67bb2bee59ea3d1fe5bad174 @@ -428,7 +428,7 @@ F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354 F src/where.c af9bf5dcec1a0e52726c550924aa91d837166251 F src/whereInt.h 78b6b4de94db84aecbdc07fe3e38f648eb391e9a -F src/wherecode.c 22a7fee2360c738533ce12615f487594849adc78 +F src/wherecode.c 1b2a96c425048bf96eb97ba49235c71f54917b1d F src/whereexpr.c 197a448b52073aee43eca3a2233fc113369eb2d4 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd @@ -1334,7 +1334,7 @@ F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2 F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6 -F test/whereD.test 9eba1f9b18e5b19a0b0bcaae5e8c037260195f2b +F test/whereD.test 4b648b4a75911474b7b4dde08a7411b165e3fa39 F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7 F test/whereG.test dde4c52a97385a55be6a7cd46be8373f0cf35501 @@ -1422,8 +1422,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 2910ef64097b890c9f8929bf609ea2827db7ac97 ab94603974a0ad5342e5aee27603162652e70492 -R a5e195d58a480f7623421e36c529b8b4 -T +closed ab94603974a0ad5342e5aee27603162652e70492 -U drh -Z e540fd89ce55e7aa63a8bd5d9b52b2c8 +P 512caa1ad30e6f699e2d006d5ab7674d55d2c746 +R 7c9589ab5b77780aed167682522067f5 +T *branch * covering-or +T *sym-covering-or * +T -sym-trunk * +U dan +Z 8b3097dd603cdb6f18b5a640e7f1b04b diff --git a/manifest.uuid b/manifest.uuid index ebf0893a75..afc72f756f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -512caa1ad30e6f699e2d006d5ab7674d55d2c746 \ No newline at end of file +a323ac3a9d42bd5cb38d724c7e1180584b91054c \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index cfff895c30..8ad1a17c39 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2376,12 +2376,16 @@ case OP_Column: { u32 t; /* A type code from the record header */ 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; @@ -2390,8 +2394,6 @@ case OP_Column: { assert( pC->eCurType!=CURTYPE_SORTER ); pCrsr = pC->uc.pCursor; - /* 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 ){ @@ -3846,7 +3848,7 @@ seek_not_found: 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 @@ -3855,6 +3857,13 @@ seek_not_found: ** 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; @@ -3869,6 +3878,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 b231cf908e..e1c6d06567 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -74,6 +74,7 @@ typedef struct AuxData AuxData; ** * A virtual table ** * A one-row "pseudotable" stored in a single register */ +typedef struct VdbeCursor VdbeCursor; struct VdbeCursor { u8 eCurType; /* One of the CURTYPE_* values above */ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ @@ -100,6 +101,8 @@ struct VdbeCursor { int seekResult; /* Result of previous sqlite3BtreeMoveto() */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ + VdbeCursor *pAltCursor; /* Set by OP_Seek */ + int *aAltMap; /* Set by OP_Seek */ #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif @@ -124,7 +127,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 @@ -423,7 +425,7 @@ struct Vdbe { void sqlite3VdbeError(Vdbe*, const char *, ...); 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 3bbc5aad53..6e368cfdbd 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3008,9 +3008,15 @@ int sqlite3VdbeCursorRestore(VdbeCursor *p){ ** If the cursor is already pointing to the correct row and that row has ** not been deleted out from under the cursor, then this routine is a no-op. */ -int sqlite3VdbeCursorMoveto(VdbeCursor *p){ +int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){ + VdbeCursor *p = *pp; if( p->eCurType==CURTYPE_BTREE ){ if( p->deferredMoveto ){ + if( p->aAltMap && p->aAltMap[*piCol] ){ + *pp = p->pAltCursor; + *piCol = p->aAltMap[*piCol] - 1; + return SQLITE_OK; + } return handleDeferredMoveto(p); } if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){ diff --git a/src/wherecode.c b/src/wherecode.c index 4fd7399efd..eacd6d08d1 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -746,6 +746,54 @@ static void codeCursorHint( # define codeCursorHint(A,B,C) /* No-op */ #endif /* SQLITE_ENABLE_CURSOR_HINTS */ +/* +** 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); + if( ai ){ + for(i=0; inColumn-1; i++){ + assert( pIdx->aiColumn[i]nCol ); + if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]] = 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. @@ -1232,7 +1280,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg); VdbeCoverage(v); }else{ - sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ + codeDeferredSeek(pWInfo, pIdx, iCur, iRowidReg, iIdxCur); } }else if( iCur!=iIdxCur ){ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); diff --git a/test/whereD.test b/test/whereD.test index 17fdac7017..1d70103a70 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 3} +} {1 one 2 two search 2} 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 3} @@ -271,5 +271,34 @@ 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} finish_test