Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -324,22 +324,15 @@ db->nStatement = 0; } /* ** 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 SQLITE4_ANY as the encoding. +** if this is not the last copy of the function, do not invoke it. */ static void functionDestroy(sqlite4 *db, FuncDef *p){ - FuncDestructor *pDestructor = p->pDestructor; - if( pDestructor ){ - pDestructor->nRef--; - if( pDestructor->nRef==0 ){ - pDestructor->xDestroy(pDestructor->pUserData); - sqlite4DbFree(db, pDestructor); - } + if( p->xDestroy ){ + p->xDestroy(p->pUserData); } } /* ** Close an existing SQLite database @@ -544,11 +537,11 @@ int nArg, void *pUserData, void (*xFunc)(sqlite4_context*,int,sqlite4_value **), void (*xStep)(sqlite4_context*,int,sqlite4_value **), void (*xFinal)(sqlite4_context*), - FuncDestructor *pDestructor + void (*xDestroy)(void *) ){ FuncDef *p; int nName; assert( sqlite4_mutex_held(db->mutex) ); @@ -586,59 +579,20 @@ /* 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->xDestroy = xDestroy; p->flags = 0; p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE4_OK; } -/* -** This function is the same as sqlite4_create_function(), except that -** it does not grab the database handle mutex or call sqlite4ApiExit(). -*/ -static int createFunctionDestructor( - sqlite4 *db, - const char *zFunc, - int nArg, - void *p, - void (*xFunc)(sqlite4_context*,int,sqlite4_value **), - void (*xStep)(sqlite4_context*,int,sqlite4_value **), - void (*xFinal)(sqlite4_context*), - void (*xDestroy)(void *) -){ - int rc; - FuncDestructor *pArg = 0; - - if( xDestroy ){ - pArg = (FuncDestructor *)sqlite4DbMallocZero(db, sizeof(FuncDestructor)); - if( !pArg ){ - xDestroy(p); - return SQLITE4_NOMEM; - } - pArg->xDestroy = xDestroy; - pArg->pUserData = p; - } - rc = sqlite4CreateFunc(db, zFunc, nArg, p, xFunc, xStep, xFinal, pArg); - if( pArg && pArg->nRef==0 ){ - assert( rc!=SQLITE4_OK ); - xDestroy(p); - sqlite4DbFree(db, pArg); - } - - return rc; -} - /* ** Create new user functions. */ int sqlite4_create_function( sqlite4 *db, @@ -650,14 +604,15 @@ void (*xFinal)(sqlite4_context*), void (*xDestroy)(void *) ){ int rc; sqlite4_mutex_enter(db->mutex); - rc = createFunctionDestructor( + rc = sqlite4CreateFunc( db, zFunc, nArg, p, xFunc, xStep, xFinal, xDestroy ); rc = sqlite4ApiExit(db, rc); + if( rc!=SQLITE4_OK && xDestroy ) xDestroy(p); sqlite4_mutex_leave(db->mutex); return rc; } int sqlite4_create_mi_function( Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -199,28 +199,23 @@ void *pArg; rc = pKV->pStoreVfunc->xGetMethod(pKV, zPragma, &pArg, &xFunc, &xDestroy); if( rc==SQLITE4_OK ){ FuncDef *pDef; - int nByte; int r1 = 0; int regOut; /* Result register */ if( sqlite4AuthCheck(pParse, SQLITE4_PRAGMA, zPragma, 0, zDb) ){ goto pragma_out; } - nByte = sizeof(FuncDef) + sizeof(FuncDestructor); - pDef = (FuncDef *)sqlite4DbMallocZero(db, nByte); + pDef = (FuncDef *)sqlite4DbMallocZero(db, sizeof(FuncDef)); if( !pDef ) goto pragma_out; pDef->flags = SQLITE4_FUNC_EPHEM; pDef->pUserData = pArg; pDef->xFunc = xFunc; - pDef->pDestructor = (FuncDestructor *)&pDef[1]; - pDef->pDestructor->nRef = 1; - pDef->pDestructor->xDestroy = xDestroy; - pDef->pDestructor->pUserData = pArg; + pDef->xDestroy = xDestroy; sqlite4VdbeSetNumCols(v, 1); sqlite4VdbeSetColName(v, 0, COLNAME_NAME, zPragma, SQLITE4_TRANSIENT); if( pList ){ Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -578,11 +578,10 @@ int is_agg = 0; /* True if is an aggregate function */ int auth; /* Authorization to use the function */ int nId; /* Number of characters in function name */ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ - u8 enc = ENC(pParse->db); /* The database encoding */ testcase( pExpr->op==TK_CONST_FUNC ); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); zId = pExpr->u.zToken; nId = sqlite4Strlen30(zId); Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -2341,11 +2341,10 @@ */ #define SQLITE4_UTF8 1 #define SQLITE4_UTF16LE 2 #define SQLITE4_UTF16BE 3 #define SQLITE4_UTF16 4 /* Use native byte order */ -#define SQLITE4_ANY 5 /* sqlite4_create_function only */ #define SQLITE4_UTF16_ALIGNED 8 /* sqlite4_create_collation only */ /* ** CAPIREF: Obtaining SQL Function Parameter Values ** Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -568,11 +568,10 @@ typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprListItem ExprListItem; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; -typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefTable FuncDefTable; typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct Fts5Index Fts5Index; typedef struct Fts5Info Fts5Info; @@ -643,15 +642,15 @@ u8 flags; /* Some combination of SQLITE4_FUNC_* */ void *pUserData; /* User data parameter */ FuncDef *pSameName; /* Next with a different name but the same hash */ void (*xFunc)(sqlite4_context*,int,sqlite4_value**); /* Regular function */ void (*xStep)(sqlite4_context*,int,sqlite4_value**); /* Aggregate step */ - void (*xFinalize)(sqlite4_context*); /* Aggregate finalizer */ + void (*xFinalize)(sqlite4_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pNextName; /* Next function with a different name */ - FuncDestructor *pDestructor; /* Reference counted destructor function */ u8 bMatchinfo; /* True for matchinfo function */ + void (*xDestroy)(void *); /* Users destructor function */ }; /* ** A table of SQL functions. ** @@ -932,30 +931,10 @@ #define SQLITE4_MAGIC_CLOSED 0x5f2246b4 /* Database is closed */ #define SQLITE4_MAGIC_SICK 0xcaad9e61 /* Error and awaiting close */ #define SQLITE4_MAGIC_BUSY 0xb07f8c8c /* Database currently in use */ #define SQLITE4_MAGIC_ERROR 0x912e4c46 /* An SQLITE4_MISUSE error occurred */ -/* -** 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 SQLITE4_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; -}; - /* ** Possible values for FuncDef.flags */ #define SQLITE4_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE4_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ @@ -3019,11 +2998,11 @@ int sqlite4SchemaToIndex(sqlite4 *db, Schema *); KeyInfo *sqlite4IndexKeyinfo(Parse *, Index *); int sqlite4CreateFunc(sqlite4 *, const char *, int, void *, void (*)(sqlite4_context*,int,sqlite4_value **), void (*)(sqlite4_context*,int,sqlite4_value **), void (*)(sqlite4_context*), - FuncDestructor *pDestructor + void (*)(void *) ); int sqlite4ApiExit(sqlite4 *db, int); int sqlite4OpenTempDatabase(Parse *); void sqlite4StrAccumInit(StrAccum*, char*, int, int); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -566,12 +566,12 @@ ** If the input FuncDef structure is ephemeral, then free it. If ** the FuncDef is not ephermal, then do nothing. */ static void freeEphemeralFunction(sqlite4 *db, FuncDef *pDef){ if( ALWAYS(pDef) && (pDef->flags & SQLITE4_FUNC_EPHEM)!=0 ){ - if( pDef->pDestructor->xDestroy ){ - pDef->pDestructor->xDestroy(pDef->pDestructor->pUserData); + if( pDef->xDestroy ){ + pDef->xDestroy(pDef->pUserData); } sqlite4DbFree(db, pDef); } } Index: test/func3.test ================================================================== --- test/func3.test +++ test/func3.test @@ -18,22 +18,22 @@ do_test func3-2.1 { set destroyed 0 proc destroy {} { set ::destroyed 1 } - sqlite4_create_function_v2 db f3 -1 utf8 -func f3 -destroy destroy + sqlite4_create_function_v2 db f3 -1 -func f3 -destroy destroy set destroyed } 0 do_test func3-2.2 { - sqlite4_create_function_v2 db f3 -1 utf8 -func f3 + sqlite4_create_function_v2 db f3 -1 -func f3 set destroyed } 1 do_test func3-3.1 { set destroyed 0 proc destroy {} { set ::destroyed 1 } - sqlite4_create_function_v2 db f3 -1 any -func f3 -destroy destroy + sqlite4_create_function_v2 db f3 -1 -func f3 -destroy destroy set destroyed } 0 do_test func3-3.2 { db close set destroyed @@ -41,12 +41,12 @@ sqlite4 db test.db do_test func3-4.1 { set destroyed 0 set rc [catch { - sqlite4_create_function_v2 db f3 -1 any -func f3 -step f3 -destroy destroy + sqlite4_create_function_v2 db f3 -1 -func f3 -step f3 -destroy destroy } msg] list $rc $msg } {1 SQLITE4_MISUSE} do_test func3-4.2 { set destroyed } 1 finish_test Index: test/test_main.c ================================================================== --- test/test_main.c +++ test/test_main.c @@ -1472,48 +1472,29 @@ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite4 *db; const char *zFunc; int nArg; - int enc; CreateFunctionV2 *p; int i; int rc; - struct EncTable { - const char *zEnc; - int enc; - } aEnc[] = { - {"utf8", SQLITE4_UTF8 }, - {"utf16", SQLITE4_UTF16 }, - {"utf16le", SQLITE4_UTF16LE }, - {"utf16be", SQLITE4_UTF16BE }, - {"any", SQLITE4_ANY }, - {"0", 0 } - }; - - if( objc<5 || (objc%2)==0 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG ENC SWITCHES..."); + if( objc<4 || (objc%2) ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG 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 = sqlite4_malloc(0, sizeof(CreateFunctionV2)); assert( p ); memset(p, 0, sizeof(CreateFunctionV2)); p->interp = interp; - for(i=5; i