]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Continuing improvements to the multi-index OR-clause optimizer. Added a
authordrh <drh@noemail.net>
Tue, 23 Dec 2008 23:56:22 +0000 (23:56 +0000)
committerdrh <drh@noemail.net>
Tue, 23 Dec 2008 23:56:22 +0000 (23:56 +0000)
few simple test cases. (CVS 6062)

FossilOrigin-Name: 55d4f493e7df8515574a75caec9967d6c71b6012

manifest
manifest.uuid
src/delete.c
src/select.c
src/sqliteInt.h
src/update.c
src/where.c
test/where7.test [new file with mode: 0644]

index 85a395b52857d2212545f4035b53b843a4424469..a060a536f3281422ecf43991922232255b23cce2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\swith\sthe\ssavepoint\scode\sand\sin-memory\sjournals.\s(CVS\s6061)
-D 2008-12-23T19:15:57
+C Continuing\simprovements\sto\sthe\smulti-index\sOR-clause\soptimizer.\s\sAdded\sa\nfew\ssimple\stest\scases.\s(CVS\s6062)
+D 2008-12-23T23:56:22
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 77635d0909c2067cee03889a1e04ce910d8fb809
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -110,7 +110,7 @@ F src/build.c 92335a6c6a7c119580be605c5dd1439602d6cf5d
 F src/callback.c bee8949d619b1b7b1e4dfac8a19c5116ae1dd12a
 F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
 F src/date.c e010095d85c895761627bb1d0d61d021aea930a9
-F src/delete.c e2392b6808496fc0a7f54662af3ba677a3e8e44a
+F src/delete.c 6249005bdd8f85db6ec5f31ddb5c07de023693cc
 F src/expr.c a385202af56d622b11d05e8d386def256155152b
 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
 F src/func.c b0e1c61301f33d67b72ab15d85c80ed76e7c98ac
@@ -154,11 +154,11 @@ F src/printf.c 9866a9a9c4a90f6d4147407f373df3fd5d5f9b6f
 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
 F src/resolve.c 18dc9f0df1d60048e012ce6632251063e0dd356a
 F src/rowset.c 2256fa4a928f750e2f3d6fc733523034beceb1d6
-F src/select.c a4316c5e8a417687e159b3d3ae689363d1dec5df
+F src/select.c 6c2a5675c21bef11d8160f3dc97e1adfbf26bbb9
 F src/shell.c 65d19f8996a160f288087e31810f24025439c62a
 F src/sqlite.h.in 065a828e299960316aa34f05b9f0f10f33afe4c8
 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
-F src/sqliteInt.h 9411acda2959c3494bafb1ac98048a53ee920ea3
+F src/sqliteInt.h 2362e805d375c547f6d91d4732da8f93e1e668af
 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c 23db1e5f27c03160987c122a078b4bb51ef0b2f8
@@ -194,7 +194,7 @@ F src/test_thread.c d74fc445e0dba0e00806117eb449b307c0b146bf
 F src/test_wsd.c c297d7d6b8a990239e1bd25935e81d612d8ae31d
 F src/tokenize.c aaa5fa6a4536a9dd7c855a3f66f32508f1612138
 F src/trigger.c 5a669d8fc9197db393ff85fa95ec882282162bb5
-F src/update.c 080889d241e4dcd1c545c8051eb6de86f4939295
+F src/update.c 8c4925f9ca664effc8a1faaad67449d2074567b1
 F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245
 F src/util.c ea62608f66f33a7e8322de83024ae37c415c0c7f
 F src/vacuum.c 383d6297bddc011ab04a9eed110db6eaf523e8e9
@@ -207,7 +207,7 @@ F src/vdbeblob.c b0dcebfafedcf9c0addc7901ad98f6f986c08935
 F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d
 F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43
 F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d
-F src/where.c 3f4e8020d605b06139b877a9714a10cc3ba63906
+F src/where.c ef69833cdf0e19a91ec1b305b506d100ac3f648f
 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911
@@ -654,6 +654,7 @@ F test/where3.test 97d3936e6a443b968f1a61cdcc0f673252000e94
 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
 F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf
