]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Ensure WHERE clause terms involving tables on the right end of a join
authordrh <drh@noemail.net>
Wed, 16 Dec 2009 22:10:49 +0000 (22:10 +0000)
committerdrh <drh@noemail.net>
Wed, 16 Dec 2009 22:10:49 +0000 (22:10 +0000)
are not prematurely evaluated when tables on the left end of the join
make use of the OR-clause optimization.
Fix for ticket [31338dca7e].

FossilOrigin-Name: 2c2de252666662f5459904fc33a9f2956cbff23c

manifest
manifest.uuid
src/sqliteInt.h
src/where.c
test/tkt-31338dca7e.test [new file with mode: 0644]
test/where8.test

index bfc6dc5643cc72f3a7945950d1851925bdde0450..69d6f433ab3b994a569e9c7b881a81873ae3290e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,8 @@
-C Modify\ssome\scomments\sin\se_fkey.test.\sNo\schanges\sto\scode\sor\stests.
-D 2009-12-16T14:49:16
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+C Ensure\sWHERE\sclause\sterms\sinvolving\stables\son\sthe\sright\send\sof\sa\sjoin\nare\snot\sprematurely\sevaluated\swhen\stables\son\sthe\sleft\send\sof\sthe\sjoin\nmake\suse\sof\sthe\sOR-clause\soptimization.\s\s\nFix\sfor\sticket\s[31338dca7e].
+D 2009-12-16T22:10:49
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in c5827ead754ab32b9585487177c93bb00b9497b3
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -166,7 +169,7 @@ F src/select.c 68c58dc49341472e4e5661a47a1a9e5f8a161340
 F src/shell.c f4948cb6d30665d755a6b5e0ec313d1094aab828
 F src/sqlite.h.in 176e993dce3c9cfe5610aca0ef99f4dfd70896bb
 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
-F src/sqliteInt.h e946a6a3f2df015cdbc7668e9626987e8badbb5f
+F src/sqliteInt.h 93d5d01d9ea57b95ba709733dce830ec056deb84
 F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6
 F src/status.c e651be6b30d397d86384c6867bc016e4913bcac7
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -219,7 +222,7 @@ F src/vdbemem.c 1e16e3a16e55f4c3452834f0e041726021aa66e0
 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
 F src/vtab.c 7c7713d66cda699f16bf1cc601d8d4f5070ab935
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c 11b5b00c49d53e767a7eb855bc60790edeca6185
+F src/where.c 5d057a1054972396c05b5e28e6636359675a60d4
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
 F test/all.test 14165b3e32715b700b5f0cbf8f6e3833dda0be45
@@ -601,6 +604,7 @@ F test/thread_common.tcl b65e6b1d1d90dc885e10ad080896c6c56eef0819
 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
 F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
 F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
+F test/tkt-31338dca7e.test 5741cd48de500347a437ba1be58c8335e83c5a5e
 F test/tkt-3fe897352e.test 8084dad39807eac10b10720c84193bd1a5980973
 F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
 F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9
@@ -745,7 +749,7 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
 F test/where6.test 42c4373595f4409d9c6a9987b4a60000ad664faf
 F test/where7.test fdd58ab9dec9f97679e65d4414bf5e07d725d79f
-F test/where8.test 434f08974964b10378d67867773a2c3aedaf1d4b
+F test/where8.test 2bb8ea44b745fcc93db150fac9ce33d12e499760
 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
 F test/where9.test be19e1a92f80985c1a121b4678bf7d2123eaa623
 F test/whereA.test 1d1674254614147c866ab9b59af6582f454a858c
