1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-10-23 21:49:02 +03:00

Make the optimization of UNION with ORDER BY and LIMIT sound by restricing

it to LIMIT 1.

FossilOrigin-Name: 7e6e665d05e5d5895bac60d68b49bdf28e2ec6d8dc4ef8c18703f2e6d4f8870f
This commit is contained in:
drh
2026-08-25 16:29:27 +00:00
parent 69785dee50
commit 46c7844d76
4 changed files with 47 additions and 23 deletions

View File

@@ -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.

View File

@@ -1 +1 @@
c58631b26ad6ee39d8db2700a26413249fd88d137c3a7944bad7af07cd7fe121
7e6e665d05e5d5895bac60d68b49bdf28e2ec6d8dc4ef8c18703f2e6d4f8870f

View File

@@ -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,

View File

@@ -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<D Q' END,'' FROM t1 LEFT JOIN v1a ON z=b;
} {}
# branch union-to-union-all
#
reset_db
do_execsql_test select1-22.1 {
CREATE TABLE t1(a); INSERT INTO t1 VALUES(1),(1),(1),(2),(3);
CREATE TABLE t2(b); INSERT INTO t2 VALUES(10),(11);
SELECT a FROM t1 UNION SELECT b FROM t2 ORDER BY 1 LIMIT 3;
} {1 2 3}
do_execsql_test select1-22.2 {
SELECT a FROM t1 UNION SELECT b FROM t2 ORDER BY 1 LIMIT 1;
} {1}
do_execsql_test select1-22.3 {
SELECT a FROM t1 UNION SELECT b FROM t2 ORDER BY 1 LIMIT 1 OFFSET 2;
} {3}
do_execsql_test select1-22.3 {
SELECT a FROM t1 UNION SELECT b FROM t2 LIMIT 1;
} {1}
do_execsql_test select1-22.3-eqp {
EXPLAIN SELECT a FROM t1 UNION SELECT b FROM t2 LIMIT 1;
} {/UNION ALL/}
do_execsql_test select1-22.4 {
SELECT a FROM t1 UNION SELECT b FROM t2 LIMIT 2;
} {1 2}
do_execsql_test select1-22.4-eqp {
EXPLAIN SELECT a FROM t1 UNION SELECT b FROM t2 LIMIT 2;
} {~/UNION ALL/}
finish_test