]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Code for user-defined aggregates added. Legacy tests all pass but there
authordrh <drh@noemail.net>
Sun, 24 Feb 2002 03:25:14 +0000 (03:25 +0000)
committerdrh <drh@noemail.net>
Sun, 24 Feb 2002 03:25:14 +0000 (03:25 +0000)
has been no testing of the new user-defined aggregate code. (CVS 392)

FossilOrigin-Name: 1e037eb303d8508cb2ea3418e71b03315d895fbd

manifest
manifest.uuid
src/expr.c
src/select.c
src/sqliteInt.h
src/vdbe.c
src/vdbe.h

index 74c9d3df2d35b8677343115988c7a5005591a0b7..3e7fea43e5d468a535b61564f99dbd1626281f2c 100644 (file)
--- 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
index cc61005f815c93ad47c0f1adf4a15c09b15ceae7..caeaedcb00aca7c33a933724e2f3f7c00bc837f1 100644 (file)
@@ -1 +1 @@
-530b0f4f2def89e200b7b0724a5967bf981bd91d
\ No newline at end of file
+1e037eb303d8508cb2ea3418e71b03315d895fbd
\ No newline at end of file
index d986c7c9a0b4aa7ce51e8e8485f1bf3655258223..eefcedc5bdc346f65b359dc3b24389d3690ff020 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.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;
index ac2fc5803b0791259b9bbaa084e50458d8c80690..987153c4ac613809f51eba7ffb247880d1c9308e 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.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);
index b86cf34675e57f3ff9dbbcabc86a2e6e16021775..89640a23d39266b317212436c5cfd25aec2f0a76 100644 (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 */
 };
 
 /*
index 5d6faa929b1a63b2cb9e7bfe7bd540bc1c0701d8..39b9e1e5a2362ec34731988ae4eed56dd10d857d 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.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;
 }
index a3b66a29c6df7063717d8a5861315474fdff2db6..490dba5dcd543b8c85ff9edcc01cd3c8327af5bf 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.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_SetInsert          64
-#define OP_SetFound           65
-#define OP_SetNotFound        66
-
-#define OP_MakeRecord         67
-#define OP_MakeKey            68
-#define OP_MakeIdxKey         69
-#define OP_IncrKey            70
-
-#define OP_Goto               71
-#define OP_If                 72
-#define OP_Halt               73
-
-#define OP_ColumnCount        74
-#define OP_ColumnName         75
-#define OP_Callback           76
-#define OP_NullCallback       77
-
-#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_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_Limit             120
-
-#define OP_MAX               120
+#define OP_AggFinalizer       64
+#define OP_AggFunc            65
+
+#define OP_SetInsert          66
+#define OP_SetFound           67
+#define OP_SetNotFound        68
+
+#define OP_MakeRecord         69
+#define OP_MakeKey            70
+#define OP_MakeIdxKey         71
+#define OP_IncrKey            72
+
+#define OP_Goto               73
+#define OP_If                 74
+#define OP_Halt               75
+
+#define OP_ColumnCount        76
+#define OP_ColumnName         77
+#define OP_Callback           78
+#define OP_NullCallback       79
+
+#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                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             121
+
+#define OP_MAX               121
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation