aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/func.c6
-rw-r--r--src/main.c76
-rw-r--r--src/sqlite.h.in11
-rw-r--r--src/sqliteInt.h26
-rw-r--r--src/test1.c134
5 files changed, 237 insertions, 16 deletions
diff --git a/src/func.c b/src/func.c
index d46141272..27b9bcfd1 100644
--- a/src/func.c
+++ b/src/func.c
@@ -1450,10 +1450,10 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
}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 3b04b75e6..67733ee71 100644
--- a/src/main.c
+++ b/src/main.c
@@ -596,10 +596,27 @@ void sqlite3CloseSavepoints(sqlite3 *db){
}
/*
+** 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 @@ int sqlite3_close(sqlite3 *db){
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 @@ int sqlite3CreateFunc(
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 @@ int sqlite3CreateFunc(
}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 @@ int sqlite3CreateFunc(
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 sqlite3CreateFunc(
*/
int sqlite3_create_function(
sqlite3 *db,
- const char *zFunctionName,
+ const char *zFunc,
int nArg,
int enc,
void *p,
@@ -1029,7 +1057,39 @@ int sqlite3_create_function(
){
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 @@ int sqlite3_create_function16(
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 @@ int sqlite3_overload_function(
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 38d9b63fe..6fdadb695 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -3350,6 +3350,17 @@ int sqlite3_create_function16(
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 6c41315f8..90d900d69 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -600,6 +600,7 @@ typedef struct Expr Expr;
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 @@ struct FuncDef {
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 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
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 f458cdb40..bf26b9ea9 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -1791,6 +1791,131 @@ static int test_create_collation_v2(
}
/*
+** 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 @@ static int test_collate_func(
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 @@ static void test_collate_needed_cb(
}
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 @@ static int alignmentCollFunc(
){
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 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "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