+F test/where7.test b04da5cee08a573c120c95781d7413a7e25ac8d5
 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
 F test/zeroblob.test 792124852ec61458a2eb527b5091791215e0be95
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
@@ -684,7 +685,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P d2105f617eeb04c8177546c45bf6c63e72757f91
-R 62a2a7b3ecef75a74f7c0626bd24ccb4
-U danielk1977
-Z 52a2a6db5bd35226b01afc59433c7e60
+P 26ceebf38e7ae7bbda3284995b03f829a2d2493f
+R 7db5298c8cbec76d68072c3747b105ad
+U drh
+Z 555ee7a0ef7229cf7451a3a18c0b778e
index 2aff376120eb305bf2f6e1043b72d300a64dfadd..161065fe898e39a8c50d767ad9242ae0592094a3 100644 (file)
@@ -1 +1 @@
-26ceebf38e7ae7bbda3284995b03f829a2d2493f
\ No newline at end of file
+55d4f493e7df8515574a75caec9967d6c71b6012
\ No newline at end of file
index 934b76da9ce07a3f86fe1f391f0a8b972938e2d9..eb683f59b169c2344eb13047a059d65c76137afd 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** in order to generate code for DELETE FROM statements.
 **
-** $Id: delete.c,v 1.190 2008/12/10 21:19:57 drh Exp $
+** $Id: delete.c,v 1.191 2008/12/23 23:56:22 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -392,21 +392,15 @@ void sqlite3DeleteFrom(
     int iRowid = ++pParse->nMem;    /* Used for storing rowid values. */
     int iRowSet = ++pParse->nMem;   /* Register for rowset of rows to delete */
 
-    /* Begin the database scan
+    /* Collect rowids of every row to be deleted.
     */
-    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
+    sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
+    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
+                               WHERE_FILL_ROWSET, iRowSet);
     if( pWInfo==0 ) goto delete_from_cleanup;
-
-    /* Remember the rowid of every item to be deleted.
-    */
-    sqlite3VdbeAddOp2(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, iRowid);
-    sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iRowid);
     if( db->flags & SQLITE_CountRows ){
       sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
     }
-
-    /* End the database scan loop.
-    */
     sqlite3WhereEnd(pWInfo);
 
     /* Open the pseudo-table used to store OLD if there are triggers.
index 03046de31b82bce2fd7821eccd19f36a54cb6fdf..e83dda11d6a080b172ab135b94dedb4c56b85100 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.494 2008/12/10 22:15:00 drh Exp $
+** $Id: select.c,v 1.495 2008/12/23 23:56:22 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -3705,7 +3705,7 @@ int sqlite3Select(
     /* This case is for non-aggregate queries
     ** Begin the database scan
     */
-    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
+    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0, 0);
     if( pWInfo==0 ) goto select_end;
 
     /* If sorting index that was created by a prior OP_OpenEphemeral 
@@ -3826,7 +3826,7 @@ int sqlite3Select(
       ** in the right order to begin with.
       */
       sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
