Add the sqlite3_create_function_v2() API, a version of create_function that allows a destructor to be specified.

FossilOrigin-Name: 9a724dfbe822c77e76721abe3443c9cb018bb2e2
diff --git a/src/func.c b/src/func.c
index d461412..27b9bcf 100644
--- a/src/func.c
+++ b/src/func.c
@@ -1450,10 +1450,10 @@
   }else{
     pInfo = (struct compareInfo*)&likeInfoNorm;
   }
-  sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0);
-  sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0);
+  sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0);
+  sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0);
   sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY, 
-      (struct compareInfo*)&globInfo, likeFunc, 0,0);
+      (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0);
   setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
   setLikeOptFlag(db, "like", 
       caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
diff --git a/src/main.c b/src/main.c
index 3b04b75..67733ee 100644
--- a/src/main.c
+++ b/src/main.c
@@ -596,10 +596,27 @@
 }
 
 /*
+** Invoke the destructor function associated with FuncDef p, if any. Except,
+** if this is not the last copy of the function, do not invoke it. Multiple
+** copies of a single function are created when create_function() is called
+** with SQLITE_ANY as the encoding.
+*/
+static void functionDestroy(sqlite3 *db, FuncDef *p){
+  FuncDestructor *pDestructor = p->pDestructor;
+  if( pDestructor ){
+    pDestructor->nRef--;
+    if( pDestructor->nRef==0 ){
+      pDestructor->xDestroy(pDestructor->pUserData);
+      sqlite3DbFree(db, pDestructor);
+    }
+  }
+}
+
+/*
 ** Close an existing SQLite database
 */
 int sqlite3_close(sqlite3 *db){
-  HashElem *i;
+  HashElem *i;                    /* Hash table iterator */
   int j;
 
   if( !db ){
@@ -667,6 +684,7 @@
     for(p=db->aFunc.a[j]; p; p=pHash){
       pHash = p->pHash;
       while( p ){
+        functionDestroy(db, p);
         pNext = p->pNext;
         sqlite3DbFree(db, p);
         p = pNext;
@@ -941,7 +959,8 @@
   void *pUserData,
   void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
   void (*xStep)(sqlite3_context*,int,sqlite3_value **),
-  void (*xFinal)(sqlite3_context*)
+  void (*xFinal)(sqlite3_context*),
+  FuncDestructor *pDestructor
 ){
   FuncDef *p;
   int nName;
@@ -969,10 +988,10 @@
   }else if( enc==SQLITE_ANY ){
     int rc;
     rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
-         pUserData, xFunc, xStep, xFinal);
+         pUserData, xFunc, xStep, xFinal, pDestructor);
     if( rc==SQLITE_OK ){
       rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
-          pUserData, xFunc, xStep, xFinal);
+          pUserData, xFunc, xStep, xFinal, pDestructor);
     }
     if( rc!=SQLITE_OK ){
       return rc;
@@ -1005,6 +1024,15 @@
   if( !p ){
     return SQLITE_NOMEM;
   }
+
+  /* If an older version of the function with a configured destructor is
+  ** being replaced invoke the destructor function here. */
+  functionDestroy(db, p);
+
+  if( pDestructor ){
+    pDestructor->nRef++;
+  }
+  p->pDestructor = pDestructor;
   p->flags = 0;
   p->xFunc = xFunc;
   p->xStep = xStep;
@@ -1019,7 +1047,7 @@
 */
 int sqlite3_create_function(
   sqlite3 *db,
-  const char *zFunctionName,
+  const char *zFunc,
   int nArg,
   int enc,
   void *p,
@@ -1029,7 +1057,39 @@
 ){
   int rc;
   sqlite3_mutex_enter(db->mutex);
-  rc = sqlite3CreateFunc(db, zFunctionName, nArg, enc, p, xFunc, xStep, xFinal);
+  rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, 0);
+  rc = sqlite3ApiExit(db, rc);
+  sqlite3_mutex_leave(db->mutex);
+  return rc;
+}
+
+int sqlite3_create_function_v2(
+  sqlite3 *db,
+  const char *zFunc,
+  int nArg,
+  int enc,
+  void *p,
+  void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+  void (*xStep)(sqlite3_context*,int,sqlite3_value **),
+  void (*xFinal)(sqlite3_context*),
+  void (*xDestroy)(void *)
+){
+  int rc;
+  FuncDestructor *pArg = 0;
+  sqlite3_mutex_enter(db->mutex);
+  if( xDestroy ){
+    pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
+    if( !pArg ) goto out;
+    pArg->xDestroy = xDestroy;
+    pArg->pUserData = p;
+  }
+  rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
+  if( pArg && pArg->nRef==0 ){
+    assert( rc!=SQLITE_OK );
+    sqlite3DbFree(db, pArg);
+  }
+
+ out:
   rc = sqlite3ApiExit(db, rc);
   sqlite3_mutex_leave(db->mutex);
   return rc;
@@ -1051,7 +1111,7 @@
   sqlite3_mutex_enter(db->mutex);
   assert( !db->mallocFailed );
   zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
-  rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal);
+  rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
   sqlite3DbFree(db, zFunc8);
   rc = sqlite3ApiExit(db, rc);
   sqlite3_mutex_leave(db->mutex);
