From: drh <> Date: Mon, 4 May 2026 18:30:02 +0000 (+0000) Subject: When using the OR-optimization on a loop created by exists-to-join, X-Git-Tag: version-3.53.1~2 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=55f2cf294f99639d05cf5f822d7f43596be4f659;p=thirdparty%2Fsqlite.git When using the OR-optimization on a loop created by exists-to-join, do the early-exit logic for the whole loop, not for each individual OR branch. FossilOrigin-Name: b827cdcb371d07cd4ffba4d3c7b404bece43fcc808db528b94a66bf77b6aab4f --- diff --git a/manifest b/manifest index 65f3addafd..7657509594 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sharmless\scompiler\swarning. -D 2026-05-02T13:18:40.879 +C When\susing\sthe\sOR-optimization\son\sa\sloop\screated\sby\sexists-to-join,\ndo\sthe\searly-exit\slogic\sfor\sthe\swhole\sloop,\snot\sfor\seach\sindividual\nOR\sbranch. +D 2026-05-04T18:30:02.624 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -818,7 +818,7 @@ F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452 F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014 F src/where.c 1096566a629cccb0a4fd66e3304f39f73349c6c62d21111f8bbae46a43a15aa8 F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da -F src/wherecode.c 676cb6cb02878643e817d9917a2d3522b83a3736b2cedd3dc8a01d7bb92af6c2 +F src/wherecode.c 4d573077652f79780d6b50840ab8cbb72053dbb4eb230780dd2a146ab034475d F src/whereexpr.c e9f7185fba366d9365aa7a97329609e4cf00b3dd0400d069fbaa5187350c17c6 F src/window.c c0a38cd32473e8e8e7bc435039f914a36ca42465506dc491c65870c01ddac9fb F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 @@ -1097,7 +1097,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650 F test/exclusive2.test cd70b1d9c6fffd336f9795b711dcc5d9ceba133ad3f7001da3fda63615bdc91e F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac -F test/existsexpr.test b39974adaa7fa1deccc065a57f10db96f9005d2ba9e6f933a5f35c9128bcc1ad +F test/existsexpr.test 1ce70ac41fb6d52525774388aa23e982428e883a850f9d56d49776d234865552 F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cced5d5af11 F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e F test/expr.test db981f8a85520e99ae20aab7ad2e9b5b0437ed09159b57ced434c672075d2e61 @@ -2197,8 +2197,9 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 555401fe048a51ecaed3ef672723b6ef8e1340c7028c11a17731abbc399bc078 -R b223b2a1ce581457407e0190a0bd4b0b +P 9957c3b907d66ed1b8afa3b71ba0ad23b2de3ec486e6232b58166fd3a410705a +Q +70a5d091241748aea63aa13bb218b573d0b84615a0682c2ec693addbfe9bc2f4 +R 3559ce67f073a9c4080e64197a47368f U drh -Z 69a574efccd0f5aa6b46b82401c04985 +Z ccc6dd7f0c85079bd8734e7b94f9e99d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2325615476..ec49605df5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9957c3b907d66ed1b8afa3b71ba0ad23b2de3ec486e6232b58166fd3a410705a +b827cdcb371d07cd4ffba4d3c7b404bece43fcc808db528b94a66bf77b6aab4f diff --git a/src/wherecode.c b/src/wherecode.c index 4375ac5f5c..8da485f64e 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -2297,7 +2297,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). */ - if( pWInfo->nLevel>1 ){ + if( pWInfo->nLevel>1 || pTabItem->fg.fromExists ){ int nNotReady; /* The number of notReady tables */ SrcItem *origSrc; /* Original list of tables */ nNotReady = pWInfo->nLevel - iLevel - 1; @@ -2310,6 +2310,13 @@ Bitmask sqlite3WhereCodeOneLoopStart( for(k=1; k<=nNotReady; k++){ memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k])); } + + /* Clear the fromExists flag on the OR-optimized table entry so that + ** the calls to sqlite3WhereEnd() do not code early-exits after the + ** first row is visited. The early exit applies to this table's + ** overall loop - including the multiple OR branches and any WHERE + ** conditions not passed to the sub-loops - not to the sub-loops. */ + pOrTab->a[0].fg.fromExists = 0; }else{ pOrTab = pWInfo->pTabList; } @@ -2553,7 +2560,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( assert( pLevel->op==OP_Return ); pLevel->p2 = sqlite3VdbeCurrentAddr(v); - if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); } + if( pWInfo->pTabList!=pOrTab ){ sqlite3DbFreeNN(db, pOrTab); } if( !untestedTerms ) disableTerm(pLevel, pTerm); }else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ diff --git a/test/existsexpr.test b/test/existsexpr.test index e2867bbdb0..3b7fc45d7c 100644 --- a/test/existsexpr.test +++ b/test/existsexpr.test @@ -585,5 +585,48 @@ do_execsql_test 13.1 { ) } {7 10} +#------------------------------------------------------------------------- +# Forum post https://sqlite.org/forum/forumpost/c8b205720c +# +reset_db +do_execsql_test 14.1.0 { + CREATE TABLE t(n INTEGER, parent INTEGER); + CREATE INDEX idx ON t(parent); + INSERT INTO t VALUES (1, NULL); + INSERT INTO t VALUES (10, NULL); +} + +do_execsql_test 14.1.1 { + SELECT count(*) FROM t b + WHERE (b.parent IS NULL OR b.parent = 99) + AND 5 <= +b.n +} 1 + +do_execsql_test 14.1.2 { + SELECT 'match' WHERE EXISTS ( + SELECT 1 FROM t b + WHERE (b.parent IS NULL OR b.parent = 99) + AND 5 <= +b.n + ) +} {match} + +do_execsql_test 14.2.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(20, 20); + + CREATE TABLE t2(c, d); + CREATE INDEX t2c ON t2(c); + CREATE INDEX t2d ON t2(d); + + INSERT INTO t2 VALUES(20, -1); + INSERT INTO t2 VALUES(20, 0); +} + +do_execsql_test 14.2.2 { + SELECT * FROM t1 WHERE EXISTS ( + SELECT 1 FROM t2 WHERE (c=a OR d=a) AND (d+1)>0 + ) +} {20 20} + finish_test