-      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
+      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
       if( pWInfo==0 ) goto select_end;
       if( pGroupBy==0 ){
         /* The optimizer is able to deliver rows in group by order so
@@ -4024,7 +4024,7 @@ int sqlite3Select(
       ** of output.
       */
       resetAccumulator(pParse, &sAggInfo);
-      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
+      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag, 0);
       if( pWInfo==0 ){
         sqlite3ExprListDelete(db, pDel);
         goto select_end;
index 56d07f8a4abe4bb4961273226a2747f4807ef33b..221813b818eeb3aa1617e3bc7cf6db51c11be5cb 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.814 2008/12/23 13:35:23 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.815 2008/12/23 23:56:22 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1595,10 +1595,11 @@ struct WhereLevel {
 /*
 ** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin().
 */
-#define WHERE_ORDERBY_NORMAL     0   /* No-op */
-#define WHERE_ORDERBY_MIN        1   /* ORDER BY processing for min() func */
-#define WHERE_ORDERBY_MAX        2   /* ORDER BY processing for max() func */
-#define WHERE_ONEPASS_DESIRED    4   /* Want to do one-pass UPDATE/DELETE */
+#define WHERE_ORDERBY_NORMAL    0x000  /* No-op */
+#define WHERE_ORDERBY_MIN       0x001  /* ORDER BY processing for min() func */
+#define WHERE_ORDERBY_MAX       0x002  /* ORDER BY processing for max() func */
+#define WHERE_ONEPASS_DESIRED   0x004  /* Want to do one-pass UPDATE/DELETE */
+#define WHERE_FILL_ROWSET       0x008  /* Save results in a RowSet object */
 
 /*
 ** The WHERE clause processing routine has two halves.  The
@@ -1610,13 +1611,13 @@ struct WhereLevel {
 struct WhereInfo {
   Parse *pParse;       /* Parsing and code generating context */
   u8 okOnePass;        /* Ok to use one-pass algorithm for UPDATE or DELETE */
+  int regRowSet;                 /* Store rowids in this rowset if >=0 */
   SrcList *pTabList;             /* List of tables in the join */
   int iTop;                      /* The very beginning of the WHERE loop */
   int iContinue;                 /* Jump here to continue with next record */
   int iBreak;                    /* Jump here to break out of the loop */
   int nLevel;                    /* Number of nested loop */
   struct WhereClause *pWC;       /* Decomposition of the WHERE clause */
-  sqlite3_index_info **apInfo;   /* Array of pointers to index info objects */
   WhereLevel a[1];               /* Information about each nest loop in WHERE */
 };
 
@@ -2260,7 +2261,7 @@ Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *,
 #endif
 void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
 void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
+WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8, int);
 void sqlite3WhereEnd(WhereInfo*);
 int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int);
 void sqlite3ExprCodeMove(Parse*, int, int, int);
index 24ad882e4491a028b8b4a5621c1d7f87e3103d26..cd1fd218f9ab2f077cf8c8fc714b6a97150b6f1c 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.190 2008/12/10 22:15:00 drh Exp $
+** $Id: update.c,v 1.191 2008/12/23 23:56:22 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -346,7 +346,7 @@ void sqlite3Update(
   */
   sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
   pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
-                             WHERE_ONEPASS_DESIRED);
+                             WHERE_ONEPASS_DESIRED, 0);
   if( pWInfo==0 ) goto update_cleanup;
   okOnePass = pWInfo->okOnePass;
 
index 8f785e5647bc12d8dd5a40900eb4c612a84a80ce..aac9b391055f9ff996cfbdcb31d5add1c12244fe 100644 (file)
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.342 2008/12/23 16:23:05 drh Exp $
+** $Id: where.c,v 1.343 2008/12/23 23:56:22 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1814,7 +1814,7 @@ static void bestIndex(
     }
   }
 
-#if SQLITE_ENABLE_MULTI_OR
+#ifndef SQLITE_OMIT_OR_OPTIMIZATION
   /* Search for an OR-clause that can be used to look up the table.
   */
   maskSrc = getMask(pWC->pMaskSet, iCur);
@@ -1850,7 +1850,7 @@ static void bestIndex(
       }
     }
   }
-#endif
+#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
 
   /* If the pSrc table is the right table of a LEFT JOIN then we may not
   ** use an index to satisfy IS NULL constraints on that table.  This is
@@ -2174,6 +2174,25 @@ static int codeAllEqualityTerms(
   return regBase;
 }
 
+/*
+** Return TRUE if the WhereClause pWC contains no terms that
+** are not virtual and which have not been coded.
+**
+** To put it another way, return TRUE if no additional WHERE clauses
+** tests are required in order to establish that the current row
+** should go to output and return FALSE if there are some terms of
+** the WHERE clause that need to be validated before outputing the row.
+*/
+static int whereRowReadyForOutput(WhereClause *pWC){
+  WhereTerm *pTerm;
+  int j;
+  for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
+    if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED))==0 ) return 0;
+  }
+  return 1;
+}
+
 /*
 ** Generate code for the start of the iLevel-th loop in the WHERE clause
 ** implementation described by pWInfo.
@@ -2195,8 +2214,10 @@ static Bitmask codeOneLoopStart(
   Parse *pParse;                  /* Parsing context */
   Vdbe *v;                        /* The prepared stmt under constructions */
   struct SrcList_item *pTabItem;  /* FROM clause term being coded */