@@ -779,7 +783,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 0daec3099d439ce8a8779260b12975f262037bc8
-R 45bbd6fcd1582d1baf524d507b053719
-U dan
-Z 4241a4f431ff69b9b947be1f424e2dae
+P 1ba4f412325fc2ce552ad7feecf93891f7f95059
+R 0f0daa13e962576cf0e0e163e05eaf4e
+U drh
+Z 0003328655e85cfcc94ce890d5aea52a
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQFLKVrtoxKgR168RlERAuSjAJ9orzNKVuBJ+JkBFgDa9NTlSgG6OwCfYqCf
+2/3GkgkUHpv1vo5CeiJiOuY=
+=yKm6
+-----END PGP SIGNATURE-----
index 81911cb5f8056007662a66b2a79fdaebfb4b0cd8..6ea0ecba5ee19f7888ccecc54796436dcbaea62b 100644 (file)
@@ -1 +1 @@
-1ba4f412325fc2ce552ad7feecf93891f7f95059
\ No newline at end of file
+2c2de252666662f5459904fc33a9f2956cbff23c
\ No newline at end of file
index 1202971a84a269911d9351e4d50d4e1ef77b90c5..1e15dbd167ef467a6c94e93a98c10ff7bc110b03 100644 (file)
@@ -1894,6 +1894,7 @@ struct WhereLevel {
 #define WHERE_OMIT_OPEN        0x0010 /* Table cursor are already open */
 #define WHERE_OMIT_CLOSE       0x0020 /* Omit close of table & index cursors */
 #define WHERE_FORCE_TABLE      0x0040 /* Do not use an index-only search */
+#define WHERE_ONETABLE_ONLY    0x0080 /* Only code the 1st table in pTabList */
 
 /*
 ** The WHERE clause processing routine has two halves.  The
@@ -1906,6 +1907,7 @@ struct WhereInfo {
   Parse *pParse;       /* Parsing and code generating context */
   u16 wctrlFlags;      /* Flags originally passed to sqlite3WhereBegin() */
   u8 okOnePass;        /* Ok to use one-pass algorithm for UPDATE or DELETE */
+  u8 untestedTerms;    /* Not all WHERE terms resolved by outer loop */
   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 */
index 067ec4079b9f2e6ced2fd43793771b1f1e7e37aa..90215edd8c43555794cdf30bf924134dfdc3764d 100644 (file)
@@ -3265,13 +3265,14 @@ static Bitmask codeOneLoopStart(
     */
     WhereClause *pOrWc;    /* The OR-clause broken out into subterms */
     WhereTerm *pFinal;     /* Final subterm within the OR-clause. */
-    SrcList oneTab;        /* Shortened table list */
+    SrcList *pOrTab;       /* Shortened table list or OR-clause generation */
 
     int regReturn = ++pParse->nMem;           /* Register used with OP_Gosub */
     int regRowset = 0;                        /* Register for RowSet object */
     int regRowid = 0;                         /* Register holding rowid */
     int iLoopBody = sqlite3VdbeMakeLabel(v);  /* Start of loop body */
     int iRetInit;                             /* Address of regReturn init */
+    int untestedTerms = 0;             /* Some terms not completely tested */
     int ii;
    
     pTerm = pLevel->plan.u.pTerm;
@@ -3280,11 +3281,29 @@ static Bitmask codeOneLoopStart(
     assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
     pOrWc = &pTerm->u.pOrInfo->wc;
     pFinal = &pOrWc->a[pOrWc->nTerm-1];
+    pLevel->op = OP_Return;
+    pLevel->p1 = regReturn;
 
-    /* Set up a SrcList containing just the table being scanned by this loop. */
-    oneTab.nSrc = 1;
-    oneTab.nAlloc = 1;
-    oneTab.a[0] = *pTabItem;
+    /* Set up a new SrcList ni 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().
+    */
+    if( pWInfo->nLevel>1 ){
+      int nNotReady;                 /* The number of notReady tables */
+      struct SrcList_item *origSrc;     /* Original list of tables */
+      nNotReady = pWInfo->nLevel - iLevel - 1;
+      pOrTab = sqlite3StackAllocRaw(pParse->db,
+                            sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
+      if( pOrTab==0 ) return notReady;
+      pOrTab->nSrc = pOrTab->nAlloc = nNotReady + 1;
+      memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
+      origSrc = pWInfo->pTabList->a;
+      for(k=1; k<=nNotReady; k++){
+        memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
+      }
+    }else{
+      pOrTab = pWInfo->pTabList;
+    }
 
     /* Initialize the rowset register to contain NULL. An SQL NULL is 
     ** equivalent to an empty rowset.
@@ -3309,8 +3328,9 @@ static Bitmask codeOneLoopStart(
       if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
         WhereInfo *pSubWInfo;          /* Info for single OR-term scan */
         /* Loop through table entries that match term pOrTerm. */
-        pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0,
-                        WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE);
+        pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0,
+                        WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
+                        WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
         if( pSubWInfo ){
           if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
             int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
@@ -3322,19 +3342,24 @@ static Bitmask codeOneLoopStart(
           }
           sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
 
+          /* The pSubWInfo->untestedTerms flag means that this OR term
+          ** contained one or more AND term from a notReady table.  The
+          ** terms from the notReady table could not be tested and will
+          ** need to be tested later.
+          */
+          if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+
           /* Finish the loop through table entries that match term pOrTerm. */
           sqlite3WhereEnd(pSubWInfo);
         }
       }
     }
     sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
-    /* sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); */
     sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
     sqlite3VdbeResolveLabel(v, iLoopBody);
 
-    pLevel->op = OP_Return;
-    pLevel->p1 = regReturn;
-    disableTerm(pLevel, pTerm);
+    if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab);
+    if( !untestedTerms ) disableTerm(pLevel, pTerm);
   }else
 #endif /* SQLITE_OMIT_OR_OPTIMIZATION */
 
