1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

First pass at optimizing max()/min() as described in #2853. Some refinements to come. (CVS 4687)

FossilOrigin-Name: c449e04f1870b1ff726c95c0bf1c6c6a22ca588a
This commit is contained in:
danielk1977
2008-01-05 17:39:29 +00:00
parent 5b6afba924
commit a9d1ccb9b0
11 changed files with 295 additions and 46 deletions

View File

@@ -1,5 +1,5 @@
C Registerify\sbinary\soperators.\s\sAdd\sregister\stracing\sto\sdebugging\soutput.\s(CVS\s4686) C First\spass\sat\soptimizing\smax()/min()\sas\sdescribed\sin\s#2853.\sSome\srefinements\sto\scome.\s(CVS\s4687)
D 2008-01-05T16:29:28 D 2008-01-05T17:39:30
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in 30789bf70614bad659351660d76b8e533f3340e9 F Makefile.in 30789bf70614bad659351660d76b8e533f3340e9
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -90,7 +90,7 @@ F src/build.c 60a3ec74b45634654e06a72224ac01bd33ac69f1
F src/callback.c 77b302b0d41468dcda78c70e706e5b84577f0fa0 F src/callback.c 77b302b0d41468dcda78c70e706e5b84577f0fa0
F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131 F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131
F src/date.c 49c5a6d2de6c12000905b4d36868b07d3011bbf6 F src/date.c 49c5a6d2de6c12000905b4d36868b07d3011bbf6
F src/delete.c cb1d5be17c99e41d1675763a57848bb5dd45191c F src/delete.c 209f33fdf34dcbaa08752437bf53aff0cef0eca6
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c cb8b65c3adc8bb39f67503dfe8db8da24ebe5d21 F src/expr.c cb8b65c3adc8bb39f67503dfe8db8da24ebe5d21
F src/func.c 996071cf0af9d967e58b69fce1909555059ebc7d F src/func.c 996071cf0af9d967e58b69fce1909555059ebc7d
@@ -131,12 +131,12 @@ F src/pragma.c dfb200ec383b5ab3e81cd7bc4e1305e71053ef9a
F src/prepare.c f1bb8eb642082e618a359c08e3e107490eafe0e3 F src/prepare.c f1bb8eb642082e618a359c08e3e107490eafe0e3
F src/printf.c eb27822ba2eec669161409ca31279a24c26ac910 F src/printf.c eb27822ba2eec669161409ca31279a24c26ac910
F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
F src/select.c 102eb03b6daa3c113fac32019dd281f01a38baa8 F src/select.c 33c60380c81283c16414040d034b76f1732ffb4e
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c 5391e889384d2062249f668110d64ed16f601c4b F src/shell.c 5391e889384d2062249f668110d64ed16f601c4b
F src/sqlite.h.in 2a7e3776534bbe6ff2cdc058f3abebe91e7e429f F src/sqlite.h.in 2a7e3776534bbe6ff2cdc058f3abebe91e7e429f
F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb
F src/sqliteInt.h 80f81e0a0b712a74f1c841e6336b51126d781df0 F src/sqliteInt.h 1e7a6545eda5e5e670000775f5d74003e0bc4fbb
F src/sqliteLimit.h ee4430f88f69bf63527967bb35ca52af7b0ccb1e F src/sqliteLimit.h ee4430f88f69bf63527967bb35ca52af7b0ccb1e
F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4 F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4
F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf
@@ -164,7 +164,7 @@ F src/test_tclvar.c b2d1115e4d489179d3f029e765211b2ad527ba59
F src/test_thread.c e297dd41db0b249646e69f97d36ec13e56e8b730 F src/test_thread.c e297dd41db0b249646e69f97d36ec13e56e8b730
F src/tokenize.c a4e04438c11fed2c67ec47fe3edbef9cca2d1b48 F src/tokenize.c a4e04438c11fed2c67ec47fe3edbef9cca2d1b48
F src/trigger.c 91ff1552b5c2cd66a077563a026d183c1dc993d6 F src/trigger.c 91ff1552b5c2cd66a077563a026d183c1dc993d6
F src/update.c ac6cdfebf88340fd68550b1d7fd6a15ad7144fd8 F src/update.c f322317ee492c0a648f8a44fd805dd85dbbe2f05
F src/utf.c ef4b7d83bae533b76c3e1bf635b113fdad86a736 F src/utf.c ef4b7d83bae533b76c3e1bf635b113fdad86a736
F src/util.c 05f31144bbd3f1a24f4139ae029c42545cb72624 F src/util.c 05f31144bbd3f1a24f4139ae029c42545cb72624
F src/vacuum.c 3f34f278809bf3eb0b62ec46ff779e9c385b28f0 F src/vacuum.c 3f34f278809bf3eb0b62ec46ff779e9c385b28f0
@@ -177,7 +177,7 @@ F src/vdbeblob.c b90f7494c408d47ce6835000b01e40b371e27baf
F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6 F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6
F src/vdbemem.c 123994fcd344993d2fb050a83b91b341bbbd08b4 F src/vdbemem.c 123994fcd344993d2fb050a83b91b341bbbd08b4
F src/vtab.c 03014b2bfa8096ecac5fcdc80d34cd76e06af52a F src/vtab.c 03014b2bfa8096ecac5fcdc80d34cd76e06af52a
F src/where.c 941635bb007484330bc1a0f3cc013b67f1c41864 F src/where.c 0d72b6431c23da6fb1b72422e364ac8fe7eb1d3a
F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/all.test ee350b9ab15b175fc0a8fb51bf2141ed3a3b9cba F test/all.test ee350b9ab15b175fc0a8fb51bf2141ed3a3b9cba
@@ -227,7 +227,7 @@ F test/check.test 024ed399600b799160378cf9d9f436bdf5dfd184
F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04 F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04
F test/collate2.test 701d9651c5707024fd86a20649af9ea55e2c0eb8 F test/collate2.test 701d9651c5707024fd86a20649af9ea55e2c0eb8
F test/collate3.test 947a77f5b8227e037a7094d0e338a5504f155cc4 F test/collate3.test 947a77f5b8227e037a7094d0e338a5504f155cc4
F test/collate4.test daf498e294dcd596b961d425c3f2dda117e4717e F test/collate4.test 4545554388daaa604e5b3def3aa2f7ed6d56e8da
F test/collate5.test e54df13eb9e1140273680b3153c6e19b39e59888 F test/collate5.test e54df13eb9e1140273680b3153c6e19b39e59888
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907 F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
F test/collate7.test e23677b1fd271505302643a98178952bb65b6f21 F test/collate7.test e23677b1fd271505302643a98178952bb65b6f21
@@ -379,8 +379,9 @@ F test/malloc_common.tcl b47137fb36e95fdafb0267745afefcd6b0a5b9dc
F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8 F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893 F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217 F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
F test/minmax.test 66434d8ee04869fe4c220b665b73748accbb9163 F test/minmax.test 5d56f08a7765dfb5c1fb303333f7444dacb37bef
F test/minmax2.test 8294b6728819608861ba0e06ac1d9a87c4d815b5 F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
F test/minmax3.test 5b89f055df7ed03334e96eec0efb804afb7de638
F test/misc1.test 1b89c02c4a33b49dee4cd1d20d161aaaba719075 F test/misc1.test 1b89c02c4a33b49dee4cd1d20d161aaaba719075
F test/misc2.test 1ee89298de9c16b61454658b24999c403e86afe4 F test/misc2.test 1ee89298de9c16b61454658b24999c403e86afe4
F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03 F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03
@@ -603,7 +604,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 6c78d2a49a3e6ee8bc31f16488a430cba9eda59d P 66396d2f0289e36b5fc0af5078c08d1b17f342ae
R fdff0af63eb57b92a8e5bcade477a016 R b1dacf0522dc83de3d6ddccfc407074e
U drh U danielk1977
Z c3fdc5267d8f427a421314acf39cdbc6 Z 85372970e5aef600b6f97b67c85860f6