-  int addrBrk;
-  int addrCont;
+  int addrBrk;                    /* Jump here to break out of the loop */
+  int addrCont;                   /* Jump here to continue with next cycle */
+  int regRowSet;       /* Write rowids to this RowSet if non-negative */
+  int codeRowSetEarly; /* True if index fully constrains the search */
   
 
   pParse = pWInfo->pParse;
@@ -2207,6 +2228,8 @@ static Bitmask codeOneLoopStart(
   iCur = pTabItem->iCursor;
   bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
   omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0;
+  regRowSet = pWInfo->regRowSet;
+  codeRowSetEarly = 0;
 
   /* Create labels for the "break" and "continue" instructions
   ** for the current loop.  Jump to addrBrk to break out of a loop.
@@ -2263,7 +2286,6 @@ static Bitmask codeOneLoopStart(
     sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
     sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
                       pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
-    sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
     pVtabIdx->needToFreeIdxStr = 0;
     for(j=0; j<nConstraint; j++){
       if( aUsage[j].omit ){
@@ -2274,6 +2296,12 @@ static Bitmask codeOneLoopStart(
     pLevel->op = OP_VNext;
     pLevel->p1 = iCur;
     pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+    codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0;
+    if( codeRowSetEarly ){
+      sqlite3VdbeAddOp2(v, OP_VRowid, iCur, iReg);
+      sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, iReg);
+    }
+    sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
   }else
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
 
@@ -2294,6 +2322,10 @@ static Bitmask codeOneLoopStart(
     addrNxt = pLevel->addrNxt;
     sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, addrNxt);
     sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, r1);
+    codeRowSetEarly = (pWC->nTerm==1 && regRowSet>=0) ?1:0;
+    if( codeRowSetEarly ){
+      sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
+    }
     sqlite3ReleaseTempReg(pParse, rtmp);
     VdbeComment((v, "pk"));
     pLevel->op = OP_Noop;
@@ -2360,11 +2392,17 @@ static Bitmask codeOneLoopStart(
     pLevel->op = bRev ? OP_Prev : OP_Next;
     pLevel->p1 = iCur;
     pLevel->p2 = start;
-    if( testOp!=OP_Noop ){
+    codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0;
+    if( codeRowSetEarly || testOp!=OP_Noop ){
       int r1 = sqlite3GetTempReg(pParse);
       sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
-      sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1);
-      sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+      if( testOp!=OP_Noop ){
+        sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1);
+        sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+      }
+      if( codeRowSetEarly ){
+        sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
+      }
       sqlite3ReleaseTempReg(pParse, r1);
     }
   }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
@@ -2547,9 +2585,16 @@ static Bitmask codeOneLoopStart(
     }
 
     /* Seek the table cursor, if required */
-    if( !omitTable ){
+    disableTerm(pLevel, pRangeStart);
+    disableTerm(pLevel, pRangeEnd);
+    codeRowSetEarly = regRowSet>=0 ? whereRowReadyForOutput(pWC) : 0;
+    if( !omitTable || codeRowSetEarly ){
       sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, r1);
-      sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1);  /* Deferred seek */
+      if( codeRowSetEarly ){
+        sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
+      }else{
+        sqlite3VdbeAddOp2(v, OP_Seek, iCur, r1);  /* Deferred seek */
+      }
     }
     sqlite3ReleaseTempReg(pParse, r1);
 
@@ -2558,11 +2603,9 @@ static Bitmask codeOneLoopStart(
     */
     pLevel->op = bRev ? OP_Prev : OP_Next;
     pLevel->p1 = iIdxCur;
-    disableTerm(pLevel, pRangeStart);
-    disableTerm(pLevel, pRangeEnd);
   }else
 
