-C Extra\s".selecttrace"\soutput\sfollowing\saggregate\sanalysis.\s\sNo\schanges\sto\nproduction\sbuilds.
-D 2017-10-26T18:43:19.787
+C Enhance\sthe\smin/max\soptimization\sso\sthat\sit\sworks\swith\sindexes\son\nexpressions.
+D 2017-10-26T20:04:28.629
F Makefile.in e016061b23e60ac9ec27c65cb577292b6bde0307ca55abd874ab3487b3b1beb2
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 37740aba9c4bb359c627eadccf1cfd7be4f5f847078723777ea7763969e533b1
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 5a461643f294ec510ca615b67256fc3861e4c8eff5f29e5940491e70553b1955
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c d055dc3a428ab2dd94ffdbaef50d90b41579be8ffe258591615d3d83bd859f7b
+F src/select.c 0b4c1ac59221c91c9f01c326105c1749874704a77310735c0417f3c3026a4f11
F src/shell.c.in f13262c8778f0cd76bf8d9c01bbf5ef66842e6b14e1705cd60d86ab32a6ce69f
F src/sqlite.h.in ab4f8a29d1580dfaeb6891fa1b83cff8229ba0daa56994707ceaca71495d9ab7
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F test/memsubsys1.test 9e7555a22173b8f1c96c281ce289b338fcba2abe8b157f8798ca195bbf1d347e
F test/memsubsys2.test 3e4a8d0c05fd3e5fa92017c64666730a520c7e08
F test/minmax.test 6751e87b409fe11b02e70a306d846fa544e25a41
-F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
+F test/minmax2.test dae92964ac87c1d2ef978c582e81a95e11c00f1cbef68980bfb2abaf10315063
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
F test/misc1.test 76737c259537586355f45e2a1e121b6e91b5476c4604ad5c53d1abfcb3acf786
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P e1faa7859451ee969f0ce02d2d4e4c54d0e2e018b37b8faf690e6b87593cce7d
-R d61bdf0ef8749a81bf4548782da0df22
+P 6fbf74ab3189b9cb20cf7cc8db0226eb935a8eab477cc83b8390492fcf2f1f76
+R c17176b8dc9ac053e5191ecbda77db6f
U drh
-Z eef6957fcaef94fa72ebc9ad261c03d1
+Z 1bd9fa3ff01b6d52accac3363bceb595
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
-** Based on the contents of the AggInfo structure indicated by the first
-** argument, this function checks if the following are true:
+** The pFunc is the only aggregate function in the query. Check to see
+** if the query is a candidate for the min/max optimization.
**
-** * 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.
+** If the query is a candidate for the min/max optimization, then set
+** *ppMinMax to be an ORDER BY clause to be used for the optimization
+** and return either WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX depending on
+** whether pFunc is a min() or max() function.
**
-** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
-** is returned as appropriate. Also, *ppMinMax is set to point to the
-** list of arguments passed to the aggregate before returning.
+** If the query is not a candidate for the min/max optimization, return
+** WHERE_ORDERBY_NORMAL (which must be zero).
**
-** Or, if the conditions above are not met, *ppMinMax is set to 0 and
-** WHERE_ORDERBY_NORMAL is returned.
+** This routine must be called after aggregate functions have been
+** located but before their arguments have been subjected to aggregate
+** analysis.
*/
-static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
- int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
-
- *ppMinMax = 0;
- if( pAggInfo->nFunc==1 ){
- Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
- ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
-
- assert( pExpr->op==TK_AGG_FUNCTION );
- if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
- const char *zFunc = pExpr->u.zToken;
- if( sqlite3StrICmp(zFunc, "min")==0 ){
- eRet = WHERE_ORDERBY_MIN;
- *ppMinMax = pEList;
- }else if( sqlite3StrICmp(zFunc, "max")==0 ){
- eRet = WHERE_ORDERBY_MAX;
- *ppMinMax = pEList;
- }
- }
+static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
+ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
+ ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
+ const char *zFunc; /* Name of aggregate function pFunc */
+ ExprList *pOrderBy;
+ u8 sortOrder;
+
+ assert( *ppMinMax==0 );
+ assert( pFunc->op==TK_AGG_FUNCTION );
+ if( pEList==0 || pEList->nExpr!=1 ) return eRet;
+ zFunc = pFunc->u.zToken;
+ if( sqlite3StrICmp(zFunc, "min")==0 ){
+ eRet = WHERE_ORDERBY_MIN;
+ sortOrder = SQLITE_SO_ASC;
+ }else if( sqlite3StrICmp(zFunc, "max")==0 ){
+ eRet = WHERE_ORDERBY_MAX;
+ sortOrder = SQLITE_SO_DESC;
+ }else{
+ return eRet;
}
-
- assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
+ *ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
+ assert( pOrderBy!=0 || db->mallocFailed );
+ if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
return eRet;
}
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
+ ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
+ u8 minMaxFlag; /* Flag for min/max queries */
#ifndef SQLITE_OMIT_EXPLAIN
int iRestoreSelectId = pParse->iSelectId;
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
}
sAggInfo.nAccumulator = sAggInfo.nColumn;
+ if( p->pGroupBy==0 && p->pHaving==0 && sAggInfo.nFunc==1 ){
+ minMaxFlag = minMaxQuery(db, sAggInfo.aFunc[0].pExpr, &pMinMaxOrderBy);
+ }else{
+ minMaxFlag = WHERE_ORDERBY_NORMAL;
+ }
for(i=0; i<sAggInfo.nFunc; i++){
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
sNC.ncFlags |= NC_InAggFunc;
} /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
else {
- ExprList *pDel = 0;
#ifndef SQLITE_OMIT_BTREECOUNT
Table *pTab;
if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
}else
#endif /* SQLITE_OMIT_BTREECOUNT */
{
- /* Check if the query is of one of the following forms:
- **
- ** SELECT min(x) FROM ...
- ** SELECT max(x) FROM ...
- **
- ** If it is, then ask the code in where.c to attempt to sort results
- ** as if there was an "ORDER BY x" or "ORDER BY x DESC" clause.
- ** If where.c is able to produce results sorted in this order, then
- ** add vdbe code to break out of the processing loop after the
- ** first iteration (since the first iteration of the loop is
- ** guaranteed to operate on the row with the minimum or maximum
- ** value of x, the only row required).
- **
- ** A special flag must be passed to sqlite3WhereBegin() to slightly
- ** modify behavior as follows:
- **
- ** + If the query is a "SELECT min(x)", then the loop coded by
- ** where.c should not iterate over any values with a NULL value
- ** for x.
- **
- ** + The optimizer code in where.c (the thing that decides which
- ** index or indices to use) should place a different priority on
- ** satisfying the 'ORDER BY' clause than it does in other cases.
- ** Refer to code and comments in where.c for details.
- */
- ExprList *pMinMax = 0;
- 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 ){
- pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
- pDel = pMinMax;
- assert( db->mallocFailed || pMinMax!=0 );
- if( !db->mallocFailed ){
- pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
- pMinMax->a[0].pExpr->op = TK_COLUMN;
- }
- }
-
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
** of output.
*/
+ assert( p->pGroupBy==0 );
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax, 0,flag,0);
+
+ /* If this query is a candidate for the min/max optimization, then
+ ** minMaxFlag will have been previously set to either
+ ** WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX and pMinMaxOrderBy will
+ ** be an appropriate ORDER BY expression for the optimization.
+ */
+ assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
+ assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
+
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
+ 0, minMaxFlag, 0);
if( pWInfo==0 ){
- sqlite3ExprListDelete(db, pDel);
goto select_end;
}
updateAccumulator(pParse, &sAggInfo);
- assert( pMinMax==0 || pMinMax->nExpr==1 );
if( sqlite3WhereIsOrdered(pWInfo)>0 ){
sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
VdbeComment((v, "%s() by index",
- (flag==WHERE_ORDERBY_MIN?"min":"max")));
+ (minMaxFlag==WHERE_ORDERBY_MIN?"min":"max")));
}
sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, &sAggInfo);
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
selectInnerLoop(pParse, p, -1, 0, 0,
pDest, addrEnd, addrEnd);
- sqlite3ExprListDelete(db, pDel);
}
sqlite3VdbeResolveLabel(v, addrEnd);
*/
select_end:
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
-
+ sqlite3ExprListDelete(db, pMinMaxOrderBy);
sqlite3DbFree(db, sAggInfo.aCol);
sqlite3DbFree(db, sAggInfo.aFunc);
#if SELECTTRACE_ENABLED
}
} {{} {}}
+# 2017-10-26. Extend the min/max optimization to indexes on expressions
+#
+do_execsql_test minmax2-11.100 {
+ CREATE TABLE t11(a,b,c);
+ INSERT INTO t11(a,b,c) VALUES(1,10,5),(2,8,11),(3,1,4),(4,20,1),(5,16,4);
+ CREATE INDEX t11bc ON t11(b+c);
+ SELECT max(b+c) FROM t11;
+} {21}
+do_execsql_test minmax2-11.110 {
+ SELECT a, max(b+c) FROM t11;
+} {4 21}
+do_test minmax2-11.111 {
+ db eval {SELECT max(b+c) FROM t11}
+ db status step
+} {0}
+do_test minmax2-11.112 {
+ db eval {SELECT max(c+b) FROM t11}
+ db status step
+} {4}
+do_execsql_test minmax2-11.120 {
+ SELECT a, min(b+c) FROM t11;
+} {3 5}
+do_test minmax2-11.121 {
+ db eval {SELECT min(b+c) FROM t11}
+ db status step
+} {0}
+do_test minmax2-11.122 {
+ db eval {SELECT min(c+b) FROM t11}
+ db status step
+} {4}
+do_execsql_test minmax2-11.130 {
+ INSERT INTO t11(a,b,c) VALUES(6,NULL,0),(7,0,NULL);
+ SELECT a, min(b+c) FROM t11;
+} {3 5}
finish_test