-C Changes\sto\sreduce\sthe\samount\sof\sstack\sspace\srequired.\s(CVS\s2661)
-D 2005-09-06T21:40:45
+C Rewrite\sthe\saggregate\shandling\slogic\sso\sthat\sit\sruns\sin\sO(1)\sspace.\nThis\sis\sthe\sfirst\scut\sat\sthe\scode.\s\sMany\sregression\stests\sfail.\s(CVS\s2662)
+D 2005-09-07T21:22:46
F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
F src/btree.c 5b3bc015c49a41c025cfdf8ad36051f3007e2cb0
F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e
-F src/build.c d61682e8d0368fbc6ff230cd4b9bd41659d5634b
+F src/build.c d9f3c0e65ada1087da21b524f6ef685e4d1a1725
F src/callback.c 9a1162c8f9dae9fad6d548339669aacb5f6cf76b
F src/complete.c 4de937dfdd4c79a501772ab2035b26082f337a79
F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940
F src/delete.c be1fc25c9e109cd8cbab42a43ee696263da7c04b
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
-F src/expr.c 8a72157fa6842e84819a8c80521be02ec471180c
+F src/expr.c e0a3f275586bb076cc5995885a328b531b5aecf2
F src/func.c 713cf33a0ab8685d44ed31a9c753983a7ff9fd6e
F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
F src/insert.c 484c73bc1309f283a31baa0e114f3ee980536397
F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b
-F src/main.c 8bcd1d2ed92dcb24bafb770eae6e4afce8ddcbde
+F src/main.c bf88855445d365b497070d85e3faa0579a9edb91
F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070
F src/os.h c4b34bd4d6fea51a420f337468b907f4edecb161
F src/os_common.h 0e7f428ba0a6c40a61bc56c4e96f493231301b73
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c cd9896287a8fd33cc267bd0c2b69c421a4808169
F src/pager.h 17b13225abd93c1e9f470060f40a21b9edb5a164
-F src/parse.y d57cdd2adc0923762b40314f08683c836a2e0c90
+F src/parse.y 4c0cf6b0646166b232693249b89e32a75c6f87d7
F src/pragma.c 69413fbdc0c6aaa493a776ea52c1b3e6cf35dfb2
F src/prepare.c 86f0d8e744b8d956eff6bc40e29049efee017610
F src/printf.c c01e9ad473d79463fb1f483b1eca5c3cbed2a4e5
F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
-F src/select.c 79bd7f97345ee4291e7f4d469307b185eb2cba8f
+F src/select.c 2f965220652f5417a70720ea3bf7703bf432fb6b
F src/shell.c b21daba017b8feef2fdc65ecde57f70209494217
F src/sqlite.h.in d6561d51025d08de4f455607f3f9f9aa76e855d5
-F src/sqliteInt.h 845ff6f8019f80baafb1bdbb8ef80fcd04d9d0f9
+F src/sqliteInt.h 97d7d13bfcccd67974b0db9c19fce4428ae9d236
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
F src/test1.c b569b60e35f0e3ea20e5ebfaf6e522a01c08d481
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
F src/util.c 5650f6fe5ee30e0678985ad7b94da91e3f85752b
F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
-F src/vdbe.c 5f0ed87252912fa1d4c989b0c6d7e3d4d2bb337a
-F src/vdbe.h 3b29a9af6c7a64ed692bef1fc5f61338f40d2f67
-F src/vdbeInt.h 7a6b3c1adfa7b23c1f4f15ce0549b5b52a85a635
+F src/vdbe.c e774761b4566540e0007ec3d6ff24ed7a89f51c9
+F src/vdbe.h c8e105979fc7aaf5b8004e9621904e3bd096dfa2
+F src/vdbeInt.h 15a32128a8173c724a90829b90af495b370c09cc
F src/vdbeapi.c 46e2fd47e2ce3c1aea9bb48bbbac31a1dc75a6ff
-F src/vdbeaux.c 2cfc66b30be5e293bd72db8084f8cb5c865e8b01
+F src/vdbeaux.c 11db0de973c850bb5d92c67af1c8f3c189f08741
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
-F src/vdbemem.c b6ae3ac842a6759bd8ec4eb1cd428520b5eafc57
+F src/vdbemem.c 3cb63f021ac5ed102d80ec888d6f76fc0dcac153
F src/where.c 92ab208abe6bec15e81616b8c1a619be23ece506
F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
F test/misc1.test 4ca69ca2e2ef33c7a0b0fc8b324111e37a522d29
F test/misc2.test 5c699af2fede2694736a9f45aea7e2f052686e15
F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03
-F test/misc4.test edd3e3adf5b6e3b995b29843565ca58dd602f9a7
+F test/misc4.test 8a28f046bac8121dc2fe623ccb75578cd51b9db0
F test/misc5.test 24bd03404039ec727028ac9cf7fd9066fd209ec9
F test/misuse.test 1c7fee3c4c0cb4008717ecccf5c72281fac0008e
F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 7ecf3654aa9a275a4cf0c3ec5f63a8c1e0a11fc9
-R a30f5538e6a4b7f1c84ee615fff6a374
+P b86bd70f301205d6ca66475a425e157b976107e2
+R 47d4cda0a17a577f38d2fea59c44e4f5
U drh
-Z 6a9878dbd0fbab628ba267b019a75820
+Z e6ce677b69437ad913acfe1b1ebf26c2
-b86bd70f301205d6ca66475a425e157b976107e2
\ No newline at end of file
+17039ec3ff4396862beedf4a8af89654b2140f58
\ No newline at end of file
** COMMIT
** ROLLBACK
**
-** $Id: build.c,v 1.344 2005/08/31 13:13:31 drh Exp $
+** $Id: build.c,v 1.345 2005/09/07 21:22:46 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
sqlite3VdbeTrace(v, trace);
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
- pParse->nTab+3, pParse->nMaxDepth+1, pParse->explain);
+ pParse->nTab+3, pParse->explain);
pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0;
}else if( pParse->rc==SQLITE_OK ){
sqlite3SrcListDelete(pName);
}
+/*
+** ppArray points into a structure where there is an array pointer
+** followed by two integers. The first integer is the
+** number of elements in the structure array. The second integer
+** is the number of allocated slots in the array.
+**
+** In other words, the structure looks something like this:
+**
+** struct Example1 {
+** struct subElem *aEntry;
+** int nEntry;
+** int nAlloc;
+** }
+**
+** The pnEntry parameter points to the equivalent of Example1.nEntry.
+**
+** This routine allocates a new slot in the array, zeros it out,
+** and returns its index. If malloc fails a negative number is returned.
+**
+** szEntry is the sizeof of a single array entry. initSize is the
+** number of array entries allocated on the initial allocation.
+*/
+int sqlite3ArrayAllocate(void **ppArray, int szEntry, int initSize){
+ char *p;
+ int *an = (int*)&ppArray[1];
+ if( an[0]>=an[1] ){
+ void *pNew;
+ an[1] = an[1]*2 + initSize;
+ pNew = sqliteRealloc(*ppArray, an[1]*szEntry);
+ if( pNew==0 ){
+ return -1;
+ }
+ *ppArray = pNew;
+ }
+ p = *ppArray;
+ memset(&p[an[0]*szEntry], 0, szEntry);
+ return an[0]++;
+}
+
/*
** Append a new element to the given IdList. Create a new IdList if
** need be.
** A new IdList is returned, or NULL if malloc() fails.
*/
IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){
+ int i;
if( pList==0 ){
pList = sqliteMalloc( sizeof(IdList) );
if( pList==0 ) return 0;
pList->nAlloc = 0;
}
- if( pList->nId>=pList->nAlloc ){
- struct IdList_item *a;
- pList->nAlloc = pList->nAlloc*2 + 5;
- a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]) );
- if( a==0 ){
- sqlite3IdListDelete(pList);
- return 0;
- }
- pList->a = a;
+ i = sqlite3ArrayAllocate((void**)&pList->a, sizeof(pList->a[0]), 5);
+ if( i<0 ){
+ sqlite3IdListDelete(pList);
+ return 0;
}
- memset(&pList->a[pList->nId], 0, sizeof(pList->a[0]));
- pList->a[pList->nId].zName = sqlite3NameFromToken(pToken);
- pList->nId++;
+ pList->a[i].zName = sqlite3NameFromToken(pToken);
return pList;
}
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.224 2005/09/05 20:06:49 drh Exp $
+** $Id: expr.c,v 1.225 2005/09/07 21:22:46 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
case TK_COLUMN:
case TK_DOT:
case TK_AGG_FUNCTION:
+ case TK_AGG_COLUMN:
#ifndef SQLITE_OMIT_SUBQUERY
case TK_SELECT:
case TK_EXISTS:
NameContext *pNC, /* Namespace to resolve expressions in. */
Expr *pExpr /* The expression to be analyzed. */
){
+ int savedHasAgg;
if( pExpr==0 ) return 0;
+ savedHasAgg = pNC->hasAgg;
+ pNC->hasAgg = 0;
walkExprTree(pExpr, nameResolverStep, pNC);
if( pNC->nErr>0 ){
ExprSetProperty(pExpr, EP_Error);
}
+ if( pNC->hasAgg ){
+ ExprSetProperty(pExpr, EP_Agg);
+ }else if( savedHasAgg ){
+ pNC->hasAgg = 1;
+ }
return ExprHasProperty(pExpr, EP_Error);
}
sqlite3VdbeAddOp(v, OP_MemStore, mem, 1);
}
- if( pExpr->pSelect ){
- sqlite3VdbeAddOp(v, OP_AggContextPush, 0, 0);
- }
-
switch( pExpr->op ){
case TK_IN: {
char affinity;
}
}
- if( pExpr->pSelect ){
- sqlite3VdbeAddOp(v, OP_AggContextPop, 0, 0);
- }
if( testAddr ){
sqlite3VdbeChangeP2(v, testAddr, sqlite3VdbeCurrentAddr(v));
}
}
op = pExpr->op;
switch( op ){
+ case TK_AGG_COLUMN: {
+ AggInfo *pAggInfo = pExpr->pAggInfo;
+ struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
+ if( !pAggInfo->directMode ){
+ sqlite3VdbeAddOp(v, OP_MemLoad, pCol->iMem, 0);
+ break;
+ }else if( pAggInfo->useSortingIdx ){
+ sqlite3VdbeAddOp(v, OP_Column, pAggInfo->sortingIdx,
+ pCol->iSorterColumn);
+ break;
+ }
+ /* Otherwise, fall thru into the TK_COLUMN case */
+ }
case TK_COLUMN: {
- if( !pParse->fillAgg && pExpr->iAgg>=0 ){
- sqlite3VdbeAddOp(v, OP_AggGet, pExpr->iAggCtx, pExpr->iAgg);
- }else if( pExpr->iColumn>=0 ){
+ if( pExpr->iColumn>=0 ){
sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn);
sqlite3ColumnDefault(v, pExpr->pTab, pExpr->iColumn);
}else{
break;
}
case TK_AGG_FUNCTION: {
- sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg);
+ AggInfo *pInfo = pExpr->pAggInfo;
+ sqlite3VdbeAddOp(v, OP_MemLoad, pInfo->aFunc[pExpr->iAgg].iMem, 0);
break;
}
case TK_CONST_FUNC:
FuncDef *pDef;
int nId;
const char *zId;
- int p2 = 0;
+ int constMask = 0;
int i;
u8 enc = pParse->db->enc;
CollSeq *pColl = 0;
nExpr = sqlite3ExprCodeExprList(pParse, pList);
for(i=0; i<nExpr && i<32; i++){
if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){
- p2 |= (1<<i);
+ constMask |= (1<<i);
}
if( pDef->needCollSeq && !pColl ){
pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
if( !pColl ) pColl = pParse->db->pDfltColl;
sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
}
- sqlite3VdbeOp3(v, OP_Function, nExpr, p2, (char*)pDef, P3_FUNCDEF);
+ sqlite3VdbeOp3(v, OP_Function, constMask, nExpr, (char*)pDef, P3_FUNCDEF);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
return 1;
}
+
/*
-** Add a new element to the pParse->aAgg[] array and return its index.
-** The new element is initialized to zero. The calling function is
-** expected to fill it in.
+** Add a new element to the pAggInfo->aCol[] array. Return the index of
+** the new element. Return a negative number if malloc fails.
*/
-static int appendAggInfo(Parse *pParse){
- if( (pParse->nAgg & 0x7)==0 ){
- int amt = pParse->nAgg + 8;
- AggExpr *aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0]));
- if( aAgg==0 ){
- return -1;
- }
- pParse->aAgg = aAgg;
+static int addAggInfoColumn(AggInfo *pInfo){
+ int i;
+ i = sqlite3ArrayAllocate((void**)&pInfo->aCol, sizeof(pInfo->aCol[0]), 3);
+ if( i<0 ){
+ return -1;
}
- memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0]));
- return pParse->nAgg++;
-}
+ return i;
+}
+
+/*
+** Add a new element to the pAggInfo->aFunc[] array. Return the index of
+** the new element. Return a negative number if malloc fails.
+*/
+static int addAggInfoFunc(AggInfo *pInfo){
+ int i;
+ i = sqlite3ArrayAllocate((void**)&pInfo->aFunc, sizeof(pInfo->aFunc[0]), 2);
+ if( i<0 ){
+ return -1;
+ }
+ return i;
+}
/*
** This is an xFunc for walkExprTree() used to implement
*/
static int analyzeAggregate(void *pArg, Expr *pExpr){
int i;
- AggExpr *pAgg;
NameContext *pNC = (NameContext *)pArg;
Parse *pParse = pNC->pParse;
SrcList *pSrcList = pNC->pSrcList;
+ AggInfo *pAggInfo = pNC->pAggInfo;
+
switch( pExpr->op ){
case TK_COLUMN: {
- for(i=0; pSrcList && i<pSrcList->nSrc; i++){
- if( pExpr->iTable==pSrcList->a[i].iCursor ){
- pAgg = pParse->aAgg;
- for(i=0; i<pParse->nAgg; i++, pAgg++){
- Expr *pE;
- if( pAgg->isAgg ) continue;
- pE = pAgg->pExpr;
- if( pE->iTable==pExpr->iTable && pE->iColumn==pExpr->iColumn ){
- break;
+ /* Check to see if the column is in one of the tables in the FROM
+ ** clause of the aggregate query */
+ if( pSrcList ){
+ struct SrcList_item *pItem = pSrcList->a;
+ for(i=0; i<pSrcList->nSrc; i++, pItem++){
+ struct AggInfo_col *pCol;
+ if( pExpr->iTable==pItem->iCursor ){
+ /* If we reach this point, it means that pExpr refers to a table
+ ** that is in the FROM clause of the aggregate query.
+ **
+ ** Make an entry for the column in pAggInfo->aCol[] if there
+ ** is not an entry there already.
+ */
+ pCol = pAggInfo->aCol;
+ for(i=0; i<pAggInfo->nColumn; i++, pCol++){
+ if( pCol->iTable==pExpr->iTable &&
+ pCol->iColumn==pExpr->iColumn ){
+ break;
+ }
}
- }
- if( i>=pParse->nAgg ){
- i = appendAggInfo(pParse);
- if( i<0 ) return 1;
- pAgg = &pParse->aAgg[i];
- pAgg->isAgg = 0;
- pAgg->pExpr = pExpr;
- }
- pExpr->iAgg = i;
- pExpr->iAggCtx = pNC->nDepth;
- return 1;
- }
+ if( i>=pAggInfo->nColumn && (i = addAggInfoColumn(pAggInfo))>=0 ){
+ pCol = &pAggInfo->aCol[i];
+ pCol->iTable = pExpr->iTable;
+ pCol->iColumn = pExpr->iColumn;
+ pCol->iMem = pParse->nMem++;
+ pCol->iSorterColumn = -1;
+ if( pAggInfo->pGroupBy ){
+ int j, n;
+ ExprList *pGB = pAggInfo->pGroupBy;
+ struct ExprList_item *pTerm = pGB->a;
+ n = pGB->nExpr;
+ for(j=0; j<n; j++, pTerm++){
+ Expr *pE = pTerm->pExpr;
+ if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable &&
+ pE->iColumn==pExpr->iColumn ){
+ pCol->iSorterColumn = j;
+ break;
+ }
+ }
+ }
+ if( pCol->iSorterColumn<0 ){
+ pCol->iSorterColumn = pAggInfo->nSortingColumn++;
+ }
+ }
+ /* There is now an entry for pExpr in pAggInfo->aCol[] (either
+ ** because it was there before or because we just created it).
+ ** Convert the pExpr to be a TK_AGG_COLUMN referring to that
+ ** pAggInfo->aCol[] entry.
+ */
+ pExpr->pAggInfo = pAggInfo;
+ pExpr->op = TK_AGG_COLUMN;
+ pExpr->iAgg = i;
+ break;
+ } /* endif pExpr->iTable==pItem->iCursor */
+ } /* end loop over pSrcList */
}
return 1;
}
case TK_AGG_FUNCTION: {
+ /* The pNC->nDepth==0 test causes aggregate functions in subqueries
+ ** to be ignored */
if( pNC->nDepth==0 ){
- pAgg = pParse->aAgg;
- for(i=0; i<pParse->nAgg; i++, pAgg++){
- if( !pAgg->isAgg ) continue;
- if( sqlite3ExprCompare(pAgg->pExpr, pExpr) ){
+ /* Check to see if pExpr is a duplicate of another aggregate
+ ** function that is already in the pAggInfo structure
+ */
+ struct AggInfo_func *pItem = pAggInfo->aFunc;
+ for(i=0; i<pAggInfo->nFunc; i++, pItem++){
+ if( sqlite3ExprCompare(pItem->pExpr, pExpr) ){
break;
}
}
- if( i>=pParse->nAgg ){
+ if( i>=pAggInfo->nFunc ){
+ /* pExpr is original. Make a new entry in pAggInfo->aFunc[]
+ */
u8 enc = pParse->db->enc;
- i = appendAggInfo(pParse);
- if( i<0 ) return 1;
- pAgg = &pParse->aAgg[i];
- pAgg->isAgg = 1;
- pAgg->pExpr = pExpr;
- pAgg->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->token.z, pExpr->token.n,
- pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+ i = addAggInfoFunc(pAggInfo);
+ if( i>=0 ){
+ pItem = &pAggInfo->aFunc[i];
+ pItem->pExpr = pExpr;
+ pItem->iMem = pParse->nMem++;
+ pItem->pFunc = sqlite3FindFunction(pParse->db,
+ pExpr->token.z, pExpr->token.n,
+ pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+ }
}
+ /* Make pExpr point to the appropriate pAggInfo->aFunc[] entry
+ */
pExpr->iAgg = i;
+ pExpr->pAggInfo = pAggInfo;
return 1;
}
}
}
+
+ /* Recursively walk subqueries looking for TK_COLUMN nodes that need
+ ** to be changed to TK_AGG_COLUMN. But increment nDepth so that
+ ** TK_AGG_FUNCTION nodes in subqueries will be unchanged.
+ */
if( pExpr->pSelect ){
pNC->nDepth++;
walkSelectExpr(pExpr->pSelect, analyzeAggregate, pNC);
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.300 2005/08/29 23:00:04 drh Exp $
+** $Id: main.c,v 1.301 2005/09/07 21:22:46 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
rc = SQLITE_OK;
}else{
rc = sqlite3VdbeReset((Vdbe*)pStmt);
- sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0, 0);
+ sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0);
}
return rc;
}
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.175 2005/07/08 12:13:05 drh Exp $
+** @(#) $Id: parse.y,v 1.176 2005/09/07 21:22:46 drh Exp $
*/
// All token codes are small integers with #defines that begin with "TK_"
// add them to the parse.h output file.
//
%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
- COLUMN AGG_FUNCTION CONST_FUNC.
+ COLUMN AGG_FUNCTION AGG_COLUMN CONST_FUNC.
// Input is a single SQL command
input ::= cmdlist.
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.260 2005/09/05 20:06:49 drh Exp $
+** $Id: select.c,v 1.261 2005/09/07 21:22:47 drh Exp $
*/
#include "sqliteInt.h"
int iBreak, /* Jump here to end the loop */
int nPop /* Number of times to pop stack when jumping */
){
- if( p->iOffset>=0 ){
+ if( p->iOffset>=0 && iContinue!=0 ){
int addr = sqlite3VdbeCurrentAddr(v) + 3;
if( nPop>0 ) addr++;
sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, 0);
sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
VdbeComment((v, "# skip OFFSET records"));
}
- if( p->iLimit>=0 ){
+ if( p->iLimit>=0 && iBreak!=0 ){
sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak);
VdbeComment((v, "# exit when LIMIT reached"));
}
}
switch( eDest ){
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/* In this mode, write each query result to the key of the temporary
** table iParm.
*/
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
case SRT_Union: {
sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT);
- sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+ if( aff ){
+ sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+ }
sqlite3VdbeAddOp(v, OP_IdxInsert, iParm, 0);
break;
}
}
}
+/*
+** The opcode at addr is an OP_OpenVirtual that created a sorting
+** index tha we ended up not needing. This routine changes that
+** opcode to OP_Noop.
+*/
+static void uncreateSortingIndex(Parse *pParse, int addr){
+ Vdbe *v = pParse->pVdbe;
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, addr);
+ sqlite3VdbeChangeP3(v, addr, 0, 0);
+ pOp->opcode = OP_Noop;
+ pOp->p1 = 0;
+ pOp->p2 = 0;
+}
+
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Return the appropriate collating sequence for the iCol-th column of
ExprList *pEList; /* Result set. */
int i; /* For-loop variable used in multiple places */
NameContext sNC; /* Local name-context */
+ ExprList *pGroupBy; /* The group by clause */
/* If this routine has run before, return immediately. */
if( p->isResolved ){
sNC.pSrcList = p->pSrc;
sNC.pNext = pOuterNC;
- /* NameContext.nDepth stores the depth of recursion for this query. For
- ** an outer query (e.g. SELECT * FROM sqlite_master) this is 1. For
- ** a subquery it is 2. For a subquery of a subquery, 3. And so on.
- ** Parse.nMaxDepth is the maximum depth for any subquery resolved so
- ** far. This is used to determine the number of aggregate contexts
- ** required at runtime.
- */
- sNC.nDepth = (pOuterNC?pOuterNC->nDepth+1:1);
- if( sNC.nDepth>pParse->nMaxDepth ){
- pParse->nMaxDepth = sNC.nDepth;
- }
-
/* Resolve names in the result set. */
pEList = p->pEList;
if( !pEList ) return SQLITE_ERROR;
** expression, do not allow aggregates in any of the other expressions.
*/
assert( !p->isAgg );
- if( p->pGroupBy || sNC.hasAgg ){
+ pGroupBy = p->pGroupBy;
+ if( pGroupBy || sNC.hasAgg ){
p->isAgg = 1;
}else{
sNC.allowAgg = 0;
/* If a HAVING clause is present, then there must be a GROUP BY clause.
*/
- if( p->pHaving && !p->pGroupBy ){
+ if( p->pHaving && !pGroupBy ){
sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
return SQLITE_ERROR;
}
if( sqlite3ExprResolveNames(&sNC, p->pWhere) ||
sqlite3ExprResolveNames(&sNC, p->pHaving) ||
processOrderGroupBy(&sNC, p->pOrderBy, "ORDER") ||
- processOrderGroupBy(&sNC, p->pGroupBy, "GROUP")
+ processOrderGroupBy(&sNC, pGroupBy, "GROUP")
){
return SQLITE_ERROR;
}
+ /* Make sure the GROUP BY clause does not contain aggregate functions.
+ */
+ if( pGroupBy ){
+ struct ExprList_item *pItem;
+
+ for(i=0, pItem=pGroupBy->a; i<pGroupBy->nExpr; i++, pItem++){
+ if( ExprHasProperty(pItem->pExpr, EP_Agg) ){
+ sqlite3ErrorMsg(pParse, "aggregate functions are not allowed in "
+ "the GROUP BY clause");
+ return SQLITE_ERROR;
+ }
+ }
+ }
+
return SQLITE_OK;
}
/*
-** An instance of the following struct is used by sqlite3Select()
-** to save aggregate related information from the Parse object
-** at the start of each call and to restore it at the end. See
-** saveAggregateInfo() and restoreAggregateInfo().
-*/
-struct AggregateInfo {
- int nAgg;
- AggExpr *aAgg;
-};
-typedef struct AggregateInfo AggregateInfo;
-
-/*
-** Copy aggregate related information from the Parse structure
-** into the AggregateInfo structure. Zero the aggregate related
-** values in the Parse struct.
+** Reset the aggregate accumulator.
+**
+** The aggregate accumulator is a set of memory cells that hold
+** intermediate results while calculating an aggregate. This
+** routine simply stores NULLs in all of those memory cells.
*/
-static void saveAggregateInfo(Parse *pParse, AggregateInfo *pInfo){
- pInfo->aAgg = pParse->aAgg;
- pInfo->nAgg = pParse->nAgg;
- pParse->aAgg = 0;
- pParse->nAgg = 0;
+static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ int addr;
+ if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){
+ return;
+ }
+ sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ for(i=0; i<pAggInfo->nColumn; i++){
+ addr = sqlite3VdbeAddOp(v, OP_MemStore, pAggInfo->aCol[i].iMem, 0);
+ }
+ for(i=0; i<pAggInfo->nFunc; i++){
+ addr = sqlite3VdbeAddOp(v, OP_MemStore, pAggInfo->aFunc[i].iMem, 0);
+ }
+ sqlite3VdbeChangeP2(v, addr, 1);
}
/*
-** Copy aggregate related information from the AggregateInfo struct
-** back into the Parse structure. The aggregate related information
-** currently stored in the Parse structure is deleted.
+** Invoke the OP_AggFinalize opcode for every aggregate function
+** in the AggInfo structure.
*/
-static void restoreAggregateInfo(Parse *pParse, AggregateInfo *pInfo){
- sqliteFree(pParse->aAgg);
- pParse->aAgg = pInfo->aAgg;
- pParse->nAgg = pInfo->nAgg;
+static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ struct AggInfo_func *pF;
+ for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
+ sqlite3VdbeAddOp(v, OP_AggFinal, pF->iMem, 0);
+ }
}
-
+
+/*
+** Update the accumulator memory cells for an aggregate based on
+** the current cursor position.
+*/
+static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ struct AggInfo_func *pF;
+ struct AggInfo_col *pC;
+ Expr fauxExpr;
+
+ pAggInfo->directMode = 1;
+ for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
+ int nArg;
+ ExprList *pList = pF->pExpr->pList;
+ if( pList ){
+ nArg = pList->nExpr;
+ sqlite3ExprCodeExprList(pParse, pList);
+ }else{
+ nArg = 0;
+ }
+ if( pF->pFunc->needCollSeq ){
+ CollSeq *pColl = 0;
+ struct ExprList_item *pItem;
+ int j;
+ for(j=0, pItem=pList->a; !pColl && j<pList->nExpr; j++, pItem++){
+ pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ }
+ sqlite3VdbeOp3(v, OP_AggStep, pF->iMem, nArg, (void*)pF->pFunc, P3_FUNCDEF);
+ }
+ memset(&fauxExpr, 0, sizeof(fauxExpr));
+ fauxExpr.op = TK_AGG_COLUMN;
+ fauxExpr.pAggInfo = pAggInfo;
+ for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
+ fauxExpr.iAgg = i;
+ sqlite3ExprCode(pParse, &fauxExpr);
+ sqlite3VdbeAddOp(v, OP_MemStore, pC->iMem, 1);
+ }
+ pAggInfo->directMode = 0;
+}
+
+
/*
** Generate code for the given SELECT statement.
**
int *pParentAgg, /* True if pParent uses aggregate functions */
char *aff /* If eDest is SRT_Union, the affinity string */
){
- int i;
- WhereInfo *pWInfo;
- Vdbe *v;
+ int i, j; /* Loop counters */
+ WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
+ Vdbe *v; /* The virtual machine under construction */
int isAgg; /* True for select lists like "count(*)" */
ExprList *pEList; /* List of columns to extract. */
SrcList *pTabList; /* List of tables to select from */
int isDistinct; /* True if the DISTINCT keyword is present */
int distinct; /* Table to use for the distinct set */
int rc = 1; /* Value to return from this function */
- AggregateInfo sAggInfo;
+ AggInfo sAggInfo; /* Information used by aggregate queries */
if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1;
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
+ memset(&sAggInfo, 0, sizeof(sAggInfo));
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/* If there is are a sequence of queries, do the earlier ones first.
}
#endif
- saveAggregateInfo(pParse, &sAggInfo);
pOrderBy = p->pOrderBy;
- if( eDest==SRT_Union || eDest==SRT_Except || eDest==SRT_Discard ){
+ if( IgnorableOrderby(eDest) ){
p->pOrderBy = 0;
}
if( sqlite3SelectResolve(pParse, p, 0) ){
/* ORDER BY is ignored for some destinations.
*/
- switch( eDest ){
- case SRT_Union:
- case SRT_Except:
- case SRT_Discard:
- pOrderBy = 0;
- break;
- default:
- break;
+ if( IgnorableOrderby(eDest) ){
+ pOrderBy = 0;
}
/* Begin generating code.
for(i=0; i<pTabList->nSrc; i++){
const char *zSavedAuthContext = 0;
int needRestoreContext;
+ struct SrcList_item *pItem = &pTabList->a[i];
- if( pTabList->a[i].pSelect==0 ) continue;
- if( pTabList->a[i].zName!=0 ){
+ if( pItem->pSelect==0 ) continue;
+ if( pItem->zName!=0 ){
zSavedAuthContext = pParse->zAuthContext;
- pParse->zAuthContext = pTabList->a[i].zName;
+ pParse->zAuthContext = pItem->zName;
needRestoreContext = 1;
}else{
needRestoreContext = 0;
}
- sqlite3Select(pParse, pTabList->a[i].pSelect, SRT_TempTable,
- pTabList->a[i].iCursor, p, i, &isAgg, 0);
+ sqlite3Select(pParse, pItem->pSelect, SRT_TempTable,
+ pItem->iCursor, p, i, &isAgg, 0);
if( needRestoreContext ){
pParse->zAuthContext = zSavedAuthContext;
}
pTabList = p->pSrc;
pWhere = p->pWhere;
- if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){
+ if( !IgnorableOrderby(eDest) ){
pOrderBy = p->pOrderBy;
}
pGroupBy = p->pGroupBy;
sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, pEList->nExpr);
}
- /* Do an analysis of aggregate expressions.
- */
- if( isAgg || pGroupBy ){
- NameContext sNC;
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
- sNC.pSrcList = pTabList;
-
- assert( pParse->nAgg==0 );
- isAgg = 1;
- if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
- goto select_end;
- }
- if( sqlite3ExprAnalyzeAggList(&sNC, pGroupBy) ){
- goto select_end;
- }
- if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
- goto select_end;
- }
- if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
- goto select_end;
- }
- }
-
- /* Reset the aggregator
- */
- if( isAgg ){
- int addr = sqlite3VdbeAddOp(v, OP_AggReset, (pGroupBy?0:1), pParse->nAgg);
- for(i=0; i<pParse->nAgg; i++){
- FuncDef *pFunc;
- if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){
- int nExpr = 0;
-#ifdef SQLITE_SSE
- Expr *pAggExpr = pParse->aAgg[i].pExpr;
- if( pAggExpr && pAggExpr->pList ){
- nExpr = pAggExpr->pList->nExpr;
- }
-#endif
- sqlite3VdbeOp3(v, OP_AggInit, nExpr, i, (char*)pFunc, P3_FUNCDEF);
- }
- }
- if( pGroupBy ){
- KeyInfo *pKey = keyInfoFromExprList(pParse, pGroupBy);
- if( 0==pKey ){
- goto select_end;
- }
- sqlite3VdbeChangeP3(v, addr, (char *)pKey, P3_KEYINFO_HANDOFF);
- }
- }
/* Initialize the memory cell to NULL for SRT_Mem or 0 for SRT_Exists
*/
distinct = -1;
}
- /* Begin the database scan
- */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,
- pGroupBy ? 0 : &pOrderBy);
- if( pWInfo==0 ) goto select_end;
+ /* Aggregate and non-aggregate queries are handled differently */
+ if( !isAgg && pGroupBy==0 ){
+ /* This case is for non-aggregate queries
+ ** Begin the database scan
+ */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy);
+ if( pWInfo==0 ) goto select_end;
- /* Use the standard inner loop if we are not dealing with
- ** aggregates
- */
- if( !isAgg ){
+ /* Use the standard inner loop
+ */
if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
iParm, pWInfo->iContinue, pWInfo->iBreak, aff) ){
goto select_end;
}
- }
- /* If we are dealing with aggregates, then do the special aggregate
- ** processing.
- */
- else{
- AggExpr *pAgg;
- int lbl1 = 0;
- pParse->fillAgg = 1;
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+ }else{
+ /* This is the processing for aggregate queries */
+ NameContext sNC; /* Name context for processing aggregate information */
+ int iAMem; /* First Mem address for storing current GROUP BY */
+ int iBMem; /* First Mem address for previous GROUP BY */
+ int iUseFlag; /* Mem address holding flag indicating that at least
+ ** one row of the input to the aggregator has been
+ ** processed */
+ int iAbortFlag; /* Mem address which causes query abort if positive */
+ int groupBySort; /* Rows come from source in GROUP BY order */
+
+
+ /* The following variables hold addresses or labels for parts of the
+ ** virtual machine program we are putting together */
+ int addrOutputRow; /* Start of subroutine that outputs a result row */
+ int addrSetAbort; /* Set the abort flag and return */
+ int addrInitializeLoop; /* Start of code that initializes the input loop */
+ int addrTopOfLoop; /* Top of the input loop */
+ int addrGroupByChange; /* Code that runs when any GROUP BY term changes */
+ int addrProcessRow; /* Code to process a single input row */
+ int addrEnd; /* End of all processing */
+ int addrSortingIdx; /* The OP_OpenVirtual for the sorting index */
+
+ addrEnd = sqlite3VdbeMakeLabel(v);
+
+ /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
+ ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
+ ** SELECT statement.
+ */
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ sNC.pSrcList = pTabList;
+ sNC.pAggInfo = &sAggInfo;
+ sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
+ if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
+ goto select_end;
+ }
+ if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
+ goto select_end;
+ }
+ if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
+ goto select_end;
+ }
+ sAggInfo.nAccumulator = sAggInfo.nColumn;
+ for(i=0; i<sAggInfo.nFunc; i++){
+ if( sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList) ){
+ goto select_end;
+ }
+ }
+
+ /* Processing for aggregates with GROUP BY is very different and
+ ** much more complex tha aggregates without a GROUP BY.
+ */
if( pGroupBy ){
- sqlite3ExprCodeExprList(pParse, pGroupBy);
- /* No affinity string is attached to the following OP_MakeRecord
- ** because we do not need to do any coercion of datatypes. */
- sqlite3VdbeAddOp(v, OP_MakeRecord, pGroupBy->nExpr, 0);
- lbl1 = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp(v, OP_AggFocus, 0, lbl1);
- }
- for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
- if( pAgg->isAgg ) continue;
- sqlite3ExprCode(pParse, pAgg->pExpr);
- sqlite3VdbeAddOp(v, OP_AggSet, 0, i);
- }
- pParse->fillAgg = 0;
- if( lbl1<0 ){
- sqlite3VdbeResolveLabel(v, lbl1);
- }
- for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
- Expr *pE;
- int nExpr;
- FuncDef *pDef;
- if( !pAgg->isAgg ) continue;
- assert( pAgg->pFunc!=0 );
- assert( pAgg->pFunc->xStep!=0 );
- pDef = pAgg->pFunc;
- pE = pAgg->pExpr;
- assert( pE!=0 );
- assert( pE->op==TK_AGG_FUNCTION );
- nExpr = sqlite3ExprCodeExprList(pParse, pE->pList);
- sqlite3VdbeAddOp(v, OP_Integer, i, 0);
- if( pDef->needCollSeq ){
- CollSeq *pColl = 0;
- int j;
- for(j=0; !pColl && j<nExpr; j++){
- pColl = sqlite3ExprCollSeq(pParse, pE->pList->a[j].pExpr);
+ KeyInfo *pKeyInfo; /* Keying information for the group by clause */
+
+ /* Create labels that we will be needing
+ */
+
+ addrInitializeLoop = sqlite3VdbeMakeLabel(v);
+ addrGroupByChange = sqlite3VdbeMakeLabel(v);
+ addrProcessRow = sqlite3VdbeMakeLabel(v);
+
+ /* If there is a GROUP BY clause we might need a sorting index to
+ ** implement it. Allocate that sorting index now. If it turns out
+ ** that we do not need it after all, the OpenVirtual instruction
+ ** will be converted into a Noop.
+ */
+ sAggInfo.sortingIdx = pParse->nTab++;
+ pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
+ addrSortingIdx =
+ sqlite3VdbeOp3(v, OP_OpenVirtual, sAggInfo.sortingIdx,
+ sAggInfo.nSortingColumn,
+ (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+
+ /* Initialize memory locations used by GROUP BY aggregate processing
+ */
+ iUseFlag = pParse->nMem++;
+ iAbortFlag = pParse->nMem++;
+ iAMem = pParse->nMem;
+ pParse->nMem += pGroupBy->nExpr;
+ iBMem = pParse->nMem;
+ pParse->nMem += pGroupBy->nExpr;
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iAbortFlag, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iUseFlag, 1);
+ sqlite3VdbeAddOp(v, OP_Null, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iAMem, 1);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addrInitializeLoop);
+
+ /* Generate a subroutine that outputs a single row of the result
+ ** set. This subroutine first looks at the iUseFlag. If iUseFlag
+ ** is less than or equal to zero, the subroutine is a no-op. If
+ ** the processing calls for the query to abort, this subroutine
+ ** increments the iAbortFlag memory location before returning in
+ ** order to signal the caller to abort.
+ */
+ addrSetAbort = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp(v, OP_MemIncr, iAbortFlag, 0);
+ sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ addrOutputRow = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp(v, OP_IfMemPos, iUseFlag, addrOutputRow+2);
+ sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ finalizeAggFunctions(pParse, &sAggInfo);
+ if( pHaving ){
+ sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, 1);
+ }
+ rc = selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
+ distinct, eDest, iParm,
+ addrOutputRow+1, addrSetAbort, aff);
+ if( rc ){
+ goto select_end;
+ }
+ sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+
+ /* Begin a loop that will extract all source rows in GROUP BY order.
+ ** This might involve two separate loops with an OP_Sort in between, or
+ ** it might be a single loop that uses an index to extract information
+ ** in the right order to begin with.
+ */
+ sqlite3VdbeResolveLabel(v, addrInitializeLoop);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy);
+ if( pGroupBy==0 ){
+ /* The optimizer is able to deliver rows in group by order so
+ ** we do not have to sort. The OP_OpenVirtual table will be
+ ** cancelled later because we still need to use the pKeyInfo
+ */
+ pGroupBy = p->pGroupBy;
+ groupBySort = 0;
+ }else{
+ /* Rows are coming out in undetermined order. We have to push
+ ** each row into a sorting index, terminate the first loop,
+ ** then loop over the sorting index in order to get the output
+ ** in sorted order
+ */
+ groupBySort = 1;
+ sqlite3ExprCodeExprList(pParse, pGroupBy);
+ sqlite3VdbeAddOp(v, OP_Sequence, sAggInfo.sortingIdx, 0);
+ j = pGroupBy->nExpr+1;
+ for(i=0; i<sAggInfo.nColumn; i++){
+ struct AggInfo_col *pCol = &sAggInfo.aCol[i];
+ if( pCol->iSorterColumn<j ) continue;
+ if( pCol->iColumn<0 ){
+ sqlite3VdbeAddOp(v, OP_Rowid, pCol->iTable, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Column, pCol->iTable, pCol->iColumn);
+ }
+ j++;
}
- if( !pColl ) pColl = pParse->db->pDfltColl;
- sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ sqlite3VdbeAddOp(v, OP_MakeRecord, j, 0);
+ sqlite3VdbeAddOp(v, OP_IdxInsert, sAggInfo.sortingIdx, 0);
+ sqlite3WhereEnd(pWInfo);
+ sqlite3VdbeAddOp(v, OP_Sort, sAggInfo.sortingIdx, 0);
+ sAggInfo.useSortingIdx = 1;
}
- sqlite3VdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_FUNCDEF);
- }
- }
- /* End the database scan loop.
- */
- sqlite3WhereEnd(pWInfo);
+ /* Evaluate the current GROUP BY terms and store in b0, b1, b2...
+ ** (b0 is memory location iBMem+0, b1 is iBMem+1, and so forth)
+ ** Then compare the current GROUP BY terms against the GROUP BY terms
+ ** from the previous row currently stored in a0, a1, a2...
+ */
+ addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
+ for(j=0; j<pGroupBy->nExpr; j++){
+ if( groupBySort ){
+ sqlite3VdbeAddOp(v, OP_Column, sAggInfo.sortingIdx, j);
+ }else{
+ sAggInfo.directMode = 1;
+ sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr);
+ }
+ sqlite3VdbeAddOp(v, OP_MemStore, iBMem+j, j<pGroupBy->nExpr-1);
+ }
+ for(j=pGroupBy->nExpr-1; j>=0; j--){
+ if( j<pGroupBy->nExpr-1 ){
+ sqlite3VdbeAddOp(v, OP_MemLoad, iBMem+j, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_MemLoad, iAMem+j, 0);
+ if( j==0 ){
+ sqlite3VdbeAddOp(v, OP_Eq, 0, addrProcessRow);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Ne, 0x100, addrGroupByChange);
+ }
+ sqlite3VdbeChangeP3(v, -1, (void*)pKeyInfo->aColl[j], P3_COLLSEQ);
+ }
- /* If we are processing aggregates, we need to set up a second loop
- ** over all of the aggregate values and process them.
- */
- if( isAgg ){
- int endagg = sqlite3VdbeMakeLabel(v);
- int startagg;
- startagg = sqlite3VdbeAddOp(v, OP_AggNext, 0, endagg);
- if( pHaving ){
- sqlite3ExprIfFalse(pParse, pHaving, startagg, 1);
- }
- if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
- iParm, startagg, endagg, aff) ){
- goto select_end;
+ /* Generate code that runs whenever the GROUP BY changes.
+ ** Change in the GROUP BY are detected by the previous code
+ ** block. If there were no changes, this block is skipped.
+ **
+ ** This code copies current group by terms in b0,b1,b2,...
+ ** over to a0,a1,a2. It then calls the output subroutine
+ ** and resets the aggregate accumulator registers in preparation
+ ** for the next GROUP BY batch.
+ */
+ sqlite3VdbeResolveLabel(v, addrGroupByChange);
+ for(j=0; j<pGroupBy->nExpr; j++){
+ sqlite3VdbeAddOp(v, OP_MemLoad, iBMem+j, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iAMem+j, 1);
+ }
+ sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
+ sqlite3VdbeAddOp(v, OP_IfMemPos, iAbortFlag, addrEnd);
+ resetAccumulator(pParse, &sAggInfo);
+
+ /* Update the aggregate accumulators based on the content of
+ ** the current row
+ */
+ sqlite3VdbeResolveLabel(v, addrProcessRow);
+ updateAccumulator(pParse, &sAggInfo);
+ sqlite3VdbeAddOp(v, OP_MemIncr, iUseFlag, 0);
+
+ /* End of the loop
+ */
+ if( groupBySort ){
+ sqlite3VdbeAddOp(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
+ }else{
+ sqlite3WhereEnd(pWInfo);
+ uncreateSortingIndex(pParse, addrSortingIdx);
+ }
+
+ /* Output the final row of result
+ */
+ sqlite3VdbeAddOp(v, OP_Gosub, 0, addrOutputRow);
+
+ } /* endif pGroupBy */
+ else {
+ /* 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);
+ updateAccumulator(pParse, &sAggInfo);
+ sqlite3WhereEnd(pWInfo);
+ finalizeAggFunctions(pParse, &sAggInfo);
+ pOrderBy = 0;
+ selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
+ eDest, iParm, addrEnd, addrEnd, aff);
}
- sqlite3VdbeAddOp(v, OP_Goto, 0, startagg);
- sqlite3VdbeResolveLabel(v, endagg);
- sqlite3VdbeAddOp(v, OP_Noop, 0, 0);
- }
+ sqlite3VdbeResolveLabel(v, addrEnd);
+
+ } /* endif aggregate query */
/* If there is an ORDER BY clause, then we need to sort the results
** and send them to the callback one by one.
** successful coding of the SELECT.
*/
select_end:
- restoreAggregateInfo(pParse, &sAggInfo);
+ sqliteFree(sAggInfo.aCol);
+ sqliteFree(sAggInfo.aFunc);
return rc;
}
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.407 2005/09/01 03:07:44 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.408 2005/09/07 21:22:47 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
/*
** Forward references to structures
*/
-typedef struct AggExpr AggExpr;
+typedef struct AggInfo AggInfo;
typedef struct AuthContext AuthContext;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
unsigned n : 31; /* Number of characters in this token */
};
+/*
+** An instance of this structure contains information needed to generate
+** code for a SELECT that contains aggregate functions.
+**
+** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a
+** pointer to this structure. The Expr.iColumn field is the index in
+** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate
+** code for that node.
+**
+** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the
+** original Select structure that describes the SELECT statement. These
+** fields do not need to be freed when deallocating the AggInfo structure.
+*/
+struct AggInfo {
+ u8 directMode; /* Direct rendering mode means take data directly
+ ** from source tables rather than from accumulators */
+ u8 useSortingIdx; /* In direct mode, reference the sorting index rather
+ ** than the source table */
+ int sortingIdx; /* Cursor number of the sorting index */
+ ExprList *pGroupBy; /* The group by clause */
+ int nSortingColumn; /* Number of columns in the sorting index */
+ struct AggInfo_col { /* For each column used in source tables */
+ int iTable; /* Cursor number of the source table */
+ int iColumn; /* Column number within the source table */
+ int iSorterColumn; /* Column number in the sorting index */
+ int iMem; /* Memory location that acts as accumulator */
+ } *aCol;
+ int nColumn; /* Number of used entries in aCol[] */
+ int nColumnAlloc; /* Number of slots allocated for aCol[] */
+ int nAccumulator; /* Number of columns that show through to the output.
+ ** Additional columns are used only as parameters to
+ ** aggregate functions */
+ struct AggInfo_func { /* For each aggregate function */
+ Expr *pExpr; /* Expression encoding the function */
+ FuncDef *pFunc; /* The aggregate function implementation */
+ int iMem; /* Memory location that acts as accumulator */
+ } *aFunc;
+ int nFunc; /* Number of entries in aFunc[] */
+ int nFuncAlloc; /* Number of slots allocated for aFunc[] */
+};
+
/*
** Each node of an expression in the parse tree is an instance
** of this structure.
Token span; /* Complete text of the expression */
int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
** iColumn-th field of the iTable-th table. */
- int iAgg; /* When op==TK_COLUMN and pParse->fillAgg==FALSE, pull
- ** result from the iAgg-th element of the aggregator */
- int iAggCtx; /* The value to pass as P1 of OP_AggGet. */
+ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
+ int iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
Select *pSelect; /* When the expression is a sub-select. Also the
** right side of "<expr> IN (<select>)" */
Table *pTab; /* Table for OP_Column expressions. */
** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
*/
struct IdList {
- int nId; /* Number of identifiers on the list */
- int nAlloc; /* Number of entries allocated for a[] below */
struct IdList_item {
char *zName; /* Name of the identifier */
int idx; /* Index in some Table.aCol[] of a column named zName */
} *a;
+ int nId; /* Number of identifiers on the list */
+ int nAlloc; /* Number of entries allocated for a[] below */
};
/*
int nRef; /* Number of names resolved by this context */
int nErr; /* Number of errors encountered while resolving names */
u8 allowAgg; /* Aggregate functions allowed here */
- u8 hasAgg;
+ u8 hasAgg; /* True if aggregates are seen */
int nDepth; /* Depth of subquery recursion. 1 for no recursion */
+ AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */
};
/*
** The results of a select can be distributed in several ways.
*/
-#define SRT_Callback 1 /* Invoke a callback with each row of result */
-#define SRT_Mem 2 /* Store result in a memory cell */
-#define SRT_Set 3 /* Store result as unique keys in a table */
-#define SRT_Union 5 /* Store result as keys in a table */
-#define SRT_Except 6 /* Remove result from a UNION table */
-#define SRT_Table 7 /* Store result as data with a unique key */
+#define SRT_Union 1 /* Store result as keys in an index */
+#define SRT_Except 2 /* Remove result from a UNION index */
+#define SRT_Discard 3 /* Do not save the results anywhere */
+
+/* The ORDER BY clause is ignored for all of the above */
+#define IgnorableOrderby(X) (X<=SRT_Discard)
+
+#define SRT_Callback 4 /* Invoke a callback with each row of result */
+#define SRT_Mem 5 /* Store result in a memory cell */
+#define SRT_Set 6 /* Store non-null results as keys in an index */
+#define SRT_Table 7 /* Store result as data and add automatic rowid */
#define SRT_TempTable 8 /* Store result in a trasient table */
-#define SRT_Discard 9 /* Do not save the results anywhere */
-#define SRT_Sorter 10 /* Store results in the sorter */
-#define SRT_Subroutine 11 /* Call a subroutine to handle results */
-#define SRT_Exists 12 /* Put 0 or 1 in a memory cell */
-
-/*
-** When a SELECT uses aggregate functions (like "count(*)" or "avg(f1)")
-** we have to do some additional analysis of expressions. An instance
-** of the following structure holds information about a single subexpression
-** somewhere in the SELECT statement. An array of these structures holds
-** all the information we need to generate code for aggregate
-** expressions.
-**
-** Note that when analyzing a SELECT containing aggregates, both
-** non-aggregate field variables and aggregate functions are stored
-** in the AggExpr array of the Parser structure.
-**
-** The pExpr field points to an expression that is part of either the
-** field list, the GROUP BY clause, the HAVING clause or the ORDER BY
-** clause. The expression will be freed when those clauses are cleaned
-** up. Do not try to delete the expression attached to AggExpr.pExpr.
-**
-** If AggExpr.pExpr==0, that means the expression is "count(*)".
-*/
-struct AggExpr {
- int isAgg; /* if TRUE contains an aggregate function */
- Expr *pExpr; /* The expression */
- FuncDef *pFunc; /* Information about the aggregate function */
-};
+#define SRT_Sorter 9 /* Store results in the sorter NOT USED */
+#define SRT_Subroutine 10 /* Call a subroutine to handle results */
+#define SRT_Exists 11 /* Put 0 or 1 in a memory cell */
/*
** An SQL parser context. A copy of this structure is passed through
u8 nameClash; /* A permanent table name clashes with temp table name */
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
- u8 fillAgg; /* If true, ignore the Expr.iAgg field. Normally false */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
TriggerStack *trigStack; /* Trigger actions being coded */
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
- int nAgg; /* Number of aggregate expressions */
- AggExpr *aAgg; /* An array of aggregate expressions */
- int nMaxDepth; /* Maximum depth of subquery recursion */
};
/*
void sqlite3DropTable(Parse*, SrcList*, int);
void sqlite3DeleteTable(sqlite3*, Table*);
void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
+int sqlite3ArrayAllocate(void**,int,int);
IdList *sqlite3IdListAppend(IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.481 2005/09/06 20:36:49 drh Exp $
+** $Id: vdbe.c,v 1.482 2005/09/07 21:22:47 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
}
}
-/*
-** Insert a new aggregate element and make it the element that
-** has focus.
-**
-** Return 0 on success and 1 if memory is exhausted.
-*/
-static int AggInsert(Agg *p, char *zKey, int nKey){
- AggElem *pElem;
- int i;
- int rc;
- pElem = sqliteMalloc( sizeof(AggElem) + nKey +
- (p->nMem-1)*sizeof(pElem->aMem[0]) );
- if( pElem==0 ) return SQLITE_NOMEM;
- pElem->zKey = (char*)&pElem->aMem[p->nMem];
- memcpy(pElem->zKey, zKey, nKey);
- pElem->nKey = nKey;
-
- if( p->pCsr ){
- rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*));
- if( rc!=SQLITE_OK ){
- sqliteFree(pElem);
- return rc;
- }
- }
-
- for(i=0; i<p->nMem; i++){
- pElem->aMem[i].flags = MEM_Null;
- }
- p->pCurrent = pElem;
- return 0;
-}
-
/*
** Pop the stack N times.
*/
/* Opcode: Function P1 P2 P3
**
** Invoke a user function (P3 is a pointer to a Function structure that
-** defines the function) with P1 arguments taken from the stack. Pop all
+** defines the function) with P2 arguments taken from the stack. Pop all
** arguments from the stack and push back the result.
**
-** P2 is a 32-bit bitmask indicating whether or not each argument to the
+** P1 is a 32-bit bitmask indicating whether or not each argument to the
** function was determined to be constant at compile time. If the first
-** argument was constant then bit 0 of P2 is set. This is used to determine
+** argument was constant then bit 0 of P1 is set. This is used to determine
** whether meta data associated with a user function argument using the
** sqlite3_set_auxdata() API may be safely retained until the next
** invocation of this opcode.
**
-** See also: AggFunc
+** See also: AggStep and AggFinal
*/
case OP_Function: {
int i;
Mem *pArg;
sqlite3_context ctx;
sqlite3_value **apVal;
- int n = pOp->p1;
+ int n = pOp->p2;
- n = pOp->p1;
apVal = p->apArg;
assert( apVal || n==0 );
** immediately call the destructor for any non-static values.
*/
if( ctx.pVdbeFunc ){
- sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p2);
+ sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1);
pOp->p3 = (char *)ctx.pVdbeFunc;
pOp->p3type = P3_VDBEFUNC;
}
break;
}
-#ifndef SQLITE_OMIT_SUBQUERY
-/* Opcode: AggContextPush * * *
-**
-** Save the state of the current aggregator. It is restored an
-** AggContextPop opcode.
-**
-*/
-case OP_AggContextPush: { /* no-push */
- p->pAgg++;
- assert( p->pAgg<&p->apAgg[p->nAgg] );
- break;
-}
-
-/* Opcode: AggContextPop * * *
-**
-** Restore the aggregator to the state it was in when AggContextPush
-** was last called. Any data in the current aggregator is deleted.
-*/
-case OP_AggContextPop: { /* no-push */
- p->pAgg--;
- assert( p->pAgg>=p->apAgg );
- break;
-}
-#endif
-
#ifndef SQLITE_OMIT_TRIGGER
/* Opcode: ContextPush * * *
**
break;
}
-/* Opcode: AggReset P1 P2 P3
-**
-** Reset the current aggregator context so that it no longer contains any
-** data. Future aggregator elements will contain P2 values each and be sorted
-** using the KeyInfo structure pointed to by P3.
-**
-** If P1 is non-zero, then only a single aggregator row is available (i.e.
-** there is no GROUP BY expression). In this case it is illegal to invoke
-** OP_AggFocus.
-*/
-case OP_AggReset: { /* no-push */
- assert( !pOp->p3 || pOp->p3type==P3_KEYINFO );
- if( pOp->p1 ){
- rc = sqlite3VdbeAggReset(0, p->pAgg, (KeyInfo *)pOp->p3);
- p->pAgg->nMem = pOp->p2; /* Agg.nMem is used by AggInsert() */
- rc = AggInsert(p->pAgg, 0, 0);
- }else{
- rc = sqlite3VdbeAggReset(db, p->pAgg, (KeyInfo *)pOp->p3);
- p->pAgg->nMem = pOp->p2;
- }
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- p->pAgg->apFunc = sqliteMalloc( p->pAgg->nMem*sizeof(p->pAgg->apFunc[0]) );
- if( p->pAgg->apFunc==0 ) goto no_mem;
- break;
-}
-
-/* Opcode: AggInit P1 P2 P3
-**
-** Initialize the function parameters for an aggregate function.
-** The aggregate will operate out of aggregate column P2.
-** P3 is a pointer to the FuncDef structure for the function.
-**
-** The P1 argument is not used by this opcode. However if the SSE
-** extension is compiled in, P1 is set to the number of arguments that
-** will be passed to the aggregate function, if any. This is used
-** by SSE to select the correct function when (de)serializing statements.
-*/
-case OP_AggInit: { /* no-push */
- int i = pOp->p2;
- assert( i>=0 && i<p->pAgg->nMem );
- p->pAgg->apFunc[i] = (FuncDef*)pOp->p3;
- break;
-}
-
-/* Opcode: AggFunc * P2 P3
+/* Opcode: AggStep P1 P2 P3
**
** Execute the step function for an aggregate. The
** function has P2 arguments. P3 is a pointer to the FuncDef
-** structure that specifies the function.
+** structure that specifies the function. Use memory location
+** P1 as the accumulator.
**
-** The top of the stack must be an integer which is the index of
-** the aggregate column that corresponds to this aggregate function.
-** Ideally, this index would be another parameter, but there are
-** no free parameters left. The integer is popped from the stack.
+** The P2 arguments are popped from the stack.
*/
-case OP_AggFunc: { /* no-push */
+case OP_AggStep: { /* no-push */
int n = pOp->p2;
int i;
Mem *pMem, *pRec;
sqlite3_value **apVal;
assert( n>=0 );
- assert( pTos->flags==MEM_Int );
- pRec = &pTos[-n];
+ pRec = &pTos[1-n];
assert( pRec>=p->aStack );
-
apVal = p->apArg;
assert( apVal || n==0 );
-
for(i=0; i<n; i++, pRec++){
apVal[i] = pRec;
storeTypeInfo(pRec, db->enc);
}
- i = pTos->i;
- assert( i>=0 && i<p->pAgg->nMem );
ctx.pFunc = (FuncDef*)pOp->p3;
- ctx.pMem = pMem = &p->pAgg->pCurrent->aMem[i];
+ assert( pOp->p1>=0 && pOp->p1<p->nMem );
+ ctx.pMem = pMem = &p->aMem[pOp->p1];
pMem->n++;
ctx.isError = 0;
ctx.pColl = 0;
ctx.pColl = (CollSeq *)pOp[-1].p3;
}
(ctx.pFunc->xStep)(&ctx, n, apVal);
- popStack(&pTos, n+1);
+ popStack(&pTos, n);
if( ctx.isError ){
rc = SQLITE_ERROR;
}
break;
}
-/* Opcode: AggFocus * P2 *
-**
-** Pop the top of the stack and use that as an aggregator key. If
-** an aggregator with that same key already exists, then make the
-** aggregator the current aggregator and jump to P2. If no aggregator
-** with the given key exists, create one and make it current but
-** do not jump.
-**
-** The order of aggregator opcodes is important. The order is:
-** AggReset AggFocus AggNext. In other words, you must execute
-** AggReset first, then zero or more AggFocus operations, then
-** zero or more AggNext operations. You must not execute an AggFocus
-** in between an AggNext and an AggReset.
-*/
-case OP_AggFocus: { /* no-push */
- char *zKey;
- int nKey;
- int res;
- assert( pTos>=p->aStack );
- Stringify(pTos, db->enc);
- zKey = pTos->z;
- nKey = pTos->n;
- assert( p->pAgg->pBtree );
- assert( p->pAgg->pCsr );
- rc = sqlite3BtreeMoveto(p->pAgg->pCsr, zKey, nKey, &res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- if( res==0 ){
- rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*),
- (char *)&p->pAgg->pCurrent);
- pc = pOp->p2 - 1;
- }else{
- rc = AggInsert(p->pAgg, zKey, nKey);
- }
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- Release(pTos);
- pTos--;
- break;
-}
-
-/* Opcode: AggSet * P2 *
+/* Opcode: AggFinal P1 * *
**
-** Move the top of the stack into the P2-th field of the current
-** aggregate. String values are duplicated into new memory.
+** Execute the finalizer function for an aggregate. P1 is
+** the memory location that is the accumulator for the aggregate.
*/
-case OP_AggSet: { /* no-push */
- AggElem *pFocus;
- int i = pOp->p2;
- pFocus = p->pAgg->pCurrent;
- assert( pTos>=p->aStack );
- if( pFocus==0 ) goto no_mem;
- assert( i>=0 && i<p->pAgg->nMem );
- rc = sqlite3VdbeMemMove(&pFocus->aMem[i], pTos);
- pTos--;
- break;
-}
-
-/* Opcode: AggGet P1 P2 *
-**
-** Push a new entry onto the stack which is a copy of the P2-th field
-** of the current aggregate. Strings are not duplicated so
-** string values will be ephemeral.
-**
-** If P1 is zero, then the value is pulled out of the current aggregate
-** in the current aggregate context. If P1 is greater than zero, then
-** the value is taken from the P1th outer aggregate context. (i.e. if
-** P1==1 then read from the aggregate context that will be restored
-** by the next OP_AggContextPop opcode).
-*/
-case OP_AggGet: {
- AggElem *pFocus;
- int i = pOp->p2;
- Agg *pAgg = &p->pAgg[-pOp->p1];
- assert( pAgg>=p->apAgg );
- pFocus = pAgg->pCurrent;
- if( pFocus==0 ){
- int res;
- if( sqlite3_malloc_failed ) goto no_mem;
- rc = sqlite3BtreeFirst(pAgg->pCsr, &res);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- if( res!=0 ){
- rc = AggInsert(pAgg, "", 1);
- pFocus = pAgg->pCurrent;
- }else{
- rc = sqlite3BtreeData(pAgg->pCsr, 0, 4, (char *)&pFocus);
- }
- }
- assert( i>=0 && i<pAgg->nMem );
- pTos++;
- sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem);
- if( pTos->flags&MEM_Str ){
- sqlite3VdbeChangeEncoding(pTos, db->enc);
- }
+case OP_AggFinal: { /* no-push */
+ Mem *pMem;
+ assert( pOp->p1>=0 && pOp->p1<p->nMem );
+ pMem = &p->aMem[pOp->p1];
+ sqlite3VdbeMemFinalize(pMem);
break;
}
-/* Opcode: AggNext * P2 *
-**
-** Make the next aggregate value the current aggregate. The prior
-** aggregate is deleted. If all aggregate values have been consumed,
-** jump to P2.
-**
-** The order of aggregator opcodes is important. The order is:
-** AggReset AggFocus AggNext. In other words, you must execute
-** AggReset first, then zero or more AggFocus operations, then
-** zero or more AggNext operations. You must not execute an AggFocus
-** in between an AggNext and an AggReset.
-*/
-case OP_AggNext: { /* no-push */
- int res;
- assert( rc==SQLITE_OK );
- CHECK_FOR_INTERRUPT;
- if( p->pAgg->searching==0 ){
- p->pAgg->searching = 1;
- if( p->pAgg->pCsr ){
- rc = sqlite3BtreeFirst(p->pAgg->pCsr, &res);
- }else{
- res = 0;
- }
- }else{
- if( p->pAgg->pCsr ){
- rc = sqlite3BtreeNext(p->pAgg->pCsr, &res);
- }else{
- res = 1;
- }
- }
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- if( res!=0 ){
- pc = pOp->p2 - 1;
- }else{
- int i;
- Mem *aMem;
-
- if( p->pAgg->pCsr ){
- rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*),
- (char *)&p->pAgg->pCurrent);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- }
- aMem = p->pAgg->pCurrent->aMem;
- for(i=0; i<p->pAgg->nMem; i++){
- FuncDef *pFunc = p->pAgg->apFunc[i];
- Mem *pMem = &aMem[i];
- sqlite3VdbeMemFinalize(pMem, pFunc);
- }
- }
- break;
-}
/* Opcode: Vacuum * * *
**
** 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.97 2005/08/19 01:07:16 drh Exp $
+** $Id: vdbe.h,v 1.98 2005/09/07 21:22:47 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*);
void sqlite3VdbeDelete(Vdbe*);
-void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
int sqlite3VdbeCurrentAddr(Vdbe*);
CollSeq *pColl; /* Collating sequence */
};
-/*
-** An Agg structure describes an Aggregator. Each Agg consists of
-** zero or more Aggregator elements (AggElem). Each AggElem contains
-** a key and one or more values. The values are used in processing
-** aggregate functions in a SELECT. The key is used to implement
-** the GROUP BY clause of a select.
-*/
-typedef struct Agg Agg;
-typedef struct AggElem AggElem;
-struct Agg {
- int nMem; /* Number of values stored in each AggElem */
- AggElem *pCurrent; /* The AggElem currently in focus */
- FuncDef **apFunc; /* Information about aggregate functions */
- Btree *pBtree; /* The tmp. btree used to group elements, if required. */
- BtCursor *pCsr; /* Read/write cursor to the table in pBtree */
- int nTab; /* Root page of the table in pBtree */
- u8 searching; /* True between the first AggNext and AggReset */
-};
-struct AggElem {
- char *zKey; /* The key to this AggElem */
- int nKey; /* Number of bytes in the key, including '\0' at end */
- Mem aMem[1]; /* The values for this AggElem */
-};
-
/*
** A Set structure is used for quick testing to see if a value
** is part of a small set. Sets are used to implement code like
int magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */
- int nAgg; /* Number of elements in apAgg */
- Agg *apAgg; /* Array of aggregate contexts */
- Agg *pAgg; /* Current aggregate context */
int nCallback; /* Number of callbacks invoked so far */
Fifo sFifo; /* A list of ROWIDs */
int contextStackTop; /* Index of top element in the context stack */
** Function prototypes
*/
void sqlite3VdbeFreeCursor(Cursor*);
-int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
void sqliteVdbePopStack(Vdbe*,int);
int sqlite3VdbeCursorMoveto(Cursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
int sqlite3VdbeMemRealify(Mem*);
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
-void sqlite3VdbeMemFinalize(Mem*, FuncDef*);
+void sqlite3VdbeMemFinalize(Mem*);
#ifndef NDEBUG
void sqlite3VdbeMemSanity(Mem*, u8);
int sqlite3VdbeOpcodeNoPush(u8);
**
** This routine is called once after all opcodes have been inserted.
**
-** Variable *pMaxFuncArgs is set to the maximum value of any P1 argument
-** to an OP_Function or P2 to an OP_AggFunc opcode. This is used by
+** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument
+** to an OP_Function or OP_AggStep opcode. This is used by
** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
**
** The integer *pMaxStack is set to the maximum number of vdbe stack
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- /* Todo: Maybe OP_AggFunc should change to use P1 in the same
- * way as OP_Function.
- */
- if( opcode==OP_Function ){
- if( pOp->p1>nMaxArgs ) nMaxArgs = pOp->p1;
- }else if( opcode==OP_AggFunc ){
+ if( opcode==OP_Function || opcode==OP_AggStep ){
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
}else if( opcode==OP_Halt ){
if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){
int nVar, /* Number of '?' see in the SQL statement */
int nMem, /* Number of memory cells to allocate */
int nCursor, /* Number of cursors to allocate */
- int nAgg, /* Number of aggregate contexts required */
int isExplain /* True if the EXPLAIN keywords is present */
){
int n;
+ nVar*sizeof(char*) /* azVar */
+ nMem*sizeof(Mem) /* aMem */
+ nCursor*sizeof(Cursor*) /* apCsr */
- + nAgg*sizeof(Agg) /* Aggregate contexts */
);
if( !sqlite3_malloc_failed ){
p->aMem = &p->aStack[nStack];
p->apArg = (Mem**)&p->aVar[nVar];
p->azVar = (char**)&p->apArg[nArg];
p->apCsr = (Cursor**)&p->azVar[nVar];
- if( nAgg>0 ){
- p->nAgg = nAgg;
- p->apAgg = (Agg*)&p->apCsr[nCursor];
- }
p->nCursor = nCursor;
for(n=0; n<nVar; n++){
p->aVar[n].flags = MEM_Null;
}
}
}
- p->pAgg = p->apAgg;
for(n=0; n<p->nMem; n++){
p->aMem[n].flags = MEM_Null;
}
#endif
}
-/*
-** Free all resources allociated with AggElem pElem, an element of
-** aggregate pAgg.
-*/
-static void freeAggElem(AggElem *pElem, Agg *pAgg){
- int i;
- for(i=0; i<pAgg->nMem; i++){
- Mem *pMem = &pElem->aMem[i];
- if( pAgg->apFunc && pAgg->apFunc[i] && (pMem->flags & MEM_Agg)!=0 ){
- sqlite3VdbeMemFinalize(pMem, pAgg->apFunc[i]);
- }
- sqlite3VdbeMemRelease(pMem);
-
- }
- sqliteFree(pElem);
-}
-
-/*
-** Reset an Agg structure. Delete all its contents.
-**
-** For installable aggregate functions, if the step function has been
-** called, make sure the finalizer function has also been called. The
-** finalizer might need to free memory that was allocated as part of its
-** private context. If the finalizer has not been called yet, call it
-** now.
-**
-** If db is NULL, then this is being called from sqliteVdbeReset(). In
-** this case clean up all references to the temp-table used for
-** aggregates (if it was ever opened).
-**
-** If db is not NULL, then this is being called from with an OP_AggReset
-** opcode. Open the temp-table, if it has not already been opened and
-** delete the contents of the table used for aggregate information, ready
-** for the next round of aggregate processing.
-*/
-int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){
- int rc = 0;
- BtCursor *pCsr;
-
- if( !pAgg ) return SQLITE_OK;
- pCsr = pAgg->pCsr;
- assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0)
- || sqlite3_malloc_failed );
-
- /* If pCsr is not NULL, then the table used for aggregate information
- ** is open. Loop through it and free the AggElem* structure pointed at
- ** by each entry. If the finalizer has not been called for an AggElem,
- ** do that too. Finally, clear the btree table itself.
- */
- if( pCsr ){
- int res;
- assert( pAgg->pBtree );
- assert( pAgg->nTab>0 );
-
- rc=sqlite3BtreeFirst(pCsr, &res);
- while( res==0 && rc==SQLITE_OK ){
- AggElem *pElem;
- rc = sqlite3BtreeData(pCsr, 0, sizeof(AggElem*), (char *)&pElem);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- assert( pAgg->apFunc!=0 );
- freeAggElem(pElem, pAgg);
- rc=sqlite3BtreeNext(pCsr, &res);
- }
- if( rc!=SQLITE_OK ){
- return rc;
- }
-
- sqlite3BtreeCloseCursor(pCsr);
- sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab);
- }else{
- /* The cursor may not be open because the aggregator was never used,
- ** or it could be that it was used but there was no GROUP BY clause.
- */
- if( pAgg->pCurrent ){
- freeAggElem(pAgg->pCurrent, pAgg);
- }
- }
-
- /* If db is not NULL and we have not yet and we have not yet opened
- ** the temporary btree then do so and create the table to store aggregate
- ** information.
- **
- ** If db is NULL, then close the temporary btree if it is open.
- */
- if( db ){
- if( !pAgg->pBtree ){
- assert( pAgg->nTab==0 );
-#ifndef SQLITE_OMIT_MEMORYDB
- rc = sqlite3BtreeFactory(db, ":memory:", 0, TEMP_PAGES, &pAgg->pBtree);
-#else
- rc = sqlite3BtreeFactory(db, 0, 0, TEMP_PAGES, &pAgg->pBtree);
-#endif
- if( rc!=SQLITE_OK ) return rc;
- sqlite3BtreeBeginTrans(pAgg->pBtree, 1);
- rc = sqlite3BtreeCreateTable(pAgg->pBtree, &pAgg->nTab, 0);
- if( rc!=SQLITE_OK ) return rc;
- }
- assert( pAgg->nTab!=0 );
-
- rc = sqlite3BtreeCursor(pAgg->pBtree, pAgg->nTab, 1,
- sqlite3VdbeRecordCompare, pKeyInfo, &pAgg->pCsr);
- if( rc!=SQLITE_OK ) return rc;
- }else{
- if( pAgg->pBtree ){
- sqlite3BtreeClose(pAgg->pBtree);
- pAgg->pBtree = 0;
- pAgg->nTab = 0;
- }
- pAgg->pCsr = 0;
- }
-
- if( pAgg->apFunc ){
- sqliteFree(pAgg->apFunc);
- pAgg->apFunc = 0;
- }
- pAgg->pCurrent = 0;
- pAgg->nMem = 0;
- pAgg->searching = 0;
- return SQLITE_OK;
-}
-
/*
** Close a cursor and release all the resources that cursor happens
** to hold.
}
sqliteFree(p->contextStack);
}
- for(i=0; i<p->nAgg; i++){
- sqlite3VdbeAggReset(0, &p->apAgg[i], 0);
- }
p->contextStack = 0;
p->contextStackDepth = 0;
p->contextStackTop = 0;
** This routine calls the finalize method for that function. The
** result of the aggregate is stored back into pMem.
*/
-void sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
- if( pFunc && pFunc->xFinalize ){
- sqlite3_context ctx;
- ctx.s.flags = MEM_Null;
- ctx.s.z = pMem->zShort;
- ctx.pMem = pMem;
- ctx.pFunc = pFunc;
- pFunc->xFinalize(&ctx);
- if( pMem->z && pMem->z!=pMem->zShort ){
- sqliteFree( pMem->z );
- }
- *pMem = ctx.s;
- if( pMem->flags & MEM_Short ){
- pMem->z = pMem->zShort;
+void sqlite3VdbeMemFinalize(Mem *pMem){
+ if( pMem->flags & MEM_Agg ){
+ FuncDef *pFunc = *(FuncDef**)&pMem->i;
+ if( pFunc && pFunc->xFinalize ){
+ sqlite3_context ctx;
+ ctx.s.flags = MEM_Null;
+ ctx.s.z = pMem->zShort;
+ ctx.pMem = pMem;
+ ctx.pFunc = pFunc;
+ pFunc->xFinalize(&ctx);
+ if( pMem->z && pMem->z!=pMem->zShort ){
+ sqliteFree( pMem->z );
+ }
+ *pMem = ctx.s;
+ if( pMem->flags & MEM_Short ){
+ pMem->z = pMem->zShort;
+ }
}
}
}
if( p->flags & (MEM_Dyn|MEM_Agg) ){
if( p->xDel ){
if( p->flags & MEM_Agg ){
- sqlite3VdbeMemFinalize(p, (FuncDef*)&p->i);
+ sqlite3VdbeMemFinalize(p);
assert( (p->flags & MEM_Agg)==0 );
sqlite3VdbeMemRelease(p);
}else{
# This file implements tests for miscellanous features that were
# left out of other test files.
#
-# $Id: misc4.test,v 1.16 2005/03/29 03:11:00 danielk1977 Exp $
+# $Id: misc4.test,v 1.17 2005/09/07 21:22:47 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
INSERT INTO Table2 VALUES(1, 'z');
INSERT INTO Table2 VALUES (1, 'a');
SELECT ID, Value FROM Table1
- UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1,2
+ UNION SELECT ID, max(Value) FROM Table2 GROUP BY 1
ORDER BY 1, 2;
}
} {{} {} 1 x 1 z}
} {1}
finish_test
-