From 5525ac1049ad73e6ddb1ad444993cc32d9980e4e Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 7 Jun 2024 21:00:42 +0000 Subject: [PATCH 1/8] Experimental optimization to rewrite a SELECT with an EXISTS(...) expression in the WHERE clause as a join. FossilOrigin-Name: 972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a --- manifest | 24 ++++--- manifest.uuid | 2 +- src/build.c | 9 ++- src/resolve.c | 1 + src/select.c | 153 +++++++++++++++++++++++++++++++++++++++++++ src/sqliteInt.h | 1 + test/existsexpr.test | 116 ++++++++++++++++++++++++++++++++ 7 files changed, 292 insertions(+), 14 deletions(-) create mode 100644 test/existsexpr.test diff --git a/manifest b/manifest index f28f11b60c..6f4efd9bdc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\squery\splanner\saccess\sto\sthe\sargument\sof\sLIMIT\seven\sif\sthat\nargument\sis\sa\sbound\sparameter. -D 2024-06-06T23:56:36.923 +C Experimental\soptimization\sto\srewrite\sa\sSELECT\swith\san\sEXISTS(...)\sexpression\sin\sthe\sWHERE\sclause\sas\sa\sjoin. +D 2024-06-07T21:00:42.970 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -697,7 +697,7 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522 F src/btree.c 8b42fc7d9efdb2df05c30e8f91ff6cfbd979724ae24bf90269028468b7a13333 F src/btree.h 55066f513eb095db935169dab1dc2f7c7a747ef223c533f5d4ad4dfed346cbd0 F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6 -F src/build.c 237ccc0290d131d646be722f418e92ee0a38043aee25e7dfdc75f8ce5b3abe4e +F src/build.c 9e6a971156db6285f726fb03ddd9d47cb0a3648198b611f462021ac96fa24135 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 64e4b1227b4ed123146f0aa2989131d1fbd9b927b11e80c9d58c6a68f9cd5ce3 @@ -753,14 +753,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c d99931f45416652895e502328ca49fe782cfc4e1ebdcda13b3736d991ebf42ce F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c 7e8d23ce7cdbfedf351a47e759f2722e8182ca10fd7580be43f4ce1f1a228145 +F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c ea0b75fce45e1f2c22f50ed2b6e2ddd7f66640948d0fc79a397917b4236a74af +F src/select.c 669cfc0392c8a0bd43e5a199cba5f796aead3423e2c529d09148adfa57ae1152 F src/shell.c.in 77d12a0dab8724819e64a14d5fbaad91a934be2b22ad329708fba9ba78993f04 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 0fec1cf17d9e4c93baf29bf74b7d03de9425299443d4abd6989a004d6eb53b60 +F src/sqliteInt.h b81970d56dcb4ed5fc53841ef8b75d42bf511cb9533584546113bc131de832eb F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1114,6 +1114,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac +F test/existsexpr.test a86e8300a2e0cd26109ba5b0d552dbc598b9ad4b79dfd26cac255839c9d5cf6a F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181 @@ -2195,8 +2196,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 f66608bd356efe492d1003663c2e1ccd7cfbf2d40393d256f8720149904ad2d5 e94dfe9928750dd98145d4d9920b298f7b0868703b487f86e0db77a41d53ccf9 -R f3d6aba1cc68a2224dd6f7df935a2c23 -U drh -Z b92cbf17475d91065d48b9fdd285839c +P c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e +R 7e4221204288c4c4593aaccf38fb6e40 +T *branch * exists-to-join +T *sym-exists-to-join * +T -sym-trunk * +U dan +Z 8ebacdbf5bd3ee682966a7aab4962dc0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a310670ce9..7a08d86957 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e \ No newline at end of file +972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a \ No newline at end of file diff --git a/src/build.c b/src/build.c index 9747810e82..e6de79e775 100644 --- a/src/build.c +++ b/src/build.c @@ -5049,14 +5049,17 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ ** are deleted by this function. */ SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){ - assert( p1 && p1->nSrc==1 ); + assert( p1 ); if( p2 ){ - SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1); + int nOld = p1->nSrc; + SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, nOld); if( pNew==0 ){ sqlite3SrcListDelete(pParse->db, p2); }else{ p1 = pNew; - memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem)); + memcpy(&p1->a[nOld], p2->a, p2->nSrc*sizeof(SrcItem)); + assert( nOld==1 || (p2->nSrc==1 && (p2->a[0].fg.jointype&JT_LTORJ)==0) ); + assert( p1->nSrc>=2 ); sqlite3DbFree(pParse->db, p2); p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype); } diff --git a/src/resolve.c b/src/resolve.c index d5c1515a74..9cb3662625 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1367,6 +1367,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); pExpr->x.pSelect->selFlags |= SF_Correlated; + if( pExpr->op==TK_EXISTS ) pParse->bHasExists = 1; } pNC->ncFlags |= NC_Subquery; } diff --git a/src/select.c b/src/select.c index 9a0f2e40f5..332fba3b85 100644 --- a/src/select.c +++ b/src/select.c @@ -7283,6 +7283,152 @@ static int fromClauseTermCanBeCoroutine( return 1; } +/* +** sqlite3WalkExpr() callback used by exprReferencesTable(). +*/ +static int exprReferencesTableExprCb(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN && pExpr->iTable==pWalker->u.iCur ){ + pWalker->eCode = 1; + } + return WRC_Continue; +} + +/* +** Return true if the expression passed as the first argument refers +** to cursor number iCur. Otherwise return false. +*/ +static int exprReferencesTable(Expr *pExpr, int iCur){ + Walker w; + memset(&w, 0, sizeof(w)); + w.u.iCur = iCur; + w.xExprCallback = exprReferencesTableExprCb; + w.xSelectCallback = sqlite3SelectWalkNoop; + sqlite3WalkExpr(&w, pExpr); + return w.eCode; +} + +/* +** Index pIdx is a UNIQUE index on the table accessed by cursor number +** iCsr. This function returns a mask of the index columns that are +** constrained to match a single, non-NULL value by the WHERE clause +** passed as the 4th argument. For example, if the index is: +** +** CREATE UNIQUE INDEX idx ON tbl(a, b, c); +** +** and pWhere: +** +** WHERE a=? AND c=? +** +** then this function returns 5. +*/ +static u64 findConstIdxTerms( + Parse *pParse, + int iCsr, + Index *pIdx, + Expr *pWhere +){ + u64 m = 0; + if( pWhere->op==TK_AND ){ + m = findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pLeft); + m |= findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pRight); + }else if( pWhere->op==TK_EQ ){ + Expr *pLeft = pWhere->pLeft; + Expr *pRight = pWhere->pRight; + if( pRight->op==TK_COLUMN && pRight->iTable==iCsr ){ + SWAP(Expr*, pLeft, pRight); + } + if( pLeft->op==TK_COLUMN + && pLeft->iTable==iCsr + && exprReferencesTable(pRight, iCsr)==0 + ){ + if( pIdx ){ + int ii; + for(ii=0; iinKeyCol; ii++){ + assert( pIdx->azColl[ii] ); + if( pLeft->iColumn==pIdx->aiColumn[ii] ){ + CollSeq *pColl = sqlite3ExprCompareCollSeq(pParse, pWhere); + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[ii])==0 ){ + m |= ((u64)1 << ii); + break; + } + } + } + }else{ + if( pLeft->iColumn<0 ) m = 1; + } + } + } + return m; +} + +/* +** Argument pWhere is the WHERE clause belonging to SELECT statement p. This +** function attempts to transform expressions of the form: +** +** EXISTS (SELECT ...) +** +** into joins. For example, given +** +** CREATE TABLE sailors(sid INTEGER PRIMARY KEY, name TEXT); +** CREATE TABLE reserves(sid INT, day DATE, PRIMARY KEY(sid, day)); +** +** SELECT name FROM sailors AS S WHERE EXISTS ( +** SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = '2022-10-25' +** ); +** +** the SELECT statement may be transformed as follows: +** +** SELECT name FROM sailors AS S, reserves AS R +** WHERE S.sid = R.sid AND R.day = '2022-10-25'; +*/ +static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ + if( pWhere && p->pSrc->nSrc>0 ){ + if( pWhere->op==TK_AND ){ + existsToJoin(pParse, p, pWhere->pLeft); + existsToJoin(pParse, p, pWhere->pRight); + } + else if( pWhere->op==TK_EXISTS && (pWhere->flags & EP_xIsSelect) ){ + Select *pSub = pWhere->x.pSelect; + if( pSub->pSrc->nSrc==1 + && (pSub->selFlags & (SF_Aggregate|SF_Correlated))==SF_Correlated + && pSub->pWhere + ){ + int bTransform = 0; /* True if EXISTS can be made into join */ + Table *pTab = pSub->pSrc->a[0].pTab; + int iCsr = pSub->pSrc->a[0].iCursor; + Index *pIdx; + if( HasRowid(pTab) && findConstIdxTerms(pParse, iCsr, 0,pSub->pWhere) ){ + bTransform = 1; + } + for(pIdx=pTab->pIndex; pIdx && bTransform==0; pIdx=pIdx->pNext){ + if( pIdx->onError && pIdx->nKeyCol<64 ){ + u64 c = findConstIdxTerms(pParse, iCsr, pIdx, pSub->pWhere); + if( c==(1 << pIdx->nKeyCol)-1 ){ + bTransform = 1; + } + } + } + if( bTransform ){ + p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); + pSub->pSrc = 0; + if( p->pWhere ){ + p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSub->pWhere); + }else{ + p->pWhere = pSub->pWhere; + } + pSub->pWhere = 0; + + sqlite3SelectDelete(pParse->db, pSub); + memset(pWhere, 0, sizeof(*pWhere)); + pWhere->op = TK_INTEGER; + pWhere->u.iValue = 1; + ExprSetProperty(pWhere, EP_IntValue); + } + } + } + } +} + /* ** Generate code for the SELECT statement given in the p argument. ** @@ -7609,6 +7755,13 @@ int sqlite3Select( } #endif + /* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt + ** to change it into a join. */ + if( pParse->bHasExists ){ + existsToJoin(pParse, p, p->pWhere); + pTabList = p->pSrc; + } + /* Do the WHERE-clause constant propagation optimization if this is ** a join. No need to speed time on this operation for non-join queries ** as the equivalent optimization will be handled by query planner in diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8866b69ea5..8d104bc253 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3827,6 +3827,7 @@ struct Parse { u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ u8 bHasWith; /* True if statement contains WITH */ + u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif diff --git a/test/existsexpr.test b/test/existsexpr.test new file mode 100644 index 0000000000..4bd55db52d --- /dev/null +++ b/test/existsexpr.test @@ -0,0 +1,116 @@ +# 2024 May 25 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix existsexpr + + +do_execsql_test 1.0 { + CREATE TABLE x1(a, b); + INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6); + CREATE UNIQUE INDEX x1a ON x1(a); + CREATE INDEX x1b ON x1(b); + + CREATE TABLE x2(x, y); + INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6); +} + +do_execsql_test 1.1 { + SELECT 1 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=5) +} {1} + +do_execsql_test 1.2 { + SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) +} {1 2 3 4 5 6} + +# With "a=x", the UNIQUE index means the EXIST can be transformed to a join. +# So no "SUBQUERY". With "b=x", the index is not UNIQUE and so there is a +# "SUBQUERY". +do_execsql_test 1.3.1 { + EXPLAIN QUERY PLAN + SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) +} {~/SUBQUERY/} +do_execsql_test 1.3.2 { + EXPLAIN QUERY PLAN + SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE b=x) +} {/SUBQUERY/} + +do_execsql_test 1.4.1 { + EXPLAIN QUERY PLAN + SELECT * FROM x2 WHERE x=1 AND EXISTS (SELECT 1 FROM x1 WHERE a=x) +} {~/SUBQUERY/} +do_execsql_test 1.4.2 { + EXPLAIN QUERY PLAN + SELECT * FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y=2 +} {~/SUBQUERY/} + +do_execsql_test 1.5 { + SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) +} {3} + +#------------------------------------------------------------------------- +do_execsql_test 2.0 { + CREATE TABLE t1(a, b); + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000 + ) INSERT INTO t1 SELECT i, i FROM s; + + CREATE TABLE t2(c, d); + WITH s(i) AS ( + SELECT 10 UNION ALL SELECT i+10 FROM s WHERE i<1000 + ) INSERT INTO t2 SELECT i, i FROM s; +} + +do_execsql_test 2.1 { + SELECT count(*) FROM t1; + SELECT count(*) FROM t2; +} {1000 100} + +do_execsql_test 2.2 { + SELECT count(*) FROM t1, t2 WHERE a=c; +} {100} + +do_execsql_test 2.3 { + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a) +} {100} +do_eqp_test 2.4 { + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a) +} {SCAN t1} + +do_execsql_test 2.5 { + CREATE UNIQUE INDEX t2c ON t2(c); + CREATE UNIQUE INDEX t1a ON t1(a); +} + +do_eqp_test 2.4.1 { + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a); +} {SCAN t1*SEARCH t2} +do_execsql_test 2.4.2 { + ANALYZE; +} +do_eqp_test 2.4.3 { + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a); +} {SCAN t2*SEARCH t1} +do_execsql_test 2.4.4 { + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c=a); +} {100} + +do_execsql_test 2.5.1 { + EXPLAIN QUERY PLAN + SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.rowid=a); +} {~/SUBQUERY/} + +finish_test + + From b111f1cb2a32d0bc49436e90e162b82c5aa50060 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 8 Jun 2024 18:13:12 +0000 Subject: [PATCH 2/8] Add extra tests for the changes on this branch. FossilOrigin-Name: a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1 --- manifest | 18 ++- manifest.uuid | 2 +- src/select.c | 29 +++-- test/existsexpr.test | 258 +++++++++++++++++++++++++++++++++++++++++- test/existsfault.test | 49 ++++++++ 5 files changed, 328 insertions(+), 28 deletions(-) create mode 100644 test/existsfault.test diff --git a/manifest b/manifest index 6f4efd9bdc..55819af278 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Experimental\soptimization\sto\srewrite\sa\sSELECT\swith\san\sEXISTS(...)\sexpression\sin\sthe\sWHERE\sclause\sas\sa\sjoin. -D 2024-06-07T21:00:42.970 +C Add\sextra\stests\sfor\sthe\schanges\son\sthis\sbranch. +D 2024-06-08T18:13:12.691 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,7 +755,7 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 669cfc0392c8a0bd43e5a199cba5f796aead3423e2c529d09148adfa57ae1152 +F src/select.c 06146768a11bfe8928fea0aabf8addedd4d146a8c4be70558f5c8ecdcc2ce338 F src/shell.c.in 77d12a0dab8724819e64a14d5fbaad91a934be2b22ad329708fba9ba78993f04 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1114,7 +1114,8 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/existsexpr.test a86e8300a2e0cd26109ba5b0d552dbc598b9ad4b79dfd26cac255839c9d5cf6a +F test/existsexpr.test b101143b177d623c35166589d3e6772021dfe4f625ac318c91726a998939cd19 +F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181 @@ -2196,11 +2197,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e -R 7e4221204288c4c4593aaccf38fb6e40 -T *branch * exists-to-join -T *sym-exists-to-join * -T -sym-trunk * +P 972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a +R 20ea5e8daae9a69a6e09a05cc19a6a37 U dan -Z 8ebacdbf5bd3ee682966a7aab4962dc0 +Z cbb8e2989ade033c119abad0907d9ec3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7a08d86957..ce5b16061c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a \ No newline at end of file +a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 332fba3b85..f21559e8da 100644 --- a/src/select.c +++ b/src/select.c @@ -7382,12 +7382,13 @@ static u64 findConstIdxTerms( ** WHERE S.sid = R.sid AND R.day = '2022-10-25'; */ static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ - if( pWhere && p->pSrc->nSrc>0 ){ + if( pWhere && p->pSrc->nSrc>0 && pParse->db->mallocFailed==0 ){ if( pWhere->op==TK_AND ){ + Expr *pRight = pWhere->pRight; existsToJoin(pParse, p, pWhere->pLeft); - existsToJoin(pParse, p, pWhere->pRight); + existsToJoin(pParse, p, pRight); } - else if( pWhere->op==TK_EXISTS && (pWhere->flags & EP_xIsSelect) ){ + else if( pWhere->op==TK_EXISTS ){ Select *pSub = pWhere->x.pSelect; if( pSub->pSrc->nSrc==1 && (pSub->selFlags & (SF_Aggregate|SF_Correlated))==SF_Correlated @@ -7401,28 +7402,26 @@ static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ bTransform = 1; } for(pIdx=pTab->pIndex; pIdx && bTransform==0; pIdx=pIdx->pNext){ - if( pIdx->onError && pIdx->nKeyCol<64 ){ + if( pIdx->onError && pIdx->nKeyCol<=63 ){ u64 c = findConstIdxTerms(pParse, iCsr, pIdx, pSub->pWhere); - if( c==(1 << pIdx->nKeyCol)-1 ){ + if( c==((u64)1 << pIdx->nKeyCol)-1 ){ bTransform = 1; } } } if( bTransform ){ - p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); - pSub->pSrc = 0; - if( p->pWhere ){ - p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSub->pWhere); - }else{ - p->pWhere = pSub->pWhere; - } - pSub->pWhere = 0; - - sqlite3SelectDelete(pParse->db, pSub); memset(pWhere, 0, sizeof(*pWhere)); pWhere->op = TK_INTEGER; pWhere->u.iValue = 1; ExprSetProperty(pWhere, EP_IntValue); + + assert( p->pWhere!=0 ); + p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); + p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSub->pWhere); + + pSub->pWhere = 0; + pSub->pSrc = 0; + sqlite3SelectDelete(pParse->db, pSub); } } } diff --git a/test/existsexpr.test b/test/existsexpr.test index 4bd55db52d..344f8cff6e 100644 --- a/test/existsexpr.test +++ b/test/existsexpr.test @@ -17,9 +17,8 @@ set testprefix existsexpr do_execsql_test 1.0 { - CREATE TABLE x1(a, b); + CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID; INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6); - CREATE UNIQUE INDEX x1a ON x1(a); CREATE INDEX x1b ON x1(b); CREATE TABLE x2(x, y); @@ -111,6 +110,261 @@ do_execsql_test 2.5.1 { SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.rowid=a); } {~/SUBQUERY/} +#------------------------------------------------------------------------- +proc do_subquery_test {tn bSub sql res} { + set r1(0) ~/SUBQUERY/ + set r1(1) /SUBQUERY/ + do_execsql_test $tn.1 "explain query plan $sql" $r1($bSub) + do_execsql_test $tn.2 $sql $res +} + +do_execsql_test 3.0 { + CREATE TABLE y1(a, b, c); + CREATE TABLE y2(x, y, z); + CREATE UNIQUE INDEX y2zy ON y2(z, y); + + INSERT INTO y1 VALUES(1, 1, 1); + INSERT INTO y1 VALUES(2, 2, 2); + INSERT INTO y1 VALUES(3, 3, 3); + INSERT INTO y1 VALUES(4, 4, 4); + + INSERT INTO y2 VALUES(1, 1, 1); + INSERT INTO y2 VALUES(3, 3, 3); +} + +do_subquery_test 3.1 0 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=a AND y=b AND x=z + ) +} { + 1 1 1 3 3 3 +} + +do_subquery_test 3.2 0 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND x=z + ) +} { + 1 1 1 3 3 3 +} + +do_subquery_test 3.3 0 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=max(a,b) AND y=min(b,a) AND c!=3 + ) +} { + 1 1 1 +} + +do_subquery_test 3.4 1 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=max(a,b) AND b=3 + ) +} { + 3 3 3 +} + +do_subquery_test 3.5 0 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=a-1 AND y=a-1 + ) +} { + 2 2 2 + 4 4 4 +} + +do_subquery_test 3.6 1 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 WHERE z=a-1 AND y+1=a + ) +} { + 2 2 2 + 4 4 4 +} + +do_subquery_test 3.7 1 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT count(*) FROM y2 WHERE z=a-1 AND y=a-1 + ) +} { + 1 1 1 + 2 2 2 + 3 3 3 + 4 4 4 +} + +do_subquery_test 3.8 1 { + SELECT * FROM y1 WHERE EXISTS ( SELECT a+1 FROM y2 ) +} { + 1 1 1 + 2 2 2 + 3 3 3 + 4 4 4 +} + +do_subquery_test 3.9 1 { + SELECT * FROM y1 WHERE EXISTS ( + SELECT 1 FROM y2 one, y2 two WHERE one.z=a-1 AND one.y=a-1 + ) +} { + 2 2 2 + 4 4 4 +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 4.0 { + CREATE TABLE tx1(a TEXT COLLATE nocase, b TEXT); + CREATE UNIQUE INDEX tx1ab ON tx1(a, b); + + INSERT INTO tx1 VALUES('a', 'a'); + INSERT INTO tx1 VALUES('B', 'b'); + INSERT INTO tx1 VALUES('c', 'c'); + INSERT INTO tx1 VALUES('D', 'd'); + INSERT INTO tx1 VALUES('e', 'e'); + + CREATE TABLE tx2(x, y); + INSERT INTO tx2 VALUES('A', 'a'); + INSERT INTO tx2 VALUES('b', 'b'); + INSERT INTO tx2 VALUES('C', 'c'); + INSERT INTO tx2 VALUES('D', 'd'); +} + +do_subquery_test 4.1 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x AND b=y + ) +} { + A a + b b + C c + D d +} + +do_subquery_test 4.2 1 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x AND b=y COLLATE nocase + ) +} { + A a + b b + C c + D d +} + +do_execsql_test 4.3 { + DROP INDEX tx1ab; + CREATE UNIQUE INDEX tx1ab ON tx1(a COLLATE binary, b); +} + +do_subquery_test 4.4 1 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x AND b=y + ) +} { + A a + b b + C c + D d +} + +do_subquery_test 4.4 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x COLLATE binary AND b=y + ) +} { + D d +} + +do_subquery_test 4.4 1 { + SELECT EXISTS ( SELECT x FROM tx1 ) FROM tx2 +} { + 1 1 1 1 +} + +do_subquery_test 4.4 1 { + SELECT (SELECT EXISTS ( SELECT x FROM tx1 ) WHERE 1) FROM tx2 +} { + 1 1 1 1 +} + +#------------------------------------------------------------------------- +proc cols {s f} { + set lCols [list] + for {set i $s} {$i<=$f} {incr i} { + lappend lCols [format "c%02d" $i] + } + join $lCols ", " +} +proc vals {n val} { + set lVal [list] + for {set i 0} {$i<$n} {incr i} { + lappend lVal $val + } + join $lVal ", " +} +proc exprs {s f} { + set lExpr [list] + for {set i $s} {$i<=$f} {incr i} { + lappend lExpr [format "c%02d = o" $i] + } + join $lExpr " AND " +} + + +do_execsql_test 5.0 " + CREATE TABLE a1( [cols 0 99] ); +" +do_execsql_test 5.1 " + -- 63 column index + CREATE UNIQUE INDEX a1idx1 ON a1( [cols 0 62] ); +" +do_execsql_test 5.2 " + -- 64 column index + CREATE UNIQUE INDEX a1idx2 ON a1( [cols 10 73] ); +" +do_execsql_test 5.2 " + -- 65 column index + CREATE UNIQUE INDEX a1idx3 ON a1( [cols 20 84] ); +" + +do_test 5.3 { + foreach v {1 2 3 4 5 6} { + execsql "INSERT INTO a1 VALUES( [vals 100 $v] )" + } +} {} + +do_execsql_test 5.4 { + CREATE TABLE a2(o); + INSERT INTO a2 VALUES(2), (5); +} + +do_subquery_test 5.5 0 " + SELECT o FROM a2 WHERE EXISTS ( + SELECT 1 FROM a1 WHERE [exprs 0 62] + ) +" { + 2 5 +} + +do_subquery_test 5.6 1 " + SELECT o FROM a2 WHERE EXISTS ( + SELECT 1 FROM a1 WHERE [exprs 10 73] + ) +" { + 2 5 +} + +do_subquery_test 5.7 1 " + SELECT o FROM a2 WHERE EXISTS ( + SELECT 1 FROM a1 WHERE [exprs 20 84] + ) +" { + 2 5 +} + + + finish_test diff --git a/test/existsfault.test b/test/existsfault.test new file mode 100644 index 0000000000..4b335d84cd --- /dev/null +++ b/test/existsfault.test @@ -0,0 +1,49 @@ +# 2024 May 25 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/malloc_common.tcl +set testprefix existsfault + +db close +sqlite3_shutdown +sqlite3_config_lookaside 0 0 +sqlite3_initialize +autoinstall_test_functions +sqlite3 db test.db + +do_execsql_test 1.0 { + CREATE TABLE x1(a, b); + INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6); + CREATE UNIQUE INDEX x1a ON x1(a); + CREATE INDEX x1b ON x1(b); + + CREATE TABLE x2(x, y); + INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6); +} + +do_faultsim_test 1 -faults oom* -prep { + sqlite3 db test.db + execsql { SELECT * FROM sqlite_schema } +} -body { + execsql { + SELECT count(*) FROM x2 WHERE EXISTS (SELECT 1 FROM x1 WHERE a=x) AND y!=11 + } +} -test { + faultsim_test_result {0 3} +} + +finish_test + + From dc9a262319d734b9a8023e34ded1f426459766bd Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 10 Jun 2024 19:31:18 +0000 Subject: [PATCH 3/8] Fix a crash that could occur when the SELECT in an EXISTS(SELECT ...) used an unknown collation sequence. FossilOrigin-Name: f3009a6d7facd1ead520d588c5ad089db76d8641cd3dae076f2405492d7defcd --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 2 +- test/existsexpr.test | 12 ++++++++++++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 55819af278..24efae1938 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sextra\stests\sfor\sthe\schanges\son\sthis\sbranch. -D 2024-06-08T18:13:12.691 +C Fix\sa\scrash\sthat\scould\soccur\swhen\sthe\sSELECT\sin\san\sEXISTS(SELECT\s...)\sused\san\sunknown\scollation\ssequence. +D 2024-06-10T19:31:18.296 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,7 +755,7 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 06146768a11bfe8928fea0aabf8addedd4d146a8c4be70558f5c8ecdcc2ce338 +F src/select.c b3fa60ba7aab607daf0d15053a66073888f3dd8d6bb1c8000349bc22d85acd80 F src/shell.c.in 77d12a0dab8724819e64a14d5fbaad91a934be2b22ad329708fba9ba78993f04 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1114,7 +1114,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/existsexpr.test b101143b177d623c35166589d3e6772021dfe4f625ac318c91726a998939cd19 +F test/existsexpr.test 7c0abb8383b492325d57e0546faf4ea52ad52c02877b797460330f17320a1314 F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 @@ -2197,8 +2197,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 972a33db0b0e924b78d5309d222d8ea298bd59c72da14ea2d14e8e2caaad1e0a -R 20ea5e8daae9a69a6e09a05cc19a6a37 +P a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1 +R 4addb7a3e408fa9c0e91b39c04b20512 U dan -Z cbb8e2989ade033c119abad0907d9ec3 +Z fa336d8505987cef2cdbba1866644a8f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ce5b16061c..f3378323f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1 \ No newline at end of file +f3009a6d7facd1ead520d588c5ad089db76d8641cd3dae076f2405492d7defcd \ No newline at end of file diff --git a/src/select.c b/src/select.c index f21559e8da..ac8b1b6ae0 100644 --- a/src/select.c +++ b/src/select.c @@ -7347,7 +7347,7 @@ static u64 findConstIdxTerms( assert( pIdx->azColl[ii] ); if( pLeft->iColumn==pIdx->aiColumn[ii] ){ CollSeq *pColl = sqlite3ExprCompareCollSeq(pParse, pWhere); - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[ii])==0 ){ + if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[ii])==0 ){ m |= ((u64)1 << ii); break; } diff --git a/test/existsexpr.test b/test/existsexpr.test index 344f8cff6e..836560f2d0 100644 --- a/test/existsexpr.test +++ b/test/existsexpr.test @@ -363,6 +363,18 @@ do_subquery_test 5.7 1 " 2 5 } +#------------------------------------------------------------------------- +reset_db +do_execsql_test 6.0 { + CREATE TABLE t1(a, b UNIQUE, c UNIQUE); + CREATE TABLE t2(a INfEGER PRIMARY KEY, b); + CREATE UNIQUE INDEX t2b ON t2(b); +} + +do_catchsql_test 6.1 { + SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c COLLATE f = a) +} {1 {no such collation sequence: f}} + finish_test From df77e56cdeeb46fab3deb9ded4d5c0e873f57ee9 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 10 Jun 2024 19:45:33 +0000 Subject: [PATCH 4/8] Add missing calls to sqlite3exprSkipCollateAndLikely() to the enchancement on this branch. FossilOrigin-Name: 078537d057d638389e3ab3bc04bcac53f342c7bf1d8d75222296ef42d09e9ee7 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 4 ++-- test/existsexpr.test | 29 +++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 24efae1938..e555256083 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\scrash\sthat\scould\soccur\swhen\sthe\sSELECT\sin\san\sEXISTS(SELECT\s...)\sused\san\sunknown\scollation\ssequence. -D 2024-06-10T19:31:18.296 +C Add\smissing\scalls\sto\ssqlite3exprSkipCollateAndLikely()\sto\sthe\senchancement\son\sthis\sbranch. +D 2024-06-10T19:45:33.614 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,7 +755,7 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c b3fa60ba7aab607daf0d15053a66073888f3dd8d6bb1c8000349bc22d85acd80 +F src/select.c 9c65721098bad3f2cf266ef8e6aa8c0d57a691a48c075d5e8b1a44fa4fa7e351 F src/shell.c.in 77d12a0dab8724819e64a14d5fbaad91a934be2b22ad329708fba9ba78993f04 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1114,7 +1114,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/existsexpr.test 7c0abb8383b492325d57e0546faf4ea52ad52c02877b797460330f17320a1314 +F test/existsexpr.test 6a917db207ca95a9da8733a2c3aefc8541f81d41169a69deb125a56686c73ed0 F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 @@ -2197,8 +2197,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a6365c778f2a6d6fa78e8520553373898f382ce73bf6496533e26291648ef5d1 -R 4addb7a3e408fa9c0e91b39c04b20512 +P f3009a6d7facd1ead520d588c5ad089db76d8641cd3dae076f2405492d7defcd +R 93b078b964e586d7ff51ddb903d178e0 U dan -Z fa336d8505987cef2cdbba1866644a8f +Z 3d590655f4ffe6769b9e658507fd34f2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f3378323f6..0d8e5a92ba 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f3009a6d7facd1ead520d588c5ad089db76d8641cd3dae076f2405492d7defcd \ No newline at end of file +078537d057d638389e3ab3bc04bcac53f342c7bf1d8d75222296ef42d09e9ee7 \ No newline at end of file diff --git a/src/select.c b/src/select.c index ac8b1b6ae0..69a07528fa 100644 --- a/src/select.c +++ b/src/select.c @@ -7332,8 +7332,8 @@ static u64 findConstIdxTerms( m = findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pLeft); m |= findConstIdxTerms(pParse, iCsr, pIdx, pWhere->pRight); }else if( pWhere->op==TK_EQ ){ - Expr *pLeft = pWhere->pLeft; - Expr *pRight = pWhere->pRight; + Expr *pLeft = sqlite3ExprSkipCollateAndLikely(pWhere->pLeft); + Expr *pRight = sqlite3ExprSkipCollateAndLikely(pWhere->pRight); if( pRight->op==TK_COLUMN && pRight->iTable==iCsr ){ SWAP(Expr*, pLeft, pRight); } diff --git a/test/existsexpr.test b/test/existsexpr.test index 836560f2d0..a5f60b944b 100644 --- a/test/existsexpr.test +++ b/test/existsexpr.test @@ -241,6 +241,35 @@ do_subquery_test 4.1 0 { D d } +do_subquery_test 4.1.1 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE (a COLLATE nocase)=x AND b=y + ) +} { + A a b b C c D d +} +do_subquery_test 4.1.2 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x AND (b COLLATE binary)=y + ) +} { + A a b b C c D d +} +do_subquery_test 4.1.1 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE x=(a COLLATE nocase) AND b=y + ) +} { + A a b b C c D d +} +do_subquery_test 4.1.2 0 { + SELECT * FROM tx2 WHERE EXISTS ( + SELECT 1 FROM tx1 WHERE a=x AND y=(b COLLATE binary) + ) +} { + A a b b C c D d +} + do_subquery_test 4.2 1 { SELECT * FROM tx2 WHERE EXISTS ( SELECT 1 FROM tx1 WHERE a=x AND b=y COLLATE nocase From ddc62664bfb5e3237b20098b30e4a99815c322e0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 11 Jun 2024 18:22:48 +0000 Subject: [PATCH 5/8] Provide the ability to disable the exists-to-join optimization using SQLITE_TESTCTRL_OPTIMIZATIONS. Provide tree-trace output for the optimization. FossilOrigin-Name: 33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 11 +++++++++-- src/sqliteInt.h | 2 ++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 0b5c406027..13e08125d6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\senhancements\sinto\sthe\sexists-to-join\sbranch. -D 2024-06-11T17:37:36.892 +C Provide\sthe\sability\sto\sdisable\sthe\sexists-to-join\soptimization\susing\nSQLITE_TESTCTRL_OPTIMIZATIONS.\s\sProvide\stree-trace\soutput\sfor\sthe\soptimization. +D 2024-06-11T18:22:48.334 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,12 +755,12 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 9c65721098bad3f2cf266ef8e6aa8c0d57a691a48c075d5e8b1a44fa4fa7e351 +F src/select.c decd5944639f604eba66bcf28161c1beb09b7762093aadc25313a0fe62ce9a5f F src/shell.c.in ad27d1d990e9e5fb7ae8fc38a717e91f55233714f59723e5618baf4a2a3d2b65 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h c62b054b581abb051018ddc503bc292f3103db1e5ee16346f1495d46c07c5d3b +F src/sqliteInt.h b0c079cec6aa6a133af52489c14a4d8c2d750889594299d83eab1ae1b12979c0 F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2197,8 +2197,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 078537d057d638389e3ab3bc04bcac53f342c7bf1d8d75222296ef42d09e9ee7 6935ac71bad3d36cc519f0325ae4447a674f257309d020cdc0741160fcce0580 -R b6e202e4f08c9d1d4b557c05911f57ed +P 5f25a9518a675efbd0525cc2f5595ee7bc7122be7cfecdf6b20c909185dea370 +R b17f1837a5067fb4548e9e703c412c5a U drh -Z 025c74f5e2115680217e065394f815d9 +Z e7e7ba1dc895b6e1b772b1a24ff19dc3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 78990c287e..f005164c32 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5f25a9518a675efbd0525cc2f5595ee7bc7122be7cfecdf6b20c909185dea370 \ No newline at end of file +33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 69a07528fa..e546a664cd 100644 --- a/src/select.c +++ b/src/select.c @@ -7421,7 +7421,14 @@ static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ pSub->pWhere = 0; pSub->pSrc = 0; - sqlite3SelectDelete(pParse->db, pSub); + sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSub); +#if TREETRACE_ENABLED + if( sqlite3TreeTrace & 0x100000 ){ + TREETRACE(0x100000,pParse,p, + ("After EXISTS-to-JOIN optimization:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif } } } @@ -7756,7 +7763,7 @@ int sqlite3Select( /* If there may be an "EXISTS (SELECT ...)" in the WHERE clause, attempt ** to change it into a join. */ - if( pParse->bHasExists ){ + if( pParse->bHasExists && OptimizationEnabled(db,SQLITE_ExistsToJoin) ){ existsToJoin(pParse, p, p->pWhere); pTabList = p->pSrc; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c976b928dd..6a1b22f92d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1126,6 +1126,7 @@ extern u32 sqlite3TreeTrace; ** 0x00020000 Transform DISTINCT into GROUP BY ** 0x00040000 SELECT tree dump after all code has been generated ** 0x00080000 NOT NULL strength reduction +** 0x00100000 EXISTS-to-JOIN optimization */ /* @@ -1924,6 +1925,7 @@ struct sqlite3 { #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ +#define SQLITE_ExistsToJoin 0x10000000 /* The EXISTS-to-JOIN optimization */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* From 0d92e66dfc42e23cf07fdf01b37f1bcbd0d8b6b9 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 12 Jun 2024 17:01:00 +0000 Subject: [PATCH 6/8] Do not attempt the exists-to-join optimization for expressions from the ON clause of joins. FossilOrigin-Name: 4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401 --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/select.c | 6 +++++- test/existsexpr.test | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 13e08125d6..beca64a81a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Provide\sthe\sability\sto\sdisable\sthe\sexists-to-join\soptimization\susing\nSQLITE_TESTCTRL_OPTIMIZATIONS.\s\sProvide\stree-trace\soutput\sfor\sthe\soptimization. -D 2024-06-11T18:22:48.334 +C Do\snot\sattempt\sthe\sexists-to-join\soptimization\sfor\sexpressions\sfrom\sthe\sON\sclause\sof\sjoins. +D 2024-06-12T17:01:00.926 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,7 +755,7 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c decd5944639f604eba66bcf28161c1beb09b7762093aadc25313a0fe62ce9a5f +F src/select.c 757ea788e20f608d09bfef209d17bba87104cab920f80de77c92b38588ef64c5 F src/shell.c.in ad27d1d990e9e5fb7ae8fc38a717e91f55233714f59723e5618baf4a2a3d2b65 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -1114,7 +1114,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/existsexpr.test 6a917db207ca95a9da8733a2c3aefc8541f81d41169a69deb125a56686c73ed0 +F test/existsexpr.test bf1201621070e79c1060c1a8cf7d65d81fc6b336d94a371c63ecb08d357af2fd F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 @@ -2197,8 +2197,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5f25a9518a675efbd0525cc2f5595ee7bc7122be7cfecdf6b20c909185dea370 -R b17f1837a5067fb4548e9e703c412c5a -U drh -Z e7e7ba1dc895b6e1b772b1a24ff19dc3 +P 33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686 +R 25697789cb2a6d5a3b4b0fd17d4887cd +U dan +Z c7e8e1ad09260243da52b623b6dff2b7 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f005164c32..26bcc7370b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686 \ No newline at end of file +4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401 \ No newline at end of file diff --git a/src/select.c b/src/select.c index e546a664cd..7a789196b6 100644 --- a/src/select.c +++ b/src/select.c @@ -7382,7 +7382,11 @@ static u64 findConstIdxTerms( ** WHERE S.sid = R.sid AND R.day = '2022-10-25'; */ static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ - if( pWhere && p->pSrc->nSrc>0 && pParse->db->mallocFailed==0 ){ + if( pWhere + && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) + && p->pSrc->nSrc>0 + && pParse->db->mallocFailed==0 + ){ if( pWhere->op==TK_AND ){ Expr *pRight = pWhere->pRight; existsToJoin(pParse, p, pWhere->pLeft); diff --git a/test/existsexpr.test b/test/existsexpr.test index a5f60b944b..81aa5799f0 100644 --- a/test/existsexpr.test +++ b/test/existsexpr.test @@ -404,6 +404,23 @@ do_catchsql_test 6.1 { SELECT a FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE c COLLATE f = a) } {1 {no such collation sequence: f}} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 7.0 { + CREATE TABLE t1(x); + CREATE TABLE t2(y UNIQUE); + + INSERT INTO t1 VALUES(1), (2); + INSERT INTO t2 VALUES(1), (3); + + SELECT * FROM t1 one LEFT JOIN t1 two ON (one.x=two.x AND EXISTS ( + SELECT 1 FROM t2 WHERE y=one.x + )); +} { + 1 1 + 2 {} +} + finish_test From 62b954d545f52a8d59db654948f28ad6b6d899f6 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 14 Jun 2024 18:06:25 +0000 Subject: [PATCH 7/8] Add tests for the change on this branch. FossilOrigin-Name: ae19ff9ba819439fd107e745b6e8e503e5b68bfdb9da58b74035413704ad3caf --- manifest | 11 ++--- manifest.uuid | 2 +- test/existsexpr2.test | 96 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 test/existsexpr2.test diff --git a/manifest b/manifest index beca64a81a..5d3e1df5e2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sattempt\sthe\sexists-to-join\soptimization\sfor\sexpressions\sfrom\sthe\sON\sclause\sof\sjoins. -D 2024-06-12T17:01:00.926 +C Add\stests\sfor\sthe\schange\son\sthis\sbranch. +D 2024-06-14T18:06:25.102 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1115,6 +1115,7 @@ F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac F test/existsexpr.test bf1201621070e79c1060c1a8cf7d65d81fc6b336d94a371c63ecb08d357af2fd +F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cced5d5af11 F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8 @@ -2197,8 +2198,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 33a3f327855b427ae6ba0057218d043a1417bc9d780728f47f23acdd836e1686 -R 25697789cb2a6d5a3b4b0fd17d4887cd +P 4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401 +R a04eb111a75540deb83a6459885c7580 U dan -Z c7e8e1ad09260243da52b623b6dff2b7 +Z 384cdb2f34ec5a8891b2a14db482f8c6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 26bcc7370b..49d1e69efe 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401 \ No newline at end of file +ae19ff9ba819439fd107e745b6e8e503e5b68bfdb9da58b74035413704ad3caf \ No newline at end of file diff --git a/test/existsexpr2.test b/test/existsexpr2.test new file mode 100644 index 0000000000..f7644bf802 --- /dev/null +++ b/test/existsexpr2.test @@ -0,0 +1,96 @@ +# 2024 June 14 +# +# 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. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +set testprefix existsexpr2 + + +do_execsql_test 1.0 { + CREATE TABLE x1(a, b, PRIMARY KEY(a)) WITHOUT ROWID; + INSERT INTO x1 VALUES(1, 2), (3, 4), (5, 6); + CREATE INDEX x1b ON x1(b); + + CREATE TABLE x2(x, y); + INSERT INTO x2 VALUES(1, 2), (3, 4), (5, 6); +} + +do_execsql_test 1.1 { + SELECT * FROM x1 WHERE EXISTS (SELECT 1 FROM x2 WHERE a!=123) +} {1 2 3 4 5 6} + +do_execsql_test 1.2 { + CREATE TABLE x3(u, v); + CREATE INDEX x3u ON x3(u); + INSERT INTO x3 VALUES + (1, 1), (1, 2), (1, 3), + (2, 1), (2, 2), (2, 3); +} + +do_execsql_test 1.3 { + SELECT * FROM x1 WHERE EXISTS ( + SELECT 1 FROM x3 WHERE u IN (1, 2, 3, 4) AND v=b + ); +} { + 1 2 +} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 2.0 { + CREATE TABLE t1(a, b, c); + CREATE INDEX t1ab ON t1(a,b); + + INSERT INTO t1 VALUES + ('abc', 1, 1), + ('abc', 2, 2), + ('abc', 2, 3), + + ('def', 1, 1), + ('def', 2, 2), + ('def', 2, 3); + + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 1), (2, 2), (3, 3); + + ANALYZE; + DELETE FROM sqlite_stat1; + INSERT INTO sqlite_stat1 VALUES('t1','t1ab','10000 5000 2'); + ANALYZE sqlite_master; +} + + +do_execsql_test 2.1 { + SELECT a,b,c FROM t1 WHERE b=2 ORDER BY a +} { + abc 2 2 + abc 2 3 + def 2 2 + def 2 3 +} + +do_execsql_test 2.2 { + SELECT x, y FROM t2 WHERE EXISTS ( + SELECT 1 FROM t1 WHERE b=x + ) +} { + 1 1 + 2 2 +} + + + +finish_test + + From 620a00ee32c64944289bc8ff8c37b23bb4e220a1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Thu, 20 Jun 2024 12:07:25 +0000 Subject: [PATCH 8/8] Do not attempt the exists-to-join optimization if the FROM clause is full. FossilOrigin-Name: 8e3a1d2850337a902ab36b1d6a0dad4ae35030b71d1e15547f6e7487c1f86d18 --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/select.c | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 5d3e1df5e2..e374d68b4b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\stests\sfor\sthe\schange\son\sthis\sbranch. -D 2024-06-14T18:06:25.102 +C Do\snot\sattempt\sthe\sexists-to-join\soptimization\sif\sthe\sFROM\sclause\sis\sfull. +D 2024-06-20T12:07:25.967 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -755,7 +755,7 @@ F src/printf.c 8b250972305e14b365561be5117ed0fd364e4fd58968776df1ce64c6280b90f9 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 9c7786f032dea81487e7d94cb17849936f0e9b8891bfc91a6ac24ab193762804 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 757ea788e20f608d09bfef209d17bba87104cab920f80de77c92b38588ef64c5 +F src/select.c 72883bd7dda5e67cdf097776ae5d83aab02fbad9c4fa3ae1e25516efb1401fd4 F src/shell.c.in ad27d1d990e9e5fb7ae8fc38a717e91f55233714f59723e5618baf4a2a3d2b65 F src/sqlite.h.in cbd3e4177791a61c056fd81e37a5b21bb6c8cb2ea8cac558c625974673f50acf F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 @@ -2198,8 +2198,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 4666433cbd9af21c2e0440b10bcb39878624a39485e2bb514553b276acb8a401 -R a04eb111a75540deb83a6459885c7580 -U dan -Z 384cdb2f34ec5a8891b2a14db482f8c6 +P ae19ff9ba819439fd107e745b6e8e503e5b68bfdb9da58b74035413704ad3caf +R 2e2a21654277825e58e413fb627048c9 +U drh +Z 5d427b052ed84b4ca59ddb925bd74f93 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 49d1e69efe..3ef5d46fbc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ae19ff9ba819439fd107e745b6e8e503e5b68bfdb9da58b74035413704ad3caf \ No newline at end of file +8e3a1d2850337a902ab36b1d6a0dad4ae35030b71d1e15547f6e7487c1f86d18 diff --git a/src/select.c b/src/select.c index 7a789196b6..832b711b8b 100644 --- a/src/select.c +++ b/src/select.c @@ -7384,7 +7384,8 @@ static u64 findConstIdxTerms( static void existsToJoin(Parse *pParse, Select *p, Expr *pWhere){ if( pWhere && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) - && p->pSrc->nSrc>0 + && p->pSrc->nSrc>0 + && p->pSrc->nSrcdb->mallocFailed==0 ){ if( pWhere->op==TK_AND ){