diff --git a/manifest b/manifest index 78fb1c9a1e..fa6d0e973e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\svarious\scollation\ssequence\sissues.\s(CVS\s1568) -D 2004-06-11T10:51:27 +C Have\sthe\svdbe\saggregator\suse\sa\sbtree\stable\sinstead\sof\sa\shash\stable.\s(CVS\s1569) +D 2004-06-11T13:19:21 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -54,7 +54,7 @@ F src/parse.y 097438674976355a10cf177bd97326c548820b86 F src/pragma.c 0bc3adea28df802074996bec067d506d55d28f84 F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 -F src/select.c 3559dcd25b3e86150fb991b866b00b5152f15cac +F src/select.c d29488f86e61e0d45dff318e1f04ba6a7e5782d0 F src/shell.c ca519519dcbbc582f6d88f7d0e7583b857fd3469 F src/sqlite.h.in 2b6afe1de6935d3dfbd6042f46a62f1b7c3b3992 F src/sqliteInt.h 625faf4c9ce2f99b9c85a2bca5c4e73736c30262 @@ -71,12 +71,12 @@ F src/update.c 168b6d523087ca4545b74ec9f3102b1f3c6b1e38 F src/utf.c c2c8e445bfea724f3502609d6389fe66651f02ab F src/util.c e8629f04d920ae968fced709dc7a3a2c62b65ac4 F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f -F src/vdbe.c 688ae431918ee4aefe53d395d7c43bb1aa32e458 +F src/vdbe.c 56a97efecedecff984599763552a96cacce86319 F src/vdbe.h 46f74444a213129bc4b5ce40124dd8ed613b0cde -F src/vdbeInt.h e27e29ffe5b8b3998032e394631944dacafe5c54 +F src/vdbeInt.h 57b7001bc18de348f2180c5fa8a85b687592a19b F src/vdbeapi.c bcf5821ed09070d586898374b905861c4dd73d0b -F src/vdbeaux.c 73764dadcdbf79aa2d948f863eae07b18589e663 -F src/vdbemem.c b1599f5d24131107a21a54e618e372e1252de958 +F src/vdbeaux.c 117d0d0a3178c203a0093d6fd7c26b69c34e9ed5 +F src/vdbemem.c 6407b5dcd6cae43ee1342d49bcd574f0f23f5beb F src/where.c dda77afaa593cd54e5955ec433076de18faf62f6 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/attach.test aed659e52635662bcd5069599aaca823533edf5a @@ -145,7 +145,7 @@ F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d F test/rowid.test 863e6e75878cccf03d166fe52023f20e09508683 F test/select1.test 2f161f9cdf9fb577336bc8c930edade538567123 F test/select2.test 91a2225926039b0d1687840735c284dbbf89f0bc -F test/select3.test f8ff60d98c7b4898f5e7326f0c5929ba56f5d047 +F test/select3.test ab2e583154ee230fa4b46b06512775a38cd9d8b0 F test/select4.test 86e72fc3b07de4fe11439aa419e37db3c49467e2 F test/select5.test 3f3f0f31e674fa61f8a3bdb6af1517dfae674081 F test/select6.test a9e31906e700e7c7592c4d0acfc022808f718baf @@ -222,7 +222,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248 F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P 0e420f72cd5885e32914b4d958bad811fdd9fb77 -R 51b6a0eb20d4bdd91945614e74564613 +P 66835ee67051027456a536e33b2f88a741654525 +R 17851c4c7a9d880622be1c17f4d88abf U danielk1977 -Z a2401ce0227bab41442da327e235dd95 +Z 37fd25919977ced5eda4bf17b983a171 diff --git a/manifest.uuid b/manifest.uuid index 392feed377..88249baf9d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -66835ee67051027456a536e33b2f88a741654525 \ No newline at end of file +8d56118f64dbaf8c8006266fa7026f900a4a16bd \ No newline at end of file diff --git a/src/select.c b/src/select.c index f64853cf56..b0e54208b7 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.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" @@ -2441,7 +2441,7 @@ int sqlite3Select( /* 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 ){ @@ -2451,6 +2451,21 @@ int sqlite3Select( 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); } } diff --git a/src/vdbe.c b/src/vdbe.c index 97e088a616..55a4e7323e 100644 --- a/src/vdbe.c +++ b/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.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" @@ -160,23 +160,25 @@ static void _storeTypeInfo(Mem *pMem){ ** 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; + + assert( p->pCsr ); + rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*)); + if( rc!=SQLITE_OK ){ + sqliteFree(pElem); + return rc; } - for(i=0, pMem=pElem->aMem; inMem; i++, pMem++){ - pMem->flags = MEM_Null; + + for(i=0; inMem; i++){ + pElem->aMem[i].flags = MEM_Null; } p->pCurrent = pElem; return 0; @@ -185,6 +187,7 @@ static int AggInsert(Agg *p, char *zKey, int nKey){ /* ** 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); @@ -194,6 +197,32 @@ static AggElem *_AggInFocus(Agg *p){ } 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. @@ -1298,7 +1327,9 @@ case OP_Function: { 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; } } @@ -4259,13 +4290,18 @@ case OP_MemIncr: { 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; @@ -4356,21 +4392,29 @@ case OP_AggFunc: { ** 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--; @@ -4383,9 +4427,11 @@ case OP_AggFocus: { ** aggregate. String values are duplicated into new memory. */ case OP_AggSet: { - AggElem *pFocus = AggInFocus(p->agg); - Mem *pMem; + AggElem *pFocus; int i = pOp->p2; + Mem *pMem; + 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 ); @@ -4409,9 +4455,11 @@ case OP_AggSet: { ** 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++; @@ -4440,19 +4488,28 @@ case OP_AggGet: { ** 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; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index c466e8e2d2..8099747518 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -223,9 +223,15 @@ 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 */ @@ -344,7 +350,7 @@ struct Vdbe { */ void sqlite3VdbeCleanupCursor(Cursor*); void sqlite3VdbeSorterReset(Vdbe*); -void sqlite3VdbeAggReset(Agg*); +int sqlite3VdbeAggReset(sqlite *, Agg *, KeyInfo *); void sqlite3VdbeKeylistFree(Keylist*); void sqliteVdbePopStack(Vdbe*,int); int sqlite3VdbeCursorMoveto(Cursor*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 0e43a1e120..cddef5a64e 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -651,8 +651,6 @@ void sqlite3VdbeMakeReady( } } - 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") @@ -711,6 +709,7 @@ void sqlite3VdbeSorterReset(Vdbe *p){ ** private context. If the finalizer has not been called yet, call it ** now. */ +#if 0 void sqlite3VdbeAggReset(Agg *pAgg){ int i; HashElem *p; @@ -747,6 +746,120 @@ void sqlite3VdbeAggReset(Agg *pAgg){ 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 @@ -839,7 +952,7 @@ static void Cleanup(Vdbe *p){ 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++){ diff --git a/src/vdbemem.c b/src/vdbemem.c index b69dfb6ad4..1c9e09cec2 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -49,7 +49,8 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ 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; } diff --git a/test/select3.test b/test/select3.test index 0e9dc05b84..fabc17f0f1 100644 --- a/test/select3.test +++ b/test/select3.test @@ -12,7 +12,7 @@ # 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 @@ -78,7 +78,7 @@ 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)