diff --git a/manifest b/manifest index 88f45368e7..8f07d307ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sunion-to-union-all\sbranch. -D 2026-08-25T15:49:27.176 +C Make\sthe\soptimization\sof\sUNION\swith\sORDER\sBY\sand\sLIMIT\ssound\sby\srestricing\nit\sto\sLIMIT\s1. +D 2026-08-25T16:29:27.136 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -743,7 +743,7 @@ F src/printf.c b480176bd0aaed9d64e804cfa3ca27fc585f65a73e7c7ca2fe9b9016db2cf6d5 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 0e0990f60c93db38c735d85e5d1741130e72dd0670aec204b5a39118b53c8044 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 5881655693910e832d15c7e9f65b8519d688bd0771268cecf0776198bb0e7d23 +F src/select.c a962dd2573404110b438d9226494d57f71609cbc5f8365e40d1f8fa25e12f6aa F src/shell.c.in 3f015aa86179b6c4615f0cb2352a20af228aa2cad23fc0daa88aa82b36ce2d0f F src/sqlite.h.in efc3f7aa00a2bb01c087435e6fa5f86103e310e6c3ea072b12cc110cf2715a75 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 @@ -1606,7 +1606,7 @@ F test/schemafault.test 1936bceca55ac82c5efbcc9fc91a1933e45c8d1e1d106b9a7e56c972 F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2ff09384 F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5 F test/seekscan1.test 2148a174ba55fa378f18fdf62eeed1f3a3cc9b6f360a948db613cad0c80b1f04 -F test/select1.test 692e84cfa29c405854c69e8a4027183d64c22952866a123fabbce741a379e889 +F test/select1.test ce7ba6aef289a8a1fd6e140fb58bcc8e095034cbc6d8aab7d0eea9145cd49818 F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56 F test/select3.test 152ce3978c8600c70413e921e39e4c5aeb8ff1e96dd722442a9981e1b8afc702 F test/select4.test 21941409ac6b65bfca83de020afb5d976d802d3d8ad216dc774a3fbbf55fe277 @@ -2219,8 +2219,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 7304e5aeafe3981670d2f415b42a4ecc2371647c492d3b13098e564b27ba541e f3ad1b4394ff0902e4ae9e8d0ea36ee198149c5ce8745851eaf84105484fedf2 -R 4cf2fbc81f77e56271b06c93e322178e +P c58631b26ad6ee39d8db2700a26413249fd88d137c3a7944bad7af07cd7fe121 +R 76e0f6f942ae68e31165a889e51c3470 U drh -Z 65583537f8dbcdebda89f9514069d603 +Z 52028eaf171cca386a7f527b8fa6e46f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 57fef555d6..bf97a8177d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c58631b26ad6ee39d8db2700a26413249fd88d137c3a7944bad7af07cd7fe121 +7e6e665d05e5d5895bac60d68b49bdf28e2ec6d8dc4ef8c18703f2e6d4f8870f diff --git a/src/select.c b/src/select.c index 74bb22346d..e1e45bb3ad 100644 --- a/src/select.c +++ b/src/select.c @@ -2943,20 +2943,16 @@ static int hasAnchor(Select *p){ } /* -** If a compound SELECT is of the form: +** Return TRUE if p is a UNION with a LIMIT of exactly 1 and no OFFSET +** clause. This is a precondition for a couple of related optimizations. ** -** SELECT ... UNION SELECT ... LIMIT 1; -** -** That is, a UNION with a LIMIT of exact 1 and no OFFET or ORDER BY, -** then convert the UNION to a UNION ALL, as that will typically run -** faster. -** -** Return true (non-zero) if the optimization is successful. Return -** false (zero) of the constraints are not met. +** False negatives are harmless (apart from resulting in a slower query). +** False positives can result in incorrect answers, however. To provoke +** false negatives for testing purposes, disable the SQLITE_UnionLimit +** optimization. */ -static int convertUnionToAll(sqlite3 *db, Select *p){ +static int unionWithLimitOne(sqlite3 *db, Select *p){ int v; - assert( p->pOrderBy==0 ); /* Prevented by caller */ if( p->op!=TK_UNION ) return 0; if( p->pLimit==0 ) return 0; if( p->pLimit->pRight ) return 0; /* No OFFSET */ @@ -2965,7 +2961,6 @@ static int convertUnionToAll(sqlite3 *db, Select *p){ if( sqlite3ExprIsInteger(p->pLimit->pLeft, &v, 0, 0)==0 ) return 0; if( v!=1 ) return 0; if( OptimizationDisabled(db, SQLITE_UnionLimit) ) return 0; - p->op = TK_ALL; return 1; } @@ -3058,7 +3053,7 @@ static int multiSelect( /* If the compound has an ORDER BY clause, then always use the merge ** algorithm. */ return multiSelectByMerge(pParse, p, pDest); - }else if( p->op!=TK_ALL && !convertUnionToAll(db,p) ){ + }else if( p->op!=TK_ALL && !unionWithLimitOne(db,p) ){ /* If the compound is EXCEPT, INTERSECT, or UNION (anything other than ** UNION ALL) then also always use the merge algorithm. However, the ** multiSelectByMerge() routine requires that the compound have an @@ -3070,7 +3065,8 @@ static int multiSelect( p->pOrderBy->a[0].u.x.iOrderByCol = 1; return multiSelectByMerge(pParse, p, pDest); }else{ - /* For a UNION ALL compound without ORDER BY, simply run the left + /* For a UNION ALL compound without ORDER BY, or for a UNION with a + ** LIMIT of exactly 1 and no OFFSET and no ORDER BY, simply run the left ** query, then run the right query */ int addr = 0; int nLimit = 0; /* Initialize to suppress harmless compiler warning */ @@ -3621,7 +3617,7 @@ static int multiSelectByMerge( /* Compute the limit registers */ computeLimitRegisters(pParse, p, labelEnd); - if( p->iLimit && (op==TK_ALL || op==TK_UNION) ){ + if( p->iLimit && (op==TK_ALL || unionWithLimitOne(db,p)) ){ regLimitA = ++pParse->nMem; regLimitB = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Copy, p->iOffset ? p->iOffset+1 : p->iLimit, diff --git a/test/select1.test b/test/select1.test index 44e63d252d..cca5bae4d1 100644 --- a/test/select1.test +++ b/test/select1.test @@ -1210,4 +1210,32 @@ do_execsql_test select1-21.1 { SELECT a,(+a)b,(+a)b,(+a)b,NOT EXISTS(SELECT null FROM t2),CASE z WHEN 487 THEN 992 WHEN 391 THEN 203 WHEN 10 THEN '?k