]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Backport check-in [62678be3df35cd]: branch-3.7.2
authordrh <drh@noemail.net>
Sat, 25 Aug 2012 00:49:08 +0000 (00:49 +0000)
committerdrh <drh@noemail.net>
Sat, 25 Aug 2012 00:49:08 +0000 (00:49 +0000)
When the same index is used for all OR-terms in a WHERE clause, then try to
use that index as a covering index.

FossilOrigin-Name: 865dfcbaa59431422b5980c66c8e29e80c4ed7c9

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

index 187eec8f3194b97acb1433bad218ac497cd3776a..545be12c58440c1039a1251fb1441287d2a565e0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Cherrypick\sthe\s[3513bf6ee090d9]\sso\sthat\sthe\ssqlite_source_id()\sfunction\sworks\scorrectly\seven\swith\snewer\sversions\sof\sFossil
-D 2011-10-25T21:18:17.179
+C Backport\scheck-in\s[62678be3df35cd]:\nWhen\sthe\ssame\sindex\sis\sused\sfor\sall\sOR-terms\sin\sa\sWHERE\sclause,\sthen\stry\sto\nuse\sthat\sindex\sas\sa\scovering\sindex.
+D 2012-08-25T00:49:08.886
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 543f91f24cd7fee774ecc0a61c19704c0c3e78fd
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -121,10 +121,10 @@ F src/callback.c da3c38d0ef5d7f04fae371e519bda61aa9cb1704
 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c 2e39d3374e785a63117e077bcba9d4a6656df363
 F src/date.c 5dd8448a0bfea8d31fb14cff487d0c06ff8c8b20
-F src/delete.c daff6cef77fe8ed57b8acfc5ecebce28244af2fa
+F src/delete.c e179bd660c559c630813b01d04db663de56a124d
 F src/expr.c 9c8147b9c2ffcd792191bad51028bf11719bac09
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c ca7cdb310a5095ce2b304c6524260595f73827dc
+F src/fkey.c b792889ef26dd4531e77de35821547c9d2b72ca1
 F src/func.c 464b0dc70618b896c402c574eb04bc5eacf35341
 F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
@@ -168,11 +168,11 @@ F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506
 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
 F src/resolve.c 36368f44569208fa074e61f4dd0b6c4fb60ca2b4
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
-F src/select.c a9828845d04c1f583b9fb5260c0cab33da88d16e
+F src/select.c 6a7353def8f6e5d800198214bf5477e8d0524e36
 F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
 F src/sqlite.h.in 2d72a6242df41c517e38eec8791abcf5484a36f1
 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
-F src/sqliteInt.h e679eece2fb35bb324a56d614d91ccf961f70cf4
+F src/sqliteInt.h da99fe107f8f51d0b624428b773122cfccb49e2a
 F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
 F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -214,7 +214,7 @@ F src/test_vfs.c 367e050ebedac89f741dbfdcade3d584a8061dc9
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
 F src/trigger.c b8bedb9c0084ceb51a40f54fcca2ce048c8de852
-F src/update.c c6be6a5af1198aeac9b25d842d97e52695ffc9e6
+F src/update.c e85ead70c972dcb0655ee2ce91422b1d50bcb7b0
 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
 F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
 F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f
@@ -230,7 +230,7 @@ F src/vtab.c 0e8e0cb30dffb078367e843e84e37ef99236c7e4
 F src/wal.c 5ac2119e23ee4424599d4275b66dc88d612a0543
 F src/wal.h 96669b645e27cd5a111ba59f0cae7743a207bc3c
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c db40458b67dbd77c658178ec6c53432807759c3f
+F src/where.c a4b2ea85eeca3350354570c3b06eee2ce00f8075
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
 F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
@@ -424,7 +424,7 @@ F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
 F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
 F test/fts3malloc.test 059592c4f37ccd30138bbf8e3e5b7982cb5c8f2e
 F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
-F test/fts3query.test 2468caf7938dbc3be2e049524320ce4faf2227b3
+F test/fts3query.test 682cf2855eebdc295f69ae1573b46f388dfac280
 F test/fts3rnd.test 707533ce943f490443ce5e696236bb1675a37635
 F test/fts3snippet.test 9f9a4a7e396c5d8ce2898be65ebabc429555430f
 F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f
