diff --git a/manifest b/manifest index 6dc9e9595d..b6fe23ef81 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\suser-defined\sfunction\sroutines\sfailed\sto\sdetect\sa\smalloc()\sfailure.\s(CVS\s397) -D 2002-02-27T01:53:13 +C Revise\sthe\sAPI\sfor\suser-defined\sfunctions.\s(CVS\s398) +D 2002-02-27T19:00:21 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -23,12 +23,12 @@ F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/build.c 7ada2426caba70cb1072ba268bedb694b5018065 F src/delete.c 950d8f9097361419f1963875f9943344b469cf02 -F src/expr.c 92ff4a2d87f7443f6081ba2d51e25c27e10c7b4e -F src/func.c bed0099aaa558f8bfc50d9349bf7da2c99903f47 +F src/expr.c ea2209a6bdefea23db988b470fc16dbf9d5b3a5a +F src/func.c f020b8c659c95ba1e46cab548ada582ebf8c6c3c F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11 -F src/main.c cbfa66d53578935790488f37f476d4932e36c01a +F src/main.c 98f6c6f288e8c9e986634504d9882f348a56edb6 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6 @@ -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 c74b6349d5630e05aceef73aaa8ed5e09a793fc4 +F src/select.c a8e90e8f8366b72830010dd6a9bfbf121d2e9370 F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e -F src/sqlite.h.in f1421919a4437a377fb712b98835a224482e776e -F src/sqliteInt.h 4076aef24feec5ad98a9aa98200af29428ef94ba +F src/sqlite.h.in 681dd71442788f900439b624ad1ea2b98f178904 +F src/sqliteInt.h 4e746aa13c5fa39a31a89b73afcd33fa164e8b4f 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 40a12067fa7b18c7e0461b3256b1353c755b59ac -F src/vdbe.c b2c51a114cff35b9ccb7bbfe57483dabf77ed63f -F src/vdbe.h 98e445d624fb7d3442b5c06058dcb01182397d12 +F src/vdbe.c 1a5378625687ced2acaa01e200cb63f07f780ad6 +F src/vdbe.h 785b2f175f8be2af285d137125f30042ce8a53b5 F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 @@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 668ef6380eba256ef82477b63aef850249a619a0 -R 672b44d109bf7bbf0ab39b1722058efe +P 085b0d671a1dac964693d69d866224de155dfe4c +R 06586960572c0c651395cc4d56fdf6f3 U drh -Z f74017b4041ef28aa366f6915101de05 +Z 67f54dfeaa3612775fa7c5da9d11c294 diff --git a/manifest.uuid b/manifest.uuid index dd7aaa092c..c77dfa24e5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -085b0d671a1dac964693d69d866224de155dfe4c \ No newline at end of file +633951f0fa11c91f93aa2862df84691750c01e73 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 4be07e5f85..563291c983 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.47 2002/02/27 01:53:13 drh Exp $ +** $Id: expr.c,v 1.48 2002/02/27 19:00:21 drh Exp $ */ #include "sqliteInt.h" @@ -1003,7 +1003,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ sqliteExprCode(pParse, pList->a[i].pExpr); } sqliteVdbeAddOp(v, OP_UserFunc, pList->nExpr, 0); - sqliteVdbeChangeP3(v, -1, (char*)pUser->xFunc, P3_POINTER); + sqliteVdbeChangeP3(v, -1, (char*)pUser, P3_POINTER); break; } default: { @@ -1396,8 +1396,7 @@ UserFunc *sqliteFindUserFunction( ){ UserFunc *pFirst, *p, *pMaybe; pFirst = p = (UserFunc*)sqliteHashFind(&db->userFunc, zName, nName); - if( p==0 ) return 0; - if( !createFlag && nArg<0 ){ + if( p && !createFlag && nArg<0 ){ while( p && p->xFunc==0 && p->xStep==0 ){ p = p->pNext; } return p; } diff --git a/src/func.c b/src/func.c index 1389beb58c..26c401e124 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.3 2002/02/26 23:55:31 drh Exp $ +** $Id: func.c,v 1.4 2002/02/27 19:00:22 drh Exp $ */ #include #include @@ -26,7 +26,7 @@ /* ** Implementation of the upper() and lower() SQL functions. */ -static void upperFunc(void *context, int argc, const char **argv){ +static void upperFunc(sqlite_func *context, int argc, const char **argv){ char *z; int i; if( argc<1 || argv[0]==0 ) return; @@ -36,7 +36,7 @@ static void upperFunc(void *context, int argc, const char **argv){ if( islower(z[i]) ) z[i] = toupper(z[i]); } } -static void lowerFunc(void *context, int argc, const char **argv){ +static void lowerFunc(sqlite_func *context, int argc, const char **argv){ char *z; int i; if( argc<1 || argv[0]==0 ) return; @@ -61,32 +61,24 @@ struct StdDevCtx { /* ** Routines used to compute the standard deviation as an aggregate. */ -static void *stdDevStep(void *stddev, int argc, char **argv){ +static void stdDevStep(sqlite_func *context, int argc, const char **argv){ StdDevCtx *p; double x; - if( argc<1 ) return 0; - if( stddev==0 ){ - p = malloc( sizeof(*p) ); - p->n = 0; - p->sum = 0.0; - p->sum2 = 0.0; - }else{ - p = (StdDevCtx*)stddev; - } + if( argc<1 ) return; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; x = atof(argv[0]); p->sum += x; p->sum2 += x*x; p->n++; - return p; } -static void stdDevFinalize(void *stddev, void *context){ - StdDevCtx *p = (StdDevCtx*)stddev; - if( context && p && p->n>1 ){ +static void stdDevFinalize(sqlite_func *context){ + StdDevCtx *p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && p->n>1 ){ double rN = p->n; sqlite_set_result_double(context, sqrt((p->sum2 - p->sum*p->sum/rN)/(rN-1.0))); } - if( stddev ) free(stddev); } /* @@ -95,7 +87,7 @@ static void stdDevFinalize(void *stddev, void *context){ ** external linkage. */ void sqliteRegisterBuildinFunctions(sqlite *db){ - sqlite_create_function(db, "upper", 1, upperFunc); - sqlite_create_function(db, "lower", 1, lowerFunc); - sqlite_create_aggregate(db, "stddev", 1, stdDevStep, stdDevFinalize); + sqlite_create_function(db, "upper", 1, upperFunc, 0); + sqlite_create_function(db, "lower", 1, lowerFunc, 0); + sqlite_create_aggregate(db, "stddev", 1, stdDevStep, stdDevFinalize, 0); } diff --git a/src/main.c b/src/main.c index 6d78afbf15..dfce13aef4 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.64 2002/02/27 01:53:13 drh Exp $ +** $Id: main.c,v 1.65 2002/02/27 19:00:22 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -643,7 +643,8 @@ int sqlite_create_function( sqlite *db, /* Add the function to this database connection */ const char *zName, /* Name of the function to add */ int nArg, /* Number of arguments */ - void (*xFunc)(void*,int,const char**) /* Implementation of the function */ + void (*xFunc)(sqlite_func*,int,const char**), /* The implementation */ + void *pUserData /* User data */ ){ UserFunc *p; if( db==0 || zName==0 ) return 1; @@ -652,14 +653,16 @@ int sqlite_create_function( p->xFunc = xFunc; p->xStep = 0; p->xFinalize = 0; + p->pUserData = pUserData; return 0; } int sqlite_create_aggregate( sqlite *db, /* Add the function to this database connection */ const char *zName, /* Name of the function to add */ int nArg, /* Number of arguments */ - void *(*xStep)(void*,int,const char**), /* The step function */ - void (*xFinalize)(void*,void*) /* The finalizer */ + void (*xStep)(sqlite_func*,int,const char**), /* The step function */ + void (*xFinalize)(sqlite_func*), /* The finalizer */ + void *pUserData /* User data */ ){ UserFunc *p; if( db==0 || zName==0 ) return 1; @@ -668,5 +671,6 @@ int sqlite_create_aggregate( p->xFunc = 0; p->xStep = xStep; p->xFinalize = xFinalize; + p->pUserData = pUserData; return 0; } diff --git a/src/select.c b/src/select.c index b5b8813689..2df1b67710 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.67 2002/02/27 01:47:12 drh Exp $ +** $Id: select.c,v 1.68 2002/02/27 19:00:22 drh Exp $ */ #include "sqliteInt.h" @@ -747,6 +747,64 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ return 0; } +/* +** This routine attempts to flatten subqueries in order to speed +** execution. It returns 1 if it makes changes and 0 if no flattening +** occurs. +** +** To understand the concept of flattening, consider the following +** query: +** +** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5 +** +** The default way of implementing this query is to execute the +** subquery first and store the results in a temporary table, then +** run the outer query on that temporary table. This requires two +** passes over the data. Furthermore, because the temporary table +** has no indices, the WHERE clause on the outer query cannot be +** optimized using indices. +** +** This routine attempts to write queries such as the above into +** a single flat select, like this: +** +** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5 +** +** The code generated for this simpification gives the same result +** but only has to scan the data once. +** +** Generally speaking, flattening is only possible if the subquery +** query is a simple query without a GROUP BY clause or the DISTINCT +** keyword and the outer query is not a join. +** +** If flattening is not possible, this routine is a no-op and return 0. +** If flattening is possible, this routine rewrites the query into +** the simplified form and return 1. +** +** All of the expression analysis must occur before this routine runs. +** This routine depends on the results of the expression analysis. +*/ +int flattenSubqueries(Select *p){ + Select *pSub; + if( p->pSrc->nId>1 ){ + return 0; /* Cannot optimize: The outer query is a join. */ + } + pSub = p->pSrc->a[0].pSelect; + if( pSub==0 ){ + return 0; /* Nothing to optimize: There is no subquery. */ + } + if( pSub->isDistinct ){ + return 0; /* Subquery contains DISTINCT keyword */ + } + if( pSub->pGroupBy ){ + return 0; /* Subquery contains a GROUP BY clause */ + } + if( pSub->pPrior ){ + return 0; /* Subquery is the union of two or more queries */ + } + + return 0; +} + /* ** Analyze the SELECT statement passed in as an argument to see if it ** is a simple min() or max() query. If it is and this query can be @@ -1056,6 +1114,14 @@ int sqliteSelect( } } + /* Try to merge subqueries in the FROM clause into the main + ** query. + */ + if( flattenSubqueries(p) ){ + pEList = p->pEList; + pWhere = p->pWhere; + } + /* Check for the special case of a min() or max() function by itself ** in the result set. */ @@ -1134,8 +1200,8 @@ int sqliteSelect( 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); + sqliteVdbeAddOp(v, OP_AggInit, 0, i); + sqliteVdbeChangeP3(v, -1, (char*)pUser, P3_POINTER); } } if( pGroupBy==0 ){ @@ -1214,7 +1280,6 @@ int sqliteSelect( 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; @@ -1223,14 +1288,16 @@ int sqliteSelect( case FN_Unknown: op = OP_AggFunc; break; } if( op!=OP_AggFunc ){ + sqliteVdbeAddOp(v, OP_AggGet, 0, i); sqliteVdbeAddOp(v, op, 0, 0); + sqliteVdbeAddOp(v, OP_AggSet, 0, i); }else{ + sqliteVdbeAddOp(v, OP_Integer, i, 0); 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); + sqliteVdbeChangeP3(v, -1, (char*)pParse->aAgg[i].pUser, P3_POINTER); } - sqliteVdbeAddOp(v, OP_AggSet, 0, i); } } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 14c5f55792..251bd35d8f 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.26 2002/02/23 23:45:45 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.27 2002/02/27 19:00:22 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -362,40 +362,80 @@ int sqlite_get_table_vprintf( va_list ap /* Arguments to the format string */ ); +/* +** A pointer to the following structure is used to communicate with +** the implementations of user-defined functions. +*/ +typedef struct sqlite_func sqlite_func; + /* ** Use the following routines to create new user-defined functions. See ** the documentation for details. */ int sqlite_create_function( - sqlite*, /* Database where the new function is registered */ - const char *zName, /* Name of the new function */ - int nArg, /* Number of arguments. -1 means any number */ - void (*xFunc)(void*,int,const char**) /* C code to implement the function */ + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the new function */ + int nArg, /* Number of arguments. -1 means any number */ + void (*xFunc)(sqlite_func*,int,const char**), /* C code to implement */ + void *pUserData /* Available via the sqlite_user_data() call */ ); int sqlite_create_aggregate( - sqlite*, /* Database where the new function is registered */ - const char *zName, /* Name of the function */ - int nArg, /* Number of arguments */ - void *(*xStep)(void*,int,const char**), /* Called for each row */ - void (*xFinalize)(void*,void*) /* Called once to get final result */ + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the function */ + int nArg, /* Number of arguments */ + void (*xStep)(sqlite_func*,int,const char**), /* Called for each row */ + void (*xFinalize)(sqlite_func*), /* Called once to get final result */ + void *pUserData /* Available via the sqlite_user_data() call */ ); /* ** The user function implementations call one of the following four routines ** in order to return their results. The first parameter to each of these -** routines is a copy of the first argument to xFunc() or the second argument -** to xFinalize(). The second parameter to these routines is the result -** to be returned. A NULL can be passed as the second parameter to -** sqlite_set_result_string() in order to return a NULL result. +** routines is a copy of the first argument to xFunc() or xFinialize() +** The second parameter to these routines is the result to be returned. +** A NULL can be passed as the second parameter to sqlite_set_result_string() +** in order to return a NULL result. ** ** The 3rd argument to _string and _error is the number of characters to ** take from the string. If this argument is negative, then all characters ** up to and including the first '\000' are used. +** +** The sqlite_set_result_string() function allocates a buffer to hold the +** result and returns a pointer to this buffer. The calling routine +** (that is, the implmentation of a user function) can alter the content +** of this buffer if desired. */ -char *sqlite_set_result_string(void*,const char*,int); -void sqlite_set_result_int(void*,int); -void sqlite_set_result_double(void*,double); -void sqlite_set_result_error(void*,const char*,int); +char *sqlite_set_result_string(sqlite_func*,const char*,int); +void sqlite_set_result_int(sqlite_func*,int); +void sqlite_set_result_double(sqlite_func*,double); +void sqlite_set_result_error(sqlite_func*,const char*,int); + +/* +** The pUserData parameter to the sqlite_create_function() and +** sqlite_create_aggregate() routines used to register user functions +** is available to the implementation of the function using this +** call. +*/ +void *sqlite_user_data(sqlite_func*); + +/* +** User aggregate functions use the following routine to allocate +** a structure for storing their context. The first time this routine +** is called for a particular aggregate, a new structure of size nBytes +** is allocated, zeroed, and returned. On subsequent calls (for the +** same aggregate instance) the same buffer is returned. The implementation +** of the aggregate can use the returned buffer to accumulate data. +** +** The buffer allocated is freed automatically be SQLite. +*/ +void *sqlite_aggregate_context(sqlite_func*, int nBytes); + +/* +** The next return returns the number of calls to xStep for a particular +** aggregate function instance. The current call to xStep counts so the +** function always returns at least 1. +*/ +int sqlite_aggregate_count(sqlite_func*); #ifdef __cplusplus } /* End of the 'extern "C"' block */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 974c89d8bf..3734450a55 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.94 2002/02/27 01:47:12 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.95 2002/02/27 19:00:22 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -206,11 +206,12 @@ struct sqlite { ** points to a linked list of these structures. */ struct UserFunc { - void (*xFunc)(void*,int,const char**); /* Regular function */ - void *(*xStep)(void*,int,const char**); /* Aggregate function step */ - void (*xFinalize)(void*,void*); /* Aggregate function finializer */ - int nArg; /* Number of arguments */ - UserFunc *pNext; /* Next function with same name */ + void (*xFunc)(sqlite_func*,int,const char**); /* Regular function */ + void *(*xStep)(sqlite_func*,int,const char**); /* Aggregate function step */ + void (*xFinalize)(sqlite_func*); /* Aggregate function finializer */ + int nArg; /* Number of arguments */ + void *pUserData; /* User data parameter */ + UserFunc *pNext; /* Next function with same name */ }; /* diff --git a/src/vdbe.c b/src/vdbe.c index 39b9e1e5a2..c285eb6f46 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.123 2002/02/24 03:25:16 drh Exp $ +** $Id: vdbe.c,v 1.124 2002/02/27 19:00:22 drh Exp $ */ #include "sqliteInt.h" #include @@ -142,17 +142,30 @@ typedef struct Mem Mem; ** 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 */ +#define STK_AggCtx 0x0040 /* zStack[] points to an agg function context */ /* -** The "context" argument for a user-defined function. +** The "context" argument for a user-defined function. A pointer to an +** instance of this structure is the first argument to the routines used +** implement user-defined SQL functions. +** +** There is a typedef for this structure in sqlite.h. So all routines, +** even the public interface to SQLite, can use a pointer to this structure. +** But this file is the only place where the internal details of this +** structure are known. +** +** This structure is defined inside of vdbe.c because it uses substructures +** (Stack) which are only defined there. */ -struct UserFuncContext { - Stack s; /* Small string, integer, and floating point values go here */ - char *z; /* Space for holding dynamic string results */ - int isError; /* Set to true for an error */ +struct sqlite_func { + UserFunc *pFunc; /* Pointer to function information. MUST BE FIRST */ + Stack s; /* Small strings, ints, and double values go here */ + char *z; /* Space for holding dynamic string results */ + void *pAgg; /* Aggregate context */ + u8 isError; /* Set to true for an error */ + u8 isStep; /* Current in the step function */ + int cnt; /* Number of times that the step function has been called */ }; -typedef struct UserFuncContext UserFuncContext; /* ** An Agg structure describes an Aggregator. Each Agg consists of @@ -168,7 +181,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 */ + UserFunc **apFunc; /* Information about user-defined aggregate functions */ }; struct AggElem { char *zKey; /* The key to this AggElem */ @@ -525,9 +538,13 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){ ** ** The sqlite_set_result_int() and sqlite_set_result_double() set the return ** value of the user function to an integer or a double. +** +** These routines are defined here in vdbe.c because they depend on knowing +** the internals of the sqlite_func structure which is only defined in that +** one source file. */ -char *sqlite_set_result_string(void *context, const char *zResult, int n){ - UserFuncContext *p = (UserFuncContext*)context; +char *sqlite_set_result_string(sqlite_func *p, const char *zResult, int n){ + assert( !p->isStep ); if( p->s.flags & STK_Dyn ){ sqliteFree(p->z); } @@ -554,49 +571,106 @@ char *sqlite_set_result_string(void *context, const char *zResult, int n){ p->s.n = n; return p->z; } -void sqlite_set_result_int(void *context, int iResult){ - UserFuncContext *p = (UserFuncContext*)context; +void sqlite_set_result_int(sqlite_func *p, int iResult){ + assert( !p->isStep ); if( p->s.flags & STK_Dyn ){ sqliteFree(p->z); } p->s.i = iResult; p->s.flags = STK_Int; } -void sqlite_set_result_double(void *context, double rResult){ - UserFuncContext *p = (UserFuncContext*)context; +void sqlite_set_result_double(sqlite_func *p, double rResult){ + assert( !p->isStep ); if( p->s.flags & STK_Dyn ){ sqliteFree(p->z); } p->s.r = rResult; p->s.flags = STK_Real; } -void sqlite_set_result_error(void *context, const char *zMsg, int n){ - UserFuncContext *p = (UserFuncContext*)context; - sqlite_set_result_string(context, zMsg, n); +void sqlite_set_result_error(sqlite_func *p, const char *zMsg, int n){ + assert( !p->isStep ); + sqlite_set_result_string(p, zMsg, n); p->isError = 1; } /* -** Reset an Agg structure. Delete all its contents. +** Extract the user data from a sqlite_func structure and return a +** pointer to it. +** +** This routine is defined here in vdbe.c because it depends on knowing +** the internals of the sqlite_func structure which is only defined in that +** one source file. +*/ +void *sqlite_user_data(sqlite_func *p){ + assert( p && p->pFunc ); + return p->pFunc->pUserData; +} + +/* +** Allocate or return the aggregate context for a user function. A new +** context is allocated on the first call. Subsequent calls return the +** same context that was returned on prior calls. +** +** This routine is defined here in vdbe.c because it depends on knowing +** the internals of the sqlite_func structure which is only defined in that +** one source file. +*/ +void *sqlite_aggregate_context(sqlite_func *p, int nByte){ + assert( p && p->pFunc && p->pFunc->xStep ); + if( p->pAgg==0 ){ + p->pAgg = sqliteMalloc( nByte ); + } + return p->pAgg; +} + +/* +** Return the number of times the Step function of a user-defined +** aggregate has been called. +** +** This routine is defined here in vdbe.c because it depends on knowing +** the internals of the sqlite_func structure which is only defined in that +** one source file. +*/ +int sqlite_aggregate_count(sqlite_func *p){ + assert( p && p->pFunc && p->pFunc->xStep ); + return p->cnt; +} + +/* +** Reset an Agg structure. Delete all its contents. +** +** For user-defined 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. */ static void AggReset(Agg *pAgg){ int i; HashElem *p; for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){ AggElem *pElem = sqliteHashData(p); - assert( pAgg->axFinalize!=0 ); + assert( pAgg->apFunc!=0 ); for(i=0; inMem; i++){ - if( pElem->aMem[i].s.flags & STK_Dyn ){ + if( pAgg->apFunc[i] && (pElem->aMem[i].s.flags & STK_AggCtx)!=0 ){ + sqlite_func ctx; + ctx.s.flags = STK_Null; + ctx.z = 0; + ctx.pAgg = pElem->aMem[i].z; + ctx.cnt = pElem->aMem[i].s.i; + ctx.isStep = 0; + ctx.isError = 0; + (*pAgg->apFunc[i]->xFinalize)(&ctx); + } + if( pElem->aMem[i].s.flags & (STK_Dyn | STK_AggCtx) ){ 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; + sqliteFree(pAgg->apFunc); + pAgg->apFunc = 0; pAgg->pCurrent = 0; pAgg->pSearch = 0; pAgg->nMem = 0; @@ -966,8 +1040,8 @@ static char *zOpName[] = { 0, "SortMakeRec", "SortMakeKey", "Sort", "SortNext", "SortCallback", "SortReset", "FileOpen", "FileRead", "FileColumn", "AggReset", "AggFocus", "AggIncr", - "AggNext", "AggSet", "AggGet", "AggFinalizer", - "AggFunc", "SetInsert", "SetFound", "SetNotFound", + "AggNext", "AggSet", "AggGet", "AggFunc", + "AggInit", "SetInsert", "SetFound", "SetNotFound", "MakeRecord", "MakeKey", "MakeIdxKey", "IncrKey", "Goto", "If", "Halt", "ColumnCount", "ColumnName", "Callback", "NullCallback", "Integer", @@ -1786,14 +1860,16 @@ case OP_Max: { /* Opcode: UserFunc P1 * P3 ** -** Invoke a user function (P3 is a pointer to the function) with -** P1 string arguments taken from the stack. Pop all arguments from -** the stack and push back the result. +** Invoke a user function (P3 is a pointer to a UserFunc structure that +** defines the function) with P1 string arguments taken from the stack. +** Pop all arguments from the stack and push back the result. +** +** See also: AggFunc */ case OP_UserFunc: { int n, i; - UserFuncContext ctx; - void (*xFunc)(void*,int,const char**); + sqlite_func ctx; + n = pOp->p1; VERIFY( if( n<=0 ) goto bad_instruction; ) VERIFY( if( p->tos+1p3; + ctx.pFunc = (UserFunc*)pOp->p3; ctx.s.flags = STK_Null; + ctx.z = 0; ctx.isError = 0; - xFunc((void*)&ctx, n, (const char**)&zStack[p->tos-n+1]); + ctx.isStep = 0; + (*ctx.pFunc->xFunc)(&ctx, n, (const char**)&zStack[p->tos-n+1]); PopStack(p, n); VERIFY( NeedStack(p, p->tos+1); ) p->tos++; @@ -4313,63 +4391,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]) ); + p->agg.apFunc = sqliteMalloc( p->agg.nMem*sizeof(p->agg.apFunc[0]) ); break; } -/* Opcode: AggFinalizer * P2 P3 +/* Opcode: AggInit * 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. +** Initialize the function parameters for a user-defined aggregate function. +** The user-defined aggregate will operate out of aggregate column P2. +** P3 is a pointer to the UserFunc structure for the function. */ -case OP_AggFinalizer: { +case OP_AggInit: { int i = pOp->p2; - VERIFY( if( p->agg.nMem<=i ) goto bad_instruction; ); - p->agg.axFinalize[i] = (void(*)(void*,void*))pOp->p3; + VERIFY( if( i<0 || i>=p->agg.nMem ) goto bad_instruction; ) + p->agg.apFunc[i] = (UserFunc*)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. +** function has P2 arguments. P3 is a pointer to the UserFunc +** structure that specifies the user-defined 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. +** The top of the stack must be an integer which is the index of +** the aggregate column that corresponds to this aggregate function. +** Ideally, this index would be another parameter, but there are +** no free parameters left. The integer is popped from the stack. */ case OP_AggFunc: { int n = pOp->p2; int i; - void *pCtx; - void *(*xStep)(void*,int,const char**); + Mem *pMem; + sqlite_func ctx; - 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].flags!=STK_Int ) goto bad_instruction; ) for(i=p->tos-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]); + i = aStack[p->tos].i; + VERIFY( if( i<0 || i>=p->agg.nMem ) goto bad_instruction; ) + ctx.pFunc = (UserFunc*)pOp->p3; + pMem = &p->agg.pCurrent->aMem[i]; + ctx.pAgg = pMem->z; + ctx.cnt = ++pMem->s.i; + ctx.isError = 0; + ctx.isStep = 1; + (ctx.pFunc->xStep)(&ctx, n, (const char**)&zStack[p->tos-n]); + pMem->z = ctx.pAgg; + pMem->s.flags = STK_AggCtx; PopStack(p, n+1); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].flags = STK_AggCtx; - zStack[p->tos] = (char*)pCtx; + if( ctx.isError ){ + rc = SQLITE_ERROR; + } break; } @@ -4517,19 +4595,23 @@ case OP_AggNext: { pc = pOp->p2 - 1; } else { int i; - UserFuncContext ctx; - void *pCtx; + sqlite_func ctx; 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( p->agg.apFunc[i]==0 ) continue; + if( p->agg.apFunc[i]->xFinalize==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); + ctx.pAgg = (void*)aMem[i].z; + ctx.cnt = aMem[i].s.i; + ctx.isStep = 0; + ctx.pFunc = p->agg.apFunc[i]; + (*p->agg.apFunc[i]->xFinalize)(&ctx); + sqliteFree( aMem[i].z ); aMem[i].s = ctx.s; aMem[i].z = ctx.z; if( (aMem[i].s.flags & STK_Str) && diff --git a/src/vdbe.h b/src/vdbe.h index 490dba5dcd..bb2bf2fbe8 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.46 2002/02/24 03:25:16 drh Exp $ +** $Id: vdbe.h,v 1.47 2002/02/27 19:00:22 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -140,8 +140,8 @@ 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_AggFunc 64 +#define OP_AggInit 65 #define OP_SetInsert 66 #define OP_SetFound 67