]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow vector-IN expressions like "(a, b) IN ( (?,?), (?,?) )" to use an index.
authordan <Dan Kennedy>
Mon, 13 Feb 2023 16:10:31 +0000 (16:10 +0000)
committerdan <Dan Kennedy>
Mon, 13 Feb 2023 16:10:31 +0000 (16:10 +0000)
FossilOrigin-Name: 1815b15ddb8785a25b7617aab19e13c2410b9377389c16a85176025b3d9400e8

manifest
manifest.uuid
src/wherecode.c
src/whereexpr.c
test/rowvalue9.test

index bc6af60ba41a0881d034372ccb5bea5f0b9f511d..4d859e0eac1c33431ab7ed00388e964d8617d91a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C In\sthe\sLIKE\soptimization,\sdo\snot\sanalyze\sthe\snew\svirtual\sWHERE\sclause\sterms\nuntil\sboth\shave\sbeen\sadded,\ssince\sthey\sare\sexpected\sto\sbe\sconsecutive\sand\nthe\sanalysis\smight\sadd\scomplementary\sterms.\nThis\sfixes\sa\sproblem\scaused\sby\s[44200596aa943963]\sand\sdiscovered\sby\sdbsqlfuzz\nand\srecorded\sas\scase\s7e3b5983727d843b910b2d9ab556e4afcd777cfb.
-D 2023-02-13T12:46:22.331
+C Allow\svector-IN\sexpressions\slike\s"(a,\sb)\sIN\s(\s(?,?),\s(?,?)\s)"\sto\suse\san\sindex.
+D 2023-02-13T16:10:31.626
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -708,8 +708,8 @@ F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
 F src/where.c 5b20d08699c9a55c0556661ff6f937dd1c8e27567b7553e48d477d3fad22ec4c
 F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c
-F src/wherecode.c 76bca3379219880d2527493b71a3be49e696f75396d3481e4de5d4ceec7886b2
-F src/whereexpr.c bbf7259c6c53c728b505eae6c4965c7c0b1abb853aadcc7cbbf4f2d1b38d6b6b
+F src/wherecode.c b82d0d33315e1526904b95155e55e61149c4462147668e1cc4567c812735eff1
+F src/whereexpr.c 60dbc85ef8948cf16e306ac28c4bf6a73cf86be4afb134fa79bd20f61c501f14
 F src/window.c 76a27cff9ea2ded0c2c3527187029259440fabcc4cc4c07b11d942c78494a614
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
@@ -1421,7 +1421,7 @@ F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3
 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
 F test/rowvalue7.test c1cbdbf407029db01f87764097c6ac02a1c5a37efd2776eff32a9cdfdf6f2dba
 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0
-F test/rowvalue9.test cb5380df82dca9db463081e26952c1e097b34fc2c2ac87453970c048d97df687
+F test/rowvalue9.test 138252b53b835208a5712e01595403a0ae32b4bc58284d9fe6bea10e58203fe4
 F test/rowvalueA.test 51f79b6098c193f838168752c9640f4eae6c63346bf64b5bed4f4e22fe2c71d0
 F test/rowvaluefault.test 963ae9cdaed30a85a29668dd514e639f3556cae903ee9f172ea972d511c54fff
 F test/rowvaluevtab.test cd9747bb3f308086944c07968f547ad6b05022e698d80b9ffbdfe09ce0b8da6f
@@ -2045,8 +2045,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 0216ce23cf23bc147c5de6de178a6689b7ad744bf0ee0098809938b5fe10708b
-R 09e9d8286e23e9b338083a6379ec9988
-U drh
-Z 7381f85d2ea8678673eb457bc621469f
+P d35de3ad3fac6b30d3f266cbe4b1e9923eb31a1eff4a869205bbc3ba122eeec5
+R 429262141303fdf6f44e745033f25b12
+T *branch * vector-in-fix
+T *sym-vector-in-fix *
+T -sym-trunk *
+U dan
+Z 208a1c0d241050968b174afabb5133b9
 # Remove this line to create a well-formed Fossil manifest.
