Code for user-defined aggregates added. Legacy tests all pass but there

has been no testing of the new user-defined aggregate code. (CVS 392)

FossilOrigin-Name: 1e037eb303d8508cb2ea3418e71b03315d895fbd
This commit is contained in:
drh 2002-02-24 03:25:14 +00:00
parent dc04c58360
commit e5095355d6
7 changed files with 214 additions and 99 deletions

View File

@ -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

View File

@ -1 +1 @@
530b0f4f2def89e200b7b0724a5967bf981bd91d
1e037eb303d8508cb2ea3418e71b03315d895fbd

View File

@ -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;

View File

@ -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; 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);
@ -1165,7 +1172,7 @@ int sqliteSelect(
}
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 ){
@ -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; 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);

View File

@ -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 */
};
/*

View File

@ -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 <ctype.h>
@ -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; 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;
@ -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+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;
}
@ -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; 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;
}

View File

@ -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