@@ -1082,7 +1142,7 @@
   sqlite3_mutex_enter(db->mutex);
   if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
     sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
-                      0, sqlite3InvalidFunction, 0, 0);
+                      0, sqlite3InvalidFunction, 0, 0, 0);
   }
   rc = sqlite3ApiExit(db, SQLITE_OK);
   sqlite3_mutex_leave(db->mutex);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 38d9b63..6fdadb6 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -3350,6 +3350,17 @@
   void (*xStep)(sqlite3_context*,int,sqlite3_value**),
   void (*xFinal)(sqlite3_context*)
 );
+int sqlite3_create_function_v2(
+  sqlite3 *db,
+  const char *zFunctionName,
+  int nArg,
+  int eTextRep,
+  void *pApp,
+  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+  void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+  void (*xFinal)(sqlite3_context*),
+  void(*xDestroy)(void*)
+);
 
 /*
 ** CAPI3REF: Text Encodings
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 6c41315..90d900d 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -600,6 +600,7 @@
 typedef struct ExprList ExprList;
 typedef struct ExprSpan ExprSpan;
 typedef struct FKey FKey;
+typedef struct FuncDestructor FuncDestructor;
 typedef struct FuncDef FuncDef;
 typedef struct FuncDefHash FuncDefHash;
 typedef struct IdList IdList;
@@ -956,6 +957,27 @@
   void (*xFinalize)(sqlite3_context*);                /* Aggregate finalizer */
   char *zName;         /* SQL name of the function. */
   FuncDef *pHash;      /* Next with a different name but the same hash */