index be067174f23617826f9fe1cfa05503deb5213242..e798d489d7861831f84f6f3e80a49b4137defcdf 100644 (file)
@@ -1 +1 @@
-d35de3ad3fac6b30d3f266cbe4b1e9923eb31a1eff4a869205bbc3ba122eeec5
\ No newline at end of file
+1815b15ddb8785a25b7617aab19e13c2410b9377389c16a85176025b3d9400e8
\ No newline at end of file
index 4c22e5dd61e6440c368d6cebcd71a9ac88b63615..860acb44cf55a4fa655e8df1449602d7a3b76cfb 100644 (file)
@@ -489,68 +489,75 @@ static Expr *removeUnindexableInClauseTerms(
   Expr *pX              /* The IN expression to be reduced */
 ){
   sqlite3 *db = pParse->db;
+  Select *pSelect;            /* Pointer to the SELECT on the RHS */
   Expr *pNew;
   pNew = sqlite3ExprDup(db, pX, 0);
   if( db->mallocFailed==0 ){
-    ExprList *pOrigRhs;         /* Original unmodified RHS */
-    ExprList *pOrigLhs;         /* Original unmodified LHS */
-    ExprList *pRhs = 0;         /* New RHS after modifications */
-    ExprList *pLhs = 0;         /* New LHS after mods */
-    int i;                      /* Loop counter */
-    Select *pSelect;            /* Pointer to the SELECT on the RHS */
-
-    assert( ExprUseXSelect(pNew) );
-    pOrigRhs = pNew->x.pSelect->pEList;
-    assert( pNew->pLeft!=0 );
-    assert( ExprUseXList(pNew->pLeft) );
-    pOrigLhs = pNew->pLeft->x.pList;
-    for(i=iEq; i<pLoop->nLTerm; i++){
-      if( pLoop->aLTerm[i]->pExpr==pX ){
-        int iField;
-        assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
-        iField = pLoop->aLTerm[i]->u.x.iField - 1;
-        if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
-        pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
-        pOrigRhs->a[iField].pExpr = 0;
-        assert( pOrigLhs->a[iField].pExpr!=0 );
-        pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr);
-        pOrigLhs->a[iField].pExpr = 0;
+    for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){
+      ExprList *pOrigRhs;         /* Original unmodified RHS */
+      ExprList *pOrigLhs = 0;     /* Original unmodified LHS */
+      ExprList *pRhs = 0;         /* New RHS after modifications */
+      ExprList *pLhs = 0;         /* New LHS after mods */
+      int i;                      /* Loop counter */
+
+      assert( ExprUseXSelect(pNew) );
+      pOrigRhs = pSelect->pEList;
+      assert( pNew->pLeft!=0 );
+      assert( ExprUseXList(pNew->pLeft) );
+      if( pSelect==pNew->x.pSelect ){
+        pOrigLhs = pNew->pLeft->x.pList;
       }
-    }
-    sqlite3ExprListDelete(db, pOrigRhs);
-    sqlite3ExprListDelete(db, pOrigLhs);
-    pNew->pLeft->x.pList = pLhs;
-    pNew->x.pSelect->pEList = pRhs;
-    if( pLhs && pLhs->nExpr==1 ){
-      /* Take care here not to generate a TK_VECTOR containing only a
-      ** single value. Since the parser never creates such a vector, some
-      ** of the subroutines do not handle this case.  */
-      Expr *p = pLhs->a[0].pExpr;
-      pLhs->a[0].pExpr = 0;
-      sqlite3ExprDelete(db, pNew->pLeft);
-      pNew->pLeft = p;
-    }
-    pSelect = pNew->x.pSelect;
-    if( pSelect->pOrderBy ){
-      /* If the SELECT statement has an ORDER BY clause, zero the 
-      ** iOrderByCol variables. These are set to non-zero when an 
-      ** ORDER BY term exactly matches one of the terms of the 
-      ** result-set. Since the result-set of the SELECT statement may
-      ** have been modified or reordered, these variables are no longer 
-      ** set correctly.  Since setting them is just an optimization, 
-      ** it's easiest just to zero them here.  */
-      ExprList *pOrderBy = pSelect->pOrderBy;
-      for(i=0; i<pOrderBy->nExpr; i++){
-        pOrderBy->a[i].u.x.iOrderByCol = 0;
+      for(i=iEq; i<pLoop->nLTerm; i++){
+        if( pLoop->aLTerm[i]->pExpr==pX ){
+          int iField;
+          assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
+          iField = pLoop->aLTerm[i]->u.x.iField - 1;
+          if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
+          pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
+          pOrigRhs->a[iField].pExpr = 0;
+          if( pOrigLhs ){
+            assert( pOrigLhs->a[iField].pExpr!=0 );
+            pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr);
+            pOrigLhs->a[iField].pExpr = 0;
+          }
+        }
+      }
+      sqlite3ExprListDelete(db, pOrigRhs);
+      if( pOrigLhs ){
+        sqlite3ExprListDelete(db, pOrigLhs);
+        pNew->pLeft->x.pList = pLhs;
+      }
+      pSelect->pEList = pRhs;
+      if( pLhs && pLhs->nExpr==1 ){
+        /* Take care here not to generate a TK_VECTOR containing only a
+        ** single value. Since the parser never creates such a vector, some
+        ** of the subroutines do not handle this case.  */
+        Expr *p = pLhs->a[0].pExpr;
+        pLhs->a[0].pExpr = 0;
+        sqlite3ExprDelete(db, pNew->pLeft);
+        pNew->pLeft = p;
+      }
+      if( pSelect->pOrderBy ){
+        /* If the SELECT statement has an ORDER BY clause, zero the 
+        ** iOrderByCol variables. These are set to non-zero when an 
+        ** ORDER BY term exactly matches one of the terms of the 
+        ** result-set. Since the result-set of the SELECT statement may
+        ** have been modified or reordered, these variables are no longer 
+        ** set correctly.  Since setting them is just an optimization, 
+        ** it's easiest just to zero them here.  */
+        ExprList *pOrderBy = pSelect->pOrderBy;
+        for(i=0; i<pOrderBy->nExpr; i++){
+          pOrderBy->a[i].u.x.iOrderByCol = 0;
+        }
       }
-    }
 
 #if 0
-    printf("For indexing, change the IN expr:\n");
-    sqlite3TreeViewExpr(0, pX, 0);
-    printf("Into:\n");
-    sqlite3TreeViewExpr(0, pNew, 0);
+      printf("For indexing, change the IN expr:\n");
+      sqlite3TreeViewExpr(0, pX, 0);
+      printf("Into:\n");
+      sqlite3TreeViewExpr(0, pNew, 0);
 #endif
+    }
   }
   return pNew;
 }