View File

@@ -1 +1 @@
66396d2f0289e36b5fc0af5078c08d1b17f342ae c449e04f1870b1ff726c95c0bf1c6c6a22ca588a

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements. ** in order to generate code for DELETE FROM statements.
** **
** $Id: delete.c,v 1.150 2008/01/05 05:20:10 drh Exp $ ** $Id: delete.c,v 1.151 2008/01/05 17:39:30 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -313,7 +313,7 @@ void sqlite3DeleteFrom(
/* Begin the database scan /* Begin the database scan
*/ */
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
if( pWInfo==0 ) goto delete_from_cleanup; if( pWInfo==0 ) goto delete_from_cleanup;
/* Remember the rowid of every item to be deleted. /* Remember the rowid of every item to be deleted.

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite. ** to handle SELECT statements in SQLite.
** **
** $Id: select.c,v 1.386 2008/01/05 05:20:10 drh Exp $ ** $Id: select.c,v 1.387 2008/01/05 17:39:30 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -2625,6 +2625,33 @@ static int flattenSubquery(
} }
#endif /* SQLITE_OMIT_VIEW */ #endif /* SQLITE_OMIT_VIEW */
/*
** Analyze the SELECT statement passed as an argument to see if it
** is a min() or max() query. Return ORDERBY_MIN or ORDERBY_MAX if
** it is, or 0 otherwise. At present, a query is considered to be
** a min()/max() query if:
**
** 1. The result set contains exactly one element, either
** min(x) or max(x), where x is a column identifier.
*/
static int minMaxQuery(Parse *pParse, Select *p){
Expr *pExpr;
ExprList *pEList = p->pEList;
if( pEList->nExpr!=1 ) return ORDERBY_NORMAL;
pExpr = pEList->a[0].pExpr;
pEList = pExpr->pList;
if( pExpr->op!=TK_AGG_FUNCTION || pEList==0 || pEList->nExpr!=1 ) return 0;
if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return ORDERBY_NORMAL;
if( pExpr->token.n!=3 ) return ORDERBY_NORMAL;
if( sqlite3StrNICmp((char*)pExpr->token.z,"min",3)==0 ){
return ORDERBY_MIN;
}else if( sqlite3StrNICmp((char*)pExpr->token.z,"max",3)==0 ){
return ORDERBY_MAX;
}
return ORDERBY_NORMAL;
}
/* /*
** Analyze the SELECT statement passed in as an argument to see if it ** Analyze the SELECT statement passed in as an argument to see if it
** is a simple min() or max() query. If it is and this query can be ** is a simple min() or max() query. If it is and this query can be
@@ -2645,6 +2672,7 @@ static int flattenSubquery(
** The parameters to this routine are the same as for sqlite3Select(). ** The parameters to this routine are the same as for sqlite3Select().
** See the header comment on that routine for additional information. ** See the header comment on that routine for additional information.
*/ */
#if 0
static int simpleMinMaxQuery(Parse *pParse, Select *p, SelectDest *pDest){ static int simpleMinMaxQuery(Parse *pParse, Select *p, SelectDest *pDest){
Expr *pExpr; Expr *pExpr;
int iCol; int iCol;
@@ -2781,6 +2809,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, SelectDest *pDest){
return 1; return 1;
} }
#endif
/* /*
** This routine resolves any names used in the result set of the ** This routine resolves any names used in the result set of the
@@ -3268,10 +3297,12 @@ int sqlite3Select(
/* Check for the special case of a min() or max() function by itself /* Check for the special case of a min() or max() function by itself
** in the result set. ** in the result set.
*/ */
#if 0
if( simpleMinMaxQuery(pParse, p, pDest) ){ if( simpleMinMaxQuery(pParse, p, pDest) ){
rc = 0; rc = 0;
goto select_end; goto select_end;
} }
#endif
/* Check to see if this is a subquery that can be "flattened" into its parent. /* Check to see if this is a subquery that can be "flattened" into its parent.
** If flattening is a possiblity, do so and return immediately. ** If flattening is a possiblity, do so and return immediately.
@@ -3345,7 +3376,7 @@ int sqlite3Select(
/* This case is for non-aggregate queries /* This case is for non-aggregate queries
** Begin the database scan ** Begin the database scan
*/ */
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
if( pWInfo==0 ) goto select_end; if( pWInfo==0 ) goto select_end;
/* If sorting index that was created by a prior OP_OpenEphemeral /* If sorting index that was created by a prior OP_OpenEphemeral
@@ -3501,7 +3532,7 @@ int sqlite3Select(
*/ */
sqlite3VdbeResolveLabel(v, addrInitializeLoop); sqlite3VdbeResolveLabel(v, addrInitializeLoop);
sqlite3VdbeAddOp2(v, OP_Gosub, 0, addrReset); sqlite3VdbeAddOp2(v, OP_Gosub, 0, addrReset);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0);
if( pWInfo==0 ) goto select_end; if( pWInfo==0 ) goto select_end;
if( pGroupBy==0 ){ if( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so /* The optimizer is able to deliver rows in group by order so
@@ -3606,14 +3637,30 @@ int sqlite3Select(
} /* endif pGroupBy */ } /* endif pGroupBy */
else { else {
ExprList *pMinMax = 0;
u8 flag;
flag = minMaxQuery(pParse, p);
if( flag ){
pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->pList);
if( pMinMax ){
pMinMax->a[0].sortOrder = ((flag==ORDERBY_MIN)?0:1);
pMinMax->a[0].pExpr->op = TK_COLUMN;
}
}
/* This case runs if the aggregate has no GROUP BY clause. The /* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row ** processing is much simpler since there is only a single row
** of output. ** of output.
*/ */
resetAccumulator(pParse, &sAggInfo); resetAccumulator(pParse, &sAggInfo);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
if( pWInfo==0 ) goto select_end; if( pWInfo==0 ) goto select_end;
updateAccumulator(pParse, &sAggInfo); updateAccumulator(pParse, &sAggInfo);
if( !pMinMax && flag ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
VdbeComment((v, "%s() by index", (flag==ORDERBY_MIN?"min":"max")));
}
sqlite3WhereEnd(pWInfo); sqlite3WhereEnd(pWInfo);
finalizeAggFunctions(pParse, &sAggInfo); finalizeAggFunctions(pParse, &sAggInfo);
pOrderBy = 0; pOrderBy = 0;
@@ -3622,6 +3669,8 @@ int sqlite3Select(
} }
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
pDest, addrEnd, addrEnd, aff); pDest, addrEnd, addrEnd, aff);
sqlite3ExprListDelete(pMinMax);
} }
sqlite3VdbeResolveLabel(v, addrEnd); sqlite3VdbeResolveLabel(v, addrEnd);

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.638 2008/01/04 19:10:29 danielk1977 Exp $ ** @(#) $Id: sqliteInt.h,v 1.639 2008/01/05 17:39:30 danielk1977 Exp $
*/ */
#ifndef _SQLITEINT_H_ #ifndef _SQLITEINT_H_
#define _SQLITEINT_H_ #define _SQLITEINT_H_
@@ -1229,6 +1229,10 @@ struct WhereLevel {
sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */ sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */
}; };
#define ORDERBY_NORMAL 0
#define ORDERBY_MIN 1
#define ORDERBY_MAX 2
/* /*
** The WHERE clause processing routine has two halves. The ** The WHERE clause processing routine has two halves. The
** first part does the start of the WHERE loop and the second ** first part does the start of the WHERE loop and the second
@@ -1735,7 +1739,7 @@ int sqlite3IsReadOnly(Parse*, Table*, int);
void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**); WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
void sqlite3WhereEnd(WhereInfo*); void sqlite3WhereEnd(WhereInfo*);
void sqlite3ExprCodeGetColumn(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeGetColumn(Vdbe*, Table*, int, int, int);
int sqlite3ExprCode(Parse*, Expr*, int); int sqlite3ExprCode(Parse*, Expr*, int);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** to handle UPDATE statements.
** **
** $Id: update.c,v 1.160 2008/01/05 05:20:10 drh Exp $ ** $Id: update.c,v 1.161 2008/01/05 17:39:30 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -339,7 +339,7 @@ void sqlite3Update(
/* Begin the database scan /* Begin the database scan
*/ */
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
if( pWInfo==0 ) goto update_cleanup; if( pWInfo==0 ) goto update_cleanup;
/* Remember the rowid of every item to be updated. /* Remember the rowid of every item to be updated.

View File

@@ -16,7 +16,7 @@
** so is applicable. Because this module is responsible for selecting ** so is applicable. Because this module is responsible for selecting
** indices, you might also think of this module as the "query optimizer". ** indices, you might also think of this module as the "query optimizer".
** **
** $Id: where.c,v 1.275 2008/01/05 05:38:21 drh Exp $ ** $Id: where.c,v 1.276 2008/01/05 17:39:30 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@@ -1969,7 +1969,8 @@ WhereInfo *sqlite3WhereBegin(
Parse *pParse, /* The parser context */ Parse *pParse, /* The parser context */
SrcList *pTabList, /* A list of all tables to be scanned */ SrcList *pTabList, /* A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */ Expr *pWhere, /* The WHERE clause */
ExprList **ppOrderBy /* An ORDER BY clause, or NULL */ ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
u8 obflag /* One of ORDERBY_MIN, ORDERBY_MAX or ORDERBY_NORMAL */
){ ){
int i; /* Loop counter */ int i; /* Loop counter */
WhereInfo *pWInfo; /* Will become the return value of this function */ WhereInfo *pWInfo; /* Will become the return value of this function */
@@ -1984,6 +1985,7 @@ WhereInfo *sqlite3WhereBegin(
int iFrom; /* First unused FROM clause element */ int iFrom; /* First unused FROM clause element */
int andFlags; /* AND-ed combination of all wc.a[].flags */ int andFlags; /* AND-ed combination of all wc.a[].flags */
sqlite3 *db; /* Database connection */ sqlite3 *db; /* Database connection */
ExprList *pOrderBy = 0;
/* The number of tables in the FROM clause is limited by the number of /* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask ** bits in a Bitmask
@@ -1993,6 +1995,10 @@ WhereInfo *sqlite3WhereBegin(
return 0; return 0;
} }
if( ppOrderBy ){
pOrderBy = *ppOrderBy;
}
/* Split the WHERE clause into separate subexpressions where each /* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator. ** subexpression is separated by an AND operator.
*/ */
@@ -2400,6 +2406,7 @@ WhereInfo *sqlite3WhereBegin(
int testOp; int testOp;
int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0; int topLimit = (pLevel->flags & WHERE_TOP_LIMIT)!=0;
int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0; int btmLimit = (pLevel->flags & WHERE_BTM_LIMIT)!=0;
int isMinQuery = 0; /* If this is an optimized SELECT min(x) ... */
/* Generate code to evaluate all constraint terms using == or IN /* Generate code to evaluate all constraint terms using == or IN
** and level the values of those terms on the stack. ** and level the values of those terms on the stack.
@@ -2428,6 +2435,22 @@ WhereInfo *sqlite3WhereBegin(
SWAP(int, topLimit, btmLimit); SWAP(int, topLimit, btmLimit);
} }
/* If this loop satisfies a sort order (pOrderBy) request that
** was passed to this function to implement a "SELECT min(x) ..."
** query, then the caller will only allow the loop to run for
** a single iteration. This means that the first row returned
** should not have a NULL value stored in 'x'. If column 'x' is
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
if( (obflag==ORDERBY_MIN)
&& (pLevel->flags&WHERE_ORDERBY)
&& (pIdx->nColumn>nEq)
&& (pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq])
){
isMinQuery = 1;
}
/* Generate the termination key. This is the key value that /* Generate the termination key. This is the key value that
** will end the search. There is no termination key if there ** will end the search. There is no termination key if there
** are no equality terms and no "X<..." term. ** are no equality terms and no "X<..." term.
@@ -2452,9 +2475,14 @@ WhereInfo *sqlite3WhereBegin(
testOp = nEq>0 ? OP_IdxGE : OP_Noop; testOp = nEq>0 ? OP_IdxGE : OP_Noop;
topEq = 1; topEq = 1;
} }
if( testOp!=OP_Noop ){ if( testOp!=OP_Noop || (isMinQuery&&bRev) ){
int nCol = nEq + topLimit; int nCol = nEq + topLimit;
pLevel->iMem = ++pParse->nMem; pLevel->iMem = ++pParse->nMem;
if( isMinQuery && !topLimit ){
nCol++;
sqlite3VdbeAddOp2(v, OP_Null, 0, 0);
topEq = 0;
}
buildIndexProbe(v, nCol, pIdx); buildIndexProbe(v, nCol, pIdx);
if( bRev ){ if( bRev ){
int op = topEq ? OP_MoveLe : OP_MoveLt; int op = topEq ? OP_MoveLe : OP_MoveLt;
@@ -2465,7 +2493,7 @@ WhereInfo *sqlite3WhereBegin(
}else if( bRev ){ }else if( bRev ){
sqlite3VdbeAddOp2(v, OP_Last, iIdxCur, brk); sqlite3VdbeAddOp2(v, OP_Last, iIdxCur, brk);
} }
/* Generate the start key. This is the key that defines the lower /* Generate the start key. This is the key that defines the lower
** bound on the search. There is no start key if there are no ** bound on the search. There is no start key if there are no
** equality terms and if there is no "X>..." term. In ** equality terms and if there is no "X>..." term. In
@@ -2489,8 +2517,13 @@ WhereInfo *sqlite3WhereBegin(
}else{ }else{
btmEq = 1; btmEq = 1;
} }
if( nEq>0 || btmLimit ){ if( nEq>0 || btmLimit || (isMinQuery&&!bRev) ){
int nCol = nEq + btmLimit; int nCol = nEq + btmLimit;
if( isMinQuery && !btmLimit ){
nCol++;
sqlite3VdbeAddOp2(v, OP_Null, 0, 0);
btmEq = 0;
}
buildIndexProbe(v, nCol, pIdx); buildIndexProbe(v, nCol, pIdx);
if( bRev ){ if( bRev ){
pLevel->iMem = ++pParse->nMem; pLevel->iMem = ++pParse->nMem;
@@ -2538,6 +2571,7 @@ WhereInfo *sqlite3WhereBegin(
*/ */
int start; int start;
int nEq = pLevel->nEq; int nEq = pLevel->nEq;
int isMinQuery = 0; /* If this is an optimized SELECT min(x) ... */
/* Generate code to evaluate all constraint terms using == or IN /* Generate code to evaluate all constraint terms using == or IN
** and leave the values of those terms on the stack. ** and leave the values of those terms on the stack.
@@ -2545,11 +2579,28 @@ WhereInfo *sqlite3WhereBegin(
codeAllEqualityTerms(pParse, pLevel, &wc, notReady); codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
nxt = pLevel->nxt; nxt = pLevel->nxt;
/* Generate a single key that will be used to both start and terminate if( (obflag==ORDERBY_MIN)
** the search && (pLevel->flags&WHERE_ORDERBY)
*/ && (pIdx->nColumn>nEq)
buildIndexProbe(v, nEq, pIdx); && (pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq])
sqlite3VdbeAddOp2(v, OP_Copy, 0, pLevel->iMem); ){
int h;
isMinQuery = 1;
for(h=0; h<nEq; h++){
sqlite3VdbeAddOp1(v, OP_Copy, 1-nEq);
}
buildIndexProbe(v, nEq, pIdx);
sqlite3VdbeAddOp2(v, OP_Copy, 0, pLevel->iMem);
sqlite3VdbeAddOp2(v, OP_Pop, 1, 0);
sqlite3VdbeAddOp2(v, OP_Null, 0, 0);
buildIndexProbe(v, nEq+1, pIdx);
}else{
/* Generate a single key that will be used to both start and
** terminate the search
*/
buildIndexProbe(v, nEq, pIdx);
sqlite3VdbeAddOp2(v, OP_Copy, 0, pLevel->iMem);
}
/* Generate code (1) to move to the first matching element of the table. /* Generate code (1) to move to the first matching element of the table.
** Then generate code (2) that jumps to "nxt" after the cursor is past ** Then generate code (2) that jumps to "nxt" after the cursor is past
@@ -2558,13 +2609,13 @@ WhereInfo *sqlite3WhereBegin(
** iteration of the scan to see if the scan has finished. */ ** iteration of the scan to see if the scan has finished. */
if( bRev ){ if( bRev ){
/* Scan in reverse order */ /* Scan in reverse order */
sqlite3VdbeAddOp2(v, OP_MoveLe, iIdxCur, nxt); sqlite3VdbeAddOp2(v, (isMinQuery?OP_MoveLt:OP_MoveLe), iIdxCur, nxt);
start = sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0); start = sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0);
sqlite3VdbeAddOp2(v, OP_IdxLT, iIdxCur, nxt); sqlite3VdbeAddOp2(v, OP_IdxLT, iIdxCur, nxt);
pLevel->op = OP_Prev; pLevel->op = OP_Prev;
}else{ }else{
/* Scan in the forward order */ /* Scan in the forward order */
sqlite3VdbeAddOp2(v, OP_MoveGe, iIdxCur, nxt); sqlite3VdbeAddOp2(v, (isMinQuery?OP_MoveGt:OP_MoveGe), iIdxCur, nxt);
start = sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0); start = sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0);
sqlite3VdbeAddOp4(v, OP_IdxGE, iIdxCur, nxt, 0, "+", P4_STATIC); sqlite3VdbeAddOp4(v, OP_IdxGE, iIdxCur, nxt, 0, "+", P4_STATIC);
pLevel->op = OP_Next; pLevel->op = OP_Next;

