]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Avoid passing constraints that are unusable due to LEFT or CROSS joins to virtual...
authordan <dan@noemail.net>
Mon, 8 Jun 2015 18:05:54 +0000 (18:05 +0000)
committerdan <dan@noemail.net>
Mon, 8 Jun 2015 18:05:54 +0000 (18:05 +0000)
FossilOrigin-Name: 80ee56dda7db3860f8be5f6968c8745138f8453f

ext/rtree/rtreeC.test
manifest
manifest.uuid
src/where.c

index 94db05a4d1b77bda2bdfe427e34c49e051a99dcf..9a64df51d5374126cf3cc087600e18b2de4b2e02 100644 (file)
@@ -269,5 +269,88 @@ ifcapable rtree {
   db close
 }
 
+#--------------------------------------------------------------------
+# Test that queries featuring LEFT or CROSS JOINS are handled correctly.
+# Handled correctly in this case means:
+#
+#   * Terms with prereqs that appear to the left of a LEFT JOIN against
+#     the virtual table are always available to xBestIndex.
+#
+#   * Terms with prereqs that appear to the right of a LEFT JOIN against
+#     the virtual table are never available to xBestIndex.
+#
+# And the same behaviour for CROSS joins.
+#
+reset_db
+do_execsql_test 7.0 {
+  CREATE TABLE xdir(x1);
+  CREATE TABLE ydir(y1);
+  CREATE VIRTUAL TABLE rt USING rtree_i32(id, xmin, xmax, ymin, ymax);
+
+  INSERT INTO xdir VALUES(5);
+  INSERT INTO ydir VALUES(10);
+
+  INSERT INTO rt VALUES(1, 2, 7, 12, 14);      -- Not a hit
+  INSERT INTO rt VALUES(2, 2, 7, 8, 12);       -- A hit!
+  INSERT INTO rt VALUES(3, 7, 11, 8, 12);      -- Not a hit!
+  INSERT INTO rt VALUES(4, 5, 5, 10, 10);      -- A hit!
+
+}
+
+proc do_eqp_execsql_test {tn sql res} {
+  set query "EXPLAIN QUERY PLAN $sql ; $sql "
+  uplevel [list do_execsql_test $tn $query $res]
+}
+
+do_eqp_execsql_test 7.1 {
+  SELECT id FROM xdir, rt, ydir 
+  ON (y1 BETWEEN ymin AND ymax)
+  WHERE (x1 BETWEEN xmin AND xmax);
+} {
+  0 0 0 {SCAN TABLE xdir} 
+  0 1 2 {SCAN TABLE ydir} 
+  0 2 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B2D3B0D1}
+  2 4
+}
+
+do_eqp_execsql_test 7.2 {
+  SELECT * FROM xdir, rt LEFT JOIN ydir 
+  ON (y1 BETWEEN ymin AND ymax)
+  WHERE (x1 BETWEEN xmin AND xmax);
+} {
+  0 0 0 {SCAN TABLE xdir} 
+  0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
+  0 2 2 {SCAN TABLE ydir} 
+
+  5 1 2 7 12 14 {}
+  5 2 2 7  8 12 10
+  5 4 5 5 10 10 10
+}
+
+do_eqp_execsql_test 7.3 {
+  SELECT id FROM xdir, rt CROSS JOIN ydir 
+  ON (y1 BETWEEN ymin AND ymax)
+  WHERE (x1 BETWEEN xmin AND xmax);
+} {
+  0 0 0 {SCAN TABLE xdir} 
+  0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
+  0 2 2 {SCAN TABLE ydir} 
+  2 4
+}
+
+do_eqp_execsql_test 7.4 {
+  SELECT id FROM rt, xdir CROSS JOIN ydir 
+  ON (y1 BETWEEN ymin AND ymax)
+  WHERE (x1 BETWEEN xmin AND xmax);
+} {
+  0 0 1 {SCAN TABLE xdir} 
+  0 1 0 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
+  0 2 2 {SCAN TABLE ydir} 
+  2 4
+}
+
+finish_test
+
+
 
 finish_test
index 899c066981972e41fa574ea67b45bce9278c2d46..fb3c09a77c6f84c123b9d9ef0ed4a68029041b70 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\stypo\sin\scomment.\s\sNo\schanges\sto\scode.
-D 2015-06-08T17:42:57.317
+C Avoid\spassing\sconstraints\sthat\sare\sunusable\sdue\sto\sLEFT\sor\sCROSS\sjoins\sto\svirtual\stable\sxBestIndex()\smethods.
+D 2015-06-08T18:05:54.638
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a7b384855b72378fd860425b128ea5f75296e9d6
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -156,7 +156,7 @@ F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
 F ext/rtree/rtree9.test b5eb13849545dfd271a54ff16784cb00d8792aea
 F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
 F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
