mirror of
https://github.com/sqlite/sqlite.git
synced 2026-10-28 10:57:26 +03:00
New optimizations for improved performance of UNION with LIMIT 1, with
or without ORDER BY. FossilOrigin-Name: b5b8ede613c40cf4a1ad0fe2af9b98d410982e0680b1da00c67ff1c2c34e0d6b
This commit is contained in:
27
manifest
27
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\sOOM\shandling\sin\slittle-used\sextension\sfile\s"expert.c".\sReport\s[bugs:/info/2026-08-25T16:02:31Z\s|\s2026-08-25T16:02:31Z].
|
||||
D 2026-08-25T16:15:15.839
|
||||
C New\soptimizations\sfor\simproved\sperformance\sof\sUNION\swith\sLIMIT\s1,\swith\nor\swithout\sORDER\sBY.
|
||||
D 2026-08-25T17:12:09.970
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
@@ -743,12 +743,12 @@ F src/printf.c b480176bd0aaed9d64e804cfa3ca27fc585f65a73e7c7ca2fe9b9016db2cf6d5
|
||||
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
|
||||
F src/resolve.c 0e0990f60c93db38c735d85e5d1741130e72dd0670aec204b5a39118b53c8044
|
||||
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
|
||||
F src/select.c ba33125b1a93984f702883c730002a466860a7226dd9ce811bf75246d4ed021e
|
||||
F src/shell.c.in be2c75686ba31e189d82f79f6003109a7a0ea34a1290d72b6d695b59f87eec04
|
||||
F src/select.c a962dd2573404110b438d9226494d57f71609cbc5f8365e40d1f8fa25e12f6aa
|
||||
F src/shell.c.in 3f015aa86179b6c4615f0cb2352a20af228aa2cad23fc0daa88aa82b36ce2d0f
|
||||
F src/sqlite.h.in efc3f7aa00a2bb01c087435e6fa5f86103e310e6c3ea072b12cc110cf2715a75
|
||||
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
|
||||
F src/sqlite3ext.h 0efd4723bad9124ea1f581d9f1ea0254ac1c6f3e5fb29e4f3dcf36c72485a456
|
||||
F src/sqliteInt.h b4cb30785d99c6b04399a0ca35fa701d5f0b6e6df0ea8bdb7a40878cb87decc6
|
||||
F src/sqliteInt.h d58ab43b8a44f3d8c216ea042f7790fe038fce2a3207a011a175110ad322f256
|
||||
F src/sqliteLimit.h c433b3fdb5e52f4226bf78d7e8081dff8fd8fbb0ce0cb1a5fee282aaa8976813
|
||||
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -992,7 +992,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
|
||||
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
|
||||
F test/collateB.test 9c840e21f9aead6fc533cea310f0bd202d5c11511088811b7e93ae7b47fccb24
|
||||
F test/colmeta.test 248a644cec4c7c371cf1e107fd8fdba708dc290866c572672b6260e3466cce79
|
||||
F test/colname.test 0e32125de701c6bd86247194c6f5639b740b4f71a0d88ee1e67ff3bda9ae99ca
|
||||
F test/colname.test 30f7fff482c141bf5baecfbbba4e22686ac4c581fae53a511908be8439fe48a7
|
||||
F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f
|
||||
F test/conflict.test 3307ffdf988e04b01c4e942d8aa369a977f085bf629f43a627c9a77f39d65926
|
||||
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
|
||||
@@ -1083,7 +1083,7 @@ F test/e_createtable.test 31b9bcb6ac8876bc7ec342d86d9c231a84c62b442093a6651dfd0f
|
||||
F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
|
||||
F test/e_droptrigger.test 235c610f8bf8ec44513e222b9085c7e49fad65ad0c1975ac2577109dd06fd8fa
|
||||
F test/e_dropview.test 74e405df7fa0f762e0c9445b166fe03955856532e2bb234c372f7c51228d75e7
|
||||
F test/e_expr.test 9bdb347b78b9f4eff9153ea97797facc179a821898588471a70808b4471a69b0
|
||||
F test/e_expr.test 5225396116ed93d68b60933bbe1668f5652f268a4074ef0b7de008f1ae773f66
|
||||
F test/e_fkey.test 2a4ad815372a4b57dad91cd37932e18e68bf545f6c946e9a4cb3e7bde92b8e84
|
||||
F test/e_fts3.test 17ba7c373aba4d4f5696ba147ee23fd1a1ef70782af050e03e262ca187c5ee07
|
||||
F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e
|
||||
@@ -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 f5afdbe971a9dd05f0c094d44eb7ee157d61326e7e15357a2a84f20e6937ed52
|
||||
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
|
||||
F test/select3.test 152ce3978c8600c70413e921e39e4c5aeb8ff1e96dd722442a9981e1b8afc702
|
||||
F test/select4.test 21941409ac6b65bfca83de020afb5d976d802d3d8ad216dc774a3fbbf55fe277
|
||||
@@ -2092,7 +2092,7 @@ F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d761
|
||||
F test/window7.test 1d31276961ae7801edc72173edaf7593e3cbc79c06d1f1f09e20d8418af403cd
|
||||
F test/window8.tcl c57364e64d816f6e26df60437e1202e2c1031c7b818a1a67535d1006862a026a
|
||||
F test/window8.test 3d931e58802b8ab8063da00f0cf30aa3351640238a952c0efb5a129e2349a4bb
|
||||
F test/window9.test 7b98a7916dd87763ea35f56ea023e3b29e99744582204ccf2937a3bac411cd4d
|
||||
F test/window9.test 234309555c3d449753e8773a0339cc380ec61e6f03d778cb54bbfa3fc42ba676
|
||||
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
|
||||
F test/windowB.test bba3bee77c1d3321077ff8951d543161d74f08f78633d84433ced7d6261dc2c2
|
||||
F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2
|
||||
@@ -2219,8 +2219,9 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||
P f3ad1b4394ff0902e4ae9e8d0ea36ee198149c5ce8745851eaf84105484fedf2
|
||||
R 3bdc73eba82986d23e6b27dd2317c611
|
||||
U dan
|
||||
Z ab1005716313da03aef3665d5bfce64d
|
||||
P c89f4f79676f34cd8399f4a8a3934ad63d4547b9528e736bad1ee0e6f5fe6305 e78b72fadfe4c263f5af56aa887891a49055edfccda9397267804b3aaf4afd53
|
||||
R f0bf0f57d459e0ea6ee0b13b3376f8b2
|
||||
T +closed e78b72fadfe4c263f5af56aa887891a49055edfccda9397267804b3aaf4afd53
|
||||
U drh
|
||||
Z 197d97631f296c0f81b1f94a1483a388
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
c89f4f79676f34cd8399f4a8a3934ad63d4547b9528e736bad1ee0e6f5fe6305
|
||||
b5b8ede613c40cf4a1ad0fe2af9b98d410982e0680b1da00c67ff1c2c34e0d6b
|
||||
|
||||
33
src/select.c
33
src/select.c
@@ -2943,11 +2943,33 @@ static int hasAnchor(Select *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to process a compound query form from
|
||||
** 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.
|
||||
**
|
||||
** 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 unionWithLimitOne(sqlite3 *db, Select *p){
|
||||
int v;
|
||||
if( p->op!=TK_UNION ) return 0;
|
||||
if( p->pLimit==0 ) return 0;
|
||||
if( p->pLimit->pRight ) return 0; /* No OFFSET */
|
||||
v = 0;
|
||||
assert( p->pLimit->pLeft!=0 );
|
||||
if( sqlite3ExprIsInteger(p->pLimit->pLeft, &v, 0, 0)==0 ) return 0;
|
||||
if( v!=1 ) return 0;
|
||||
if( OptimizationDisabled(db, SQLITE_UnionLimit) ) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to process a compound query formed from
|
||||
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
|
||||
** INTERSECT
|
||||
**
|
||||
** "p" points to the right-most of the two queries. the query on the
|
||||
** "p" points to the right-most of the two queries. The query on the
|
||||
** left is p->pPrior. The left query could also be a compound query
|
||||
** in which case this routine will be called recursively.
|
||||
**
|
||||
@@ -3031,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 ){
|
||||
}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
|
||||
@@ -3043,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 */
|
||||
@@ -3594,7 +3617,7 @@ static int multiSelectByMerge(
|
||||
|
||||
/* Compute the limit registers */
|
||||
computeLimitRegisters(pParse, p, labelEnd);
|
||||
if( p->iLimit && op==TK_ALL ){
|
||||
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,
|
||||
|
||||
@@ -12594,6 +12594,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
|
||||
{ 0x10000000, 1, "OrderBySubq" },
|
||||
{ 0x20000000, 1, "StarQuery" },
|
||||
{ 0x40000000, 1, "ExistsToJoin" },
|
||||
{ 0x80000000, 1, "UnionLimit" },
|
||||
{ 0xffffffff, 0, "All" },
|
||||
};
|
||||
unsigned int curOpt;
|
||||
|
||||
@@ -1968,6 +1968,7 @@ struct sqlite3 {
|
||||
#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */
|
||||
#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */
|
||||
#define SQLITE_ExistsToJoin 0x40000000 /* The EXISTS-to-JOIN optimization */
|
||||
#define SQLITE_UnionLimit 0x80000000 /* Optimizations for UNION + LIMIT */
|
||||
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
|
||||
|
||||
/*
|
||||
|
||||
@@ -403,7 +403,7 @@ do_execsql_test colname-9.330 { -- added 2019-08-10 to invalidate
|
||||
DROP TABLE IF EXISTS t1; -- a couple assert()s that were
|
||||
CREATE TABLE t1(a); -- added by ticket 3b44500725
|
||||
INSERT INTO t1 VALUES(17),(2),(99),(-3),(7);
|
||||
SELECT (SELECT avg(a) UNION SELECT min(a) OVER()) FROM t1;
|
||||
SELECT (SELECT avg(a) UNION SELECT min(a) OVER() ORDER BY 1) FROM t1;
|
||||
} {17}
|
||||
|
||||
# Issue detected by OSSFuzz on 2017-12-24 (Christmas Eve)
|
||||
|
||||
@@ -1896,7 +1896,7 @@ do_expr_test e_expr-35.1.3 { (SELECT count(*) FROM t2) } integer 3
|
||||
do_expr_test e_expr-35.1.4 { (SELECT 4 FROM t2) } integer 4
|
||||
|
||||
do_expr_test e_expr-35.1.5 {
|
||||
(SELECT b FROM t2 UNION SELECT a+1 FROM t2)
|
||||
(SELECT b FROM t2 UNION SELECT a+1 FROM t2 ORDER BY 1)
|
||||
} null {}
|
||||
do_expr_test e_expr-35.1.6 {
|
||||
(SELECT a FROM t2 UNION SELECT COALESCE(b, 55) FROM t2 ORDER BY 1)
|
||||
|
||||
@@ -1210,4 +1210,25 @@ 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.4 {
|
||||
SELECT a FROM t1 UNION SELECT b FROM t2 LIMIT 2;
|
||||
} {1 2}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -261,6 +261,7 @@ do_execsql_test 8.4 {
|
||||
SELECT(
|
||||
SELECT x UNION
|
||||
SELECT sum( avg((SELECT x FROM v1)) ) OVER()
|
||||
ORDER BY 1
|
||||
)
|
||||
FROM v1;
|
||||
} {0.0 0.0}
|
||||
|
||||
Reference in New Issue
Block a user