-#if SQLITE_ENABLE_MULTI_OR
+#ifndef SQLITE_OMIT_OR_OPTIMIZATION
   if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
     /* Case 4:  Two or more separately indexed terms connected by OR
     **
@@ -2591,7 +2634,7 @@ static Bitmask codeOneLoopStart(
     **        Goto       0, A
     **     B:
     */
-    int regRowset;         /* Register holding the RowSet object */
+    int regOrRowset;       /* Register holding the RowSet object */
     int regNextRowid;      /* Register holding next rowid */
     WhereTerm *pTerm;      /* The complete OR-clause */
     WhereClause *pOrWc;    /* The OR-clause broken out into subterms */
@@ -2603,33 +2646,41 @@ static Bitmask codeOneLoopStart(
     assert( pTerm->eOperator==WO_OR );
     assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
     pOrWc = &pTerm->u.pOrInfo->wc;
-    
-    regRowset = sqlite3GetTempReg(pParse);
-    regNextRowid = sqlite3GetTempReg(pParse);
-    sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
+    codeRowSetEarly = (regRowSet>=0 && pWC->nTerm==1) ?1:0;
+
+    if( codeRowSetEarly ){
+      regOrRowset = regRowSet;
+    }else{
+      regOrRowset = sqlite3GetTempReg(pParse);
+      sqlite3VdbeAddOp2(v, OP_Null, 0, regOrRowset);
+    }
     oneTab.nSrc = 1;
     oneTab.nAlloc = 1;
     oneTab.a[0] = *pTabItem;
     for(j=0, pOrTerm=pOrWc->a; j<pOrWc->nTerm; j++, pOrTerm++){
       WhereInfo *pSubWInfo;
       if( pOrTerm->leftCursor!=iCur ) continue;
-      pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, 0);
+      pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0,
+                        WHERE_FILL_ROWSET, regOrRowset);
       if( pSubWInfo ){
-        sqlite3VdbeAddOp2(v, OP_Rowid, oneTab.a[0].iCursor, regNextRowid);
-        sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowset, regNextRowid);
         pSubWInfo->a[0].plan.wsFlags |= WHERE_IDX_ONLY;
         sqlite3WhereEnd(pSubWInfo);
       }
     }
     sqlite3VdbeResolveLabel(v, addrCont);
-    addrCont = 
-       sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowset, addrBrk, regNextRowid);
-    sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid);
-    sqlite3ReleaseTempReg(pParse, regNextRowid);
-    pLevel->op = OP_Goto;
-    pLevel->p2 = addrCont;
+    if( !codeRowSetEarly ){
+      regNextRowid = sqlite3GetTempReg(pParse);
+      addrCont = 
+         sqlite3VdbeAddOp3(v, OP_RowSetRead, regOrRowset,addrBrk,regNextRowid);
+      sqlite3VdbeAddOp2(v, OP_Seek, iCur, regNextRowid);
+      sqlite3ReleaseTempReg(pParse, regNextRowid);
+      /* sqlite3ReleaseTempReg(pParse, regOrRowset); // Preserve the RowSet */
+      pLevel->op = OP_Goto;
+      pLevel->p2 = addrCont;
+    }
+    disableTerm(pLevel, pTerm);
   }else
-#endif /* SQLITE_ENABLE_MULTI_OR */
+#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
 
   {
     /* Case 5:  There is no usable index.  We must do a complete
@@ -2641,6 +2692,7 @@ static Bitmask codeOneLoopStart(
     pLevel->p1 = iCur;
     pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, OP_Rewind, iCur, addrBrk);
     pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+    codeRowSetEarly = 0;
   }
   notReady &= ~getMask(pWC->pMaskSet, iCur);
 
@@ -2685,6 +2737,25 @@ static Bitmask codeOneLoopStart(
       pTerm->wtFlags |= TERM_CODED;
     }
   }
+
+  /*
+  ** If it was requested to store the results in a rowset and that has
+  ** not already been do, then do so now.
+  */
+  if( regRowSet>=0 && !codeRowSetEarly ){
+    int r1 = sqlite3GetTempReg(pParse);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+    if(  (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+      sqlite3VdbeAddOp2(v, OP_VRowid, iCur, r1);
+    }else
+#endif
+    {
+      sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
+    }
+    sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, r1);
+    sqlite3ReleaseTempReg(pParse, r1);
+  }
+
   return notReady;
 }
 