index e458952c4a6cc92b43439012fde7e3ba230a8f81..5a84b98e74bf3bb295750e1f5178f1b17491de8d 100644 (file)
@@ -1440,7 +1440,7 @@ static void exprAnalyze(
    && pTerm->u.x.iField==0
    && pExpr->pLeft->op==TK_VECTOR
    && ALWAYS( ExprUseXSelect(pExpr) )
-   && pExpr->x.pSelect->pPrior==0
+   && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values))
 #ifndef SQLITE_OMIT_WINDOWFUNC
    && pExpr->x.pSelect->pWin==0
 #endif
index 611aa3fbb7044f0e749e84e679e364e805bdc215..aee5e7ea4f1eb77336f92f145af14ab638ed4864 100644 (file)
@@ -308,6 +308,46 @@ do_catchsql_test 8.2 {
   SELECT a FROM t1 NATURAL JOIN t1 WHERE (a,b)> (SELECT 2 IN (SELECT 2,2), 2);
 } {1 {sub-select returns 2 columns - expected 1}}
 
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 9.0 {
+  CREATE TABLE t1(a, b);
+  INSERT INTO t1 VALUES(1, 1), (1, 2), (2, 2), (2, 3), (3, 3), (3, 4), (4, 4);
+}
+
+do_execsql_test 9.1 {
+  SELECT * FROM t1 WHERE (a, b) IN ( (3, 3), (2, 2) );
+} {
+  2 2 3 3
+}
+do_execsql_test 9.2 {
+  CREATE INDEX i1 ON t1(a);
+}
+
+do_execsql_test 9.4 {
+  SELECT * FROM t1 WHERE (a, b) IN ( (3, 3), (2, 2) );
+} {
+  2 2 3 3
+}
+do_eqp_test 9.4e {
+  SELECT * FROM t1 WHERE (a, b) IN ( (3, 3), (2, 2) );
+} {
+  *SEARCH t1 USING INDEX i1*
+}
+
+do_execsql_test 9.5 {
+  CREATE INDEX i2 ON t1(b, a);
+  SELECT * FROM t1 WHERE (a, b) IN ( (3, 3), (2, 2) );
+} {
+  2 2 3 3
+}
+do_eqp_test 9.5e {
+  SELECT * FROM t1 WHERE (a, b) IN ( (3, 3), (2, 2) );
+} {
+  *SEARCH t1 USING COVERING INDEX i2*
+}
+
 
 finish_test