Rewrite the aggregate handling logic so that it runs in O(1) space.
This is the first cut at the code. Many regression tests fail. (CVS 2662) FossilOrigin-Name: 17039ec3ff4396862beedf4a8af89654b2140f58
This commit is contained in:
parent
79158e1865
commit
13449892ef
34
manifest
34
manifest
@ -1,5 +1,5 @@
|
||||
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
|
||||
@ -34,19 +34,19 @@ F src/attach.c 4b21689700a72ae281fa85dbaff06b2a62bd49ee
|
||||
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
|
||||
@ -58,15 +58,15 @@ F src/os_win.c 4aad6cd49a2a546f945491a9e6a0b7d061cf47c5
|
||||
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
|
||||
@ -80,13 +80,13 @@ F src/update.c a9d2c5f504212d62da1b094476f1389c0e02f83f
|
||||
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
|
||||
@ -178,7 +178,7 @@ F test/minmax.test 9429a06f1f93acf76fcacafd17160a4392e88526
|
||||
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
|
||||
@ -306,7 +306,7 @@ F www/tclsqlite.tcl 3df553505b6efcad08f91e9b975deb2e6c9bb955
|
||||
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
|
||||
|
@ -1 +1 @@
|
||||
b86bd70f301205d6ca66475a425e157b976107e2
|
||||
17039ec3ff4396862beedf4a8af89654b2140f58
|
61
src/build.c
61
src/build.c
@ -22,7 +22,7 @@
|
||||
** 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>
|
||||
@ -103,7 +103,7 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
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 ){
|
||||
@ -2469,6 +2469,45 @@ exit_drop_index:
|
||||
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.
|
||||
@ -2476,24 +2515,18 @@ exit_drop_index:
|
||||
** 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;
|
||||
}
|
||||
|
||||
|
200
src/expr.c
200
src/expr.c
@ -12,7 +12,7 @@
|
||||
** 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>
|
||||
@ -695,6 +695,7 @@ static int exprNodeIsConstant(void *pArg, Expr *pExpr){
|
||||
case TK_COLUMN:
|
||||
case TK_DOT:
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_AGG_COLUMN:
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case TK_SELECT:
|
||||
case TK_EXISTS:
|
||||
@ -1230,11 +1231,19 @@ int sqlite3ExprResolveNames(
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1287,10 +1296,6 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, mem, 1);
|
||||
}
|
||||
|
||||
if( pExpr->pSelect ){
|
||||
sqlite3VdbeAddOp(v, OP_AggContextPush, 0, 0);
|
||||
}
|
||||
|
||||
switch( pExpr->op ){
|
||||
case TK_IN: {
|
||||
char affinity;
|
||||
@ -1403,9 +1408,6 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
|
||||
}
|
||||
}
|
||||
|
||||
if( pExpr->pSelect ){
|
||||
sqlite3VdbeAddOp(v, OP_AggContextPop, 0, 0);
|
||||
}
|
||||
if( testAddr ){
|
||||
sqlite3VdbeChangeP2(v, testAddr, sqlite3VdbeCurrentAddr(v));
|
||||
}
|
||||
@ -1448,10 +1450,21 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
}
|
||||
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{
|
||||
@ -1600,7 +1613,8 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
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:
|
||||
@ -1610,7 +1624,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
FuncDef *pDef;
|
||||
int nId;
|
||||
const char *zId;
|
||||
int p2 = 0;
|
||||
int constMask = 0;
|
||||
int i;
|
||||
u8 enc = pParse->db->enc;
|
||||
CollSeq *pColl = 0;
|
||||
@ -1621,7 +1635,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
|
||||
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);
|
||||
@ -1631,7 +1645,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *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
|
||||
@ -2047,23 +2061,32 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
|
||||
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
|
||||
@ -2074,63 +2097,112 @@ static int appendAggInfo(Parse *pParse){
|
||||
*/
|
||||
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);
|
||||
|
@ -14,7 +14,7 @@
|
||||
** 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"
|
||||
@ -875,7 +875,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
|
||||
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;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
** 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_"
|
||||
@ -93,7 +93,7 @@ struct AttachKey { int type; Token key; };
|
||||
// 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.
|
||||
|
578
src/select.c
578
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.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"
|
||||
|
||||
@ -346,7 +346,7 @@ static void codeLimiter(
|
||||
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);
|
||||
@ -357,7 +357,7 @@ static void codeLimiter(
|
||||
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"));
|
||||
}
|
||||
@ -435,13 +435,15 @@ static int selectInnerLoop(
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1350,6 +1352,20 @@ static void createSortingIndex(Parse *pParse, Select *p, ExprList *pOrderBy){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
@ -2276,6 +2292,7 @@ int sqlite3SelectResolve(
|
||||
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 ){
|
||||
@ -2319,18 +2336,6 @@ int sqlite3SelectResolve(
|
||||
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;
|
||||
@ -2345,7 +2350,8 @@ int sqlite3SelectResolve(
|
||||
** 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;
|
||||
@ -2353,7 +2359,7 @@ int sqlite3SelectResolve(
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@ -2370,49 +2376,112 @@ int sqlite3SelectResolve(
|
||||
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.
|
||||
**
|
||||
@ -2475,9 +2544,9 @@ int sqlite3Select(
|
||||
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 */
|
||||
@ -2488,10 +2557,11 @@ int sqlite3Select(
|
||||
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.
|
||||
@ -2507,9 +2577,8 @@ int sqlite3Select(
|
||||
}
|
||||
#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) ){
|
||||
@ -2548,14 +2617,8 @@ int sqlite3Select(
|
||||
|
||||
/* 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.
|
||||
@ -2576,23 +2639,24 @@ int sqlite3Select(
|
||||
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;
|
||||
@ -2652,55 +2716,6 @@ int sqlite3Select(
|
||||
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
|
||||
*/
|
||||
@ -2721,94 +2736,264 @@ int sqlite3Select(
|
||||
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;
|
||||
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);
|
||||
}
|
||||
if( !pColl ) pColl = pParse->db->pDfltColl;
|
||||
sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
|
||||
}
|
||||
sqlite3VdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_FUNCDEF);
|
||||
}
|
||||
}
|
||||
/* 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 */
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* 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) ){
|
||||
/* 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;
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, startagg);
|
||||
sqlite3VdbeResolveLabel(v, endagg);
|
||||
sqlite3VdbeAddOp(v, OP_Noop, 0, 0);
|
||||
}
|
||||
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 ){
|
||||
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++;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
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.
|
||||
@ -2840,6 +3025,7 @@ int sqlite3Select(
|
||||
** successful coding of the SELECT.
|
||||
*/
|
||||
select_end:
|
||||
restoreAggregateInfo(pParse, &sAggInfo);
|
||||
sqliteFree(sAggInfo.aCol);
|
||||
sqliteFree(sAggInfo.aFunc);
|
||||
return rc;
|
||||
}
|
||||
|
111
src/sqliteInt.h
111
src/sqliteInt.h
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** 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_
|
||||
@ -298,7 +298,7 @@ extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */
|
||||
/*
|
||||
** Forward references to structures
|
||||
*/
|
||||
typedef struct AggExpr AggExpr;
|
||||
typedef struct AggInfo AggInfo;
|
||||
typedef struct AuthContext AuthContext;
|
||||
typedef struct CollSeq CollSeq;
|
||||
typedef struct Column Column;
|
||||
@ -788,6 +788,47 @@ struct Token {
|
||||
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.
|
||||
@ -847,9 +888,8 @@ struct Expr {
|
||||
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. */
|
||||
@ -912,12 +952,12 @@ struct ExprList {
|
||||
** 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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1031,8 +1071,9 @@ struct NameContext {
|
||||
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 */
|
||||
};
|
||||
|
||||
@ -1079,42 +1120,21 @@ struct Select {
|
||||
/*
|
||||
** 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_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 */
|
||||
#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 */
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
};
|
||||
/* 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_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
|
||||
@ -1135,7 +1155,6 @@ struct Parse {
|
||||
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 */
|
||||
@ -1162,9 +1181,6 @@ struct Parse {
|
||||
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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1421,6 +1437,7 @@ void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||
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*);
|
||||
|
297
src/vdbe.c
297
src/vdbe.c
@ -43,7 +43,7 @@
|
||||
** 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"
|
||||
@ -160,38 +160,6 @@ static void _storeTypeInfo(Mem *pMem){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
@ -1131,26 +1099,25 @@ case OP_CollSeq: { /* no-push */
|
||||
/* 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 );
|
||||
|
||||
@ -1189,7 +1156,7 @@ case OP_Function: {
|
||||
** 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;
|
||||
}
|
||||
@ -4028,31 +3995,6 @@ case OP_FifoRead: {
|
||||
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 * * *
|
||||
**
|
||||
@ -4199,64 +4141,16 @@ case OP_IfMemPos: { /* no-push */
|
||||
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;
|
||||
@ -4264,21 +4158,17 @@ case OP_AggFunc: { /* no-push */
|
||||
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;
|
||||
@ -4289,165 +4179,26 @@ case OP_AggFunc: { /* no-push */
|
||||
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 *
|
||||
/* Opcode: AggFinal P1 * *
|
||||
**
|
||||
** 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.
|
||||
** Execute the finalizer function for an aggregate. P1 is
|
||||
** the memory location that is the accumulator for the aggregate.
|
||||
*/
|
||||
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 *
|
||||
**
|
||||
** Move the top of the stack into the P2-th field of the current
|
||||
** aggregate. String values are duplicated into new memory.
|
||||
*/
|
||||
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--;
|
||||
case OP_AggFinal: { /* no-push */
|
||||
Mem *pMem;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nMem );
|
||||
pMem = &p->aMem[pOp->p1];
|
||||
sqlite3VdbeMemFinalize(pMem);
|
||||
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);
|
||||
}
|
||||
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 * * *
|
||||
**
|
||||
|
@ -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.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_
|
||||
@ -108,7 +108,7 @@ void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
|
||||
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*);
|
||||
|
@ -201,30 +201,6 @@ struct sqlite3_context {
|
||||
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
|
||||
@ -309,9 +285,6 @@ struct Vdbe {
|
||||
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 */
|
||||
@ -349,7 +322,6 @@ struct Vdbe {
|
||||
** Function prototypes
|
||||
*/
|
||||
void sqlite3VdbeFreeCursor(Cursor*);
|
||||
int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
|
||||
void sqliteVdbePopStack(Vdbe*,int);
|
||||
int sqlite3VdbeCursorMoveto(Cursor*);
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
@ -391,7 +363,7 @@ double sqlite3VdbeRealValue(Mem*);
|
||||
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);
|
||||
|
144
src/vdbeaux.c
144
src/vdbeaux.c
@ -215,8 +215,8 @@ int sqlite3VdbeOpcodeNoPush(u8 op){
|
||||
**
|
||||
** 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
|
||||
@ -239,12 +239,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){
|
||||
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 ){
|
||||
@ -663,7 +658,6 @@ void sqlite3VdbeMakeReady(
|
||||
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;
|
||||
@ -706,7 +700,6 @@ void sqlite3VdbeMakeReady(
|
||||
+ 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];
|
||||
@ -717,17 +710,12 @@ void sqlite3VdbeMakeReady(
|
||||
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;
|
||||
}
|
||||
@ -768,129 +756,6 @@ void sqlite3VdbeMakeReady(
|
||||
#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.
|
||||
@ -944,9 +809,6 @@ static void Cleanup(Vdbe *p){
|
||||
}
|
||||
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;
|
||||
|
@ -192,20 +192,23 @@ int sqlite3VdbeMemStringify(Mem *pMem, int enc){
|
||||
** 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,7 +222,7 @@ void sqlite3VdbeMemRelease(Mem *p){
|
||||
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{
|
||||
|
@ -13,7 +13,7 @@
|
||||
# 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
|
||||
@ -96,7 +96,7 @@ do_test misc4-3.1 {
|
||||
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}
|
||||
@ -177,4 +177,3 @@ do_test misc4-6.2 {
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user