@@ -818,6 +818,7 @@ F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
 F test/where9.test 24f19ad14bb1b831564ced5273e681e495662848
 F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
 F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
+F test/whereD.test 6b6270480480a30038029dd6cbceef54d009dbc6
 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
@@ -852,7 +853,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 14bc58ca70336aed62069f223324304835991c55
-R af5db398d4569192dd77a70de5aadb2c
+P 89d63a0e1db6a86fa173fb8a4e3a3904ef9e488c
+R 1c2de8c77bbe509e9e60c8893c5a6225
 U drh
-Z e43e421cba9e7717a13b85e140461f3f
+Z d3dad17d9f134d722fd6b0b2a2f50b29
index 8bc48638879e53bff201296f75ff8096705ae832..e5a58c23d83336cbb41cc5b6d74d789e3ece532a 100644 (file)
@@ -1 +1 @@
-89d63a0e1db6a86fa173fb8a4e3a3904ef9e488c
\ No newline at end of file
+865dfcbaa59431422b5980c66c8e29e80c4ed7c9
\ No newline at end of file
index ca516fbf1139a5856c44b7f67803ff6030f4a4e1..13436ea1814ced9c1ae9ff71a0e143026b356857 100644 (file)
@@ -363,7 +363,7 @@ void sqlite3DeleteFrom(
     */
     sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
     pWInfo = sqlite3WhereBegin(
-        pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
+        pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
     );
     if( pWInfo==0 ) goto delete_from_cleanup;
     regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
index 2e03545d4058e6708ff9643b3020ef10d3faf9f2..37e12cf15c1a5afa66b22a005777e9b100f3a053 100644 (file)
@@ -548,7 +548,7 @@ static void fkScanChildren(
   ** clause. If the constraint is not deferred, throw an exception for
   ** each row found. Otherwise, for deferred constraints, increment the
   ** deferred constraint counter by nIncr for each row selected.  */
-  pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0);
+  pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
   if( nIncr>0 && pFKey->isDeferred==0 ){
     sqlite3ParseToplevel(pParse)->mayAbort = 1;
   }
index dd812a709c1aa2eb0fb1fe3c0d67a6493b327c55..a9fad2a5da6bd19ed123862927e0dcfc965fcb32 100644 (file)
@@ -3837,7 +3837,7 @@ int sqlite3Select(
     ExprList *pDist = (isDistinct ? p->pEList : 0);
 
     /* Begin the database scan. */
-    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
+    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0,0);
     if( pWInfo==0 ) goto select_end;
     if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
 
@@ -4004,7 +4004,7 @@ int sqlite3Select(
       ** in the right order to begin with.
       */
       sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
-      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
+      pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0, 0);
       if( pWInfo==0 ) goto select_end;
       if( pGroupBy==0 ){
         /* The optimizer is able to deliver rows in group by order so
@@ -4263,7 +4263,7 @@ int sqlite3Select(
         ** of output.
         */
         resetAccumulator(pParse, &sAggInfo);
-        pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
+        pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax,0,flag,0);
         if( pWInfo==0 ){
           sqlite3ExprListDelete(db, pDel);
           goto select_end;
index 4de16fc712d408d2acbfa3b80a63e5c483788aee..2d97bfb90447062026dcb36ae62451aa62481268 100644 (file)
@@ -1877,6 +1877,7 @@ struct WhereLevel {
         int addrInTop;         /* Top of the IN loop */
       } *aInLoop;           /* Information about each nested IN operator */
     } in;                 /* Used when plan.wsFlags&WHERE_IN_ABLE */
+    Index *pCovidx;       /* Possible covering index for WHERE_MULTI_OR */
   } u;
 
   /* The following field is really not part of the current level.  But
@@ -2678,7 +2679,8 @@ 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**,ExprList*,u16);
+WhereInfo *sqlite3WhereBegin(
+    Parse*,SrcList*,Expr*,ExprList**,ExprList*,u16,int);
 void sqlite3WhereEnd(WhereInfo*);
 int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
 void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
index a2fd7b9c0ce10e7a112a897012759c4db7a5edd5..665f3fff56317e05b76b9cd977576dbfa8851fca 100644 (file)
@@ -313,7 +313,7 @@ void sqlite3Update(
   */
   sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
   pWInfo = sqlite3WhereBegin(
-      pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
+      pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
   );
   if( pWInfo==0 ) goto update_cleanup;
   okOnePass = pWInfo->okOnePass;
