-C Registerify\sbinary\soperators.\s\sAdd\sregister\stracing\sto\sdebugging\soutput.\s(CVS\s4686)
-D 2008-01-05T16:29:28
+C First\spass\sat\soptimizing\smax()/min()\sas\sdescribed\sin\s#2853.\sSome\srefinements\sto\scome.\s(CVS\s4687)
+D 2008-01-05T17:39:30
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in 30789bf70614bad659351660d76b8e533f3340e9
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F src/callback.c 77b302b0d41468dcda78c70e706e5b84577f0fa0
F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131
F src/date.c 49c5a6d2de6c12000905b4d36868b07d3011bbf6
-F src/delete.c cb1d5be17c99e41d1675763a57848bb5dd45191c
+F src/delete.c 209f33fdf34dcbaa08752437bf53aff0cef0eca6
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c cb8b65c3adc8bb39f67503dfe8db8da24ebe5d21
F src/func.c 996071cf0af9d967e58b69fce1909555059ebc7d
F src/prepare.c f1bb8eb642082e618a359c08e3e107490eafe0e3
F src/printf.c eb27822ba2eec669161409ca31279a24c26ac910
F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
-F src/select.c 102eb03b6daa3c113fac32019dd281f01a38baa8
+F src/select.c 33c60380c81283c16414040d034b76f1732ffb4e
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c 5391e889384d2062249f668110d64ed16f601c4b
F src/sqlite.h.in 2a7e3776534bbe6ff2cdc058f3abebe91e7e429f
F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb
-F src/sqliteInt.h 80f81e0a0b712a74f1c841e6336b51126d781df0
+F src/sqliteInt.h 1e7a6545eda5e5e670000775f5d74003e0bc4fbb
F src/sqliteLimit.h ee4430f88f69bf63527967bb35ca52af7b0ccb1e
F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4
F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf
F src/test_thread.c e297dd41db0b249646e69f97d36ec13e56e8b730
F src/tokenize.c a4e04438c11fed2c67ec47fe3edbef9cca2d1b48
F src/trigger.c 91ff1552b5c2cd66a077563a026d183c1dc993d6
-F src/update.c ac6cdfebf88340fd68550b1d7fd6a15ad7144fd8
+F src/update.c f322317ee492c0a648f8a44fd805dd85dbbe2f05
F src/utf.c ef4b7d83bae533b76c3e1bf635b113fdad86a736
F src/util.c 05f31144bbd3f1a24f4139ae029c42545cb72624
F src/vacuum.c 3f34f278809bf3eb0b62ec46ff779e9c385b28f0
F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6
F src/vdbemem.c 123994fcd344993d2fb050a83b91b341bbbd08b4
F src/vtab.c 03014b2bfa8096ecac5fcdc80d34cd76e06af52a
-F src/where.c 941635bb007484330bc1a0f3cc013b67f1c41864
+F src/where.c 0d72b6431c23da6fb1b72422e364ac8fe7eb1d3a
F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/all.test ee350b9ab15b175fc0a8fb51bf2141ed3a3b9cba
F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04
F test/collate2.test 701d9651c5707024fd86a20649af9ea55e2c0eb8
F test/collate3.test 947a77f5b8227e037a7094d0e338a5504f155cc4
-F test/collate4.test daf498e294dcd596b961d425c3f2dda117e4717e
+F test/collate4.test 4545554388daaa604e5b3def3aa2f7ed6d56e8da
F test/collate5.test e54df13eb9e1140273680b3153c6e19b39e59888
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
F test/collate7.test e23677b1fd271505302643a98178952bb65b6f21
F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
-F test/minmax.test 66434d8ee04869fe4c220b665b73748accbb9163
-F test/minmax2.test 8294b6728819608861ba0e06ac1d9a87c4d815b5
+F test/minmax.test 5d56f08a7765dfb5c1fb303333f7444dacb37bef
+F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
+F test/minmax3.test 5b89f055df7ed03334e96eec0efb804afb7de638
F test/misc1.test 1b89c02c4a33b49dee4cd1d20d161aaaba719075
F test/misc2.test 1ee89298de9c16b61454658b24999c403e86afe4
F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 6c78d2a49a3e6ee8bc31f16488a430cba9eda59d
-R fdff0af63eb57b92a8e5bcade477a016
-U drh
-Z c3fdc5267d8f427a421314acf39cdbc6
+P 66396d2f0289e36b5fc0af5078c08d1b17f342ae
+R b1dacf0522dc83de3d6ddccfc407074e
+U danielk1977
+Z 85372970e5aef600b6f97b67c85860f6
-66396d2f0289e36b5fc0af5078c08d1b17f342ae
\ No newline at end of file
+c449e04f1870b1ff726c95c0bf1c6c6a22ca588a
\ No newline at end of file
** This file contains C code routines that are called by the parser
** 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"
/* Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
if( pWInfo==0 ) goto delete_from_cleanup;
/* Remember the rowid of every item to be deleted.
** This file contains C code routines that are called by the parser
** 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"
}
#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
** is a simple min() or max() query. If it is and this query can be
** The parameters to this routine are the same as for sqlite3Select().
** See the header comment on that routine for additional information.
*/
+#if 0
static int simpleMinMaxQuery(Parse *pParse, Select *p, SelectDest *pDest){
Expr *pExpr;
int iCol;
return 1;
}
+#endif
/*
** This routine resolves any names used in the result set of the
/* Check for the special case of a min() or max() function by itself
** in the result set.
*/
+#if 0
if( simpleMinMaxQuery(pParse, p, pDest) ){
rc = 0;
goto select_end;
}
+#endif
/* 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.
/* This case is for non-aggregate queries
** Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0);
if( pWInfo==0 ) goto select_end;
/* If sorting index that was created by a prior OP_OpenEphemeral
*/
sqlite3VdbeResolveLabel(v, addrInitializeLoop);
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( pGroupBy==0 ){
/* The optimizer is able to deliver rows in group by order so
} /* endif pGroupBy */
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
** processing is much simpler since there is only a single row
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, flag);
if( pWInfo==0 ) goto select_end;
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);
finalizeAggFunctions(pParse, &sAggInfo);
pOrderBy = 0;
}
selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
pDest, addrEnd, addrEnd, aff);
+
+ sqlite3ExprListDelete(pMinMax);
}
sqlite3VdbeResolveLabel(v, addrEnd);
*************************************************************************
** 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_
#define _SQLITEINT_H_
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
** first part does the start of the WHERE loop and the second
void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**);
+WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
void sqlite3WhereEnd(WhereInfo*);
void sqlite3ExprCodeGetColumn(Vdbe*, Table*, int, int, int);
int sqlite3ExprCode(Parse*, Expr*, int);
** This file contains C code routines that are called by the parser
** 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"
/* Begin the database scan
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
if( pWInfo==0 ) goto update_cleanup;
/* Remember the rowid of every item to be updated.
** so is applicable. Because this module is responsible for selecting
** 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"
Parse *pParse, /* The parser context */
SrcList *pTabList, /* A list of all tables to be scanned */
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 */
WhereInfo *pWInfo; /* Will become the return value of this function */
int iFrom; /* First unused FROM clause element */
int andFlags; /* AND-ed combination of all wc.a[].flags */
sqlite3 *db; /* Database connection */
+ ExprList *pOrderBy = 0;
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
return 0;
}
+ if( ppOrderBy ){
+ pOrderBy = *ppOrderBy;
+ }
+
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
int testOp;
int topLimit = (pLevel->flags & WHERE_TOP_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
** and level the values of those terms on the stack.
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
** will end the search. There is no termination key if there
** are no equality terms and no "X<..." term.
testOp = nEq>0 ? OP_IdxGE : OP_Noop;
topEq = 1;
}
- if( testOp!=OP_Noop ){
+ if( testOp!=OP_Noop || (isMinQuery&&bRev) ){
int nCol = nEq + topLimit;
pLevel->iMem = ++pParse->nMem;
+ if( isMinQuery && !topLimit ){
+ nCol++;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, 0);
+ topEq = 0;
+ }
buildIndexProbe(v, nCol, pIdx);
if( bRev ){
int op = topEq ? OP_MoveLe : OP_MoveLt;
}else if( bRev ){
sqlite3VdbeAddOp2(v, OP_Last, iIdxCur, brk);
}
-
+
/* 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
** equality terms and if there is no "X>..." term. In
}else{
btmEq = 1;
}
- if( nEq>0 || btmLimit ){
+ if( nEq>0 || btmLimit || (isMinQuery&&!bRev) ){
int nCol = nEq + btmLimit;
+ if( isMinQuery && !btmLimit ){
+ nCol++;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, 0);
+ btmEq = 0;
+ }
buildIndexProbe(v, nCol, pIdx);
if( bRev ){
pLevel->iMem = ++pParse->nMem;
*/
int start;
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
** and leave the values of those terms on the stack.
codeAllEqualityTerms(pParse, pLevel, &wc, notReady);
nxt = pLevel->nxt;
- /* 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);
+ if( (obflag==ORDERBY_MIN)
+ && (pLevel->flags&WHERE_ORDERBY)
+ && (pIdx->nColumn>nEq)
+ && (pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq])
+ ){
+ 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.
** Then generate code (2) that jumps to "nxt" after the cursor is past
** iteration of the scan to see if the scan has finished. */
if( bRev ){
/* 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);
sqlite3VdbeAddOp2(v, OP_IdxLT, iIdxCur, nxt);
pLevel->op = OP_Prev;
}else{
/* 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);
sqlite3VdbeAddOp4(v, OP_IdxGE, iIdxCur, nxt, 0, "+", P4_STATIC);
pLevel->op = OP_Next;
# This file implements regression tests for SQLite library. The
# 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]
source $testdir/tester.tcl
count {
SELECT min(a) FROM collate4t1;
}
-} {10 2}
+} {10 1}
do_test collate4-4.4 {
count {
SELECT max(a) FROM collate4t1;
}
-} {20 1}
+} {20 0}
do_test collate4-4.5 {
# Test that the index with collation type NUMERIC is not used.
execsql {
# aggregate min() and max() functions and which are handled as
# 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]
source $testdir/tester.tcl
} {1}
do_test minmax-1.6 {
set sqlite_search_count
-} {2}
+} {1}
do_test minmax-1.7 {
set sqlite_search_count 0
execsql {SELECT max(x) FROM t1}
} {20}
do_test minmax-1.8 {
set sqlite_search_count
-} {1}
+} {0}
do_test minmax-1.9 {
set sqlite_search_count 0
execsql {SELECT max(y) FROM t1}
# optimization works right in the presence of descending
# 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]
source $testdir/tester.tcl
} {1}
do_test minmax2-1.6 {
set sqlite_search_count
-} {2}
+} {1}
do_test minmax2-1.7 {
set sqlite_search_count 0
execsql {SELECT max(x) FROM t1}
} {20}
do_test minmax2-1.8 {
set sqlite_search_count
-} {1}
+} {0}
do_test minmax2-1.9 {
set sqlite_search_count 0
execsql {SELECT max(y) FROM t1}
--- /dev/null
+# 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
+