]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Revise the API for user-defined functions. (CVS 398)
authordrh <drh@noemail.net>
Wed, 27 Feb 2002 19:00:20 +0000 (19:00 +0000)
committerdrh <drh@noemail.net>
Wed, 27 Feb 2002 19:00:20 +0000 (19:00 +0000)
FossilOrigin-Name: 633951f0fa11c91f93aa2862df84691750c01e73

manifest
manifest.uuid
src/expr.c
src/func.c
src/main.c
src/select.c
src/sqlite.h.in
src/sqliteInt.h
src/vdbe.c
src/vdbe.h

index 6dc9e9595d90a5ca39bfe1b34b868ca894be6766..b6fe23ef814a5f026e9603d60f4c2718785e6843 100644 (file)
--- 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
index dd7aaa092cbd86eb8eb278d6449ae87418e76dd4..c77dfa24e5b0c67e364370c4733a94cfa568075b 100644 (file)
@@ -1 +1 @@
-085b0d671a1dac964693d69d866224de155dfe4c
\ No newline at end of file
+633951f0fa11c91f93aa2862df84691750c01e73
\ No newline at end of file
index 4be07e5f8524b4a1baf736c80d9331ef3f68df5a..563291c983225f033682ccb5e7ae90a09f4bc578 100644 (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.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;
   }
index 1389beb58c67213cab47ca3528925251fac0ec13..26c401e124d9b863ae0252d0b878dcf22b2a044b 100644 (file)
@@ -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 <ctype.h>
 #include <math.h>
@@ -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);
 }
index 6d78afbf1502959aab141f11f607a150a36f87da..dfce13aef442993345cb660f7c9af7a24d816535 100644 (file)
@@ -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;
 }
index b5b88136897add9fb97fd0fcdb198ae65d7817f2..2df1b677106d13898480d8a29bbe803de4324c7b 100644 (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.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; 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);
+        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; 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;
@@ -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);
     }
   }
 
index 14c5f55792aedd4e5324ea7fc3f2d3099b582b90..251bd35d8f08bb73941fe17367cb124461ec0546 100644 (file)
@@ -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(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.
 */
-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);
+int sqlite_aggregate_count(sqlite_func*);
 
 #ifdef __cplusplus
 }  /* End of the 'extern "C"' block */
index 974c89d8bfee830bbe8f5f739af877e2b1e921cb..3734450a555cd434ccb2dd480f972871933a2d45 100644 (file)
@@ -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 */
 };
 
 /*
index 39b9e1e5a2362ec34731988ae4eed56dd10d857d..c285eb6f46ad0a2a0c8606cd40b83f8bebe9cd7b 100644 (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.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 <ctype.h>
@@ -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.
-*/
-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 */
+** 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 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; i<pAgg->nMem; 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+1<n ) goto not_enough_stack; )
@@ -1802,10 +1878,12 @@ case OP_UserFunc: {
       if( Stringify(p, i) ) goto no_mem;
     }
   }
-  xFunc = (void(*)(void*,int,const char**))pOp->p3;
+  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+1<n ) goto not_enough_stack; )
+  VERIFY( if( aStack[p->tos].flags!=STK_Int ) goto bad_instruction; )
   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]);
+  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; i<p->agg.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) &&
index 490dba5dcd543b8c85ff9edcc01cd3c8327af5bf..bb2bf2fbe845d121fbaef4c1e61f05b85b7bcded 100644 (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.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