@@ -3362,7 +3387,12 @@ static Bitmask codeOneLoopStart(
     testcase( pTerm->wtFlags & TERM_VIRTUAL );
     testcase( pTerm->wtFlags & TERM_CODED );
     if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
-    if( (pTerm->prereqAll & notReady)!=0 ) continue;
+    if( (pTerm->prereqAll & notReady)!=0 ){
+      testcase( pWInfo->untestedTerms==0
+               && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
+      pWInfo->untestedTerms = 1;
+      continue;
+    }
     pE = pTerm->pExpr;
     assert( pE!=0 );
     if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
@@ -3385,7 +3415,12 @@ static Bitmask codeOneLoopStart(
       testcase( pTerm->wtFlags & TERM_VIRTUAL );
       testcase( pTerm->wtFlags & TERM_CODED );
       if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
-      if( (pTerm->prereqAll & notReady)!=0 ) continue;
+      if( (pTerm->prereqAll & notReady)!=0 ){
+        testcase( pWInfo->untestedTerms==0
+               && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
+        pWInfo->untestedTerms = 1;
+        continue;
+      }
       assert( pTerm->pExpr );
       sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
       pTerm->wtFlags |= TERM_CODED;
@@ -3528,6 +3563,7 @@ WhereInfo *sqlite3WhereBegin(
 ){
   int i;                     /* Loop counter */
   int nByteWInfo;            /* Num. bytes allocated for WhereInfo struct */
+  int nTabList;              /* Number of elements in pTabList */
   WhereInfo *pWInfo;         /* Will become the return value of this function */
   Vdbe *v = pParse->pVdbe;   /* The virtual database engine */
   Bitmask notReady;          /* Cursors that are not yet positioned */
@@ -3547,6 +3583,13 @@ WhereInfo *sqlite3WhereBegin(
     return 0;
   }
 
+  /* This function normally generates a nested loop for all tables in 
+  ** pTabList.  But if the WHERE_ONETABLE_ONLY flag is set, then we should
+  ** only generate code for the first table in pTabList and assume that
+  ** any cursors associated with subsequent tables are uninitialized.
+  */
+  nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc;
+
   /* Allocate and initialize the WhereInfo structure that will become the
   ** return value. A single allocation is used to store the WhereInfo
   ** struct, the contents of WhereInfo.a[], the WhereClause structure
@@ -3555,7 +3598,7 @@ WhereInfo *sqlite3WhereBegin(
   ** some architectures. Hence the ROUND8() below.
   */
   db = pParse->db;
-  nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel));
+  nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
   pWInfo = sqlite3DbMallocZero(db, 
       nByteWInfo + 
       sizeof(WhereClause) +
@@ -3564,7 +3607,7 @@ WhereInfo *sqlite3WhereBegin(
   if( db->mallocFailed ){
     goto whereBeginError;
   }
-  pWInfo->nLevel = pTabList->nSrc;
+  pWInfo->nLevel = nTabList;
   pWInfo->pParse = pParse;
   pWInfo->pTabList = pTabList;
   pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
@@ -3583,7 +3626,7 @@ WhereInfo *sqlite3WhereBegin(
   /* Special case: a WHERE clause that is constant.  Evaluate the
   ** expression and either jump over all of the code or fall thru.
   */
-  if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
+  if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
     sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
     pWhere = 0;
   }
@@ -3603,6 +3646,11 @@ WhereInfo *sqlite3WhereBegin(
   ** to virtual table cursors are set. This is used to selectively disable 
   ** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful 
   ** with virtual tables.
+  **
+  ** Note that bitmasks are created for all pTabList->nSrc tables in
+  ** pTabList, not just the first nTabList tables.  nTabList is normally
+  ** equal to pTabList->nSrc but might be shortened to 1 if the
+  ** WHERE_ONETABLE_ONLY flag is set.
   */
   assert( pWC->vmask==0 && pMaskSet->n==0 );
   for(i=0; i<pTabList->nSrc; i++){
@@ -3654,7 +3702,7 @@ WhereInfo *sqlite3WhereBegin(
   pLevel = pWInfo->a;
   andFlags = ~0;
   WHERETRACE(("*** Optimizer Start ***\n"));
-  for(i=iFrom=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+  for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
     WhereCost bestPlan;         /* Most efficient plan seen so far */
     Index *pIdx;                /* Index for FROM table at pTabItem */
     int j;                      /* For looping over FROM tables */
@@ -3699,8 +3747,8 @@ WhereInfo *sqlite3WhereBegin(
     */
     for(isOptimal=1; isOptimal>=0 && bestJ<0; isOptimal--){
       Bitmask mask = (isOptimal ? 0 : notReady);
-      assert( (pTabList->nSrc-iFrom)>1 || isOptimal );
-      for(j=iFrom, pTabItem=&pTabList->a[j]; j<pTabList->nSrc; j++, pTabItem++){
+      assert( (nTabList-iFrom)>1 || isOptimal );
+      for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
         int doNotReorder;    /* True if this table should not be reordered */
         WhereCost sCost;     /* Cost information from best[Virtual]Index() */
         ExprList *pOrderBy;  /* ORDER BY clause for index to optimize */
@@ -3797,7 +3845,7 @@ WhereInfo *sqlite3WhereBegin(
   ** searching those tables.
   */
   sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
-  for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+  for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
     Table *pTab;     /* Table to open */
     int iDb;         /* Index of database containing table/index */
 
@@ -3876,7 +3924,7 @@ WhereInfo *sqlite3WhereBegin(
   ** program.
   */
   notReady = ~(Bitmask)0;
-  for(i=0; i<pTabList->nSrc; i++){
+  for(i=0; i<nTabList; i++){
     notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
     pWInfo->iContinue = pWInfo->a[i].addrCont;
   }
@@ -3888,7 +3936,7 @@ WhereInfo *sqlite3WhereBegin(
   ** the index is listed as "{}".  If the primary key is used the
   ** index name is '*'.
   */
-  for(i=0; i<pTabList->nSrc; i++){
+  for(i=0; i<nTabList; i++){
     char *z;
     int n;
     pLevel = &pWInfo->a[i];
@@ -3956,7 +4004,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
   /* Generate loop termination code.
   */
   sqlite3ExprCacheClear(pParse);
-  for(i=pTabList->nSrc-1; i>=0; i--){
+  for(i=pWInfo->nLevel-1; i>=0; i--){
     pLevel = &pWInfo->a[i];
     sqlite3VdbeResolveLabel(v, pLevel->addrCont);
     if( pLevel->op!=OP_Noop ){
@@ -4002,7 +4050,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
 
   /* Close all of the cursors that were opened by sqlite3WhereBegin.
   */
-  for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+  assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
+  for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
     struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
     Table *pTab = pTabItem->pTab;
     assert( pTab!=0 );
diff --git a/test/tkt-31338dca7e.test b/test/tkt-31338dca7e.test
new file mode 100644 (file)
index 0000000..3f66816
--- /dev/null
@@ -0,0 +1,77 @@
+# 2009 December 16
+#
+# 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.
+#
+# This file implements tests to verify that ticket [31338dca7e] has been
+# fixed.  Ticket [31338dca7e] demonstrates problems with the OR-clause
+# optimization in joins where the WHERE clause is of the form
+#
+#     (x AND y) OR z
+#
+# And the x and y subterms from from different tables of the join.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tkt-31338-1.1 {
+  db eval {
+    CREATE TABLE t1(x);
+    CREATE TABLE t2(y);
+    INSERT INTO t1 VALUES(111);
+    INSERT INTO t1 VALUES(222);
+    INSERT INTO t2 VALUES(333);
+    INSERT INTO t2 VALUES(444);
+    SELECT * FROM t1, t2
+     WHERE (x=111 AND y!=444) OR x=222
+     ORDER BY x, y;
+  }
+} {111 333 222 333 222 444}
+
+do_test tkt-31338-1.2 {
+  db eval {
+    CREATE INDEX t1x ON t1(x);
+    SELECT * FROM t1, t2
+     WHERE (x=111 AND y!=444) OR x=222
+     ORDER BY x, y;
+  }
+} {111 333 222 333 222 444}
+
+do_test tkt-31338-2.1 {
+  db eval {
+    CREATE TABLE t3(v,w);
+    CREATE TABLE t4(x,y);
+    CREATE TABLE t5(z);
+    INSERT INTO t3 VALUES(111,222);
+    INSERT INTO t3 VALUES(333,444);
+    INSERT INTO t4 VALUES(222,333);
+    INSERT INTO t4 VALUES(444,555);
+    INSERT INTO t5 VALUES(888);
+    INSERT INTO t5 VALUES(999);
+    
+    SELECT * FROM t3, t4, t5
+     WHERE (v=111 AND x=w AND z!=999) OR (v=333 AND x=444)
+     ORDER BY v, w, x, y, z;
+  }
+} {111 222 222 333 888 333 444 444 555 888 333 444 444 555 999}
+
+do_test tkt-31338-2.2 {
+  db eval {
+   CREATE INDEX t3v ON t3(v);
+   CREATE INDEX t4x ON t4(x);
+    SELECT * FROM t3, t4, t5
+     WHERE (v=111 AND x=w AND z!=999) OR (v=333 AND x=444)
+     ORDER BY v, w, x, y, z;
+  }
+} {111 222 222 333 888 333 444 444 555 888 333 444 444 555 999}
+
+
+finish_test
index 04146fe60a17ef2126851cf48e6a7d9449763abc..04657c047316bdf2ee5bb075d8ef30f6c0880fc2 100644 (file)
@@ -398,6 +398,10 @@ do_test where8-4.1 {
     INSERT INTO t4 VALUES('his', 'of', 378678316.5);
     INSERT INTO t4 VALUES(271.2019091, 'viewed', 3282306647);
     INSERT INTO t4 VALUES('hills', 'all', 'peak');
+    CREATE TABLE t5(s);
+    INSERT INTO t5 VALUES('tab-t5');
+    CREATE TABLE t6(t);
+    INSERT INTO t6 VALUES(123456);
     COMMIT;
   }
 } {}
@@ -639,6 +643,14 @@ foreach idxsql {
 197  { SELECT * FROM t3, t4 WHERE g = 2643383279 AND f = g }
 198  { SELECT * FROM t3, t4 WHERE g < 8979323846 }
 199  { SELECT * FROM t3, t4 WHERE 'are' <= b }
+200  { SELECT * FROM t3, t4 WHERE (a=1415926535 AND f=8628034825)
+                               OR (a=6939937510 AND f=2643383279) }
+201  { SELECT * FROM t3, t4, t5, t6
+        WHERE (a=1415926535 AND f=8628034825 AND s!='hello' AND t!=5)
+           OR (a=6939937510 AND f=2643383279 AND s='tab-t5' AND t=123456) }
+202  { SELECT * FROM t3, t4, t5, t6
+        WHERE (a=1415926535 AND f=8628034825 AND s!='hello' AND t==5)
+           OR (a=6939937510 AND f=2643383279 AND s='tab-t5' AND t!=123456) }
 
   } {
     do_test where8-4.$A.$B.1 {