diff --git a/manifest b/manifest index 2d40caee72..f80e6ba02c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sname\sof\sthe\ssanity_check\sPRAGMA\sto\s"integrity_check"\sand\smake\nit\savailable\son\sall\scompiles.\s(CVS\s381) -D 2002-02-19T13:39:21 +C Optimize\ssimple\smin()\sand\smax()\squeries.\s(CVS\s382) +D 2002-02-19T15:00:07 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -19,8 +19,8 @@ F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1 F publish.sh 5b59f4aff037aafa0e4a3b6fa599495dbd73f360 F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 -F src/btree.c 715209d8fff117d2e3024134c5b626f440fb0395 -F src/btree.h 9fd16f33b9f2efe733fecff7ca000d2950a74d9f +F src/btree.c 8256ea416850fbd2af0ae50779b1a4b604de13c2 +F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/build.c 088acf87a92b00edda1206ccafac3518660b1b3b F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a @@ -36,7 +36,7 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 F src/parse.y b82278917959eefd05bd08c90e07a4fa5917ea51 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c d2bbaf4cba97b4c40503d0dc47e8b729e7088e0b +F src/select.c 6dadbd3ba1e86334c9af321b48f6e3df1992f602 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c @@ -50,8 +50,8 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/tokenize.c 9e98f94469694a763992860596137e78dbae0cc0 F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6 -F src/vdbe.c 3c788341b581c9381ab2ecd4868041ed57ff27ad -F src/vdbe.h 20a662572485329cb0a6b648bf7c8cb6baec401d +F src/vdbe.c 2b03a7d39f05321e5570373cb2f3734c3cf86b70 +F src/vdbe.h b4d35e159fbb80a74728b4a96e5b789fffce6f57 F src/where.c f79bc3179379b46b131a67ab10713779368dceee F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 @@ -124,7 +124,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 72c5a92aa6e3ae712af152cff8d1dc5b997b538e -R 50515068ff6057399f03cf57fa91cbb8 +P c6e9048e66c8d8e2d5f6c62aa724eef3e9d9f572 +R 41f437c3e65a91e5a2a52b6609362036 U drh -Z 763c5eb79aff590e79bd0703f9429a9c +Z ee034e966e6d17b9655294654ff0765f diff --git a/manifest.uuid b/manifest.uuid index f1345d0d14..a91c486c4e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c6e9048e66c8d8e2d5f6c62aa724eef3e9d9f572 \ No newline at end of file +cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 8831ad4789..b47d838305 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.53 2002/02/19 13:39:22 drh Exp $ +** $Id: btree.c,v 1.54 2002/02/19 15:00:07 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1325,6 +1325,30 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ return rc; } +/* Move the cursor to the last entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty and there is no first element. +*/ +int sqliteBtreeLast(BtCursor *pCur, int *pRes){ + int rc; + Pgno pgno; + if( pCur->pPage==0 ) return SQLITE_ABORT; + rc = moveToRoot(pCur); + if( rc ) return rc; + if( pCur->pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + *pRes = 0; + while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){ + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + pCur->idx = pCur->pPage->nCell-1; + pCur->bSkipNext = 0; + return rc; +} + /* Move the cursor so that it points to an entry near pKey. ** Return a success code. ** diff --git a/src/btree.h b/src/btree.h index 2c63f5d544..351aa28ecb 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.23 2002/02/19 13:39:22 drh Exp $ +** @(#) $Id: btree.h,v 1.24 2002/02/19 15:00:08 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -43,6 +43,7 @@ int sqliteBtreeDelete(BtCursor*); int sqliteBtreeInsert(BtCursor*, const void *pKey, int nKey, const void *pData, int nData); int sqliteBtreeFirst(BtCursor*, int *pRes); +int sqliteBtreeLast(BtCursor*, int *pRes); int sqliteBtreeNext(BtCursor*, int *pRes); int sqliteBtreeKeySize(BtCursor*, int *pSize); int sqliteBtreeKey(BtCursor*, int offset, int amt, char *zBuf); diff --git a/src/select.c b/src/select.c index 866c63f9b5..036c35ec54 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.61 2002/02/18 03:21:46 drh Exp $ +** $Id: select.c,v 1.62 2002/02/19 15:00:08 drh Exp $ */ #include "sqliteInt.h" @@ -710,6 +710,102 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ return 0; } +/* +** 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 +** satisfied using a single seek to the beginning or end of an index, +** then generate the code for this SELECT return 1. If this is not a +** simple min() or max() query, then return 0; +** +** A simply min() or max() query looks like this: +** +** SELECT min(a) FROM table; +** SELECT max(a) FROM table; +** +** The query may have only a single table in its FROM argument. There +** can be no GROUP BY or HAVING or WHERE clauses. The result set must +** be the min() or max() of a single column of the table. The column +** in the min() or max() function must be indexed. +** +** The parameters to this routine are the same as for sqliteSelect(). +** See the header comment on that routine for additional information. +*/ +static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ + Expr *pExpr; + int iCol; + Table *pTab; + Index *pIdx; + int base; + Vdbe *v; + int openOp; + int seekOp; + int cont; + ExprList eList; + struct ExprList_item eListItem; + + /* Check to see if this query is a simple min() or max() query. Return + ** zero if it is not. + */ + if( p->pGroupBy || p->pHaving || p->pWhere ) return 0; + if( p->pSrc->nId!=1 ) return 0; + if( p->pEList->nExpr!=1 ) return 0; + pExpr = p->pEList->a[0].pExpr; + if( pExpr->op!=TK_AGG_FUNCTION ) return 0; + if( pExpr->pList==0 || pExpr->pList->nExpr!=1 ) return 0; + if( pExpr->iColumn!=FN_Min && pExpr->iColumn!=FN_Max ) return 0; + seekOp = pExpr->iColumn==FN_Min ? OP_Rewind : OP_Last; + pExpr = pExpr->pList->a[0].pExpr; + if( pExpr->op!=TK_COLUMN ) return 0; + iCol = pExpr->iColumn; + pTab = p->pSrc->a[0].pTab; + + /* If we get to here, it means the query is of the correct form. + ** Check to make sure we have an index. + */ + if( iCol<0 ){ + pIdx = 0; + }else{ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nColumn>=1 ); + if( pIdx->aiColumn[0]==iCol ) break; + } + if( pIdx==0 ) return 0; + } + + /* Identify column names if we will be using in the callback. This + ** step is skipped if the output is going to a table or a memory cell. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) return 0; + if( eDest==SRT_Callback ){ + generateColumnNames(pParse, p->pSrc, p->pEList); + } + + /* Begin generating code + */ + base = pParse->nTab; + eList.nExpr = 1; + memset(&eListItem, 0, sizeof(eListItem)); + eList.a = &eListItem; + eList.a[0].pExpr = pExpr; + openOp = pTab->isTemp ? OP_OpenAux : OP_Open; + sqliteVdbeAddOp(v, openOp, base, pTab->tnum); + if( pIdx==0 ){ + sqliteVdbeAddOp(v, seekOp, base, 0); + }else{ + sqliteVdbeAddOp(v, openOp, base+1, pIdx->tnum); + sqliteVdbeAddOp(v, seekOp, base+1, 0); + sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0); + sqliteVdbeAddOp(v, OP_Close, base+1, 0); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0); + } + cont = sqliteVdbeMakeLabel(v); + selectInnerLoop(pParse, &eList, base, 1, 0, -1, eDest, iParm, cont, cont); + sqliteVdbeResolveLabel(v, cont); + sqliteVdbeAddOp(v, OP_Close, base, 0); + return 1; +} + /* ** Generate code for the given SELECT statement. ** @@ -911,6 +1007,13 @@ int sqliteSelect( } } + /* Check for the special case of a min() or max() function by itself + ** in the result set. + */ + if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){ + goto select_end; + } + /* Begin generating code. */ v = sqliteGetVdbe(pParse); diff --git a/src/vdbe.c b/src/vdbe.c index a356a15180..788c5a9d22 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.118 2002/02/19 13:39:23 drh Exp $ +** $Id: vdbe.c,v 1.119 2002/02/19 15:00:08 drh Exp $ */ #include "sqliteInt.h" #include @@ -866,29 +866,30 @@ static char *zOpName[] = { 0, "Close", "MoveTo", "NewRecno", "PutIntKey", "PutStrKey", "Distinct", "Found", "NotFound", "IsUnique", "NotExists", "Delete", "Column", - "KeyAsData", "Recno", "FullKey", "Rewind", - "Next", "Destroy", "Clear", "CreateIndex", - "CreateTable", "IntegrityCk", "IdxPut", "IdxDelete", - "IdxRecno", "IdxGT", "IdxGE", "MemLoad", - "MemStore", "ListWrite", "ListRewind", "ListRead", - "ListReset", "SortPut", "SortMakeRec", "SortMakeKey", - "Sort", "SortNext", "SortCallback", "SortReset", - "FileOpen", "FileRead", "FileColumn", "AggReset", - "AggFocus", "AggIncr", "AggNext", "AggSet", - "AggGet", "SetInsert", "SetFound", "SetNotFound", - "MakeRecord", "MakeKey", "MakeIdxKey", "IncrKey", - "Goto", "If", "Halt", "ColumnCount", - "ColumnName", "Callback", "NullCallback", "Integer", - "String", "Pop", "Dup", "Pull", - "Push", "MustBeInt", "Add", "AddImm", - "Subtract", "Multiply", "Divide", "Remainder", - "BitAnd", "BitOr", "BitNot", "ShiftLeft", - "ShiftRight", "AbsValue", "Precision", "Min", - "Max", "Like", "Glob", "Eq", - "Ne", "Lt", "Le", "Gt", - "Ge", "IsNull", "NotNull", "Negative", - "And", "Or", "Not", "Concat", - "Noop", "Strlen", "Substr", "Limit", + "KeyAsData", "Recno", "FullKey", "Last", + "Rewind", "Next", "Destroy", "Clear", + "CreateIndex", "CreateTable", "IntegrityCk", "IdxPut", + "IdxDelete", "IdxRecno", "IdxGT", "IdxGE", + "MemLoad", "MemStore", "ListWrite", "ListRewind", + "ListRead", "ListReset", "SortPut", "SortMakeRec", + "SortMakeKey", "Sort", "SortNext", "SortCallback", + "SortReset", "FileOpen", "FileRead", "FileColumn", + "AggReset", "AggFocus", "AggIncr", "AggNext", + "AggSet", "AggGet", "SetInsert", "SetFound", + "SetNotFound", "MakeRecord", "MakeKey", "MakeIdxKey", + "IncrKey", "Goto", "If", "Halt", + "ColumnCount", "ColumnName", "Callback", "NullCallback", + "Integer", "String", "Pop", "Dup", + "Pull", "Push", "MustBeInt", "Add", + "AddImm", "Subtract", "Multiply", "Divide", + "Remainder", "BitAnd", "BitOr", "BitNot", + "ShiftLeft", "ShiftRight", "AbsValue", "Precision", + "Min", "Max", "Like", "Glob", + "Eq", "Ne", "Lt", "Le", + "Gt", "Ge", "IsNull", "NotNull", + "Negative", "And", "Or", "Not", + "Concat", "Noop", "Strlen", "Substr", + "Limit", }; /* @@ -3096,7 +3097,11 @@ case OP_Column: { /* Figure out how many bytes in the column data and where the column ** data begins. */ - if( payloadSize<256 ){ + if( payloadSize==0 ){ + aStack[tos].flags = STK_Null; + p->tos = tos; + break; + }else if( payloadSize<256 ){ idxWidth = 1; }else if( payloadSize<65536 ){ idxWidth = 2; @@ -3216,6 +3221,29 @@ case OP_FullKey: { break; } +/* Opcode: Last P1 P2 * +** +** The next use of the Recno or Column or Next instruction for P1 +** will refer to the last entry in the database table or index. +** If the table or index is empty and P2>0, then jump immediately to P2. +** If P2 is 0 or if the table or index is not empty, fall through +** to the following instruction. +*/ +case OP_Last: { + int i = pOp->p1; + BtCursor *pCrsr; + + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + int res; + sqliteBtreeLast(pCrsr, &res); + p->aCsr[i].atFirst = res==0; + if( res && pOp->p2>0 ){ + pc = pOp->p2 - 1; + } + } + break; +} + /* Opcode: Rewind P1 P2 * ** ** The next use of the Recno or Column or Next instruction for P1 diff --git a/src/vdbe.h b/src/vdbe.h index 7936b13e40..2f10d591f8 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.42 2002/02/19 13:39:23 drh Exp $ +** $Id: vdbe.h,v 1.43 2002/02/19 15:00:08 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -97,112 +97,113 @@ typedef struct VdbeOp VdbeOp; #define OP_KeyAsData 25 #define OP_Recno 26 #define OP_FullKey 27 -#define OP_Rewind 28 -#define OP_Next 29 +#define OP_Last 28 +#define OP_Rewind 29 +#define OP_Next 30 -#define OP_Destroy 30 -#define OP_Clear 31 -#define OP_CreateIndex 32 -#define OP_CreateTable 33 -#define OP_IntegrityCk 34 +#define OP_Destroy 31 +#define OP_Clear 32 +#define OP_CreateIndex 33 +#define OP_CreateTable 34 +#define OP_IntegrityCk 35 -#define OP_IdxPut 35 -#define OP_IdxDelete 36 -#define OP_IdxRecno 37 -#define OP_IdxGT 38 -#define OP_IdxGE 39 +#define OP_IdxPut 36 +#define OP_IdxDelete 37 +#define OP_IdxRecno 38 +#define OP_IdxGT 39 +#define OP_IdxGE 40 -#define OP_MemLoad 40 -#define OP_MemStore 41 +#define OP_MemLoad 41 +#define OP_MemStore 42 -#define OP_ListWrite 42 -#define OP_ListRewind 43 -#define OP_ListRead 44 -#define OP_ListReset 45 +#define OP_ListWrite 43 +#define OP_ListRewind 44 +#define OP_ListRead 45 +#define OP_ListReset 46 -#define OP_SortPut 46 -#define OP_SortMakeRec 47 -#define OP_SortMakeKey 48 -#define OP_Sort 49 -#define OP_SortNext 50 -#define OP_SortCallback 51 -#define OP_SortReset 52 +#define OP_SortPut 47 +#define OP_SortMakeRec 48 +#define OP_SortMakeKey 49 +#define OP_Sort 50 +#define OP_SortNext 51 +#define OP_SortCallback 52 +#define OP_SortReset 53 -#define OP_FileOpen 53 -#define OP_FileRead 54 -#define OP_FileColumn 55 +#define OP_FileOpen 54 +#define OP_FileRead 55 +#define OP_FileColumn 56 -#define OP_AggReset 56 -#define OP_AggFocus 57 -#define OP_AggIncr 58 -#define OP_AggNext 59 -#define OP_AggSet 60 -#define OP_AggGet 61 +#define OP_AggReset 57 +#define OP_AggFocus 58 +#define OP_AggIncr 59 +#define OP_AggNext 60 +#define OP_AggSet 61 +#define OP_AggGet 62 -#define OP_SetInsert 62 -#define OP_SetFound 63 -#define OP_SetNotFound 64 +#define OP_SetInsert 63 +#define OP_SetFound 64 +#define OP_SetNotFound 65 -#define OP_MakeRecord 65 -#define OP_MakeKey 66 -#define OP_MakeIdxKey 67 -#define OP_IncrKey 68 +#define OP_MakeRecord 66 +#define OP_MakeKey 67 +#define OP_MakeIdxKey 68 +#define OP_IncrKey 69 -#define OP_Goto 69 -#define OP_If 70 -#define OP_Halt 71 +#define OP_Goto 70 +#define OP_If 71 +#define OP_Halt 72 -#define OP_ColumnCount 72 -#define OP_ColumnName 73 -#define OP_Callback 74 -#define OP_NullCallback 75 +#define OP_ColumnCount 73 +#define OP_ColumnName 74 +#define OP_Callback 75 +#define OP_NullCallback 76 -#define OP_Integer 76 -#define OP_String 77 -#define OP_Pop 78 -#define OP_Dup 79 -#define OP_Pull 80 -#define OP_Push 81 -#define OP_MustBeInt 82 +#define OP_Integer 77 +#define OP_String 78 +#define OP_Pop 79 +#define OP_Dup 80 +#define OP_Pull 81 +#define OP_Push 82 +#define OP_MustBeInt 83 -#define OP_Add 83 -#define OP_AddImm 84 -#define OP_Subtract 85 -#define OP_Multiply 86 -#define OP_Divide 87 -#define OP_Remainder 88 -#define OP_BitAnd 89 -#define OP_BitOr 90 -#define OP_BitNot 91 -#define OP_ShiftLeft 92 -#define OP_ShiftRight 93 -#define OP_AbsValue 94 -#define OP_Precision 95 -#define OP_Min 96 -#define OP_Max 97 -#define OP_Like 98 -#define OP_Glob 99 -#define OP_Eq 100 -#define OP_Ne 101 -#define OP_Lt 102 -#define OP_Le 103 -#define OP_Gt 104 -#define OP_Ge 105 -#define OP_IsNull 106 -#define OP_NotNull 107 -#define OP_Negative 108 -#define OP_And 109 -#define OP_Or 110 -#define OP_Not 111 -#define OP_Concat 112 -#define OP_Noop 113 +#define OP_Add 84 +#define OP_AddImm 85 +#define OP_Subtract 86 +#define OP_Multiply 87 +#define OP_Divide 88 +#define OP_Remainder 89 +#define OP_BitAnd 90 +#define OP_BitOr 91 +#define OP_BitNot 92 +#define OP_ShiftLeft 93 +#define OP_ShiftRight 94 +#define OP_AbsValue 95 +#define OP_Precision 96 +#define OP_Min 97 +#define OP_Max 98 +#define OP_Like 99 +#define OP_Glob 100 +#define OP_Eq 101 +#define OP_Ne 102 +#define OP_Lt 103 +#define OP_Le 104 +#define OP_Gt 105 +#define OP_Ge 106 +#define OP_IsNull 107 +#define OP_NotNull 108 +#define OP_Negative 109 +#define OP_And 110 +#define OP_Or 111 +#define OP_Not 112 +#define OP_Concat 113 +#define OP_Noop 114 -#define OP_Strlen 114 -#define OP_Substr 115 +#define OP_Strlen 115 +#define OP_Substr 116 -#define OP_Limit 116 +#define OP_Limit 117 -#define OP_MAX 116 +#define OP_MAX 117 /* ** Prototypes for the VDBE interface. See comments on the implementation