]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental attempt to make better use of covering indexes within OR queries.
authordan <dan@noemail.net>
Fri, 29 Jan 2016 19:29:45 +0000 (19:29 +0000)
committerdan <dan@noemail.net>
Fri, 29 Jan 2016 19:29:45 +0000 (19:29 +0000)
FossilOrigin-Name: a323ac3a9d42bd5cb38d724c7e1180584b91054c

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

index abb80cb617bbcb693dc9bb76d601583b94cfeb5b..4025b2f01aa962ee308bfb21d0c1c5f534929b27 100644 (file)
--- 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
index ebf0893a7586d6e5bc7bff7c918710fc278301b6..afc72f756f494c768d12dcdcff8a617994293d73 100644 (file)
@@ -1 +1 @@
-512caa1ad30e6f699e2d006d5ab7674d55d2c746
\ No newline at end of file
+a323ac3a9d42bd5cb38d724c7e1180584b91054c
\ No newline at end of file
index cfff895c306bc301ec35ef411d6c6963d1178ffb..8ad1a17c3980c53b7ab8f8f3516f032d86f7f444 100644 (file)
@@ -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->p1<p->nCursor );
-  pC = p->apCsr[pOp->p1];
   assert( pC!=0 );
   assert( p2<pC->nField );
   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;
 }
   
index b231cf908e66499ff1acdaf9ba64c91d83bd947c..e1c6d06567ce936f06fa0f0632c22fbe2b73bca9 100644 (file)
@@ -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*);
index 3bbc5aad5323c78f910d248227508516ca871394..6e368cfdbd7b1775bc5626f0a5224fb92799eabe 100644 (file)
@@ -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) ){
index 4fd7399efdba81faa50746700debab36783985ca..eacd6d08d1d86ed0ccec53191f4d6012e65136f7 100644 (file)
@@ -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; i<pIdx->nColumn-1; i++){
+        assert( pIdx->aiColumn[i]<pTab->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);
index 17fdac7017c5d592870d4040a65a2cbe3fe00c15..1d70103a70ed7bf888d4bb259efce2825fc4d286 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 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