View File

@@ -12,7 +12,7 @@
# This file implements regression tests for SQLite library. The # This file implements regression tests for SQLite library. The
# focus of this script is page cache subsystem. # focus of this script is page cache subsystem.
# #
# $Id: collate4.test,v 1.8 2005/04/01 10:47:40 drh Exp $ # $Id: collate4.test,v 1.9 2008/01/05 17:39:30 danielk1977 Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -591,12 +591,12 @@ do_test collate4-4.3 {
count { count {
SELECT min(a) FROM collate4t1; SELECT min(a) FROM collate4t1;
} }
} {10 2} } {10 1}
do_test collate4-4.4 { do_test collate4-4.4 {
count { count {
SELECT max(a) FROM collate4t1; SELECT max(a) FROM collate4t1;
} }
} {20 1} } {20 0}
do_test collate4-4.5 { do_test collate4-4.5 {
# Test that the index with collation type NUMERIC is not used. # Test that the index with collation type NUMERIC is not used.
execsql { execsql {

View File

@@ -13,7 +13,7 @@
# aggregate min() and max() functions and which are handled as # aggregate min() and max() functions and which are handled as
# as a special case. # as a special case.
# #
# $Id: minmax.test,v 1.19 2006/03/26 01:21:23 drh Exp $ # $Id: minmax.test,v 1.20 2008/01/05 17:39:30 danielk1977 Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -68,14 +68,14 @@ do_test minmax-1.5 {
} {1} } {1}
do_test minmax-1.6 { do_test minmax-1.6 {
set sqlite_search_count set sqlite_search_count
} {2} } {1}
do_test minmax-1.7 { do_test minmax-1.7 {
set sqlite_search_count 0 set sqlite_search_count 0
execsql {SELECT max(x) FROM t1} execsql {SELECT max(x) FROM t1}
} {20} } {20}
do_test minmax-1.8 { do_test minmax-1.8 {
set sqlite_search_count set sqlite_search_count
} {1} } {0}
do_test minmax-1.9 { do_test minmax-1.9 {
set sqlite_search_count 0 set sqlite_search_count 0
execsql {SELECT max(y) FROM t1} execsql {SELECT max(y) FROM t1}

View File

@@ -15,7 +15,7 @@
# optimization works right in the presence of descending # optimization works right in the presence of descending
# indices. Ticket #2514. # indices. Ticket #2514.
# #
# $Id: minmax2.test,v 1.1 2007/07/18 18:17:12 drh Exp $ # $Id: minmax2.test,v 1.2 2008/01/05 17:39:30 danielk1977 Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -71,14 +71,14 @@ do_test minmax2-1.5 {
} {1} } {1}
do_test minmax2-1.6 { do_test minmax2-1.6 {
set sqlite_search_count set sqlite_search_count
} {2} } {1}
do_test minmax2-1.7 { do_test minmax2-1.7 {
set sqlite_search_count 0 set sqlite_search_count 0
execsql {SELECT max(x) FROM t1} execsql {SELECT max(x) FROM t1}
} {20} } {20}
do_test minmax2-1.8 { do_test minmax2-1.8 {
set sqlite_search_count set sqlite_search_count
} {1} } {0}
do_test minmax2-1.9 { do_test minmax2-1.9 {
set sqlite_search_count 0 set sqlite_search_count 0
execsql {SELECT max(y) FROM t1} execsql {SELECT max(y) FROM t1}

144
test/minmax3.test Normal file
View File

@@ -0,0 +1,144 @@
# 2008 January 5
#
# 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.
#
#***********************************************************************
# $Id: minmax3.test,v 1.1 2008/01/05 17:39:30 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Do an SQL statement. Append the search count to the end of the result.
#
proc count sql {
set ::sqlite_search_count 0
return [concat [execsql $sql] $::sqlite_search_count]
}
# This procedure sets the value of the file-format in file 'test.db'
# to $newval. Also, the schema cookie is incremented.
#
proc set_file_format {newval} {
set bt [btree_open test.db 10 0]
btree_begin_transaction $bt
set meta [btree_get_meta $bt]
lset meta 2 $newval ;# File format
lset meta 1 [expr [lindex $meta 1]+1] ;# Schema cookie
eval "btree_update_meta $bt $meta"
btree_commit $bt
btree_close $bt
}
# Create the file as file-format 4 (DESC index support). This is
# required to exercise a few cases in where.c.
#
execsql { select * from sqlite_master }
set_file_format 4
do_test minmax3-1.0 {
execsql {
BEGIN;
CREATE TABLE t1(x, y, z);
INSERT INTO t1 VALUES('1', 'I', 'one');
INSERT INTO t1 VALUES('2', 'IV', 'four');
INSERT INTO t1 VALUES('2', NULL, 'three');
INSERT INTO t1 VALUES('2', 'II', 'two');
INSERT INTO t1 VALUES('2', 'V', 'five');
INSERT INTO t1 VALUES('3', 'VI', 'six');
COMMIT;
}
} {}
do_test minmax3-1.1.1 {
# Linear scan.
count { SELECT max(y) FROM t1 WHERE x = '2'; }
} {V 5}
do_test minmax3-1.1.2 {
# Index optimizes the WHERE x='2' constraint.
execsql { CREATE INDEX i1 ON t1(x) }
count { SELECT max(y) FROM t1 WHERE x = '2'; }
} {V 9}
do_test minmax3-1.1.3 {
# Index optimizes the WHERE x='2' constraint and the MAX(y).
execsql { CREATE INDEX i2 ON t1(x,y) }
count { SELECT max(y) FROM t1 WHERE x = '2'; }
} {V 1}
do_test minmax3-1.1.4 {
# Index optimizes the WHERE x='2' constraint and the MAX(y).
execsql { DROP INDEX i2 ; CREATE INDEX i2 ON t1(x, y DESC) }
count { SELECT max(y) FROM t1 WHERE x = '2'; }
} {V 1}
do_test minmax3-1.1.5 {
count { SELECT max(y) FROM t1 WHERE x = '2' AND y != 'V'; }
} {IV 2}
do_test minmax3-1.1.6 {
count { SELECT max(y) FROM t1 WHERE x = '2' AND y < 'V'; }
} {IV 1}
do_test minmax3-1.1.6 {
count { SELECT max(y) FROM t1 WHERE x = '2' AND z != 'five'; }
} {IV 4}
do_test minmax3-1.2.1 {
# Linear scan of t1.
execsql { DROP INDEX i1 ; DROP INDEX i2 }
count { SELECT min(y) FROM t1 WHERE x = '2'; }
} {II 5}
do_test minmax3-1.2.2 {
# Index i1 optimizes the WHERE x='2' constraint.
execsql { CREATE INDEX i1 ON t1(x) }
count { SELECT min(y) FROM t1 WHERE x = '2'; }
} {II 9}
do_test minmax3-1.2.3 {
# Index i2 optimizes the WHERE x='2' constraint and the min(y).
execsql { CREATE INDEX i2 ON t1(x,y) }
count { SELECT min(y) FROM t1 WHERE x = '2'; }
} {II 1}
do_test minmax3-1.2.4 {
# Index optimizes the WHERE x='2' constraint and the MAX(y).
execsql { DROP INDEX i2 ; CREATE INDEX i2 ON t1(x, y DESC) }
count { SELECT min(y) FROM t1 WHERE x = '2'; }
} {II 1}
do_test minmax3-1.3.1 {
# Linear scan
execsql { DROP INDEX i1 ; DROP INDEX i2 }
count { SELECT min(y) FROM t1; }
} {I 5}
do_test minmax3-1.3.2 {
# Index i1 optimizes the min(y)
execsql { CREATE INDEX i1 ON t1(y) }
count { SELECT min(y) FROM t1; }
} {I 1}
do_test minmax3-1.3.3 {
# Index i1 optimizes the min(y)
execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(y DESC) }
count { SELECT min(y) FROM t1; }
} {I 1}
do_test minmax3-1.4.1 {
# Linear scan
execsql { DROP INDEX i1 }
count { SELECT max(y) FROM t1; }
} {VI 5}
do_test minmax3-1.4.2 {
# Index i1 optimizes the max(y)
execsql { CREATE INDEX i1 ON t1(y) }
count { SELECT max(y) FROM t1; }
} {VI 0}
do_test minmax3-1.4.3 {
# Index i1 optimizes the max(y)
execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(y DESC) }
execsql { SELECT y from t1}
count { SELECT max(y) FROM t1; }
} {VI 0}
do_test minmax3-1.4.4 {
execsql { DROP INDEX i1 }
} {}
finish_test