]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Make use of covering indexes in the OR optimization.
authordrh <drh@noemail.net>
Wed, 5 Jul 2017 16:20:49 +0000 (16:20 +0000)
committerdrh <drh@noemail.net>
Wed, 5 Jul 2017 16:20:49 +0000 (16:20 +0000)
FossilOrigin-Name: fcbd6abdb1a4cf622ff7e85625b9c2a9bbae92410359872924b7fc1e35046a75

manifest
manifest.uuid
src/select.c
src/vdbe.c
src/vdbeInt.h
src/vdbeaux.c
src/where.c
test/whereD.test

index d0b3c3e3d11a3b59a9ba9bc80c7b0fd738b62e2d..8e31fba7b844be65c84f5244b99ad75b6aed561b 100644 (file)
--- 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
index 6b7c3241758834d61c523e022b1951b83be0502f..387d721e48558d8d39066b1e1ea293dcee47cefc 100644 (file)
@@ -1 +1 @@
-b7ae4b879fc086e9543493843377ae90ceff1fe49c97b4c23367012034c3c9d5
\ No newline at end of file
+fcbd6abdb1a4cf622ff7e85625b9c2a9bbae92410359872924b7fc1e35046a75
\ No newline at end of file
index 8d31da3ddc1a73a034e03a57be35017852da9fc5..58d21758a4c93a59eaf59801adbc6452370fd726 100644 (file)
@@ -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; i<nOrderBy; i++, pItem++){
+    aPermute[0] = nOrderBy;
+    for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
       assert( pItem->u.x.iOrderByCol>0
           && pItem->u.x.iOrderByCol<=p->pEList->nExpr );
       aPermute[i] = pItem->u.x.iOrderByCol - 1;
index f2de90d14c17de2a8a4741bd19cb3c856e73e92e..750f61f57846b78d778f8200f0a83f29cb51cfe9 100644 (file)
@@ -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->p1<p->nCursor );
-  pC = p->apCsr[pOp->p1];
   assert( pC!=0 );
   assert( p2<pC->nField );
   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;
 }
   
index d3955af31e86b28802e7060b684d116d548cac2d..b3ebccababf7f5a4ba30fe578e08f34128a43c3b 100644 (file)
@@ -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*);
index 9c5d9acca9b130882d2c6995e6c693f730fe757f..f1262489e6a913fd8b260514bfa5e1f24210c783 100644 (file)
@@ -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; i<n && j<nTemp-7; i++){
+        if( j>1 ) zTemp[j++] = ',';
+        sqlite3_snprintf(nTemp-j, zTemp+j, "%d", ai[i]);
+        j += sqlite3Strlen30(zTemp+j);
+      }
+      if( i<n ){
+        memcpy(zTemp+j, ",...]", 6);
+      }else{
+        memcpy(zTemp+j, "]", 2);
+      }
       break;
     }
     case P4_SUBPROGRAM: {
@@ -2818,8 +2832,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->deferredMoveto ){
+    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) ){
index eff53bc9ba4fd96bfc04950761b62553a8951469..0df25fc87a31f6e2da6a6e03184dff18ea68dcda 100644 (file)
@@ -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; i<pIdx->nColumn-1; i++){
+        assert( pIdx->aiColumn[i]<pTab->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);
index db993040b014cec18af943fd4b96f93f77edcca8..82c1cb71879308f212375b8a7c3051e2764610c7 100644 (file)
@@ -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