Fix multiple issues with the ORDER BY LIMIT optimization. This is the

proposed resolution to ticket [9936b2fa443fec03ff25].

FossilOrigin-Name: 206720129ed2fa8875a286266d05b99fb2caf8671e4b74b26a6286a2073fcd8b
This commit is contained in:
drh 2018-09-08 20:09:46 +00:00
parent c93c614543
commit 6ee5a7b481
6 changed files with 97 additions and 27 deletions

View File

@ -1,5 +1,5 @@
C Add\sa\smissing\scall\sto\sfree()\sin\sLemon.
D 2018-09-08T16:55:18.635
C Fix\smultiple\sissues\swith\sthe\sORDER\sBY\sLIMIT\soptimization.\s\sThis\sis\sthe\nproposed\sresolution\sto\sticket\s[9936b2fa443fec03ff25].
D 2018-09-08T20:09:46.990
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 6b650013511fd9d8b094203ac268af9220d292cc7d4e1bc9fbca15aacd8c7995
@ -501,12 +501,12 @@ F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 352c6af1a99441206ff62a6f7429dbf537827f42c428639695220b9c8639e33b
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04
F src/select.c ed6192ddd09a97169cb1c6d732b26c0f647b72d5fa8ca401c7ee1180fbbe521a
F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f
F src/sqlite.h.in cdf2a539cd0570322a94bcb97c01c56feb1be0657ec7cfb8273c89d19fff87a9
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
F src/sqliteInt.h c72e50d876b1fd909db70399a228d022a44b6a7326a6031f76594acdfd25b69e
F src/sqliteInt.h f63f04e3db06b605ead2d9bb8eda8bc3add3baf3da9655954dfb24860e625933
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -586,7 +586,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c df50883d93689d009be5ad9bdc4e53a4ee45fcc291087ec9272569d00b360791
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c ba7225773931760cf60bf22f34d0cce2588df7ce5ce0f215a52eb88234b55ac4
F src/where.c 155809967fbab889374dedf970ea6561b8fb519fcb165d6ba00776552ecc5cde
F src/where.c 5192013a21843523f4772087a56e59db73bd4c7401349968120dc0693b7c4eb4
F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4
F src/wherecode.c 2b6cd1b27736cc803060289e04ecf9849976106f4077aa67d1a2c0e3ec420159
F src/whereexpr.c d87df2c00ecc0c2ef4409562608d19cec259a6a03ca72b86fc999db9c07ce119
@ -1052,7 +1052,7 @@ F test/like.test 11cfd7d4ef8625389df9efc46735ff0b0b41d5e62047ef0f3bc24c380d28a7a
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
F test/like3.test 3608a2042b6f922f900fbfd5d3ce4e7eca57f7c4
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
F test/limit2.test 360982809e03211636d2b18ddbc97d5da06826941370607e4b00e113f827cb5a
F test/limit2.test 9409b033284642a859fafc95f29a5a6a557bd57c1f0d7c3f554bd64ed69df77e
F test/loadext.test d077450695ddb5c1ea3ad7d48e5f5850fe732ad9
F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7
F test/lock.test be4fe08118fb988fed741f429b7dd5d65e1c90db
@ -1764,7 +1764,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P e812e5d59a699e8b82c51d465d9c0f09df6a1e6996b5499814dca99c5f8020d5
R 985ed317810844fbd8e283aa0c269697
U mistachkin
Z e4dcf479c4692c03c98ec48ee992b041
P 8b4cf33aafe09d9009119dcbd464b54be9605af5701002ee458819efa6e2e1f9
R 314bbe63eed86b2216509901a995337d
U drh
Z 40fd1d6c79004b4386462b0fab14be9d

View File

@ -1 +1 @@
8b4cf33aafe09d9009119dcbd464b54be9605af5701002ee458819efa6e2e1f9
206720129ed2fa8875a286266d05b99fb2caf8671e4b74b26a6286a2073fcd8b

View File

