Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -85,57 +85,27 @@ #endif /* BUILD_sqlite */ #define NUM_PREPARED_STMTS 10 #define MAX_PREPARED_STMTS 100 -/* -** Fixed string table index enumeration. These enumeration values are used as -** indexes into the pFixedStrings array. -*/ -typedef enum { - FS_NEW, FS_STEP, FS_INVERSE, FS_VALUE, FS_DESTROY, FS_NUM_FIXED_STRINGS -} FixedString; - -/* -** Fixed string table, indexed by the above enumeration. This table is -** initialized by Sqlite3_Init() and cleaned up by Sqlite3_Unload(). -*/ -static Tcl_Obj *pFixedStrings[FS_NUM_FIXED_STRINGS]; - -/* -** TCL SQL function flags. -*/ -#define FF_AGGREGATE 1 /* This is an aggregate or window function */ -#define FF_CLASS 2 /* pScript is a TclOO-style class name */ - /* Forward declaration */ typedef struct SqliteDb SqliteDb; /* -** New SQL functions can be created as TCL command prefixes. Each such function +** New SQL functions can be created as TCL scripts. Each such function ** is described by an instance of the following structure. */ typedef struct SqlFunc SqlFunc; struct SqlFunc { Tcl_Interp *interp; /* The TCL interpret to execute the function */ Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */ - Tcl_Obj *pInitial; /* Constructor argument(s) to the "new" method */ SqliteDb *pDb; /* Database connection that owns this function */ - int flags; /* Function configuration FF_* bitmask */ + int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */ char *zName; /* Name of this function */ SqlFunc *pNext; /* Next function on the list of them all */ }; -/* -** Persistent aggregate/window function state data structure. -*/ -typedef struct { - int initialized; /* Nonzero if initialization has taken place */ - int window; /* Nonzero if tclSqlFuncValue() was called */ - Tcl_Obj *pObj; /* TCL state data. */ -} SqlFuncState; - /* ** New collation sequences function can be created as TCL scripts. Each such ** function is described by an instance of the following structure. */ typedef struct SqlCollate SqlCollate; @@ -453,10 +423,37 @@ return TCL_OK; } #else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */ #define closeIncrblobChannels(pDb) #endif + +/* +** Look at the script prefix in pCmd. We will be executing this script +** after first appending one or more arguments. This routine analyzes +** the script to see if it is safe to use Tcl_EvalObjv() on the script +** rather than the more general Tcl_EvalEx(). Tcl_EvalObjv() is much +** faster. +** +** Scripts that are safe to use with Tcl_EvalObjv() consists of a +** command name followed by zero or more arguments with no [...] or $ +** or {...} or ; to be seen anywhere. Most callback scripts consist +** of just a single procedure name and they meet this requirement. +*/ +static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){ + /* We could try to do something with Tcl_Parse(). But we will instead + ** just do a search for forbidden characters. If any of the forbidden + ** characters appear in pCmd, we will report the string as unsafe. + */ + const char *z; + int n; + z = Tcl_GetStringFromObj(pCmd, &n); + while( n-- > 0 ){ + int c = *(z++); + if( c=='$' || c=='[' || c==';' ) return 0; + } + return 1; +} /* ** Find an SqlFunc structure with the given name. Or create a new ** one if an existing one cannot be found. Return a pointer to the ** structure. @@ -474,11 +471,10 @@ } } pNew->interp = pDb->interp; pNew->pDb = pDb; pNew->pScript = 0; - pNew->pInitial = 0; pNew->pNext = pDb->pFunc; pDb->pFunc = pNew; return pNew; } @@ -523,13 +519,10 @@ while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; assert( pFunc->pDb==pDb ); Tcl_DecrRefCount(pFunc->pScript); - if( pFunc->pInitial ){ - Tcl_DecrRefCount(pFunc->pInitial); - } Tcl_Free((char*)pFunc); } while( pDb->pCollate ){ SqlCollate *pCollate = pDb->pCollate; pDb->pCollate = pCollate->pNext; @@ -903,65 +896,29 @@ Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(pCmd); return (atoi(Tcl_GetStringResult(p->interp))); } -/* Forward declaration */ -static SqlFuncState *tclSqlFuncState(sqlite3_context *context); - /* -** This function calls a TCL command prefix to implement an SQL function. -** -** The command prefix is automatically determined. If the function is a scalar -** function, or the "-state" calling convention is used, or the method is "new", -** the TCL script provided at the time the function was defined is used as the -** command prefix. If the function is an aggregate function, and the "-class" -** calling convention is used, and the method is not "new", the current state -** value is used as the command prefix. -** -** If the function is a scalar function, the next paragraph does not apply, and -** the method argument is ignored. For scalar functions, the only arguments are -** SQL function argument values. -** -** The first argument to the command prefix is the method name. If the method -** is not "new" and the "-state" calling convention is used, the second argument -** is the current state value. If the method is "new" and the "-state" calling -** convention is used, the value of the "-initial" switch (defaulting to empty -** string) is the second argument. If the method is "new" and the "-class" -** calling convention is used, the elements of the value of the "-initial" -** switch (defaulting to empty list) are the second and subsequent arguments. -** After these arguments are appended SQL function argument values, if supplied. -** -** On success, the return value is a pointer to the Tcl_Obj result. On failure, -** the return value is NULL, and an error message is stored in the SQL context. +** This routine is called to evaluate an SQL function implemented +** using TCL script. */ -static Tcl_Obj *tclSqlFuncCall( - sqlite3_context *context, - FixedString method, - int argc, - sqlite3_value **argv -){ - SqlFunc *pFunc = sqlite3_user_data(context); +static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ + SqlFunc *p = sqlite3_user_data(context); Tcl_Obj *pCmd; - int flags = 0; int i; int rc; - /* Determine the command prefix. */ - if( (~pFunc->flags & (FF_AGGREGATE|FF_CLASS)) || method==FS_NEW ){ - pCmd = pFunc->pScript; - }else{ - pCmd = tclSqlFuncState(context)->pObj; - } - - if( !(pFunc->flags & FF_AGGREGATE) && argc==0 ){ + if( argc==0 ){ /* If there are no arguments to the function, call Tcl_EvalObjEx on the ** script object directly. This allows the TCL compiler to generate ** bytecode for the command on the first invocation and thus make ** subsequent invocations much faster. */ + pCmd = p->pScript; Tcl_IncrRefCount(pCmd); - flags = TCL_EVAL_DIRECT; + rc = Tcl_EvalObjEx(p->interp, pCmd, 0); + Tcl_DecrRefCount(pCmd); }else{ /* If there are arguments to the function, make a shallow copy of the ** script object, lappend the arguments, then evaluate the copy. ** ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated. @@ -970,305 +927,98 @@ ** of the list to tclCmdNameType, that alternate representation will ** be preserved and reused on the next invocation. */ Tcl_Obj **aArg; int nArg; - if( Tcl_ListObjGetElements(pFunc->interp, pCmd, &nArg, &aArg) ){ - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; + if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + return; } pCmd = Tcl_NewListObj(nArg, aArg); Tcl_IncrRefCount(pCmd); - - /* Append aggregate/window function initial arguments. */ - if( pFunc->flags & FF_AGGREGATE ){ - /* Append method name argument. */ - if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, - pFixedStrings[method]) ){ - Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; - } - - /* Append state argument or "new" constructor arguments. */ - if( !(pFunc->flags & FF_CLASS) ){ - Tcl_Obj *pState; - if( method!=FS_NEW ){ - pState = tclSqlFuncState(context)->pObj; - }else if( pFunc->pInitial ){ - pState = pFunc->pInitial; - }else{ - pState = Tcl_NewObj(); - } - if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, pState) ){ - if( method==FS_NEW && !pFunc->pInitial ){ - Tcl_DecrRefCount(pState); - } - Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; - } - }else if( method==FS_NEW && pFunc->pInitial - && Tcl_ListObjAppendList(pFunc->interp, pCmd, pFunc->pInitial) ){ - Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; - } - } - - /* Append SQL arguments. */ for(i=0; i=-2147483647 && v<=2147483647 ){ pVal = Tcl_NewIntObj((int)v); }else{ pVal = Tcl_NewWideIntObj(v); } break; } case SQLITE_FLOAT: { - pVal = Tcl_NewDoubleObj(sqlite3_value_double(argv[i])); - break; - } - case SQLITE_NULL: { - pVal = Tcl_NewStringObj(pFunc->pDb->zNull, -1); - break; - } - default: { - int bytes = sqlite3_value_bytes(argv[i]); - pVal = Tcl_NewStringObj((char *)sqlite3_value_text(argv[i]), bytes); - break; - } - } - if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, pVal) ){ - Tcl_DecrRefCount(pCmd); - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; - } - } - } - - /* Invoke the completed command object, then deallocate it if it was a - ** temporary list object created above. */ - rc = Tcl_EvalObjEx(pFunc->interp, pCmd, flags); - Tcl_DecrRefCount(pCmd); - - if( rc && rc!=TCL_RETURN ){ - sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1); - return 0; - }else{ - return Tcl_GetObjResult(pFunc->interp); - } -} - -/* -** This function converts a Tcl_Obj result to an SQL value and stores it into -** the SQL context. -*/ -static void tclSqlFuncResult( - sqlite3_context *context, - Tcl_Obj *pVar -){ - int n; - u8 *data; - const char *zType = pVar->typePtr ? pVar->typePtr->name : ""; - char c = zType[0]; - if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ - /* Only return a BLOB type if the Tcl variable is a bytearray and - ** has no string representation. */ - data = Tcl_GetByteArrayFromObj(pVar, &n); - sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT); - }else if( c=='b' && strcmp(zType,"boolean")==0 ){ - Tcl_GetIntFromObj(0, pVar, &n); - sqlite3_result_int(context, n); - }else if( c=='d' && strcmp(zType,"double")==0 ){ - double r; - Tcl_GetDoubleFromObj(0, pVar, &r); - sqlite3_result_double(context, r); - }else if( (c=='w' && strcmp(zType,"wideInt")==0) || - (c=='i' && strcmp(zType,"int")==0) ){ - Tcl_WideInt v; - Tcl_GetWideIntFromObj(0, pVar, &v); - sqlite3_result_int64(context, v); - }else{ - data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); - sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT); - } -} - -/* - * This function returns the address of a state structure that persists for the - * duration of the execution of an aggregate or window function. - * - * If the "-state" (default) calling convention is selected, the pObj field - * points to the Tcl_Obj most recently returned by the "new", "step", or - * "inverse" methods of the TCL command prefix registered when the SQL function - * was first created. This Tcl_Obj will be used as the second argument (i.e. - * the one following the method name) the next time the command prefix is used. - * - * If the "-class" calling convention is selected, the pObj field points to the - * Tcl_Obj containing the command prefix to invoke. This command prefix is the - * result value obtained by invoking the "new" method, as with the "-state" - * calling convention. - * - * In event of error, the pObj field will be NULL, and the TCL should not be - * called again for the duration of the SQL function execution. -*/ -static SqlFuncState *tclSqlFuncState(sqlite3_context *context){ - SqlFuncState *pState = sqlite3_aggregate_context(context, sizeof(*pState)); - if( !pState->initialized ){ - pState->initialized = 1; - pState->window = 0; - pState->pObj = tclSqlFuncCall(context, FS_NEW, 0, NULL); - if( pState->pObj ){ - Tcl_IncrRefCount(pState->pObj); - } - } - return pState; -} - -/* -** Destructor for custom SQL functions defined in TCL. When execution of an SQL -** function completes, either successfully or due to error, the "destroy" method -** is invoked so that script-level cleanup may be performed. Next, the current -** state Tcl_Obj is deallocated and its pointer is set NULL so that TCL will not -** be invoked again for the current SQL function. -**/ -static void tclSqlFuncDestroy(sqlite3_context *context) -{ - SqlFuncState *pState = tclSqlFuncState(context); - if( pState->pObj ){ - tclSqlFuncCall(context, FS_DESTROY, 0, NULL); - Tcl_DecrRefCount(pState->pObj); - pState->pObj = NULL; - } -} - -/* -** Common implementation of tclSqlFuncStep() and tclSqlFuncInverse(). -*/ -static void tclSqlFuncStepOrInverse( - sqlite3_context *context, - FixedString method, - int argc, - sqlite3_value **argv -){ - SqlFuncState *pState = tclSqlFuncState(context); - if( pState->pObj ){ - Tcl_Obj *pVal = tclSqlFuncCall(context, method, argc, argv); - if( !pVal ){ - tclSqlFuncDestroy(context); - }else if( !(((SqlFunc *)sqlite3_user_data(context))->flags & FF_CLASS) ){ - Tcl_IncrRefCount(pVal); - Tcl_DecrRefCount(pState->pObj); - pState->pObj = pVal; - } - } -} - -/* -** This routine is called to evaluate a scalar SQL function implemented -** using TCL script. -*/ -static void tclSqlFuncScalar( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Tcl_Obj *pVal = tclSqlFuncCall(context, 0, argc, argv); - if( pVal ){ - tclSqlFuncResult(context, pVal); - } -} - -/* -** This routine is called for each row when it is added to the aggregate or -** window to evaluate an SQL function implemented using TCL script. -** -** If this is the first row in the group, the "new" method (i.e. first argument -** to the command prefix) is used so that initialization may occur. -** -** The "step" method is invoked using the SQL arguments. -** -** When using the "-state" calling convention, the state object is replaced with -** the result value obtained from the "step" method. -*/ -static void tclSqlFuncStep( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - tclSqlFuncStepOrInverse(context, FS_STEP, argc, argv); -} - -/* -** This routine is called for the final row of each group, or in event of error, -** or if there were no rows, to evaluate an aggregate or window SQL function -** implemented using TCL script. -** -** If there were no rows, the "new" method will be invoked first to initialize. -** -** If this is a non-window function, the "value" method is invoked. The check -** for window functions is done to avoid invoking "value" twice for the last row -** of each group. -** -** For both window and non-window aggregate functions, "destroy" is invoked. -*/ -static void tclSqlFuncFinal(sqlite3_context *context){ - SqlFuncState *pState = tclSqlFuncState(context); - if( pState->pObj ){ - if( !pState->window ){ - Tcl_Obj *pVal = tclSqlFuncCall(context, FS_VALUE, 0, NULL); - if( pVal ){ - tclSqlFuncResult(context, pVal); - } - } - tclSqlFuncDestroy(context); - } -} - -/* -** This routine is called for each row to evaluate a window SQL function -** implemented using TCL script. The "value" method is invoked. -*/ -static void tclSqlFuncValue(sqlite3_context *context){ - SqlFuncState *pState = tclSqlFuncState(context); - if( pState->pObj ){ - Tcl_Obj *pVal = tclSqlFuncCall(context, FS_VALUE, 0, NULL); - if( pVal ){ - tclSqlFuncResult(context, pVal); - }else{ - tclSqlFuncDestroy(context); - } - pState->window = 1; - } -} - -/* -** This routine is called for each row when it is removed from the window to -** evaluate an SQL function implemented using TCL script. -** -** The "inverse" method is invoked using the SQL arguments. -** -** When using the "-state" calling convention, the state object is replaced with -** the result value obtained from the "step" method. -*/ -static void tclSqlFuncInverse( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - tclSqlFuncStepOrInverse(context, FS_INVERSE, argc, argv); + double r = sqlite3_value_double(pIn); + pVal = Tcl_NewDoubleObj(r); + break; + } + case SQLITE_NULL: { + pVal = Tcl_NewStringObj(p->pDb->zNull, -1); + break; + } + default: { + int bytes = sqlite3_value_bytes(pIn); + pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes); + break; + } + } + rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal); + if( rc ){ + Tcl_DecrRefCount(pCmd); + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + return; + } + } + if( !p->useEvalObjv ){ + /* Tcl_EvalObjEx() will automatically call Tcl_EvalObjv() if pCmd + ** is a list without a string representation. To prevent this from + ** happening, make sure pCmd has a valid string representation */ + Tcl_GetString(pCmd); + } + rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); + Tcl_DecrRefCount(pCmd); + } + + if( rc && rc!=TCL_RETURN ){ + sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); + }else{ + Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); + int n; + u8 *data; + const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); + char c = zType[0]; + if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ + /* Only return a BLOB type if the Tcl variable is a bytearray and + ** has no string representation. */ + data = Tcl_GetByteArrayFromObj(pVar, &n); + sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT); + }else if( c=='b' && strcmp(zType,"boolean")==0 ){ + Tcl_GetIntFromObj(0, pVar, &n); + sqlite3_result_int(context, n); + }else if( c=='d' && strcmp(zType,"double")==0 ){ + double r; + Tcl_GetDoubleFromObj(0, pVar, &r); + sqlite3_result_double(context, r); + }else if( (c=='w' && strcmp(zType,"wideInt")==0) || + (c=='i' && strcmp(zType,"int")==0) ){ + Tcl_WideInt v; + Tcl_GetWideIntFromObj(0, pVar, &v); + sqlite3_result_int64(context, v); + }else{ + data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); + sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT); + } + } } #ifndef SQLITE_OMIT_AUTHORIZATION /* ** This is the authentication function. It appends the authentication @@ -2882,109 +2632,64 @@ } break; } /* - ** $db function NAME ?SWITCHES? SCRIPT + ** $db function NAME [-argcount N] [-deterministic] SCRIPT ** ** Create a new SQL function called NAME. Whenever that function is ** called, invoke SCRIPT to evaluate the function. */ case DB_FUNCTION: { - static const char *SW_strs[] = { - "-argcount", "-deterministic", "-state", "-class", "-initial", - "-scalar", "-aggregate", "-window", 0 - }; - enum SW_enum { - SW_ARGCOUNT, SW_DETERMINISTIC, SW_STATE, SW_CLASS, SW_INITIAL, - SW_SCALAR, SW_AGGREGATE, SW_WINDOW - }; - enum {TY_SCALAR, TY_AGGREGATE, TY_WINDOW} type = TY_SCALAR; - int sqlFlags = SQLITE_UTF8; - int tclFlags = 0; + int flags = SQLITE_UTF8; SqlFunc *pFunc; Tcl_Obj *pScript; - Tcl_Obj *pInitial = NULL; char *zName; int nArg = -1; int i; if( objc<4 ){ Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT"); return TCL_ERROR; } for(i=3; i<(objc-1); i++){ const char *z = Tcl_GetString(objv[i]); - if( Tcl_GetIndexFromObj(interp, objv[i], SW_strs, "option", 0, &choice) ){ - return TCL_ERROR; - } - if( i==(objc-2) && (choice==SW_ARGCOUNT || choice==SW_INITIAL) ){ - Tcl_AppendResult(interp, "option requires an argument: ", z, (char *)0); - return TCL_ERROR; - } - switch( (enum SW_enum)choice ){ - case SW_ARGCOUNT: + int n = strlen30(z); + if( n>2 && strncmp(z, "-argcount",n)==0 ){ + if( i==(objc-2) ){ + Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0); + return TCL_ERROR; + } if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR; if( nArg<0 ){ Tcl_AppendResult(interp, "number of arguments must be non-negative", (char*)0); return TCL_ERROR; } i++; - break; - case SW_INITIAL : pInitial = objv[++i] ; break; - case SW_DETERMINISTIC: sqlFlags |= SQLITE_DETERMINISTIC; break; - case SW_STATE : tclFlags &= ~FF_CLASS ; break; - case SW_CLASS : tclFlags |= FF_CLASS ; break; - case SW_SCALAR : type = TY_SCALAR ; break; - case SW_AGGREGATE : type = TY_AGGREGATE ; break; - case SW_WINDOW : type = TY_WINDOW ; break; - } - } - if( type!=TY_SCALAR ){ - tclFlags |= FF_AGGREGATE; - }else if( tclFlags&FF_CLASS ){ - Tcl_AppendResult(interp, "-class requires -window or -aggregate", - (char *)0); - return TCL_ERROR; - }else if( pInitial ){ - Tcl_AppendResult(interp, "-initial requires -window or -aggregate", - (char *)0); - return TCL_ERROR; + }else + if( n>2 && strncmp(z, "-deterministic",n)==0 ){ + flags |= SQLITE_DETERMINISTIC; + }else{ + Tcl_AppendResult(interp, "bad option \"", z, + "\": must be -argcount or -deterministic", (char*)0 + ); + return TCL_ERROR; + } } pScript = objv[objc-1]; zName = Tcl_GetStringFromObj(objv[2], 0); pFunc = findSqlFunc(pDb, zName); if( pFunc==0 ) return TCL_ERROR; if( pFunc->pScript ){ Tcl_DecrRefCount(pFunc->pScript); } - if( pFunc->pInitial ){ - Tcl_DecrRefCount(pFunc->pInitial); - } pFunc->pScript = pScript; Tcl_IncrRefCount(pScript); - pFunc->pInitial = pInitial; - if( pInitial ){ - Tcl_IncrRefCount(pInitial); - } - pFunc->flags = tclFlags; - switch( type ){ - case TY_SCALAR: - rc = sqlite3_create_function(pDb->db, zName, nArg, sqlFlags, - pFunc, tclSqlFuncScalar, 0, 0); - break; - case TY_AGGREGATE: - rc = sqlite3_create_function(pDb->db, zName, nArg, sqlFlags, - pFunc, 0, tclSqlFuncStep, tclSqlFuncFinal); - break; - case TY_WINDOW: - rc = sqlite3_create_window_function(pDb->db, zName, nArg, sqlFlags, - pFunc, tclSqlFuncStep, tclSqlFuncFinal, tclSqlFuncValue, - tclSqlFuncInverse, 0); - break; - } + pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); + rc = sqlite3_create_function(pDb->db, zName, nArg, flags, + pFunc, tclSqlFunc, 0, 0); if( rc!=SQLITE_OK ){ rc = TCL_ERROR; Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); } break; @@ -3974,21 +3679,10 @@ ** The EXTERN macros are required by TCL in order to work on windows. */ EXTERN int Sqlite3_Init(Tcl_Interp *interp){ int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR; if( rc==TCL_OK ){ - if( !*pFixedStrings ){ -#define FIXED_STRING(i, s) \ - (pFixedStrings[(i)] = Tcl_NewStringObj((s), sizeof((s)) - 1), \ - Tcl_IncrRefCount(pFixedStrings[(i)])) - FIXED_STRING(FS_NEW , "new" ); - FIXED_STRING(FS_STEP , "step" ); - FIXED_STRING(FS_INVERSE, "inverse"); - FIXED_STRING(FS_VALUE , "value" ); - FIXED_STRING(FS_DESTROY, "destroy"); -#undef FIXED_STRING - } Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); #ifndef SQLITE_3_SUFFIX_ONLY /* The "sqlite" alias is undocumented. It is here only to support ** legacy scripts. All new scripts should use only the "sqlite3" ** command. */ @@ -3997,23 +3691,12 @@ rc = Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION); } return rc; } EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } -EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ - int i; - if( flags==TCL_UNLOAD_DETACH_FROM_PROCESS ){ - for(i=0; i