-F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06
+F ext/rtree/rtreeC.test 90aaaffe2fd4f0dcd12289cad5515f6d41f45ffd
 F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
 F ext/rtree/rtreeE.test 45a147a64a76306172819562309681d8e90f94bb
 F ext/rtree/rtreeF.test 66deb9fd1611c7ca2e374adba63debdc2dbb12b4
@@ -327,7 +327,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
 F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
-F src/where.c a328fcc3342044992644b6a11bf301593b8dafb4
+F src/where.c 38b2c4bea9e7a76f882d49c2808e0907e29e2a6d
 F src/whereInt.h 5f87e3c4b0551747d119730dfebddd3c54f04047
 F src/wherecode.c 0669481cabaf5caf934b6bb825df15bc57f60d40
 F src/whereexpr.c 9ce1c9cfedbf80c93c7d899497025ec8256ce652
@@ -1285,7 +1285,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 50f336818c8509d8b8bde282e9399d2b2b5ea70a
-R 960d1cfe6672a4659b85889f9d77d89d
-U mistachkin
-Z 915f959f38ed01d6c5f7a7f0fd181c25
+P e49c291735e613e384f6da044ef865dd274cabc8
+R 861f3c87e22a2bd19cbd1623c84fcecb
+T *branch * vtab-left-join
+T *sym-vtab-left-join *
+T -sym-trunk *
+U dan
+Z 3d7670af603531efb0f4fc0b8b662b61
index 3649605487ef60abb90209f94e5bb8da0c0ab706..2e337e3eb995cd7dac99485de74ff1414f16d9e5 100644 (file)
@@ -1 +1 @@
-e49c291735e613e384f6da044ef865dd274cabc8
\ No newline at end of file
+80ee56dda7db3860f8be5f6968c8745138f8453f
\ No newline at end of file
index 5c5022c9f689878c5e522f1686a6f1ff5372e588..738c5dbdc4da5bfc221c0bac9513b7d4c068a731 100644 (file)
@@ -757,6 +757,7 @@ end_auto_index_create:
 static sqlite3_index_info *allocateIndexInfo(
   Parse *pParse,
   WhereClause *pWC,
+  Bitmask mUnusable,              /* Ignore terms with these prereqs */
   struct SrcList_item *pSrc,
   ExprList *pOrderBy
 ){
@@ -773,6 +774,7 @@ static sqlite3_index_info *allocateIndexInfo(
   ** to this virtual table */
   for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
     if( pTerm->leftCursor != pSrc->iCursor ) continue;
+    if( pTerm->prereqRight & mUnusable ) continue;
     assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
     testcase( pTerm->eOperator & WO_IN );
     testcase( pTerm->eOperator & WO_ISNULL );
@@ -827,6 +829,7 @@ static sqlite3_index_info *allocateIndexInfo(
   for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
     u8 op;
     if( pTerm->leftCursor != pSrc->iCursor ) continue;
+    if( pTerm->prereqRight & mUnusable ) continue;
     assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
     testcase( pTerm->eOperator & WO_IN );
     testcase( pTerm->eOperator & WO_IS );
@@ -2666,10 +2669,32 @@ static int whereLoopAddBtree(
 /*
 ** Add all WhereLoop objects for a table of the join identified by
 ** pBuilder->pNew->iTab.  That table is guaranteed to be a virtual table.
+**
+** If there are no LEFT or CROSS JOIN joins in the query, both mExtra and
+** mUnusable are set to 0. Otherwise, mExtra is a mask of all FROM clause
+** entries that occur before the virtual table in the FROM clause and are
+** separated from it by at least one LEFT or CROSS JOIN. Similarly, the
+** mUnusable mask contains all FROM clause entries that occur after the
+** virtual table and are separated from it by at least one LEFT or 
+** CROSS JOIN. 
+**
+** For example, if the query were:
+**
+**   ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6;
+**
+** then mExtra corresponds to (t1, t2) and mUnusable to (t5, t6).
+**
+** All the tables in mExtra must be scanned before the current virtual 
+** table. So any terms for which all prerequisites are satisfied by 
+** mExtra may be specified as "usable" in all calls to xBestIndex. 
+** Conversely, all tables in mUnusable must be scanned after the current
+** virtual table, so any terms for which the prerequisites overlap with
+** mUnusable should always be configured as "not-usable" for xBestIndex.
 */
 static int whereLoopAddVirtual(
   WhereLoopBuilder *pBuilder,  /* WHERE clause information */
-  Bitmask mExtra
+  Bitmask mExtra,              /* Tables that must be scanned before this one */
+  Bitmask mUnusable            /* Tables that must be scanned after this one */
 ){
   WhereInfo *pWInfo;           /* WHERE analysis context */
   Parse *pParse;               /* The parsing context */
@@ -2690,6 +2715,7 @@ static int whereLoopAddVirtual(
   WhereLoop *pNew;
   int rc = SQLITE_OK;
 
+  assert( (mExtra & mUnusable)==0 );
   pWInfo = pBuilder->pWInfo;
   pParse = pWInfo->pParse;
   db = pParse->db;
@@ -2698,7 +2724,7 @@ static int whereLoopAddVirtual(
   pSrc = &pWInfo->pTabList->a[pNew->iTab];
   pTab = pSrc->pTab;
   assert( IsVirtual(pTab) );
-  pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
+  pIdxInfo = allocateIndexInfo(pParse, pWC, mUnusable, pSrc,pBuilder->pOrderBy);
   if( pIdxInfo==0 ) return SQLITE_NOMEM;
   pNew->prereq = 0;
   pNew->rSetup = 0;
@@ -2728,7 +2754,7 @@ static int whereLoopAddVirtual(
           if( (pTerm->eOperator & WO_IN)!=0 ){
             seenIn = 1;
           }
-          if( pTerm->prereqRight!=0 ){
+          if( (pTerm->prereqRight & ~mExtra)!=0 ){
             seenVar = 1;
           }else if( (pTerm->eOperator & WO_IN)==0 ){
             pIdxCons->usable = 1;
@@ -2736,7 +2762,7 @@ static int whereLoopAddVirtual(
           break;
         case 1:    /* Constants with IN operators */
           assert( seenIn );
-          pIdxCons->usable = (pTerm->prereqRight==0);
+          pIdxCons->usable = (pTerm->prereqRight & ~mExtra)==0;
           break;
         case 2:    /* Variables without IN */
           assert( seenVar );
@@ -2835,7 +2861,11 @@ whereLoopAddVtab_exit:
 ** Add WhereLoop entries to handle OR terms.  This works for either
 ** btrees or virtual tables.
 */
-static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
+static int whereLoopAddOr(
+  WhereLoopBuilder *pBuilder, 
+  Bitmask mExtra, 
+  Bitmask mUnusable
+){
   WhereInfo *pWInfo = pBuilder->pWInfo;
   WhereClause *pWC;
   WhereLoop *pNew;
@@ -2894,14 +2924,14 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
 #endif
 #ifndef SQLITE_OMIT_VIRTUALTABLE
         if( IsVirtual(pItem->pTab) ){
-          rc = whereLoopAddVirtual(&sSubBuild, mExtra);
+          rc = whereLoopAddVirtual(&sSubBuild, mExtra, mUnusable);
         }else
 #endif
         {
           rc = whereLoopAddBtree(&sSubBuild, mExtra);
         }
         if( rc==SQLITE_OK ){
-          rc = whereLoopAddOr(&sSubBuild, mExtra);
+          rc = whereLoopAddOr(&sSubBuild, mExtra, mUnusable);
         }
         assert( rc==SQLITE_OK || sCur.n==0 );
         if( sCur.n==0 ){
@@ -2963,33 +2993,44 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
   int iTab;
   SrcList *pTabList = pWInfo->pTabList;
   struct SrcList_item *pItem;
+  struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
   sqlite3 *db = pWInfo->pParse->db;
-  int nTabList = pWInfo->nLevel;
   int rc = SQLITE_OK;
-  u8 priorJoinType = 0;
   WhereLoop *pNew;
 
+  pNew = pBuilder->pNew;
+  whereLoopInit(pNew);
+
   /* Loop over the tables in the join, from left to right */
   pNew = pBuilder->pNew;
   whereLoopInit(pNew);
-  for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){
+  for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
+    Bitmask mUnusable = 0;
     pNew->iTab = iTab;
     pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
-    if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){
+    if( (pItem->jointype & (JT_LEFT|JT_CROSS))!=0 ){
+      /* This condition is true when pItem is the FROM clause term on the
+      ** right-hand-side of a LEFT or CROSS JOIN.  */
       mExtra = mPrior;
     }
-    priorJoinType = pItem->jointype;
     if( IsVirtual(pItem->pTab) ){
-      rc = whereLoopAddVirtual(pBuilder, mExtra);
+      struct SrcList_item *p;
+      for(p=&pItem[1]; p<pEnd; p++){
+        if( mUnusable || (p->jointype & (JT_LEFT|JT_CROSS)) ){
+          mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
+        }
+      }
+      rc = whereLoopAddVirtual(pBuilder, mExtra, mUnusable);
     }else{
       rc = whereLoopAddBtree(pBuilder, mExtra);
     }
     if( rc==SQLITE_OK ){
-      rc = whereLoopAddOr(pBuilder, mExtra);
+      rc = whereLoopAddOr(pBuilder, mExtra, mUnusable);
     }
     mPrior |= pNew->maskSelf;
     if( rc || db->mallocFailed ) break;
   }
+
   whereLoopClear(db, pNew);
   return rc;
 }