index 00c25a5a1de4154efd4bce5619f91f19dfbb37ee..4f48605ef851a00891d928a3a00394263aa9e5cf 100644 (file)
@@ -3568,7 +3568,7 @@ static int codeAllEqualityTerms(
     int r1;
     int k = pIdx->aiColumn[j];
     pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
-    if( NEVER(pTerm==0) ) break;
+    if( pTerm==0 ) break;
     /* The following true for indices with redundant columns. 
     ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
     testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
@@ -4242,6 +4242,8 @@ static Bitmask codeOneLoopStart(
     WhereClause *pOrWc;    /* The OR-clause broken out into subterms */
     WhereTerm *pFinal;     /* Final subterm within the OR-clause. */
     SrcList *pOrTab;       /* Shortened table list or OR-clause generation */
+    Index *pCov = 0;             /* Potential covering index (or NULL) */
+    int iCovCur = pParse->nTab++;  /* Cursor used for index scans (if any) */
 
     int regReturn = ++pParse->nMem;           /* Register used with OP_Gosub */
     int regRowset = 0;                        /* Register for RowSet object */
@@ -4260,7 +4262,7 @@ static Bitmask codeOneLoopStart(
     pLevel->op = OP_Return;
     pLevel->p1 = regReturn;
 
-    /* Set up a new SrcList ni pOrTab containing the table being scanned
+    /* Set up a new SrcList in pOrTab containing the table being scanned
     ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
     ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
     */
@@ -4307,8 +4309,10 @@ static Bitmask codeOneLoopStart(
         /* Loop through table entries that match term pOrTerm. */
         pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
                         WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
-                        WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
+                        WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur);
+        assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed );
         if( pSubWInfo ){
+          WhereLevel *pLvl;
           explainOneScan(
               pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
           );
@@ -4329,11 +4333,36 @@ static Bitmask codeOneLoopStart(
           */
           if( pSubWInfo->untestedTerms ) untestedTerms = 1;
 
+          /* If all of the OR-connected terms are optimized using the same
+          ** index, and the index is opened using the same cursor number
+          ** by each call to sqlite3WhereBegin() made by this loop, it may
+          ** be possible to use that index as a covering index.
+          **
+          ** If the call to sqlite3WhereBegin() above resulted in a scan that
+          ** uses an index, and this is either the first OR-connected term
+          ** processed or the index is the same as that used by all previous
+          ** terms, set pCov to the candidate covering index. Otherwise, set 
+          ** pCov to NULL to indicate that no candidate covering index will 
+          ** be available.
+          */
+          pLvl = &pSubWInfo->a[0];
+          if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0
+           && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0
+           && (ii==0 || pLvl->plan.u.pIdx==pCov)
+          ){
+            assert( pLvl->iIdxCur==iCovCur );
+            pCov = pLvl->plan.u.pIdx;
+          }else{
+            pCov = 0;
+          }
+
           /* Finish the loop through table entries that match term pOrTerm. */
           sqlite3WhereEnd(pSubWInfo);
         }
       }
     }
+    pLevel->u.pCovidx = pCov;
+    pLevel->iIdxCur = iCovCur;
     sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
     sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
     sqlite3VdbeResolveLabel(v, iLoopBody);
