From: dan Date: Fri, 27 Aug 2010 17:48:52 +0000 (+0000) Subject: Add the sqlite3_create_function_v2() API, a version of create_function that allows... X-Git-Tag: experimental~107 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d2199f0f8d8f16b1b9ce39eba8d5763d246f6502;p=thirdparty%2Fsqlite.git 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/manifest b/manifest index 50698ee7c2..eba8174179 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,5 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -C Refactor\sthe\simplementation\sof\sthe\sscratch\smemory\sallocator.\s\sAdd\sthe\nSQLITE_TESTCTRL_SCRATCHMALLOC\sinterface\sto\sfacilitate\stesting. -D 2010-08-27T17:16:45 +C Add\sthe\ssqlite3_create_function_v2()\sAPI,\sa\sversion\sof\screate_function\sthat\sallows\sa\sdestructor\sto\sbe\sspecified. +D 2010-08-27T17:48:52 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 543f91f24cd7fee774ecc0a61c19704c0c3e78fd F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -129,7 +126,7 @@ F src/delete.c 7ed8a8c8b5f748ece92df173d7e0f7810c899ebd F src/expr.c 9ee507c3dc6eaa5657cbd1dad026cdeda89c559f F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 58bbf52c6ddd3f64ca40a3230f9e548a83a5cb16 -F src/func.c 464b0dc70618b896c402c574eb04bc5eacf35341 +F src/func.c caa6c5134106d95cced4db80ce3fdcdde4f6c8d4 F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 @@ -139,7 +136,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 6d422ea91cf3d2d00408c5a8f2391cd458da85f8 -F src/main.c bdd23be253843d3f34faa8f5bd3f92ebbf05b94b +F src/main.c 185e558e8e38a36a78fdb36b378571f8fc136a7a F src/malloc.c f34c9253326fcd2dad0041801992ccf18ddd6ab5 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2 @@ -174,14 +171,14 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c 8add6cab889fc02e1492eda8dba462ccf11f51dd F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056 -F src/sqlite.h.in 60a40abb36376b4cdf6a9ac7604ed5d3ea2d0bf8 +F src/sqlite.h.in 55498e6664eecf9a1db722d473445dbd210fe5f7 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 -F src/sqliteInt.h 78ed6bd32777ad06a8b9910d17307aeeae555485 +F src/sqliteInt.h e3055cc4bd65c6a097b8e9132df807ae683bef9f F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c b1565eb727ec7121b59287fed77fc378118bfb69 -F src/test1.c 55005c9781b157b1d215ba145768783b9abae78c +F src/test1.c 2d3ab2cacced2adfee13a6d93b3570ada4072c39 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94 F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee @@ -431,6 +428,7 @@ F test/fts3snippet.test 9f9a4a7e396c5d8ce2898be65ebabc429555430f F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f +F test/func3.test 0d43ff07970a47e58d37e1fea1ae6a2458e2590f F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6 F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 @@ -850,14 +848,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 51049479a8577e03cc353f71f6e13a10c8323d91 -R 9667b04fa2c6e7e750d5c74a6bbee707 -U drh -Z c2de87f687a677d94fa81a950a4c025c ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.6 (GNU/Linux) - -iD8DBQFMd/MAoxKgR168RlERAvLjAJ9ChwPUiFzrhTLciw7Q4AEhSu2EvACfZWP3 -sBXwHRcpD05MSHO9+3nFRAQ= -=R7/R ------END PGP SIGNATURE----- +P a3475ddfbe4526e6e0b334fd1376ee7c31508b80 +R ce2fe209cb36d8732f735448dc28c68e +U dan +Z 0f4ca6d40dff81bf94d3eaa37cf31103 diff --git a/manifest.uuid b/manifest.uuid index 519e28e978..7620966ab3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a3475ddfbe4526e6e0b334fd1376ee7c31508b80 \ No newline at end of file +9a724dfbe822c77e76721abe3443c9cb018bb2e2 \ No newline at end of file diff --git a/src/func.c b/src/func.c index d461412725..27b9bcfd12 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 3b04b75e68..67733ee716 100644 --- a/src/main.c +++ b/src/main.c @@ -595,11 +595,28 @@ void sqlite3CloseSavepoints(sqlite3 *db){ db->isTransactionSavepoint = 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 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 38d9b63fea..6fdadb6955 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 6c41315f85..90d900d690 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 f458cdb40b..bf26b9ea9a 100644 --- a/src/test1.c +++ b/src/test1.c @@ -1790,6 +1790,131 @@ static int test_create_collation_v2( return TCL_OK; } +/* +** 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; ipFunc = 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? */ @@ -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 = nKey10 && 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 diff --git a/test/func3.test b/test/func3.test new file mode 100644 index 0000000000..22b5031efe --- /dev/null +++ b/test/func3.test @@ -0,0 +1,71 @@ +# 2010 August 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing that destructor functions associated +# with functions created using sqlite3_create_function_v2() is +# correctly invoked. +# +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +do_test func3-1.1 { + set destroyed 0 + proc destroy {} { set ::destroyed 1 } + sqlite3_create_function_v2 db f2 -1 any -func f2 -destroy destroy + set destroyed +} 0 +do_test func3-1.2 { + sqlite3_create_function_v2 db f2 -1 utf8 -func f2 + set destroyed +} 0 +do_test func3-1.3 { + sqlite3_create_function_v2 db f2 -1 utf16le -func f2 + set destroyed +} 0 +do_test func3-1.4 { + sqlite3_create_function_v2 db f2 -1 utf16be -func f2 + set destroyed +} 1 + +do_test func3-2.1 { + set destroyed 0 + proc destroy {} { set ::destroyed 1 } + sqlite3_create_function_v2 db f3 -1 utf8 -func f3 -destroy destroy + set destroyed +} 0 +do_test func3-2.2 { + sqlite3_create_function_v2 db f3 -1 utf8 -func f3 + set destroyed +} 1 + +do_test func3-3.1 { + set destroyed 0 + proc destroy {} { set ::destroyed 1 } + sqlite3_create_function_v2 db f3 -1 any -func f3 -destroy destroy + set destroyed +} 0 +do_test func3-3.2 { + db close + set destroyed +} 1 + +sqlite3 db test.db +do_test func3-4.1 { + set destroyed 0 + set rc [catch { + sqlite3_create_function_v2 db f3 -1 any -func f3 -step f3 -destroy destroy + } msg] + list $rc $msg +} {1 SQLITE_MISUSE} +do_test func3-4.2 { set destroyed } 0 + +finish_test