From: dan Date: Mon, 13 Feb 2023 16:10:31 +0000 (+0000) Subject: Allow vector-IN expressions like "(a, b) IN ( (?,?), (?,?) )" to use an index. X-Git-Tag: version-3.41.0~15^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0c699385111dceb708dd493fa14ecf19f3737e8f;p=thirdparty%2Fsqlite.git Allow vector-IN expressions like "(a, b) IN ( (?,?), (?,?) )" to use an index. FossilOrigin-Name: 1815b15ddb8785a25b7617aab19e13c2410b9377389c16a85176025b3d9400e8 --- diff --git a/manifest b/manifest index bc6af60ba4..4d859e0eac 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index be067174f2..e798d489d7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d35de3ad3fac6b30d3f266cbe4b1e9923eb31a1eff4a869205bbc3ba122eeec5 \ No newline at end of file +1815b15ddb8785a25b7617aab19e13c2410b9377389c16a85176025b3d9400e8 \ No newline at end of file diff --git a/src/wherecode.c b/src/wherecode.c index 4c22e5dd61..860acb44cf 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -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; inLTerm; 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; inExpr; i++){ - pOrderBy->a[i].u.x.iOrderByCol = 0; + for(i=iEq; inLTerm; 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; inExpr; 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; } diff --git a/src/whereexpr.c b/src/whereexpr.c index e458952c4a..5a84b98e74 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -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 diff --git a/test/rowvalue9.test b/test/rowvalue9.test index 611aa3fbb7..aee5e7ea4f 100644 --- a/test/rowvalue9.test +++ b/test/rowvalue9.test @@ -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