Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.187 2004/06/11 10:51:35 danielk1977 Exp $ +** $Id: select.c,v 1.188 2004/06/11 13:19:21 danielk1977 Exp $ */ #include "sqliteInt.h" /* @@ -2439,20 +2439,35 @@ } /* Reset the aggregator */ if( isAgg ){ - sqlite3VdbeAddOp(v, OP_AggReset, 0, pParse->nAgg); + int addr = sqlite3VdbeAddOp(v, OP_AggReset, 0, pParse->nAgg); for(i=0; inAgg; i++){ FuncDef *pFunc; if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){ sqlite3VdbeOp3(v, OP_AggInit, 0, i, (char*)pFunc, P3_FUNCDEF); } } if( pGroupBy==0 ){ sqlite3VdbeAddOp(v, OP_String8, 0, 0); sqlite3VdbeAddOp(v, OP_AggFocus, 0, 0); + }else{ + int sz = sizeof(KeyInfo) + pGroupBy->nExpr*sizeof(CollSeq*); + KeyInfo *pKey = (KeyInfo *)sqliteMalloc(sz); + if( 0==pKey ){ + goto select_end; + } + pKey->enc = pParse->db->enc; + pKey->nField = pGroupBy->nExpr; + for(i=0; inExpr; i++){ + pKey->aColl[i] = sqlite3ExprCollSeq(pParse, pGroupBy->a[i].pExpr); + if( !pKey->aColl[i] ){ + pKey->aColl[i] = pParse->db->pDfltColl; + } + } + sqlite3VdbeChangeP3(v, addr, (char *)pKey, P3_KEYINFO_HANDOFF); } } /* Initialize the memory cell to NULL */ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** 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.364 2004/06/11 10:51:37 danielk1977 Exp $ +** $Id: vdbe.c,v 1.365 2004/06/11 13:19:21 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include "vdbeInt.h" @@ -158,44 +158,73 @@ ** has focus. ** ** Return 0 on success and 1 if memory is exhausted. */ static int AggInsert(Agg *p, char *zKey, int nKey){ - AggElem *pElem, *pOld; + AggElem *pElem; int i; - Mem *pMem; + int rc; pElem = sqliteMalloc( sizeof(AggElem) + nKey + (p->nMem-1)*sizeof(pElem->aMem[0]) ); - if( pElem==0 ) return 1; + if( pElem==0 ) return SQLITE_NOMEM; pElem->zKey = (char*)&pElem->aMem[p->nMem]; memcpy(pElem->zKey, zKey, nKey); pElem->nKey = nKey; - pOld = sqlite3HashInsert(&p->hash, pElem->zKey, pElem->nKey, pElem); - if( pOld!=0 ){ - assert( pOld==pElem ); /* Malloc failed on insert */ - sqliteFree(pOld); - return 0; - } - for(i=0, pMem=pElem->aMem; inMem; i++, pMem++){ - pMem->flags = MEM_Null; + + assert( p->pCsr ); + rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*)); + if( rc!=SQLITE_OK ){ + sqliteFree(pElem); + return rc; + } + + for(i=0; inMem; i++){ + pElem->aMem[i].flags = MEM_Null; } p->pCurrent = pElem; return 0; } /* ** Get the AggElem currently in focus */ +#if 0 #define AggInFocus(P) ((P).pCurrent ? (P).pCurrent : _AggInFocus(&(P))) static AggElem *_AggInFocus(Agg *p){ HashElem *pElem = sqliteHashFirst(&p->hash); if( pElem==0 ){ AggInsert(p,"",1); pElem = sqliteHashFirst(&p->hash); } return pElem ? sqliteHashData(pElem) : 0; } +#endif +/* +** Store a pointer to the AggElem currently in focus in *ppElem. Return +** SQLITE_OK if successful, otherwise an error-code. +*/ +static int AggInFocus(Agg *p, AggElem **ppElem){ + int rc; + int res; + + if( p->pCurrent ){ + *ppElem = p->pCurrent; + return SQLITE_OK; + } + + rc = sqlite3BtreeFirst(p->pCsr, &res); + if( rc!=SQLITE_OK ){ + return rc; + } + if( res!=0 ){ + rc = AggInsert(p,"",1); + *ppElem = p->pCurrent; + }else{ + rc = sqlite3BtreeData(p->pCsr, 0, 4, (char *)ppElem); + } + return rc; +} /* ** Pop the stack N times. */ static void popStack(Mem **ppTos, int N){ @@ -1296,11 +1325,13 @@ if( ctx.pVdbeFunc ){ int mask = pOp->p2; for(i=0; inAux; i++){ struct AuxData *pAux = &ctx.pVdbeFunc->apAux[i]; if( (i>31 || !(mask&(1<pAux ){ - pAux->xDelete(pAux->pAux); + if( pAux->xDelete ){ + pAux->xDelete(pAux->pAux); + } pAux->pAux = 0; } } pOp->p3 = (char *)ctx.pVdbeFunc; pOp->p3type = P3_VDBEFUNC; @@ -4257,17 +4288,22 @@ pc = pOp->p2 - 1; } break; } -/* Opcode: AggReset * P2 * +/* Opcode: AggReset * P2 P3 ** ** Reset the aggregator so that it no longer contains any data. -** Future aggregator elements will contain P2 values each. +** Future aggregator elements will contain P2 values each and be sorted +** using the KeyInfo structure pointed to by P3. */ case OP_AggReset: { - sqlite3VdbeAggReset(&p->agg); + assert( !pOp->p3 || pOp->p3type==P3_KEYINFO ); + rc = sqlite3VdbeAggReset(db, &p->agg, (KeyInfo *)pOp->p3); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } p->agg.nMem = pOp->p2; p->agg.apFunc = sqliteMalloc( p->agg.nMem*sizeof(p->agg.apFunc[0]) ); if( p->agg.apFunc==0 ) goto no_mem; break; } @@ -4354,25 +4390,33 @@ ** 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: { - AggElem *pElem; char *zKey; int nKey; - + int res; assert( pTos>=p->aStack ); Stringify(pTos, db->enc); zKey = pTos->z; nKey = pTos->n; - pElem = sqlite3HashFind(&p->agg.hash, zKey, nKey); - if( pElem ){ - p->agg.pCurrent = pElem; + rc = sqlite3BtreeMoveto(p->agg.pCsr, zKey, nKey, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + if( res==0 ){ + rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), + (char *)&p->agg.pCurrent); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } pc = pOp->p2 - 1; }else{ - AggInsert(&p->agg, zKey, nKey); - if( sqlite3_malloc_failed ) goto no_mem; + rc = AggInsert(&p->agg, zKey, nKey); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } } Release(pTos); pTos--; break; } @@ -4381,13 +4425,15 @@ ** ** 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: { - AggElem *pFocus = AggInFocus(p->agg); + AggElem *pFocus; + int i = pOp->p2; Mem *pMem; - int i = pOp->p2; + rc = AggInFocus(&p->agg, &pFocus); + if( rc!=SQLITE_OK ) goto abort_due_to_error; assert( pTos>=p->aStack ); if( pFocus==0 ) goto no_mem; assert( i>=0 && iagg.nMem ); Deephemeralize(pTos); pMem = &pFocus->aMem[i]; @@ -4407,13 +4453,15 @@ ** 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. */ case OP_AggGet: { - AggElem *pFocus = AggInFocus(p->agg); + AggElem *pFocus; Mem *pMem; int i = pOp->p2; + rc = AggInFocus(&p->agg, &pFocus); + if( rc!=SQLITE_OK ) goto abort_due_to_error; if( pFocus==0 ) goto no_mem; assert( i>=0 && iagg.nMem ); pTos++; pMem = &pFocus->aMem[i]; *pTos = *pMem; @@ -4438,23 +4486,32 @@ ** 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: { + int res; CHECK_FOR_INTERRUPT; - if( p->agg.pSearch==0 ){ - p->agg.pSearch = sqliteHashFirst(&p->agg.hash); + if( p->agg.searching==0 ){ + p->agg.searching = 1; + rc = sqlite3BtreeFirst(p->agg.pCsr, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; }else{ - p->agg.pSearch = sqliteHashNext(p->agg.pSearch); + rc = sqlite3BtreeNext(p->agg.pCsr, &res); + if( rc!=SQLITE_OK ) goto abort_due_to_error; } - if( p->agg.pSearch==0 ){ + if( res!=0 ){ pc = pOp->p2 - 1; - } else { + }else{ int i; sqlite3_context ctx; Mem *aMem; - p->agg.pCurrent = sqliteHashData(p->agg.pSearch); + + rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), + (char *)&p->agg.pCurrent); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } aMem = p->agg.pCurrent->aMem; for(i=0; iagg.nMem; i++){ int freeCtx; if( p->agg.apFunc[i]==0 ) continue; if( p->agg.apFunc[i]->xFinalize==0 ) continue; Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -221,13 +221,19 @@ 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 */ +#if 0 HashElem *pSearch; /* The hash element for pCurrent */ Hash hash; /* Hash table of all aggregate elements */ - FuncDef **apFunc; /* Information about aggregate functions */ +#endif + Btree *pBtree; /* The temporary btree used to group elements */ + 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 */ @@ -342,11 +348,11 @@ /* ** Function prototypes */ void sqlite3VdbeCleanupCursor(Cursor*); void sqlite3VdbeSorterReset(Vdbe*); -void sqlite3VdbeAggReset(Agg*); +int sqlite3VdbeAggReset(sqlite *, Agg *, KeyInfo *); void sqlite3VdbeKeylistFree(Keylist*); void sqliteVdbePopStack(Vdbe*,int); int sqlite3VdbeCursorMoveto(Cursor*); #if !defined(NDEBUG) || defined(VDBE_PROFILE) void sqlite3VdbePrintOp(FILE*, int, Op*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -649,12 +649,10 @@ for(n=0; nnVar; n++){ p->apVar[n].flags = MEM_Null; } } - sqlite3HashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0); - p->agg.pSearch = 0; #ifdef SQLITE_DEBUG if( (p->db->flags & SQLITE_VdbeListing)!=0 || sqlite3OsFileExists("vdbe_explain") ){ int i; @@ -709,10 +707,11 @@ ** 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 0 void sqlite3VdbeAggReset(Agg *pAgg){ int i; HashElem *p; for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){ AggElem *pElem = sqliteHashData(p); @@ -745,10 +744,124 @@ pAgg->apFunc = 0; pAgg->pCurrent = 0; pAgg->pSearch = 0; pAgg->nMem = 0; } +#endif + +/* +** 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(sqlite *db, Agg *pAgg, KeyInfo *pKeyInfo){ + int i; + int rc = 0; + BtCursor *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( res!=SQLITE_OK ){ + return rc; + } + assert( pAgg->apFunc!=0 ); + for(i=0; inMem; i++){ + Mem *pMem = &pElem->aMem[i]; + if( pAgg->apFunc[i] && (pMem->flags & MEM_AggCtx)!=0 ){ + sqlite3_context ctx; + ctx.pFunc = pAgg->apFunc[i]; + ctx.s.flags = MEM_Null; + ctx.pAgg = pMem->z; + ctx.cnt = pMem->i; + ctx.isStep = 0; + ctx.isError = 0; + (*pAgg->apFunc[i]->xFinalize)(&ctx); + if( pMem->z!=0 && pMem->z!=pMem->z ){ + sqliteFree(pMem->z); + } + }else if( pMem->flags&MEM_Dyn ){ + sqliteFree(pMem->z); + } + } + sqliteFree(pElem); + rc=sqlite3BtreeNext(pCsr, &res); + } + if( rc!=SQLITE_OK ){ + return rc; + } + + sqlite3BtreeCloseCursor(pCsr); + sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab); + } + + /* 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 ); + rc = sqlite3BtreeFactory(db, 0, 0, TEMP_PAGES, &pAgg->pBtree); + if( rc!=SQLITE_OK ) return rc; + sqlite3BtreeBeginTrans(pAgg->pBtree, 1, 0); + 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; +} + /* ** Delete a keylist */ void sqlite3VdbeKeylistFree(Keylist *p){ @@ -837,11 +950,11 @@ if( p->zLine ){ sqliteFree(p->zLine); p->zLine = 0; } p->nLineAlloc = 0; - sqlite3VdbeAggReset(&p->agg); + sqlite3VdbeAggReset(0, &p->agg, 0); if( p->keylistStack ){ int ii; for(ii = 0; ii < p->keylistStackDepth; ii++){ sqlite3VdbeKeylistFree(p->keylistStack[ii]); } Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -47,11 +47,12 @@ */ char *z; int n; int rc; - rc = sqlite3utfTranslate(pMem->z, pMem->n, pMem->enc, &z, &n, desiredEnc); + rc = sqlite3utfTranslate(pMem->z, pMem->n, pMem->enc, (void **)&z, + &n, desiredEnc); if( rc!=SQLITE_OK ){ return rc; } if( pMem->flags&MEM_Dyn ){ sqliteFree(pMem->z); Index: test/select3.test ================================================================== --- test/select3.test +++ test/select3.test @@ -10,11 +10,11 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing aggregate functions and the # GROUP BY and HAVING clauses of SELECT statements. # -# $Id: select3.test,v 1.10 2004/05/27 17:22:56 drh Exp $ +# $Id: select3.test,v 1.11 2004/06/11 13:19:22 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Build some test data @@ -76,11 +76,11 @@ } {1 1 3 1 5 2 7 4 9 8 11 15} do_test select3-2.7 { execsql { SELECT log*2+1 AS x, count(*) AS y FROM t1 GROUP BY x ORDER BY y } -} {1 1 3 1 5 2 7 4 9 8 11 15} +} {3 1 1 1 5 2 7 4 9 8 11 15} do_test select3-2.8 { execsql { SELECT log*2+1 AS x, count(*) AS y FROM t1 GROUP BY x ORDER BY 10-(x+y) } } {11 15 9 8 7 4 5 2 3 1 1 1}