+  FuncDestructor *pDestructor;   /* Reference counted destructor function */
+};
+
+/*
+** This structure encapsulates a user-function destructor callback (as
+** configured using create_function_v2()) and a reference counter. When
+** create_function_v2() is called to create a function with a destructor,
+** a single object of this type is allocated. FuncDestructor.nRef is set to 
+** the number of FuncDef objects created (either 1 or 3, depending on whether
+** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor
+** member of each of the new FuncDef objects is set to point to the allocated
+** FuncDestructor.
+**
+** Thereafter, when one of the FuncDef objects is deleted, the reference
+** count on this object is decremented. When it reaches 0, the destructor
+** is invoked and the FuncDestructor structure freed.
+*/
+struct FuncDestructor {
+  int nRef;
+  void (*xDestroy)(void *);
+  void *pUserData;
 };
 
 /*
@@ -2921,7 +2943,9 @@
 KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
 int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, 
   void (*)(sqlite3_context*,int,sqlite3_value **),
-  void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*));
+  void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
+  FuncDestructor *pDestructor
+);
 int sqlite3ApiExit(sqlite3 *db, int);
 int sqlite3OpenTempDatabase(Parse *);
 
diff --git a/src/test1.c b/src/test1.c
index f458cdb..bf26b9e 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -1791,6 +1791,131 @@
 }
 
 /*
+** USAGE: sqlite3_create_function_v2 DB NAME NARG ENC ?SWITCHES?
+**
+** Available switches are:
+**
+**   -func    SCRIPT
+**   -step    SCRIPT
+**   -final   SCRIPT
+**   -destroy SCRIPT
+*/
+typedef struct CreateFunctionV2 CreateFunctionV2;
+struct CreateFunctionV2 {
+  Tcl_Interp *interp;
+  Tcl_Obj *pFunc;                 /* Script for function invocation */
+  Tcl_Obj *pStep;                 /* Script for agg. step invocation */
+  Tcl_Obj *pFinal;                /* Script for agg. finalization invocation */
+  Tcl_Obj *pDestroy;              /* Destructor script */
+};
+static void cf2Func(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
+}
+static void cf2Step(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
+}
+static void cf2Final(sqlite3_context *ctx){
+}
+static void cf2Destroy(void *pUser){
+  CreateFunctionV2 *p = (CreateFunctionV2 *)pUser;
+
+  if( p->interp && p->pDestroy ){
+    int rc = Tcl_EvalObjEx(p->interp, p->pDestroy, 0);
+    if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp);
+  }
+
+  if( p->pFunc ) Tcl_DecrRefCount(p->pFunc); 
+  if( p->pStep ) Tcl_DecrRefCount(p->pStep); 
+  if( p->pFinal ) Tcl_DecrRefCount(p->pFinal); 
+  if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy); 
+  sqlite3_free(p);
+}
+static int test_create_function_v2(
+  ClientData clientData,          /* Not used */
+  Tcl_Interp *interp,             /* The invoking TCL interpreter */
+  int objc,                       /* Number of arguments */
+  Tcl_Obj *CONST objv[]           /* Command arguments */
+){
+  sqlite3 *db;
+  const char *zFunc;
+  int nArg;
+  int enc;
+  CreateFunctionV2 *p;
+  int i;
+  int rc;
+
+  struct EncTable {
+    const char *zEnc;
+    int enc;
+  } aEnc[] = {
+    {"utf8",    SQLITE_UTF8 },
+    {"utf16",   SQLITE_UTF16 },
+    {"utf16le", SQLITE_UTF16LE },
+    {"utf16be", SQLITE_UTF16BE },
+    {"any",     SQLITE_ANY },
+    {"0", 0 }
+  };
+
+  if( objc<5 || (objc%2)==0 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG ENC SWITCHES...");
+    return TCL_ERROR;
+  }
+
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  zFunc = Tcl_GetString(objv[2]);
+  if( Tcl_GetIntFromObj(interp, objv[3], &nArg) ) return TCL_ERROR;
+  if( Tcl_GetIndexFromObjStruct(interp, objv[4], aEnc, sizeof(aEnc[0]), 
+          "encoding", 0, &enc)
+  ){
+    return TCL_ERROR;
+  }
+  enc = aEnc[enc].enc;
+
+  p = sqlite3_malloc(sizeof(CreateFunctionV2));
+  assert( p );
+  memset(p, 0, sizeof(CreateFunctionV2));
+  p->interp = interp;
+
+  for(i=5; i<objc; i+=2){
+    int iSwitch;
+    const char *azSwitch[] = {"-func", "-step", "-final", "-destroy", 0};
+    if( Tcl_GetIndexFromObj(interp, objv[i], azSwitch, "switch", 0, &iSwitch) ){
+      sqlite3_free(p);
+      return TCL_ERROR;
+    }
+
+    switch( iSwitch ){
+      case 0: p->pFunc = objv[i+1];      break;
+      case 1: p->pStep = objv[i+1];      break;
+      case 2: p->pFinal = objv[i+1];     break;
+      case 3: p->pDestroy = objv[i+1];   break;
+    }
+  }
+  if( p->pFunc ) p->pFunc = Tcl_DuplicateObj(p->pFunc); 
+  if( p->pStep ) p->pStep = Tcl_DuplicateObj(p->pStep); 
+  if( p->pFinal ) p->pFinal = Tcl_DuplicateObj(p->pFinal); 
+  if( p->pDestroy ) p->pDestroy = Tcl_DuplicateObj(p->pDestroy); 
+
+  if( p->pFunc ) Tcl_IncrRefCount(p->pFunc); 
+  if( p->pStep ) Tcl_IncrRefCount(p->pStep); 
+  if( p->pFinal ) Tcl_IncrRefCount(p->pFinal); 
+  if( p->pDestroy ) Tcl_IncrRefCount(p->pDestroy); 
+
+  rc = sqlite3_create_function_v2(db, zFunc, nArg, enc, (void *)p, 
+      (p->pFunc ? cf2Func : 0),
+      (p->pStep ? cf2Step : 0),
+      (p->pFinal ? cf2Final : 0),
+      cf2Destroy
+  );
+  if( rc!=SQLITE_OK ){
+    p->interp = 0;
+    cf2Destroy(p);
+    Tcl_ResetResult(interp);
+    Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
 ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC?
 */
 static int test_load_extension(
@@ -2291,7 +2416,7 @@
   int nB, const void *zB
 ){
   Tcl_Interp *i = pTestCollateInterp;
-  int encin = (int)pCtx;
+  int encin = SQLITE_PTR_TO_INT(pCtx);
   int res;
   int n;
 
@@ -2420,7 +2545,7 @@
   }
   zNeededCollation[i] = 0;
   sqlite3_create_collation(
-      db, "test_collate", ENC(db), (void *)enc, test_collate_func);
+      db, "test_collate", ENC(db), SQLITE_INT_TO_PTR(enc), test_collate_func);
 }
 
 /*
@@ -2469,8 +2594,8 @@
 ){
   int rc, n;
   n = nKey1<nKey2 ? nKey1 : nKey2;
-  if( nKey1>0 && 1==(1&(int)pKey1) ) unaligned_string_counter++;
-  if( nKey2>0 && 1==(1&(int)pKey2) ) unaligned_string_counter++;
+  if( nKey1>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey1))) ) unaligned_string_counter++;
+  if( nKey2>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey2))) ) unaligned_string_counter++;
   rc = memcmp(pKey1, pKey2, n);
   if( rc==0 ){
     rc = nKey1 - nKey2;
@@ -5187,6 +5312,7 @@
      { "file_control_lockproxy_test", file_control_lockproxy_test,  0   },
      { "file_control_chunksize_test", file_control_chunksize_test,  0   },
      { "sqlite3_vfs_list",           vfs_list,     0   },
+     { "sqlite3_create_function_v2", test_create_function_v2, 0 },
 
      /* Functions from os.h */
 #ifndef SQLITE_OMIT_UTF16