@ -68,8 +68,8 @@ struct SortCtx {
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
int labelDone; /* Jump here when done, ex: LIMIT reached */
int labelOBLopt; /* Jump here when sorter is full */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
u8 nDefer; /* Number of valid entries in aDefer[] */
struct DeferredCsr {
@ -693,10 +693,10 @@ static void pushOntoSorter(
** than LIMIT+OFFSET items in the sorter.
**
** If the new record does not need to be inserted into the sorter,
** jump to the next iteration of the loop. Or, if the
** pSort->bOrderedInnerLoop flag is set to indicate that the inner
** loop delivers items in sorted order, jump to the next iteration
** of the outer loop.
** jump to the next iteration of the loop. If the pSort->labelOBLopt
** value is not zero, then it is a label of where to jump. Otherwise,
** just bypass the row insert logic. See the header comment on the
** sqlite3WhereOrderByLimitOptLabel() function for additional info.
*/
int iCsr = pSort->iECursor;
sqlite3VdbeAddOp2(v, OP_IfNotZero, iLimit, sqlite3VdbeCurrentAddr(v)+4);
@ -718,9 +718,8 @@ static void pushOntoSorter(
sqlite3VdbeAddOp4Int(v, op, pSort->iECursor, regRecord,
regBase+nOBSat, nBase-nOBSat);
if( iSkip ){
assert( pSort->bOrderedInnerLoop==0 || pSort->bOrderedInnerLoop==1 );
sqlite3VdbeChangeP2(v, iSkip,
sqlite3VdbeCurrentAddr(v) + pSort->bOrderedInnerLoop);
pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v));
}
}
@ -6060,7 +6059,7 @@ int sqlite3Select(
}
if( sSort.pOrderBy ){
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
sSort.labelOBLopt = sqlite3WhereOrderByLimitOptLabel(pWInfo);
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
sSort.pOrderBy = 0;
}

View File

@ -3923,7 +3923,7 @@ void sqlite3WhereEnd(WhereInfo*);
LogEst sqlite3WhereOutputRowCount(WhereInfo*);
int sqlite3WhereIsDistinct(WhereInfo*);
int sqlite3WhereIsOrdered(WhereInfo*);
int sqlite3WhereOrderedInnerLoop(WhereInfo*);
int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
int sqlite3WhereIsSorted(WhereInfo*);
int sqlite3WhereContinueLabel(WhereInfo*);
int sqlite3WhereBreakLabel(WhereInfo*);

View File

@ -67,15 +67,38 @@ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
}
/*
** Return TRUE if the innermost loop of the WHERE clause implementation
** returns rows in ORDER BY order for complete run of the inner loop.
** In the ORDER BY LIMIT optimization, if the inner-most loop is known
** to emit rows in increasing order, and if the last row emitted by the
** inner-most loop did not fit within the sorter, then we can skip all
** subsequent rows for the current iteration of the inner loop (because they
** will not fit in the sorter either) and continue with the second inner
** loop - the loop immediately outside the inner-most.
**
** Across multiple iterations of outer loops, the output rows need not be
** sorted. As long as rows are sorted for just the innermost loop, this
** routine can return TRUE.
** When a row does not fit in the sorter (because the sorter already
** holds LIMIT+OFFSET rows that are smaller), then a jump is made to the
** label returned by this function.
**
** If the ORDER BY LIMIT optimization applies, the jump destination should
** be the continuation for the second-inner-most loop. If the ORDER BY
** LIMIT optimization does not apply, then the jump destination should
** be the continuation for the inner-most loop.
**
** It is always safe for this routine to return the continuation of the
** inner-most loop, in the sense that a correct answer will result.
** Returning the continuation the second inner loop is an optimization
** that might make the code run a little faster, but should not change
** the final answer.
*/
int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){
return pWInfo->bOrderedInnerLoop;
int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){
WhereLevel *pInner;
if( !pWInfo->bOrderedInnerLoop ){
/* The ORDER BY LIMIT optimization does not apply. Jump to the
** continuation of the inner-most loop. */
return pWInfo->iContinue;
}
pInner = &pWInfo->a[pWInfo->nLevel-1];
if( pInner->addrNxt ) return pInner->addrNxt;
return pInner->addrBrk;
}
/*
@ -4248,6 +4271,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
}
pWInfo->bOrderedInnerLoop = 0;
if( pWInfo->pOrderBy ){
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){

View File

@ -167,4 +167,51 @@ do_execsql_test 600 {
SELECT y FROM t1, t2 WHERE a=x AND b<=y ORDER BY b DESC;
} {3}
# Ticket https://www.sqlite.org/src/info/9936b2fa443fec03 2018-09-08
# Infinite loop due to the ORDER BY LIMIT optimization.
#
do_execsql_test 700 {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
CREATE TABLE t1(aa VARCHAR PRIMARY KEY NOT NULL,bb,cc,x VARCHAR(400));
INSERT INTO t1(aa,bb,cc) VALUES('maroon','meal','lecture');
INSERT INTO t1(aa,bb,cc) VALUES('reality','meal','catsear');
CREATE TABLE t2(aa VARCHAR PRIMARY KEY, dd INT DEFAULT 1, ee, x VARCHAR(100));
INSERT INTO t2(aa,dd,ee) VALUES('maroon',0,'travel'),('reality',0,'hour');
CREATE INDEX t2x1 ON t2(dd,ee);
ANALYZE;
DROP TABLE IF EXISTS sqlite_stat4;
DELETE FROM sqlite_stat1;
INSERT INTO sqlite_stat1 VALUES
('t2','t2x1','3 3 3'),
('t2','sqlite_autoindex_t2_1','3 1'),
('t1','sqlite_autoindex_t1_1','2 1');
ANALYZE sqlite_master;
SELECT *
FROM t1 LEFT JOIN t2 ON t1.aa=t2.aa
WHERE t1.bb='meal'
ORDER BY t2.dd DESC
LIMIT 1;
} {maroon meal lecture {} maroon 0 travel {}}
do_execsql_test 710 {
DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1(aa, bb);
INSERT INTO t1 VALUES('maroon','meal');
CREATE TABLE t2(cc, dd, ee, x VARCHAR(100));
INSERT INTO t2(cc,dd,ee) VALUES('maroon',1,'one');
INSERT INTO t2(cc,dd,ee) VALUES('maroon',2,'two');
INSERT INTO t2(cc,dd,ee) VALUES('maroon',0,'zero');
CREATE INDEX t2ddee ON t2(dd,ee);
CREATE INDEX t2cc ON t2(cc);
ANALYZE;
SELECT t2.cc, t2.dd, t2.ee FROM t1 CROSS JOIN t2 ON t1.aa=t2.cc
ORDER BY t2.dd LIMIT 1;
} {maroon 0 zero}
do_execsql_test 720 {
SELECT t2.cc, t2.dd, t2.ee FROM t1 CROSS JOIN t2 ON t1.aa=t2.cc
WHERE t1.bb='meal'
ORDER BY t2.dd LIMIT 1;
} {maroon 0 zero}
finish_test