diff --git a/manifest b/manifest index 74c9d3df2d..3e7fea43e5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -23,7 +23,7 @@ F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d 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 @@ -37,11 +37,11 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 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 @@ -51,8 +51,8 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f 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 @@ -126,7 +126,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 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 diff --git a/manifest.uuid b/manifest.uuid index cc61005f81..caeaedcb00 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -530b0f4f2def89e200b7b0724a5967bf981bd91d \ No newline at end of file +1e037eb303d8508cb2ea3418e71b03315d895fbd \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index d986c7c9a0..eefcedc5bd 100644 --- a/src/expr.c +++ b/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.43 2002/02/23 23:45:45 drh Exp $ +** $Id: expr.c,v 1.44 2002/02/24 03:25:15 drh Exp $ */ #include "sqliteInt.h" @@ -1257,6 +1257,12 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ if( i<0 ) return 1; pParse->aAgg[i].isAgg = 1; pParse->aAgg[i].pExpr = pExpr; + if( pExpr->iColumn==FN_Unknown ){ + pParse->aAgg[i].pUser = sqliteFindUserFunction(pParse->db, + pExpr->token.z, pExpr->token.n, pExpr->pList->nExpr, 0); + }else{ + pParse->aAgg[i].pUser = 0; + } } pExpr->iAgg = i; break; diff --git a/src/select.c b/src/select.c index ac2fc5803b..987153c4ac 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.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" @@ -1101,6 +1101,13 @@ int sqliteSelect( */ if( isAgg ){ sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg); + for(i=0; inAgg; 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); @@ -1165,7 +1172,7 @@ int sqliteSelect( } for(i=0; inAgg; i++){ Expr *pE; - int op; + int op, j; if( !pParse->aAgg[i].isAgg ) continue; pE = pParse->aAgg[i].pExpr; if( pE==0 ){ @@ -1173,21 +1180,30 @@ int sqliteSelect( 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; jpList->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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index b86cf34675..89640a23d3 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.92 2002/02/24 01:55:17 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.93 2002/02/24 03:25:16 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -509,6 +509,7 @@ struct Select { struct AggExpr { int isAgg; /* if TRUE contains an aggregate function */ Expr *pExpr; /* The expression */ + UserFunc *pUser; /* User-defined aggregate function */ }; /* diff --git a/src/vdbe.c b/src/vdbe.c index 5d6faa929b..39b9e1e5a2 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** 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 @@ -138,6 +138,12 @@ typedef struct Mem Mem; #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. */ @@ -162,6 +168,7 @@ struct Agg { 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 */ @@ -577,14 +584,19 @@ static void AggReset(Agg *pAgg){ HashElem *p; for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){ AggElem *pElem = sqliteHashData(p); + assert( pAgg->axFinalize!=0 ); for(i=0; inMem; 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; @@ -954,21 +966,22 @@ static char *zOpName[] = { 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", }; /* @@ -4300,6 +4313,63 @@ case OP_MemLoad: { 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+1tos-n; itos; 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; } @@ -4393,7 +4463,7 @@ case OP_AggSet: { 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; @@ -4446,7 +4516,28 @@ case OP_AggNext: { 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; iagg.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; } diff --git a/src/vdbe.h b/src/vdbe.h index a3b66a29c6..490dba5dcd 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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.45 2002/02/23 23:45:46 drh Exp $ +** $Id: vdbe.h,v 1.46 2002/02/24 03:25:16 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -140,72 +140,73 @@ typedef struct VdbeOp VdbeOp; #define OP_AggNext 61 #define OP_AggSet 62 #define OP_AggGet 63 +#define OP_AggFinalizer 64 +#define OP_AggFunc 65 -#define OP_SetInsert 64 -#define OP_SetFound 65 -#define OP_SetNotFound 66 +#define OP_SetInsert 66 +#define OP_SetFound 67 +#define OP_SetNotFound 68 -#define OP_MakeRecord 67 -#define OP_MakeKey 68 -#define OP_MakeIdxKey 69 -#define OP_IncrKey 70 +#define OP_MakeRecord 69 +#define OP_MakeKey 70 +#define OP_MakeIdxKey 71 +#define OP_IncrKey 72 -#define OP_Goto 71 -#define OP_If 72 -#define OP_Halt 73 +#define OP_Goto 73 +#define OP_If 74 +#define OP_Halt 75 -#define OP_ColumnCount 74 -#define OP_ColumnName 75 -#define OP_Callback 76 -#define OP_NullCallback 77 +#define OP_ColumnCount 76 +#define OP_ColumnName 77 +#define OP_Callback 78 +#define OP_NullCallback 79 -#define OP_Integer 78 -#define OP_String 79 -#define OP_Pop 80 -#define OP_Dup 81 -#define OP_Pull 82 -#define OP_Push 83 -#define OP_MustBeInt 84 +#define OP_Integer 80 +#define OP_String 81 +#define OP_Pop 82 +#define OP_Dup 83 +#define OP_Pull 84 +#define OP_Push 85 +#define OP_MustBeInt 86 -#define OP_Add 85 -#define OP_AddImm 86 -#define OP_Subtract 87 -#define OP_Multiply 88 -#define OP_Divide 89 -#define OP_Remainder 90 -#define OP_BitAnd 91 -#define OP_BitOr 92 -#define OP_BitNot 93 -#define OP_ShiftLeft 94 -#define OP_ShiftRight 95 -#define OP_AbsValue 96 -#define OP_Precision 97 -#define OP_Min 98 -#define OP_Max 99 -#define OP_Like 100 -#define OP_Glob 101 -#define OP_Eq 102 -#define OP_Ne 103 -#define OP_Lt 104 -#define OP_Le 105 -#define OP_Gt 106 -#define OP_Ge 107 -#define OP_IsNull 108 -#define OP_NotNull 109 -#define OP_Negative 110 -#define OP_And 111 -#define OP_Or 112 -#define OP_Not 113 -#define OP_Concat 114 -#define OP_Noop 115 -#define OP_Strlen 116 -#define OP_Substr 117 -#define OP_UserFunc 118 -#define OP_UserAgg 119 +#define OP_Add 87 +#define OP_AddImm 88 +#define OP_Subtract 89 +#define OP_Multiply 90 +#define OP_Divide 91 +#define OP_Remainder 92 +#define OP_BitAnd 93 +#define OP_BitOr 94 +#define OP_BitNot 95 +#define OP_ShiftLeft 96 +#define OP_ShiftRight 97 +#define OP_AbsValue 98 +#define OP_Precision 99 +#define OP_Min 100 +#define OP_Max 101 +#define OP_Like 102 +#define OP_Glob 103 +#define OP_Eq 104 +#define OP_Ne 105 +#define OP_Lt 106 +#define OP_Le 107 +#define OP_Gt 108 +#define OP_Ge 109 +#define OP_IsNull 110 +#define OP_NotNull 111 +#define OP_Negative 112 +#define OP_And 113 +#define OP_Or 114 +#define OP_Not 115 +#define OP_Concat 116 +#define OP_Noop 117 +#define OP_Strlen 118 +#define OP_Substr 119 +#define OP_UserFunc 120 -#define OP_Limit 120 +#define OP_Limit 121 -#define OP_MAX 120 +#define OP_MAX 121 /* ** Prototypes for the VDBE interface. See comments on the implementation