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:
14
manifest
14
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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
c58631b26ad6ee39d8db2700a26413249fd88d137c3a7944bad7af07cd7fe121
|
||||
7e6e665d05e5d5895bac60d68b49bdf28e2ec6d8dc4ef8c18703f2e6d4f8870f
|
||||
|
||||
26
src/select.c
26
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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user