@@ -4549,7 +4578,8 @@ WhereInfo *sqlite3WhereBegin(
   Expr *pWhere,         /* The WHERE clause */
   ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
   ExprList *pDistinct,  /* The select-list for DISTINCT queries - or NULL */
-  u16 wctrlFlags        /* One of the WHERE_* flags defined in sqliteInt.h */
+  u16 wctrlFlags,       /* One of the WHERE_* flags defined in sqliteInt.h */
+  int iIdxCur           /* If WHERE_ONETABLE_ONLY is set, index cursor number */
 ){
   int i;                     /* Loop counter */
   int nByteWInfo;            /* Num. bytes allocated for WhereInfo struct */
@@ -4866,7 +4896,13 @@ WhereInfo *sqlite3WhereBegin(
     testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
     testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
     if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
-      pLevel->iIdxCur = pParse->nTab++;
+      if( (wctrlFlags & WHERE_ONETABLE_ONLY) 
+       && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0 
+      ){
+        pLevel->iIdxCur = iIdxCur;
+      }else{
+        pLevel->iIdxCur = pParse->nTab++;
+      }
     }else{
       pLevel->iIdxCur = -1;
     }
@@ -5118,6 +5154,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
   */
   assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
   for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
+    Index *pIdx = 0;
     struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
     Table *pTab = pTabItem->pTab;
     assert( pTab!=0 );
@@ -5147,12 +5184,15 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
     ** that reference the table and converts them into opcodes that
     ** reference the index.
     */
-    if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){
+    if( pLevel->plan.wsFlags & WHERE_INDEXED ){
+      pIdx = pLevel->plan.u.pIdx;
+    }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
+      pIdx = pLevel->u.pCovidx;
+    }
+    if( pIdx && !db->mallocFailed){
       int k, j, last;
       VdbeOp *pOp;
-      Index *pIdx = pLevel->plan.u.pIdx;
 
-      assert( pIdx!=0 );
       pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
       last = sqlite3VdbeCurrentAddr(v);
       for(k=pWInfo->iTop; k<last; k++, pOp++){
index 68467df9fe560439bc14e096dcecde750c145b67..fc5188c0bc012666ae05b13edb59b4b7db0d7c75 100644 (file)
@@ -116,6 +116,7 @@ do_test fts3query-4.1 {
     CREATE TABLE bt(title);
   }
 } {}
+if 0 {
 do_test fts3query-4.2 {
   eqp "SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date"
 } {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE ft VIRTUAL TABLE INDEX 1:}}
@@ -128,7 +129,6 @@ do_test fts3query-4.4 {
 do_test fts3query-4.5 {
   eqp "SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date"
 } {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE bt USING PRIMARY KEY}}
-
+}
 
 finish_test
