mirror of https://github.com/sqlite/sqlite
In order to optimize out the ORDER BY clause,
outer loops must generate values for ORDER BY terms that are unique or else the inner loops must generate no more than a single row. Fix for ticket [a179fe7465]. FossilOrigin-Name: 2936f7466e162dfb003bda26d35358d1b3063112
This commit is contained in:
commit
d9883570f5
13
manifest
13
manifest
|
@ -1,5 +1,5 @@
|
|||
C Candidate\sfix\sfor\sticket\s[6bfb98dfc0c]:\sMake\ssure\sinvalid\scursors\sdrop\sall\nreferences\sto\sdatabase\spages\sprior\sto\sdoing\sany\sinsert\sor\supdate.
|
||||
D 2013-03-27T03:15:23.329
|
||||
C In\sorder\sto\soptimize\sout\sthe\sORDER\sBY\sclause,\s\nouter\sloops\smust\sgenerate\svalues\sfor\sORDER\sBY\sterms\sthat\sare\sunique\sor\selse\nthe\sinner\sloops\smust\sgenerate\sno\smore\sthan\sa\ssingle\srow.\nFix\sfor\sticket\s[a179fe7465].
|
||||
D 2013-03-27T17:20:10.024
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in df3e48659d80e1b7765785d8d66c86b320f72cc7
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
|
@ -252,7 +252,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
|
|||
F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2
|
||||
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
|
||||
F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b
|
||||
F src/where.c 9a16c0b84bbeb054d11fda96e9e037ae310bd54e
|
||||
F src/where.c 4ad2329c439a30ddb915a780f6f80bdffafe3a64
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
|
@ -646,6 +646,7 @@ F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
|||
F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
|
||||
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 31c04bec797dda1bde337810b52efa08d1f1f08e
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
|
@ -1040,7 +1041,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
|||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P 5062db672c00c3365d51cd6b39815078f5b6b525
|
||||
R 444ec66222f6e3876b5a1bc78bf6602c
|
||||
P 322a5f086d9ee46017f750df81527799a54ae258 c77ee6e20d3a8c91a8bf4c9063c36a95c70e17cf
|
||||
R 7a3bcce0c2bcd988dcd25474f2b83a5d
|
||||
U drh
|
||||
Z 3d135e15ecdab16eb6decd45c9d13940
|
||||
Z 25cf293fe59d9bbc42bbcd9448a91b58
|
||||
|
|
|
@ -1 +1 @@
|
|||
322a5f086d9ee46017f750df81527799a54ae258
|
||||
2936f7466e162dfb003bda26d35358d1b3063112
|
44
src/where.c
44
src/where.c
|
@ -262,6 +262,8 @@ struct WhereCost {
|
|||
#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
|
||||
#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
|
||||
#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
|
||||
#define WHERE_OB_UNIQUE 0x00004000 /* Values in ORDER BY columns are
|
||||
** different for every output row */
|
||||
#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
|
||||
#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
|
||||
#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
|
||||
|
@ -2903,7 +2905,8 @@ static int isSortingIndex(
|
|||
WhereBestIdx *p, /* Best index search context */
|
||||
Index *pIdx, /* The index we are testing */
|
||||
int base, /* Cursor number for the table to be sorted */
|
||||
int *pbRev /* Set to 1 for reverse-order scan of pIdx */
|
||||
int *pbRev, /* Set to 1 for reverse-order scan of pIdx */
|
||||
int *pbObUnique /* ORDER BY column values will different in every row */
|
||||
){
|
||||
int i; /* Number of pIdx terms used */
|
||||
int j; /* Number of ORDER BY terms satisfied */
|
||||
|
@ -2917,12 +2920,16 @@ static int isSortingIndex(
|
|||
int nPriorSat; /* ORDER BY terms satisfied by outer loops */
|
||||
int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
|
||||
int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */
|
||||
int outerObUnique; /* Outer loops generate different values in
|
||||
** every row for the ORDER BY columns */
|
||||
|
||||
if( p->i==0 ){
|
||||
nPriorSat = 0;
|
||||
outerObUnique = 1;
|
||||
}else{
|
||||
u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags;
|
||||
nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
|
||||
if( (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
|
||||
if( (wsFlags & WHERE_ORDERED)==0 ){
|
||||
/* This loop cannot be ordered unless the next outer loop is
|
||||
** also ordered */
|
||||
return nPriorSat;
|
||||
|
@ -2932,6 +2939,9 @@ static int isSortingIndex(
|
|||
** optimization is disabled */
|
||||
return nPriorSat;
|
||||
}
|
||||
testcase( wsFlags & WHERE_OB_UNIQUE );
|
||||
testcase( wsFlags & WHERE_ALL_UNIQUE );
|
||||
outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0;
|
||||
}
|
||||
pOrderBy = p->pOrderBy;
|
||||
assert( pOrderBy!=0 );
|
||||
|
@ -3073,11 +3083,26 @@ static int isSortingIndex(
|
|||
uniqueNotNull = 0;
|
||||
}
|
||||
}
|
||||
if( seenRowid ){
|
||||
uniqueNotNull = 1;
|
||||
}else if( uniqueNotNull==0 || i<pIdx->nColumn ){
|
||||
uniqueNotNull = 0;
|
||||
}
|
||||
|
||||
/* If we have not found at least one ORDER BY term that matches the
|
||||
** index, then show no progress. */
|
||||
if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat;
|
||||
|
||||
/* Either the outer queries must generate rows where there are no two
|
||||
** rows with the same values in all ORDER BY columns, or else this
|
||||
** loop must generate just a single row of output. Example: Suppose
|
||||
** the outer loops generate A=1 and A=1, and this loop generates B=3
|
||||
** and B=4. Then without the following test, ORDER BY A,B would
|
||||
** generate the wrong order output: 1,3 1,4 1,3 1,4
|
||||
*/
|
||||
if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat;
|
||||
*pbObUnique = uniqueNotNull;
|
||||
|
||||
/* Return the necessary scan order back to the caller */
|
||||
*pbRev = sortOrder & 1;
|
||||
|
||||
|
@ -3085,7 +3110,7 @@ static int isSortingIndex(
|
|||
** possible for a single row from this table to match, then skip over
|
||||
** any additional ORDER BY terms dealing with this table.
|
||||
*/
|
||||
if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){
|
||||
if( uniqueNotNull ){
|
||||
/* Advance j over additional ORDER BY terms associated with base */
|
||||
WhereMaskSet *pMS = p->pWC->pMaskSet;
|
||||
Bitmask m = ~getMask(pMS, base);
|
||||
|
@ -3369,12 +3394,14 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
|||
** variable. */
|
||||
if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
|
||||
int bRev = 2;
|
||||
WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
|
||||
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
|
||||
WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
|
||||
bRev, pc.plan.nOBSat));
|
||||
int bObUnique = 0;
|
||||
WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat));
|
||||
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique);
|
||||
WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n",
|
||||
bRev, bObUnique, pc.plan.nOBSat));
|
||||
if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
|
||||
pc.plan.wsFlags |= WHERE_ORDERED;
|
||||
if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE;
|
||||
}
|
||||
if( nOrderBy==pc.plan.nOBSat ){
|
||||
bSort = 0;
|
||||
|
@ -3468,7 +3495,8 @@ static void bestBtreeIndex(WhereBestIdx *p){
|
|||
** So this computation assumes table records are about twice as big
|
||||
** as index records
|
||||
*/
|
||||
if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY
|
||||
if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE))
|
||||
==WHERE_IDX_ONLY
|
||||
&& (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
|
||||
&& sqlite3GlobalConfig.bUseCis
|
||||
&& OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# 2013 March 26
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing that the optimizations that disable
|
||||
# ORDER BY clauses work correctly on multi-value primary keys and
|
||||
# unique indices when only some prefix of the terms in the key are
|
||||
# used. See ticket http://www.sqlite.org/src/info/a179fe74659
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix orderby4
|
||||
|
||||
# Generate test data for a join. Verify that the join gets the
|
||||
# correct answer.
|
||||
#
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a, b, PRIMARY KEY(a,b));
|
||||
INSERT INTO t1 VALUES(1,1),(1,2);
|
||||
CREATE TABLE t2(x, y, PRIMARY KEY(x,y));
|
||||
INSERT INTO t2 VALUES(3,3),(4,4);
|
||||
SELECT a, x FROM t1, t2 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
do_execsql_test 1.2 {
|
||||
SELECT a, x FROM t1 CROSS JOIN t2 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
do_execsql_test 1.3 {
|
||||
SELECT a, x FROM t2 CROSS JOIN t1 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t3(a);
|
||||
INSERT INTO t3 VALUES(1),(1);
|
||||
CREATE INDEX t3a ON t3(a);
|
||||
CREATE TABLE t4(x);
|
||||
INSERT INTO t4 VALUES(3),(4);
|
||||
CREATE INDEX t4x ON t4(x);
|
||||
SELECT a, x FROM t3, t4 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
do_execsql_test 2.2 {
|
||||
SELECT a, x FROM t3 CROSS JOIN t4 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
do_execsql_test 2.3 {
|
||||
SELECT a, x FROM t4 CROSS JOIN t3 ORDER BY 1, 2;
|
||||
} {1 3 1 3 1 4 1 4}
|
||||
|
||||
finish_test
|
Loading…
Reference in New Issue