@@ -2813,7 +2884,8 @@ WhereInfo *sqlite3WhereBegin(
   SrcList *pTabList,    /* A list of all tables to be scanned */
   Expr *pWhere,         /* The WHERE clause */
   ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
-  u8 wctrlFlags         /* One of the WHERE_* flags defined in sqliteInt.h */
+  u8 wctrlFlags,        /* One of the WHERE_* flags defined in sqliteInt.h */
+  int regRowSet         /* Register hold RowSet if WHERE_FILL_ROWSET is set */
 ){
   int i;                     /* Loop counter */
   WhereInfo *pWInfo;         /* Will become the return value of this function */
@@ -2858,6 +2930,7 @@ WhereInfo *sqlite3WhereBegin(
   pWInfo->pParse = pParse;
   pWInfo->pTabList = pTabList;
   pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
+  pWInfo->regRowSet = (wctrlFlags & WHERE_FILL_ROWSET) ? regRowSet : -1;
   pWInfo->pWC = pWC = (WhereClause*)&pWInfo->a[pWInfo->nLevel];
   pMaskSet = (WhereMaskSet*)&pWC[1];
 
diff --git a/test/where7.test b/test/where7.test
new file mode 100644 (file)
index 0000000..e7ca4f7
--- /dev/null
@@ -0,0 +1,94 @@
+# 2008 December 23
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing the multi-index OR clause optimizer.
+#
+# $Id: where7.test,v 1.1 2008/12/23 23:56:22 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !or_opt {
+  finish_test
+  return
+}
+
+# Evaluate SQL.  Return the result set followed by the
+# and the number of full-scan steps.
+#
+proc count_steps {sql} {
+  set r [db eval $sql]
+  lappend r scan [db status step]
+}
+
+# Build some test data
+#
+do_test where7-1.1 {
+  execsql {
+    CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d);
+    INSERT INTO t1 VALUES(1,2,3,4);
+    INSERT INTO t1 VALUES(2,3,4,5);
+    INSERT INTO t1 VALUES(3,4,6,8);
+    INSERT INTO t1 VALUES(4,5,10,15);
+    INSERT INTO t1 VALUES(5,10,100,1000);
+    CREATE INDEX t1b ON t1(b);
+    CREATE INDEX t1c ON t1(c);
+    SELECT * FROM t1;
+  }
+} {1 2 3 4 2 3 4 5 3 4 6 8 4 5 10 15 5 10 100 1000}
+do_test where7-1.2 {
+  count_steps {
+    SELECT a FROM t1 WHERE b=3 OR c=6
+  }
+} {2 3 scan 0}
+do_test where7-1.3 {
+  count_steps {
+    SELECT a FROM t1 WHERE b=3 OR +c=6
+  }
+} {2 3 scan 4}
+do_test where7-1.4 {
+  count_steps {
+    SELECT a FROM t1 WHERE +b=3 OR c=6
+  }
+} {2 3 scan 4}
+do_test where7-1.5 {
+  count_steps {
+    SELECT a FROM t1 WHERE 3=b OR c=6
+  }
+} {2 3 scan 0}
+do_test where7-1.6 {
+  count_steps {
+    SELECT a FROM t1 WHERE (3=b OR c=6) AND +a>0
+  }
+} {2 3 scan 0}
+do_test where7-1.7 {
+  count_steps {
+    SELECT a FROM t1 WHERE (b=3 OR c>10)
+  }
+} {2 5 scan 0}
+do_test where7-1.8 {
+  count_steps {
+    SELECT a FROM t1 WHERE (b=3 OR c>=10)
+  }
+} {2 4 5 scan 0}
+do_test where7-1.9 {
+  count_steps {
+    SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4)
+  }
+} {2 4 5 scan 0}
+do_test where7-1.10 {
+  count_steps {
+    SELECT a FROM t1 WHERE (b=3 OR c>=10 OR c=4 OR b>10)
+  }
+} {2 4 5 scan 0}
+
+
+finish_test