From 743ae4c68b51fe56e6a269a79532224d2e45ce12 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 5 Apr 2024 20:01:28 +0000 Subject: [PATCH 1/4] Experimental enhancement in which expressions of the form "expr IN table" can be pushed down into subexpressions. FossilOrigin-Name: 2cbd7838fd6ffdf210f34671cd2e3e749a076a3a6f155bbe5f910a67db31c5b1 --- manifest | 22 ++++++------ manifest.uuid | 2 +- src/expr.c | 60 ++++++++++++++++++++++++++++---- src/parse.y | 4 +++ src/sqliteInt.h | 3 +- test/pushdown.test | 85 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 8b598b2633..8fd5496617 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Check-in\s[a9657c87c53c1922]\sis\swrong:\sthe\sIndexedExpr.bMaybeNullRow\sflag\sis\nrequired\sfor\svirtual\scolumns\sif\sthey\sare\spart\sof\san\souter\sjoin.\s\sAdd\sa\ntest\scase\s(derived\sfrom\sdbsqlfuzz\sb9e65e2f110df998f1306571fae7af6c01e4d92b)\nto\sprove\sit. -D 2024-04-05T13:56:05.021 +C Experimental\senhancement\sin\swhich\sexpressions\sof\sthe\sform\s"expr\sIN\stable"\ncan\sbe\spushed\sdown\sinto\ssubexpressions. +D 2024-04-05T20:01:28.787 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -700,7 +700,7 @@ F src/date.c 126ba2ab10aeb2e7ba6e089b5f07b747c0625b8287f78b60da346eda8d23c875 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 -F src/expr.c cd46ce2ebe46a30e63087dbe8aff7d6e34ba8acc8c658e8e18fcaa35f2711391 +F src/expr.c 48e6732170b5f3c07f9ebbdbe12c478e2867ea4be4361946ed26e2c8d607f7e8 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00 F src/func.c 283d4f3b2751a1d9339fd93a8a013d1948fd5f4474a3cab0955eb4fafd445d0f @@ -739,7 +739,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c ff60e98138d2499082ac6230f01ac508aba545315debccfca2fd6042f5f10fcd F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a -F src/parse.y 5bcef16094213efcc365a9d4dc4e3131f09251dc8838dce4a9e5f9764bff5b82 +F src/parse.y 67b9965a501599d5a1a4333125fab0dd230e03e28045700c9b331f7c3269bda5 F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 @@ -755,7 +755,7 @@ F src/shell.c.in 0354ca51eee5fbf6af394a7ef9f5ef6823ef45b743db65431f6777e4d5be219 F src/sqlite.h.in 32389e0d584551b300d0157881336162c14315a424cbf385c0d65eb7c2e31f7b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h 170f85c5a9d6189cf9b5dd304f4d40b73a8c9762fb927f1327e8ed2cb99c06b6 +F src/sqliteInt.h a0d0f014baafcb61f5d0a6d206d4b190644d57bb762dff2073903595653d6319 F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1510,7 +1510,7 @@ F test/printf.test 685fec5a0c5af2490ab0632775a301554361d674211d690f5bee0a97b0533 F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60 F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc -F test/pushdown.test 1495a09837a1cedfc0adf07ba42dc6b83be05a2c15de331b67c39a0e22078238 +F test/pushdown.test a8472a771ff80f9652477a688a5beed40b1f6de6546ed3a38150d3b272891173 F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 F test/quickcheck.test a4b7e878cd97e46108291c409b0bf8214f29e18fddd68a42bc5c1375ad1fb80a @@ -2184,9 +2184,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 c7896e88850669e18e89d44c4169d4f4a5d4b904bea6ccb2ac64f93b6d348a42 -Q -a9657c87c53c19228a42559c82c54b504a5ad729e407e9e2c7dabcc0c949b261 -R 00fdd25fa9a3c1e58bfe6d44fd3eb812 +P 4484ec6d26b31305e31de89bdbae26344d8083a7e7de20861430d31737d9979c +R 445c665a7eec3209ded422866778f161 +T *branch * pushdown-IN-table +T *sym-pushdown-IN-table * +T -sym-trunk * U drh -Z d45c3b35e63ad3a95f4d8ac3c99323f3 +Z 5ecd3bbce21aa8f702309c384d09dab0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0dad7c2062..7d0dacd7c0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4484ec6d26b31305e31de89bdbae26344d8083a7e7de20861430d31737d9979c \ No newline at end of file +2cbd7838fd6ffdf210f34671cd2e3e749a076a3a6f155bbe5f910a67db31c5b1 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index edfea3fc92..f2b252792b 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2500,7 +2500,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } } -static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){ +static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ Walker w; w.eCode = initFlag; w.pParse = pParse; @@ -2509,7 +2509,6 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){ #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif - w.u.iCur = iCur; sqlite3WalkExpr(&w, p); return w.eCode; } @@ -2529,7 +2528,7 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){ ** function and on its parameters. */ int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ - return exprIsConst(pParse, p, 1, 0); + return exprIsConst(pParse, p, 1); } /* @@ -2546,7 +2545,44 @@ int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). */ static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ - return exprIsConst(pParse, p, 2, 0); + return exprIsConst(pParse, p, 2); +} + +/* +** This routine examines sub-SELECT statements as an expression is being +** walked as part of sqlite3ExprIsTableConstant() (hereafter IsTabConst()). +** Most SELECT statements will cause IsTabConst() to return false. However, +** if: +** +** (1) The SELECT is the right-hand side of an IN operator, and +** (2) Nothing in the SELECT refers to anything other than itself +** +** Then this routine causes the sub-SELECT to be bypassed, so that if +** nothing else is amiss the IsTabConst() routine can return true. +*/ +static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ + int savedCursor; + assert( pSelect!=0 ); + assert( pWalker->eCode==3 || pWalker->eCode==0 ); + if( (pSelect->selFlags & SF_RhsOfIN)==0 ){ + pWalker->eCode = 0; + return WRC_Abort; + } + assert( pSelect->pSrc!=0 ); + assert( pSelect->pSrc->nSrc==1 ); + assert( pSelect->pWhere==0 ); + assert( pSelect->pGroupBy==0 ); + assert( pSelect->pHaving==0 ); + assert( pSelect->pOrderBy==0 ); + assert( pSelect->pPrior==0 ); + assert( pSelect->pNext==0 ); + assert( pSelect->pLimit==0 ); + assert( pSelect->pWith==0 ); + savedCursor = pWalker->u.iCur; + pWalker->u.iCur = pSelect->pSrc->a[0].iCursor; + sqlite3WalkExprList(pWalker, pSelect->pEList); + pWalker->u.iCur = savedCursor; + return WRC_Prune; } /* @@ -2554,9 +2590,19 @@ static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ ** for any single row of the table with cursor iCur. In other words, the ** expression must not refer to any non-deterministic function nor any ** table other than iCur. +** +** 2024-04-05: Operators of the form "expr IN table" are now allowed, where +** "table" is the name of a table. */ -int sqlite3ExprIsTableConstant(Expr *p, int iCur){ - return exprIsConst(0, p, 3, iCur); +static int sqlite3ExprIsTableConstant(Expr *p, int iCur){ + Walker w; + w.eCode = 3; + w.pParse = 0; + w.xExprCallback = exprNodeIsConstant; + w.xSelectCallback = exprSelectWalkTableConstant; + w.u.iCur = iCur; + sqlite3WalkExpr(&w, p); + return w.eCode; } /* @@ -2713,7 +2759,7 @@ int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprList *pGroupBy){ */ int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ assert( isInit==0 || isInit==1 ); - return exprIsConst(0, p, 4+isInit, 0); + return exprIsConst(0, p, 4+isInit); } #ifdef SQLITE_ENABLE_CURSOR_HINTS diff --git a/src/parse.y b/src/parse.y index d22c8e6fc2..7a3d637663 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1369,6 +1369,10 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A = sqlite3PExpr(pParse, TK_IN, A, 0); sqlite3PExprAddSelect(pParse, A, pSelect); + if( pParse->nErr==0 ){ + assert( pSelect!=0 ); + pSelect->selFlags |= SF_RhsOfIN; + } if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0); } expr(A) ::= EXISTS LP select(Y) RP. { diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 365ff2c7f5..29acb570de 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3585,6 +3585,7 @@ struct Select { #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ +#define SF_RhsOfIN 0x20000000 /* Right-hand-side of an IN operator */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) @@ -5081,7 +5082,7 @@ int sqlite3ExprTruthValue(const Expr*); int sqlite3ExprIsConstant(Parse*,Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); -int sqlite3ExprIsTableConstant(Expr*,int); +// int sqlite3ExprIsTableConstant(Expr*,int); int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); diff --git a/test/pushdown.test b/test/pushdown.test index 1fbe6f34cd..036fe2e2d8 100644 --- a/test/pushdown.test +++ b/test/pushdown.test @@ -227,4 +227,89 @@ do_execsql_test 5.0 { WHERE e>0; } {- - 3 4 5} + +# 2024-04-05 +# Allow push-down of operators of the form "expr IN table". +# +reset_db +do_execsql_test 6.0 { + CREATE TABLE t01(w,x,y,z); + CREATE TABLE t02(w,x,y,z); + CREATE VIEW t0(w,x,y,z) AS + SELECT w,x,y,z FROM t01 UNION ALL SELECT w,x,y,z FROM t02; + CREATE INDEX t01x ON t01(w,x,y); + CREATE INDEX t02x ON t02(w,x,y); + CREATE VIEW v1(k) AS VALUES(77),(88),(99); + CREATE TABLE k1(k); + INSERT INTO k1 SELECT * FROM v1; +} +do_eqp_test 6.1 { + WITH k(n) AS (VALUES(77),(88),(99)) + SELECT max(z) FROM t0 WHERE w=123 AND x IN k AND y BETWEEN 44 AND 55; +} { + QUERY PLAN + |--CO-ROUTINE t0 + | `--COMPOUND QUERY + | |--LEFT-MOST SUBQUERY + | | |--SEARCH t01 USING INDEX t01x (w=? AND x=? AND y>? AND y? AND y? AND y? AND y? AND y? AND y Date: Sat, 6 Apr 2024 12:19:50 +0000 Subject: [PATCH 2/4] Generalize pushdown to allow any uncorrelated subquery to be pushed down. FossilOrigin-Name: 87c45fb0d5f5ca5d6d1ad27bef83f294231d17d94299e1997364a7975b423e38 --- manifest | 24 ++++++++++++------------ manifest.uuid | 2 +- src/expr.c | 32 +++++--------------------------- src/parse.y | 4 ---- src/resolve.c | 2 ++ src/sqliteInt.h | 3 +-- 6 files changed, 21 insertions(+), 46 deletions(-) diff --git a/manifest b/manifest index 8fd5496617..581e5d22d1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Experimental\senhancement\sin\swhich\sexpressions\sof\sthe\sform\s"expr\sIN\stable"\ncan\sbe\spushed\sdown\sinto\ssubexpressions. -D 2024-04-05T20:01:28.787 +C Generalize\spushdown\sto\sallow\sany\suncorrelated\ssubquery\sto\sbe\spushed\sdown. +D 2024-04-06T12:19:50.927 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -700,7 +700,7 @@ F src/date.c 126ba2ab10aeb2e7ba6e089b5f07b747c0625b8287f78b60da346eda8d23c875 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 -F src/expr.c 48e6732170b5f3c07f9ebbdbe12c478e2867ea4be4361946ed26e2c8d607f7e8 +F src/expr.c f2f4768a25d62f645d11233930c0514f5f05fdadd8150e17d64187dd5aa41e4a F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00 F src/func.c 283d4f3b2751a1d9339fd93a8a013d1948fd5f4474a3cab0955eb4fafd445d0f @@ -739,7 +739,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c ff60e98138d2499082ac6230f01ac508aba545315debccfca2fd6042f5f10fcd F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a -F src/parse.y 67b9965a501599d5a1a4333125fab0dd230e03e28045700c9b331f7c3269bda5 +F src/parse.y 5bcef16094213efcc365a9d4dc4e3131f09251dc8838dce4a9e5f9764bff5b82 F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 @@ -748,14 +748,14 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 371f6115cb69286ebc12c6f2d7511279c2e47d9f54f475d46a554d687a3b312c F src/printf.c 87b67bba3662a0523f39ae6b084a3907109702f717c654d6cecb838af5cd57f1 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c eb1860b134fb044fd819c4347105c148d5aac7c6498032be2829e5cc95619b28 +F src/resolve.c f242e509b2ff9e31799d26ddd87f20db1124426203aa16833a694d401b50d650 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 15a221347789e393b39e7d2d2bd102167979c95a1ce0675bb870b86a24ca6cf4 F src/shell.c.in 0354ca51eee5fbf6af394a7ef9f5ef6823ef45b743db65431f6777e4d5be2199 F src/sqlite.h.in 32389e0d584551b300d0157881336162c14315a424cbf385c0d65eb7c2e31f7b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h a0d0f014baafcb61f5d0a6d206d4b190644d57bb762dff2073903595653d6319 +F src/sqliteInt.h e9ef8bb759f6ed7c41971bd8189dca3d38cc8bc69c463a6f49aaa9401857091a F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -2184,11 +2184,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 4484ec6d26b31305e31de89bdbae26344d8083a7e7de20861430d31737d9979c -R 445c665a7eec3209ded422866778f161 -T *branch * pushdown-IN-table -T *sym-pushdown-IN-table * -T -sym-trunk * +P 2cbd7838fd6ffdf210f34671cd2e3e749a076a3a6f155bbe5f910a67db31c5b1 +R 3f1ebbef3f30ee834eac030ff4cc2f9a +T *branch * pushdown-subquery +T *sym-pushdown-subquery * +T -sym-pushdown-IN-table * U drh -Z 5ecd3bbce21aa8f702309c384d09dab0 +Z 00cc18dd5b8247ec2ec6349e18d457dd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7d0dacd7c0..b3cce22c82 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2cbd7838fd6ffdf210f34671cd2e3e749a076a3a6f155bbe5f910a67db31c5b1 \ No newline at end of file +87c45fb0d5f5ca5d6d1ad27bef83f294231d17d94299e1997364a7975b423e38 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index f2b252792b..9513e5ffd5 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2550,38 +2550,17 @@ static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ /* ** This routine examines sub-SELECT statements as an expression is being -** walked as part of sqlite3ExprIsTableConstant() (hereafter IsTabConst()). -** Most SELECT statements will cause IsTabConst() to return false. However, -** if: -** -** (1) The SELECT is the right-hand side of an IN operator, and -** (2) Nothing in the SELECT refers to anything other than itself -** -** Then this routine causes the sub-SELECT to be bypassed, so that if -** nothing else is amiss the IsTabConst() routine can return true. +** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered +** constant as long as they are uncorrelated - meaning that they do not +** contain any terms from outer contexts. */ static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ - int savedCursor; assert( pSelect!=0 ); assert( pWalker->eCode==3 || pWalker->eCode==0 ); - if( (pSelect->selFlags & SF_RhsOfIN)==0 ){ + if( (pSelect->selFlags & SF_Correlated)!=0 ){ pWalker->eCode = 0; return WRC_Abort; } - assert( pSelect->pSrc!=0 ); - assert( pSelect->pSrc->nSrc==1 ); - assert( pSelect->pWhere==0 ); - assert( pSelect->pGroupBy==0 ); - assert( pSelect->pHaving==0 ); - assert( pSelect->pOrderBy==0 ); - assert( pSelect->pPrior==0 ); - assert( pSelect->pNext==0 ); - assert( pSelect->pLimit==0 ); - assert( pSelect->pWith==0 ); - savedCursor = pWalker->u.iCur; - pWalker->u.iCur = pSelect->pSrc->a[0].iCursor; - sqlite3WalkExprList(pWalker, pSelect->pEList); - pWalker->u.iCur = savedCursor; return WRC_Prune; } @@ -2591,8 +2570,7 @@ static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ ** expression must not refer to any non-deterministic function nor any ** table other than iCur. ** -** 2024-04-05: Operators of the form "expr IN table" are now allowed, where -** "table" is the name of a table. +** Enhanced on 2024-04-06: Allow pExpr to contain uncorrelated subqueries. */ static int sqlite3ExprIsTableConstant(Expr *p, int iCur){ Walker w; diff --git a/src/parse.y b/src/parse.y index 7a3d637663..d22c8e6fc2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1369,10 +1369,6 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] { if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E); A = sqlite3PExpr(pParse, TK_IN, A, 0); sqlite3PExprAddSelect(pParse, A, pSelect); - if( pParse->nErr==0 ){ - assert( pSelect!=0 ); - pSelect->selFlags |= SF_RhsOfIN; - } if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0); } expr(A) ::= EXISTS LP select(Y) RP. { diff --git a/src/resolve.c b/src/resolve.c index 6e0c9906a6..7378addf5d 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1349,6 +1349,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); + assert( pExpr->x.pSelect ); if( pNC->ncFlags & NC_SelfRef ){ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); }else{ @@ -1357,6 +1358,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); + pExpr->x.pSelect->selFlags |= SF_Correlated; } pNC->ncFlags |= NC_Subquery; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 29acb570de..a735c445e7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3585,7 +3585,7 @@ struct Select { #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ -#define SF_RhsOfIN 0x20000000 /* Right-hand-side of an IN operator */ +#define SF_Correlated 0x20000000 /* True if references the outer context */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) @@ -5082,7 +5082,6 @@ int sqlite3ExprTruthValue(const Expr*); int sqlite3ExprIsConstant(Parse*,Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); -// int sqlite3ExprIsTableConstant(Expr*,int); int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); From 6951c4977976ddca0b9e4ce75e79fc69c44dfd77 Mon Sep 17 00:00:00 2001 From: drh <> Date: Sat, 6 Apr 2024 18:30:09 +0000 Subject: [PATCH 3/4] Do not allow changes to sqlite3ExprIsTableConstant() that support pushdown of subqueries interfere with the hash-join logic. FossilOrigin-Name: 8682931f9c6b40e1b09139c10bbe932c38330b5eb0c5c84f2432ad19a6793e37 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/expr.c | 25 +++++++++++++++++++------ src/select.c | 2 +- src/sqliteInt.h | 2 +- src/where.c | 4 ++-- 6 files changed, 33 insertions(+), 20 deletions(-) diff --git a/manifest b/manifest index b13e8b0b82..781df2e045 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\stesting\slogic\sfixes\son\strunk\sinto\sthe\spushdown-subquery\sbranch. -D 2024-04-06T17:42:14.598 +C Do\snot\sallow\schanges\sto\ssqlite3ExprIsTableConstant()\sthat\ssupport\spushdown\sof\nsubqueries\sinterfere\swith\sthe\shash-join\slogic. +D 2024-04-06T18:30:09.352 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -700,7 +700,7 @@ F src/date.c 126ba2ab10aeb2e7ba6e089b5f07b747c0625b8287f78b60da346eda8d23c875 F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782 F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43 F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 -F src/expr.c f2f4768a25d62f645d11233930c0514f5f05fdadd8150e17d64187dd5aa41e4a +F src/expr.c a475049e45329a4586ce8967963784a72023cef0f5821a2265ccacb7f68e0bc6 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00 F src/func.c 283d4f3b2751a1d9339fd93a8a013d1948fd5f4474a3cab0955eb4fafd445d0f @@ -750,12 +750,12 @@ F src/printf.c 87b67bba3662a0523f39ae6b084a3907109702f717c654d6cecb838af5cd57f1 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c f242e509b2ff9e31799d26ddd87f20db1124426203aa16833a694d401b50d650 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 15a221347789e393b39e7d2d2bd102167979c95a1ce0675bb870b86a24ca6cf4 +F src/select.c 6208d6d4acafc944b9916df28e63fc4e47b922b0335d0db057f68e334b87fc8e F src/shell.c.in 0354ca51eee5fbf6af394a7ef9f5ef6823ef45b743db65431f6777e4d5be2199 F src/sqlite.h.in 32389e0d584551b300d0157881336162c14315a424cbf385c0d65eb7c2e31f7b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 -F src/sqliteInt.h e9ef8bb759f6ed7c41971bd8189dca3d38cc8bc69c463a6f49aaa9401857091a +F src/sqliteInt.h 257a7addbdb67b1f9f514a28904ed3f574de16bd87c21570faf30600c9c39f40 F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728 F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -835,7 +835,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89 F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2 -F src/where.c 9982128f010a699560fb5be37633801c7641ac3540f49db43cec878025929a0e +F src/where.c 09253e913dc1a93aa6e0e1a5d25a392b8fa39096cc30d230dd302264b3be0661 F src/whereInt.h 82a13766f13d1a53b05387c2e60726289ef26404bc7b9b1f7770204d97357fb8 F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1 F src/whereexpr.c 7b64295f1d82ad0928df435925dd7bbd5997b44a026153113eace0d9e71ff435 @@ -2184,8 +2184,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 87c45fb0d5f5ca5d6d1ad27bef83f294231d17d94299e1997364a7975b423e38 c6e873d4db3ef36a0d561e64ead6feada5d1654c0757b4b6e55f671c9db66469 -R 5c918916ac2b7aa6a88b463bfdfa1fb1 +P a4e1f03dcad70e828a4b56e7a332946daf84d6eae078e3ec9a248b8157a53963 +R d7462427e22b33861c511b137742aefb U drh -Z 8a9056dc314aaed8a12c375787e3cb55 +Z cfcd3c15df77757710cc4630347f7777 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6465b19d72..545d7417cd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a4e1f03dcad70e828a4b56e7a332946daf84d6eae078e3ec9a248b8157a53963 \ No newline at end of file +8682931f9c6b40e1b09139c10bbe932c38330b5eb0c5c84f2432ad19a6793e37 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 9513e5ffd5..0ccb39cc6e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2570,14 +2570,22 @@ static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ ** expression must not refer to any non-deterministic function nor any ** table other than iCur. ** -** Enhanced on 2024-04-06: Allow pExpr to contain uncorrelated subqueries. +** Consider uncorrelated subqueries to be constants if the bAllowSubq +** parameter is true. */ -static int sqlite3ExprIsTableConstant(Expr *p, int iCur){ +static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ Walker w; w.eCode = 3; w.pParse = 0; w.xExprCallback = exprNodeIsConstant; - w.xSelectCallback = exprSelectWalkTableConstant; + if( bAllowSubq ){ + w.xSelectCallback = exprSelectWalkTableConstant; + }else{ + w.xSelectCallback = sqlite3SelectWalkFail; +#ifdef SQLITE_DEBUG + w.xSelectCallback2 = sqlite3SelectWalkAssert2; +#endif + } w.u.iCur = iCur; sqlite3WalkExpr(&w, p); return w.eCode; @@ -2598,7 +2606,10 @@ static int sqlite3ExprIsTableConstant(Expr *p, int iCur){ ** ** (1) pExpr cannot refer to any table other than pSrc->iCursor. ** -** (2) pExpr cannot use subqueries or non-deterministic functions. +** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is +** true and the subquery is non-correlated +** +** (2b) pExpr cannot use non-deterministic functions. ** ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) @@ -2627,7 +2638,8 @@ static int sqlite3ExprIsTableConstant(Expr *p, int iCur){ int sqlite3ExprIsSingleTableConstraint( Expr *pExpr, /* The constraint */ const SrcList *pSrcList, /* Complete FROM clause */ - int iSrc /* Which element of pSrcList to use */ + int iSrc, /* Which element of pSrcList to use */ + int bAllowSubq /* Allow non-correlated subqueries */ ){ const SrcItem *pSrc = &pSrcList->a[iSrc]; if( pSrc->fg.jointype & JT_LTORJ ){ @@ -2652,7 +2664,8 @@ int sqlite3ExprIsSingleTableConstraint( } } } - return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ + /* Rules (1), (2a), and (2b) handled by the following: */ + return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); } diff --git a/src/select.c b/src/select.c index b200518518..b13aae1f9a 100644 --- a/src/select.c +++ b/src/select.c @@ -5260,7 +5260,7 @@ static int pushDownWhereTerms( } #endif - if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a735c445e7..d80a646093 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -5082,7 +5082,7 @@ int sqlite3ExprTruthValue(const Expr*); int sqlite3ExprIsConstant(Parse*,Expr*); int sqlite3ExprIsConstantOrFunction(Expr*, u8); int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); -int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); +int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS int sqlite3ExprContainsSubquery(Expr*); #endif diff --git a/src/where.c b/src/where.c index a1837b5879..65b6ae2c92 100644 --- a/src/where.c +++ b/src/where.c @@ -942,7 +942,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** WHERE clause (or the ON clause of a LEFT join) that constrain which ** rows of the target table (pSrc) that can be used. */ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) ){ pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); @@ -1211,7 +1211,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( for(pTerm=pWInfo->sWC.a; pTermpExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) ){ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); } From ac94cf3ecedfdaac757f5ae295703dff259cce7d Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 8 Apr 2024 06:37:19 +0000 Subject: [PATCH 4/4] Enhancements to the fuzzer invariant checker to turn optimizations on and off. FossilOrigin-Name: 67594481379824823105939fea2ce1fe280667f6db91735ac78b4b6164a78dab --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/fuzzcheck.c | 16 +++++++++++----- test/fuzzinvariants.c | 30 ++++++++++++++++++++++++------ 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/manifest b/manifest index 9489e7d05e..f59889aa63 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\stesting\senhancements\sinto\sthe\spushdown-subquery\sbranch. -D 2024-04-07T18:55:57.788 +C Enhancements\sto\sthe\sfuzzer\sinvariant\schecker\sto\sturn\soptimizations\son\sand\noff. +D 2024-04-08T06:37:19.669 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1246,7 +1246,7 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634 F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830 F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2 -F test/fuzzcheck.c e6a40f53ac5624aa5b7c4f31c385f09ba088d524cecc4512fd3057caeed8f530 +F test/fuzzcheck.c dc159967609d00b0cfe619e735cbbf8482570aca85711397034b0662b6c18fc7 F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517 F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -1258,7 +1258,7 @@ F test/fuzzdata8.db 4a53b6d077c6a5c23b609d8d3ac66996fa55ba3f8d02f9b6efdd0214a767 F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc -F test/fuzzinvariants.c d89b81c3bdad7b2837f8cce645d5b563ffa965f6d819ce33f0f6d82a71c6ca9a +F test/fuzzinvariants.c 0729b9d8ed77ad0f8c5c7601168a707d5803087d2da030ede9057c51c809cc6c F test/gcfault.test 4ea410ac161e685f17b19e1f606f58514a2850e806c65b846d05f60d436c5b0d F test/gencol1.test e169bdfa11c7ed5e9f322a98a7db3afe9e66235750b68c923efee8e1876b46ec F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 @@ -2184,8 +2184,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 27865e316f8dfbf4c20290cf1be3024d7518fec46655e34f3fc435e15346c63e 6431538f0bb3bb8606786f3c3e5c055c4bc387098dd3bdc8a94f6fda61c47f52 -R a6f74894c0cda8635456b45fc1556948 +P 287ff24b26a512ff7648679767e68244f6eef95df6a49c46ed1f2594030ed523 +R f668a18b739f9caf1d0ed3baa3bf6c8f U drh -Z d0677700c03f1b93b120f5cc4adf200f +Z 70cb6868aa0f914c12631462fc26d140 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c911a505d2..30cfd87b8f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -287ff24b26a512ff7648679767e68244f6eef95df6a49c46ed1f2594030ed523 \ No newline at end of file +67594481379824823105939fea2ce1fe280667f6db91735ac78b4b6164a78dab \ No newline at end of file diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index e4ad1c1137..6cae348bd6 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -979,7 +979,8 @@ extern int fuzz_invariant( int iRow, /* The row number for pStmt */ int nRow, /* Total number of output rows */ int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */ - int eVerbosity /* How much debugging output */ + int eVerbosity, /* How much debugging output */ + unsigned int dbOpt /* Default optimization flags */ ); /* Implementation of sqlite_dbdata and sqlite_dbptr */ @@ -1031,7 +1032,12 @@ static int recoverDatabase(sqlite3 *db){ /* ** Run the SQL text */ -static int runDbSql(sqlite3 *db, const char *zSql, unsigned int *pBtsFlags){ +static int runDbSql( + sqlite3 *db, /* Run SQL on this database connection */ + const char *zSql, /* The SQL to be run */ + unsigned int *pBtsFlags, + unsigned int dbOpt /* Default optimization flags */ +){ int rc; sqlite3_stmt *pStmt; int bCorrupt = 0; @@ -1107,7 +1113,7 @@ static int runDbSql(sqlite3 *db, const char *zSql, unsigned int *pBtsFlags){ iRow++; for(iCnt=0; iCnt<99999; iCnt++){ rc = fuzz_invariant(db, pStmt, iCnt, iRow, nRow, - &bCorrupt, eVerbosity); + &bCorrupt, eVerbosity, dbOpt); if( rc==SQLITE_DONE ) break; if( rc!=SQLITE_ERROR ) g.nInvariant++; if( eVerbosity>0 ){ @@ -1330,7 +1336,7 @@ int runCombinedDbSqlInput( char cSaved = zSql[i+1]; zSql[i+1] = 0; if( sqlite3_complete(zSql+j) ){ - rc = runDbSql(cx.db, zSql+j, &btsFlags); + rc = runDbSql(cx.db, zSql+j, &btsFlags, dbOpt); j = i+1; } zSql[i+1] = cSaved; @@ -1340,7 +1346,7 @@ int runCombinedDbSqlInput( } } if( j100 ) return SQLITE_DONE; zTest = fuzz_invariant_sql(pStmt, iCnt); if( zTest==0 ) return SQLITE_DONE; + if( noOpt ){ + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, ~dbOpt); + } rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0); + if( noOpt ){ + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, dbOpt); + } if( rc ){ if( eVerbosity ){ printf("invariant compile failed: %s\n%s\n", @@ -212,7 +226,7 @@ int fuzz_invariant( } sqlite3_finalize(pCk); if( rc==SQLITE_DONE ){ - reportInvariantFailed(pStmt, pTestStmt, iRow); + reportInvariantFailed(pStmt, pTestStmt, iRow, dbOpt, noOpt); return SQLITE_INTERNAL; }else if( eVerbosity>0 ){ printf("invariant-error ignored due to the use of virtual tables\n"); @@ -488,13 +502,17 @@ static void printRow(sqlite3_stmt *pStmt, int iRow){ static void reportInvariantFailed( sqlite3_stmt *pOrig, /* The original query */ sqlite3_stmt *pTest, /* The alternative test query with a missing row */ - int iRow /* Row number in pOrig */ + int iRow, /* Row number in pOrig */ + unsigned int dbOpt, /* Optimization flags on pOrig */ + int noOpt /* True if opt flags inverted for pTest */ ){ int iTestRow = 0; printf("Invariant check failed on row %d.\n", iRow); - printf("Original query --------------------------------------------------\n"); + printf("Original query (opt-flags: 0x%08x) --------------------------\n", + dbOpt); printf("%s\n", sqlite3_expanded_sql(pOrig)); - printf("Alternative query -----------------------------------------------\n"); + printf("Alternative query (opt-flags: 0x%08x) -----------------------\n", + noOpt ? ~dbOpt : dbOpt); printf("%s\n", sqlite3_expanded_sql(pTest)); printf("Result row that is missing from the alternative -----------------\n"); printRow(pOrig, iRow);