-C Move\sthe\sbuild-in\sfunction\sdefinitions\sinto\sa\snew\ssource\sfile\s"func.c".\s(CVS\s391)
-D 2002-02-24T01:55:16
+C Code\sfor\suser-defined\saggregates\sadded.\s\sLegacy\stests\sall\spass\sbut\sthere\nhas\sbeen\sno\stesting\sof\sthe\snew\suser-defined\saggregate\scode.\s(CVS\s392)
+D 2002-02-24T03:25:15
F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c 1da051784be0155ae579d47890db74f0186f9b9f
F src/delete.c 950d8f9097361419f1963875f9943344b469cf02
-F src/expr.c 4f9db24c4e90585fd046703d4f91c10b453867fa
+F src/expr.c 5ed59aed47431a540263da2ca79c37a97c23e8fd
F src/func.c f06739ac3266fe237a8079415c75b4fe27f9b604
F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 61d4a739956aaeb124cdf12c34c66e99ae34212c
+F src/select.c 410e4dfff7d2cfb8712529519b94410e2c7e7930
F src/shell.c cbf48bf0ca35c4e0d8a7d2a86f7724f52c525cd7
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in f1421919a4437a377fb712b98835a224482e776e
-F src/sqliteInt.h 55a15c38dbb2cfcf148b56b51945b50d5d41f254
+F src/sqliteInt.h d20712633cd07c547c5dde24a4d4c43c368334f7
F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997
F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6
-F src/vdbe.c 7d4c6e2f4861bed0ef5abd5d39153bfd401e30ff
-F src/vdbe.h c3be021687ab2eb8517052a24e1df7e7355f0d77
+F src/vdbe.c b2c51a114cff35b9ccb7bbfe57483dabf77ed63f
+F src/vdbe.h 98e445d624fb7d3442b5c06058dcb01182397d12
F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585
F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P c490a1ff951c5d4a2de8e4f8d349189bfaef7f74
-R 0358ce63954d177a9cd11817b7f3c75f
+P 530b0f4f2def89e200b7b0724a5967bf981bd91d
+R b1dfb8bf18a05ccd329e7446e18d18c8
U drh
-Z 379bf92a060e853d2b3e64f2e8f8e8ad
+Z 0bcdda8b20886809bb64b19128bb3b89
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.65 2002/02/23 02:32:10 drh Exp $
+** $Id: select.c,v 1.66 2002/02/24 03:25:16 drh Exp $
*/
#include "sqliteInt.h"
*/
if( isAgg ){
sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg);
+ for(i=0; i<pParse->nAgg; i++){
+ UserFunc *pUser;
+ if( (pUser = pParse->aAgg[i].pUser)!=0 && pUser->xFinalize!=0 ){
+ sqliteVdbeAddOp(v, OP_AggFinalizer, 0, i);
+ sqliteVdbeChangeP3(v, -1, (char*)pUser->xFinalize, P3_POINTER);
+ }
+ }
if( pGroupBy==0 ){
sqliteVdbeAddOp(v, OP_String, 0, 0);
sqliteVdbeAddOp(v, OP_AggFocus, 0, 0);
}
for(i=0; i<pParse->nAgg; i++){
Expr *pE;
- int op;
+ int op, j;
if( !pParse->aAgg[i].isAgg ) continue;
pE = pParse->aAgg[i].pExpr;
if( pE==0 ){
continue;
}
assert( pE->op==TK_AGG_FUNCTION );
- assert( pE->pList!=0 && pE->pList->nExpr==1 );
- sqliteExprCode(pParse, pE->pList->a[0].pExpr);
+ assert( pE->pList!=0 );
+ for(j=0; j<pE->pList->nExpr; j++){
+ sqliteExprCode(pParse, pE->pList->a[j].pExpr);
+ }
sqliteVdbeAddOp(v, OP_AggGet, 0, i);
switch( pE->iColumn ){
- case FN_Min: op = OP_Min; break;
- case FN_Max: op = OP_Max; break;
- case FN_Avg: op = OP_Add; break;
- case FN_Sum: op = OP_Add; break;
+ case FN_Min: op = OP_Min; break;
+ case FN_Max: op = OP_Max; break;
+ case FN_Avg: op = OP_Add; break;
+ case FN_Sum: op = OP_Add; break;
+ case FN_Unknown: op = OP_AggFunc; break;
+ }
+ if( op!=OP_AggFunc ){
+ sqliteVdbeAddOp(v, op, 0, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_AggFunc, 0, pE->pList->nExpr);
+ assert( pParse->aAgg[i].pUser!=0 );
+ assert( pParse->aAgg[i].pUser->xStep!=0 );
+ sqliteVdbeChangeP3(v,-1,(char*)pParse->aAgg[i].pUser->xStep,P3_POINTER);
}
- sqliteVdbeAddOp(v, op, 0, 0);
sqliteVdbeAddOp(v, OP_AggSet, 0, i);
}
}
-
/* End the database scan loop.
*/
sqliteWhereEnd(pWInfo);
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.122 2002/02/23 23:45:45 drh Exp $
+** $Id: vdbe.c,v 1.123 2002/02/24 03:25:16 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */
#define STK_Static 0x0020 /* zStack[] points to a static string */
+/* The following STK_ value appears only in AggElem.aMem.s.flag fields.
+** It indicates that the corresponding AggElem.aMem.z points to a
+** user-defined aggregate context that needs to be finalized.
+*/
+#define STK_AggCtx 0x0040 /* zStack[] points to an user function context */
+
/*
** The "context" argument for a user-defined function.
*/
AggElem *pCurrent; /* The AggElem currently in focus */
HashElem *pSearch; /* The hash element for pCurrent */
Hash hash; /* Hash table of all aggregate elements */
+ void (**axFinalize)(void*,void*); /* Array of nMem finalizers */
};
struct AggElem {
char *zKey; /* The key to this AggElem */
HashElem *p;
for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){
AggElem *pElem = sqliteHashData(p);
+ assert( pAgg->axFinalize!=0 );
for(i=0; i<pAgg->nMem; i++){
if( pElem->aMem[i].s.flags & STK_Dyn ){
sqliteFree(pElem->aMem[i].z);
+ }else if( pAgg->axFinalize[i] && (pElem->aMem[i].s.flags & STK_AggCtx) ){
+ (pAgg->axFinalize[i])((void*)pElem->aMem[i].z, 0);
}
}
sqliteFree(pElem);
}
sqliteHashClear(&pAgg->hash);
+ sqliteFree(pAgg->axFinalize);
+ pAgg->axFinalize = 0;
pAgg->pCurrent = 0;
pAgg->pSearch = 0;
pAgg->nMem = 0;
"SortMakeRec", "SortMakeKey", "Sort", "SortNext",
"SortCallback", "SortReset", "FileOpen", "FileRead",
"FileColumn", "AggReset", "AggFocus", "AggIncr",
- "AggNext", "AggSet", "AggGet", "SetInsert",
- "SetFound", "SetNotFound", "MakeRecord", "MakeKey",
- "MakeIdxKey", "IncrKey", "Goto", "If",
- "Halt", "ColumnCount", "ColumnName", "Callback",
- "NullCallback", "Integer", "String", "Pop",
- "Dup", "Pull", "Push", "MustBeInt",
- "Add", "AddImm", "Subtract", "Multiply",
- "Divide", "Remainder", "BitAnd", "BitOr",
- "BitNot", "ShiftLeft", "ShiftRight", "AbsValue",
- "Precision", "Min", "Max", "Like",
- "Glob", "Eq", "Ne", "Lt",
- "Le", "Gt", "Ge", "IsNull",
- "NotNull", "Negative", "And", "Or",
- "Not", "Concat", "Noop", "Strlen",
- "Substr", "UserFunc", "UserAgg", "Limit",
+ "AggNext", "AggSet", "AggGet", "AggFinalizer",
+ "AggFunc", "SetInsert", "SetFound", "SetNotFound",
+ "MakeRecord", "MakeKey", "MakeIdxKey", "IncrKey",
+ "Goto", "If", "Halt", "ColumnCount",
+ "ColumnName", "Callback", "NullCallback", "Integer",
+ "String", "Pop", "Dup", "Pull",
+ "Push", "MustBeInt", "Add", "AddImm",
+ "Subtract", "Multiply", "Divide", "Remainder",
+ "BitAnd", "BitOr", "BitNot", "ShiftLeft",
+ "ShiftRight", "AbsValue", "Precision", "Min",
+ "Max", "Like", "Glob", "Eq",
+ "Ne", "Lt", "Le", "Gt",
+ "Ge", "IsNull", "NotNull", "Negative",
+ "And", "Or", "Not", "Concat",
+ "Noop", "Strlen", "Substr", "UserFunc",
+ "Limit",
};
/*
case OP_AggReset: {
AggReset(&p->agg);
p->agg.nMem = pOp->p2;
+ p->agg.axFinalize = sqliteMalloc( p->agg.nMem*sizeof(p->agg.axFinalize[0]) );
+ break;
+}
+
+/* Opcode: AggFinalizer * P2 P3
+**
+** Register a finializer function for the P2-th column of the aggregate.
+** The P3 parameter is a pointer to the finalizer.
+** There should be one instance of this opcode immediately following
+** each AggReset for each user defined aggregate function that is used
+** in a SELECT.
+**
+** All finalizers must be registered so that user-defined aggregate
+** function contexts can be deallocated if the VDBE aborts.
+*/
+case OP_AggFinalizer: {
+ int i = pOp->p2;
+ VERIFY( if( p->agg.nMem<=i ) goto bad_instruction; );
+ p->agg.axFinalize[i] = (void(*)(void*,void*))pOp->p3;
+ break;
+}
+
+/* Opcode: AggFunc * P2 P3
+**
+** Execute the step function for a user-defined aggregate. The
+** function has P2 arguments. P3 is a pointer to the step function.
+**
+** The top of the stack should be the function context. The P2
+** parameters occur below the function context on the stack. The
+** revised function context remains on the stack after this op-code
+** finishes.
+*/
+case OP_AggFunc: {
+ int n = pOp->p2;
+ int i;
+ void *pCtx;
+ void *(*xStep)(void*,int,const char**);
+
+ if( aStack[p->tos].flags & STK_AggCtx ){
+ pCtx = zStack[p->tos];
+ }else{
+ pCtx = 0;
+ }
+ VERIFY( if( n<=0 ) goto bad_instruction; )
+ VERIFY( if( p->tos+1<n ) goto not_enough_stack; )
+ for(i=p->tos-n; i<p->tos; i++){
+ if( (aStack[i].flags & STK_Null)==0 ){
+ if( Stringify(p, i) ) goto no_mem;
+ }
+ }
+ xStep = (void*(*)(void*,int,const char**))pOp->p3;
+ pCtx = xStep(pCtx, n, (const char**)&zStack[p->tos-n]);
+ PopStack(p, n+1);
+ VERIFY( NeedStack(p, p->tos+1); )
+ p->tos++;
+ aStack[p->tos].flags = STK_AggCtx;
+ zStack[p->tos] = (char*)pCtx;
break;
}
pMem->z = zStack[tos];
zStack[tos] = 0;
aStack[tos].flags = 0;
- }else if( pMem->s.flags & STK_Static ){
+ }else if( pMem->s.flags & (STK_Static|STK_AggCtx) ){
pMem->z = zStack[tos];
}else if( pMem->s.flags & STK_Str ){
pMem->z = pMem->s.z;
if( p->agg.pSearch==0 ){
pc = pOp->p2 - 1;
} else {
+ int i;
+ UserFuncContext ctx;
+ void *pCtx;
+ Mem *aMem;
+ int nErr = 0;
p->agg.pCurrent = sqliteHashData(p->agg.pSearch);
+ aMem = p->agg.pCurrent->aMem;
+ for(i=0; i<p->agg.nMem; i++){
+ if( p->agg.axFinalize[i]==0 ) continue;
+ if( (aMem[i].s.flags & STK_AggCtx)==0 ) continue;
+ ctx.s.flags = STK_Null;
+ ctx.z = 0;
+ pCtx = (void*)aMem[i].z;
+ (*p->agg.axFinalize[i])(pCtx, &ctx);
+ aMem[i].s = ctx.s;
+ aMem[i].z = ctx.z;
+ if( (aMem[i].s.flags & STK_Str) &&
+ (aMem[i].s.flags & (STK_Dyn|STK_Static))==0 ){
+ aMem[i].z = aMem[i].s.z;
+ }
+ nErr += ctx.isError;
+ }
}
break;
}