-
diff --git a/test/whereD.test b/test/whereD.test
new file mode 100644 (file)
index 0000000..97e4f20
--- /dev/null
@@ -0,0 +1,164 @@
+# 2012 August 24
+#
+# 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 that an index may be used as a covering
+# index when there are OR expressions in the WHERE clause. 
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix whereD
+
+do_execsql_test 1.1 {
+  CREATE TABLE t(i,j,k,m,n);
+  CREATE INDEX ijk ON t(i,j,k);
+  CREATE INDEX jmn ON t(j,m,n);
+
+  INSERT INTO t VALUES(3, 3, 'three', 3, 'tres');
+  INSERT INTO t VALUES(2, 2, 'two', 2, 'dos');
+  INSERT INTO t VALUES(1, 1, 'one', 1, 'uno');
+  INSERT INTO t VALUES(4, 4, 'four', 4, 'cuatro');
+} {}
+
+do_execsql_test 1.2 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {one two}
+do_execsql_test 1.3 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (+i=2 AND j=2);
+} {one two}
+do_execsql_test 1.4 {
+  SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {uno dos}
+do_execsql_test 1.5 {
+  SELECT k, n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2);
+} {one uno two dos}
+do_execsql_test 1.6 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.7 {
+  SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {uno dos tres}
+do_execsql_test 1.8 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2);
+} {one two}
+do_execsql_test 1.9 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3);
+} {one two three}
+do_execsql_test 1.10 {
+  SELECT n FROM t WHERE (i=1 AND j=1) OR (i=2 AND j=2) OR (j=3 AND m=3);
+} {uno dos tres}
+do_execsql_test 1.11 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.12 {
+  SELECT n FROM t WHERE (i=1 AND j=1) OR (j=2 AND m=2) OR (i=3 AND j=3);
+} {uno dos tres}
+do_execsql_test 1.13 {
+  SELECT k FROM t WHERE (j=1 AND m=1) OR (i=2 AND j=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.14 {
+  SELECT k FROM t WHERE (i=1 AND j=1) OR (j=2 AND i=2) OR (i=3 AND j=3);
+} {one two three}
+do_execsql_test 1.15 {
+  SELECT k FROM t WHERE (i=1 AND j=2) OR (i=2 AND j=1) OR (i=3 AND j=4);
+} {}
+do_execsql_test 1.16 {
+  SELECT k FROM t WHERE (i=1 AND (j=1 or j=2)) OR (i=3 AND j=3);
+} {one three}
+
+do_execsql_test 2.0 {
+  CREATE TABLE t1(a,b,c,d);
+  CREATE INDEX t1b ON t1(b);
+  CREATE INDEX t1c ON t1(c);
+  CREATE INDEX t1d ON t1(d);
+  CREATE TABLE t2(x,y);
+  CREATE INDEX t2y ON t2(y);
+  
+  INSERT INTO t1 VALUES(1,2,3,4);
+  INSERT INTO t1 VALUES(5,6,7,8);
+  INSERT INTO t2 VALUES(1,2);
+  INSERT INTO t2 VALUES(2,7);
+  INSERT INTO t2 VALUES(3,4);
+} {}
+do_execsql_test 2.1 {
+  SELECT a, x FROM t1 JOIN t2 ON +y=d OR x=7 ORDER BY a, x;
+} {1 3}
+do_execsql_test 2.2 {
+  SELECT a, x FROM t1 JOIN t2 ON y=d OR x=7 ORDER BY a, x;
+} {1 3}
+
+
+# Similar to [do_execsql_test], except that two elements are appended
+# to the result - the string "search" and the number of times test variable
+# sqlite3_search_count is incremented by running the supplied SQL. e.g.
+# 
+#   do_searchcount_test 1.0 { SELECT * FROM t1 } {x y search 2}
+#
+proc do_searchcount_test {tn sql res} {
+  uplevel [subst -nocommands {
+    do_test $tn {
+      set ::sqlite_search_count 0
+      concat [db eval {$sql}] search [set ::sqlite_search_count]
+    } [list $res]
+  }] 
+}
+
+do_execsql_test 3.0 {
+  CREATE TABLE t3(a, b, c);
+  CREATE UNIQUE INDEX i3 ON t3(a, b);
+  INSERT INTO t3 VALUES(1, 'one', 'i');
+  INSERT INTO t3 VALUES(3, 'three', 'iii');
+  INSERT INTO t3 VALUES(6, 'six', 'vi');
+  INSERT INTO t3 VALUES(2, 'two', 'ii');
+  INSERT INTO t3 VALUES(4, 'four', 'iv');
+  INSERT INTO t3 VALUES(5, 'five', 'v');
+
+  CREATE TABLE t4(x PRIMARY KEY, y);
+  INSERT INTO t4 VALUES('a', 'one');
+  INSERT INTO t4 VALUES('b', 'two');
+} {}
+
+do_searchcount_test 3.1 {
+  SELECT a, b FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
+} {1 one 2 two search 2}
+
+do_searchcount_test 3.2 {
+  SELECT a, c FROM t3 WHERE (a=1 AND b='one') OR (a=2 AND b='two')
+} {1 i 2 ii search 4}
+
+do_searchcount_test 3.4.1 {
+  SELECT y FROM t4 WHERE x='a'
+} {one search 2}
+do_searchcount_test 3.4.2 {
+  SELECT a, b FROM t3 WHERE 
+        (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) 
+     OR (a=2 AND b='two')
+} {1 one 2 two search 4}
+do_searchcount_test 3.4.3 {
+  SELECT a, b FROM t3 WHERE 
+        (a=2 AND b='two')
+     OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) 
+} {2 two 1 one search 4}
+do_searchcount_test 3.4.4 {
+  SELECT a, b FROM t3 WHERE 
+        (a=2 AND b=(SELECT y FROM t4 WHERE x='b')) 
+     OR (a=1 AND b=(SELECT y FROM t4 WHERE x='a')) 
+} {2 two 1 one search 6}
+
+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}
+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}
+
+finish_test