mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-14 00:22:38 +03:00
Attempt to further generalize the min/max optimization so that, if an appropriate index exists, it can be used by any aggregate query that contains only a single aggregate of the form max(colname) or min(colname) and does not contain a GROUP BY clause.
FossilOrigin-Name: 7280e14cd8f55896451847b432171e8750a07c81
This commit is contained in:
19
manifest
19
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Allow\sthe\serror\smessage\sfrom\s"PRAGMA\sintegrity_check"\sto\sbe\slonger\sthan\n20,000\sbytes.
|
C Attempt\sto\sfurther\sgeneralize\sthe\smin/max\soptimization\sso\sthat,\sif\san\sappropriate\sindex\sexists,\sit\scan\sbe\sused\sby\sany\saggregate\squery\sthat\scontains\sonly\sa\ssingle\saggregate\sof\sthe\sform\smax(colname)\sor\smin(colname)\sand\sdoes\snot\scontain\sa\sGROUP\sBY\sclause.
|
||||||
D 2012-12-12T14:30:03.521
|
D 2012-12-13T16:37:10.621
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400
|
F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -174,7 +174,7 @@ F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
|
|||||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||||
F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f
|
F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f
|
||||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||||
F src/select.c e6daa524bbdfa98f4abdb8cb281498f0047d3161
|
F src/select.c f6c73171209b4af4d6b4258b6740415790415e2e
|
||||||
F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019
|
F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019
|
||||||
F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177
|
F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177
|
||||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||||
@@ -613,7 +613,7 @@ F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
|
|||||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||||
F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
|
F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
|
||||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||||
F test/minmax.test c61518429e66e228efc79661fbd2dc3e4924ec44
|
F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||||
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
|
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
|
||||||
@@ -1025,7 +1025,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||||
P cd0b37c52658bfdf992b1e3dc467bae1835a94ae
|
P 120c82d56ecb574f46db01679317b8062d8779ed
|
||||||
R c074324819c1cb0b1b063013401b3d51
|
R 39203e605a8f25e92556c6b1c71c785c
|
||||||
U drh
|
T *branch * minmax-opt
|
||||||
Z 0c15681c6bbc86d62fafd9ce06645760
|
T *sym-minmax-opt *
|
||||||
|
T -sym-trunk *
|
||||||
|
U dan
|
||||||
|
Z ab1716797ba85b3e90d83237db257cf3
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
120c82d56ecb574f46db01679317b8062d8779ed
|
7280e14cd8f55896451847b432171e8750a07c81
|
||||||
69
src/select.c
69
src/select.c
@@ -3160,34 +3160,43 @@ static int flattenSubquery(
|
|||||||
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Analyze the SELECT statement passed as an argument to see if it
|
** Based on the contents of the AggInfo structure indicated by the first
|
||||||
** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
|
** argument, this function checks if the following are true:
|
||||||
** it is, or 0 otherwise. At present, a query is considered to be
|
|
||||||
** a min()/max() query if:
|
|
||||||
**
|
**
|
||||||
** 1. There is a single object in the FROM clause.
|
** * the query contains just a single aggregate function,
|
||||||
|
** * the aggregate function is either min() or max(), and
|
||||||
|
** * the argument to the aggregate function is a column value.
|
||||||
**
|
**
|
||||||
** 2. There is a single expression in the result set, and it is
|
** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
|
||||||
** either min(x) or max(x), where x is a column reference.
|
** is returned as appropriate. Also, *ppMinMax is set to point to the
|
||||||
|
** list of arguments passed to the aggregate before returning.
|
||||||
|
**
|
||||||
|
** Or, if the conditions above are not met, *ppMinMax is set to 0 and
|
||||||
|
** WHERE_ORDERBY_NORMAL is returned.
|
||||||
*/
|
*/
|
||||||
static u8 minMaxQuery(Select *p){
|
static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
|
||||||
Expr *pExpr;
|
int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
|
||||||
ExprList *pEList = p->pEList;
|
|
||||||
|
|
||||||
if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
|
*ppMinMax = 0;
|
||||||
pExpr = pEList->a[0].pExpr;
|
if( pAggInfo->nFunc==1 ){
|
||||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
|
Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
|
||||||
if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0;
|
ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
|
||||||
pEList = pExpr->x.pList;
|
|
||||||
if( pEList==0 || pEList->nExpr!=1 ) return 0;
|
assert( pExpr->op==TK_AGG_FUNCTION );
|
||||||
if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
|
if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
|
||||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
const char *zFunc = pExpr->u.zToken;
|
||||||
if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){
|
if( sqlite3StrICmp(zFunc, "min")==0 ){
|
||||||
return WHERE_ORDERBY_MIN;
|
eRet = WHERE_ORDERBY_MIN;
|
||||||
}else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){
|
*ppMinMax = pEList;
|
||||||
return WHERE_ORDERBY_MAX;
|
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
|
||||||
|
eRet = WHERE_ORDERBY_MAX;
|
||||||
|
*ppMinMax = pEList;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return WHERE_ORDERBY_NORMAL;
|
|
||||||
|
assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
|
||||||
|
return eRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4527,11 +4536,17 @@ int sqlite3Select(
|
|||||||
** Refer to code and comments in where.c for details.
|
** Refer to code and comments in where.c for details.
|
||||||
*/
|
*/
|
||||||
ExprList *pMinMax = 0;
|
ExprList *pMinMax = 0;
|
||||||
u8 flag = minMaxQuery(p);
|
u8 flag = WHERE_ORDERBY_NORMAL;
|
||||||
|
|
||||||
|
assert( p->pGroupBy==0 );
|
||||||
|
assert( flag==0 );
|
||||||
|
if( p->pHaving==0 ){
|
||||||
|
flag = minMaxQuery(&sAggInfo, &pMinMax);
|
||||||
|
}
|
||||||
|
assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
|
||||||
|
|
||||||
if( flag ){
|
if( flag ){
|
||||||
assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
|
pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
|
||||||
assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 );
|
|
||||||
pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
|
|
||||||
pDel = pMinMax;
|
pDel = pMinMax;
|
||||||
if( pMinMax && !db->mallocFailed ){
|
if( pMinMax && !db->mallocFailed ){
|
||||||
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
|
set ::testprefix minmax
|
||||||
|
|
||||||
do_test minmax-1.0 {
|
do_test minmax-1.0 {
|
||||||
execsql {
|
execsql {
|
||||||
@@ -536,7 +537,96 @@ do_test minmax-12.17 {
|
|||||||
}
|
}
|
||||||
} {5}
|
} {5}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
reset_db
|
||||||
|
|
||||||
|
proc do_test_13 {op name sql1 sql2 res} {
|
||||||
|
set ::sqlite_search_count 0
|
||||||
|
uplevel [list do_execsql_test $name.1 $sql1 $res]
|
||||||
|
set a $::sqlite_search_count
|
||||||
|
|
||||||
|
set ::sqlite_search_count 0
|
||||||
|
uplevel [list do_execsql_test $name.2 $sql2 $res]
|
||||||
|
set b $::sqlite_search_count
|
||||||
|
|
||||||
|
uplevel [list do_test $name.3 [list expr "$a $op $b"] 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run a test named $name. Check that SQL statements $sql1 and $sql2 both
|
||||||
|
# return the same result, but that $sql2 increments the $sqlite_search_count
|
||||||
|
# variable more often (indicating that it is visiting more rows to determine
|
||||||
|
# the result).
|
||||||
|
#
|
||||||
|
proc do_test_13_opt {name sql1 sql2 res} {
|
||||||
|
uplevel [list do_test_13 < $name $sql1 $sql2 $res]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Like [do_test_13_noopt], except this time check that the $sqlite_search_count
|
||||||
|
# variable is incremented the same number of times by both SQL statements.
|
||||||
|
#
|
||||||
|
proc do_test_13_noopt {name sql1 sql2 res} {
|
||||||
|
uplevel [list do_test_13 == $name $sql1 $sql2 $res]
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 13.1 {
|
||||||
|
CREATE TABLE t1(a, b, c);
|
||||||
|
INSERT INTO t1 VALUES('a', 1, 1);
|
||||||
|
INSERT INTO t1 VALUES('b', 6, 6);
|
||||||
|
INSERT INTO t1 VALUES('c', 5, 5);
|
||||||
|
INSERT INTO t1 VALUES('a', 4, 4);
|
||||||
|
INSERT INTO t1 VALUES('a', 5, 5);
|
||||||
|
INSERT INTO t1 VALUES('c', 6, 6);
|
||||||
|
INSERT INTO t1 VALUES('b', 4, 4);
|
||||||
|
INSERT INTO t1 VALUES('c', 7, 7);
|
||||||
|
INSERT INTO t1 VALUES('b', 2, 2);
|
||||||
|
INSERT INTO t1 VALUES('b', 3, 3);
|
||||||
|
INSERT INTO t1 VALUES('a', 3, 3);
|
||||||
|
INSERT INTO t1 VALUES('b', 5, 5);
|
||||||
|
INSERT INTO t1 VALUES('c', 4, 4);
|
||||||
|
INSERT INTO t1 VALUES('c', 3, 3);
|
||||||
|
INSERT INTO t1 VALUES('a', 2, 2);
|
||||||
|
SELECT * FROM t1 ORDER BY a, b, c;
|
||||||
|
} {a 1 1 a 2 2 a 3 3 a 4 4 a 5 5
|
||||||
|
b 2 2 b 3 3 b 4 4 b 5 5 b 6 6
|
||||||
|
c 3 3 c 4 4 c 5 5 c 6 6 c 7 7
|
||||||
|
}
|
||||||
|
do_execsql_test 13.2 { CREATE INDEX i1 ON t1(a, b, c) }
|
||||||
|
|
||||||
|
do_test_13_opt 13.3 {
|
||||||
|
SELECT min(b) FROM t1 WHERE a='b'
|
||||||
|
} {
|
||||||
|
SELECT min(c) FROM t1 WHERE a='b'
|
||||||
|
} {2}
|
||||||
|
|
||||||
|
do_test_13_opt 13.4 {
|
||||||
|
SELECT a, min(b) FROM t1 WHERE a='b'
|
||||||
|
} {
|
||||||
|
SELECT a, min(c) FROM t1 WHERE a='b'
|
||||||
|
} {b 2}
|
||||||
|
|
||||||
|
do_test_13_opt 13.4 {
|
||||||
|
SELECT a||c, max(b)+4 FROM t1 WHERE a='c'
|
||||||
|
} {
|
||||||
|
SELECT a||c, max(c)+4 FROM t1 WHERE a='c'
|
||||||
|
} {c7 11}
|
||||||
|
|
||||||
|
do_test_13_noopt 13.5 {
|
||||||
|
SELECT a||c, max(b+1) FROM t1 WHERE a='c'
|
||||||
|
} {
|
||||||
|
SELECT a||c, max(c+1) FROM t1 WHERE a='c'
|
||||||
|
} {c7 8}
|
||||||
|
|
||||||
|
do_test_13_noopt 13.6 {
|
||||||
|
SELECT count(b) FROM t1 WHERE a='c'
|
||||||
|
} {
|
||||||
|
SELECT count(c) FROM t1 WHERE a='c'
|
||||||
|
} {5}
|
||||||
|
|
||||||
|
do_test_13_noopt 13.7 {
|
||||||
|
SELECT min(b), count(b) FROM t1 WHERE a='a';
|
||||||
|
} {
|
||||||
|
SELECT min(c), count(c) FROM t1 WHERE a='a';
|
||||||
|
} {1 5}
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|||||||
Reference in New Issue
Block a user