mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Add the new "bind_fallback" method to the "sqlite3" object in the TCL
interface. FossilOrigin-Name: c7f70b6d96338dba201e005104e7f7148c1a8cd767ab05e35b44617c4c797bc5
This commit is contained in:
127
src/tclsqlite.c
127
src/tclsqlite.c
@@ -159,6 +159,7 @@ struct SqliteDb {
|
||||
char *zTraceV2; /* The trace_v2 callback routine */
|
||||
char *zProfile; /* The profile callback routine */
|
||||
char *zProgress; /* The progress callback routine */
|
||||
char *zBindFallback; /* Callback to invoke on a binding miss */
|
||||
char *zAuth; /* The authorization callback routine */
|
||||
int disableAuth; /* Disable the authorizer if it exists */
|
||||
char *zNull; /* Text to substitute for an SQL NULL value */
|
||||
@@ -549,6 +550,9 @@ static void SQLITE_TCLAPI DbDeleteCmd(void *db){
|
||||
if( pDb->zProfile ){
|
||||
Tcl_Free(pDb->zProfile);
|
||||
}
|
||||
if( pDb->zBindFallback ){
|
||||
Tcl_Free(pDb->zBindFallback);
|
||||
}
|
||||
if( pDb->zAuth ){
|
||||
Tcl_Free(pDb->zAuth);
|
||||
}
|
||||
@@ -1301,6 +1305,8 @@ static int dbPrepareAndBind(
|
||||
int iParm = 0; /* Next free entry in apParm */
|
||||
char c;
|
||||
int i;
|
||||
int needResultReset = 0; /* Need to invoke Tcl_ResetResult() */
|
||||
int rc = SQLITE_OK; /* Value to return */
|
||||
Tcl_Interp *interp = pDb->interp;
|
||||
|
||||
*ppPreStmt = 0;
|
||||
@@ -1388,6 +1394,25 @@ static int dbPrepareAndBind(
|
||||
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
|
||||
if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){
|
||||
Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0);
|
||||
if( pVar==0 && pDb->zBindFallback!=0 ){
|
||||
Tcl_Obj *pCmd;
|
||||
int rx;
|
||||
pCmd = Tcl_NewStringObj(pDb->zBindFallback, -1);
|
||||
Tcl_IncrRefCount(pCmd);
|
||||
Tcl_ListObjAppendElement(interp, pCmd, Tcl_NewStringObj(zVar,-1));
|
||||
if( needResultReset ) Tcl_ResetResult(interp);
|
||||
needResultReset = 1;
|
||||
rx = Tcl_EvalObjEx(interp, pCmd, TCL_EVAL_DIRECT);
|
||||
Tcl_DecrRefCount(pCmd);
|
||||
if( rx==TCL_OK ){
|
||||
pVar = Tcl_GetObjResult(interp);
|
||||
}else if( rx==TCL_ERROR ){
|
||||
rc = TCL_ERROR;
|
||||
break;
|
||||
}else{
|
||||
pVar = 0;
|
||||
}
|
||||
}
|
||||
if( pVar ){
|
||||
int n;
|
||||
u8 *data;
|
||||
@@ -1423,12 +1448,14 @@ static int dbPrepareAndBind(
|
||||
}else{
|
||||
sqlite3_bind_null(pStmt, i);
|
||||
}
|
||||
if( needResultReset ) Tcl_ResetResult(pDb->interp);
|
||||
}
|
||||
}
|
||||
pPreStmt->nParm = iParm;
|
||||
*ppPreStmt = pPreStmt;
|
||||
if( needResultReset && rc==TCL_OK ) Tcl_ResetResult(pDb->interp);
|
||||
|
||||
return TCL_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1887,35 +1914,36 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
int choice;
|
||||
int rc = TCL_OK;
|
||||
static const char *DB_strs[] = {
|
||||
"authorizer", "backup", "busy",
|
||||
"cache", "changes", "close",
|
||||
"collate", "collation_needed", "commit_hook",
|
||||
"complete", "copy", "deserialize",
|
||||
"enable_load_extension", "errorcode", "eval",
|
||||
"exists", "function", "incrblob",
|
||||
"interrupt", "last_insert_rowid", "nullvalue",
|
||||
"onecolumn", "preupdate", "profile",
|
||||
"progress", "rekey", "restore",
|
||||
"rollback_hook", "serialize", "status",
|
||||
"timeout", "total_changes", "trace",
|
||||
"trace_v2", "transaction", "unlock_notify",
|
||||
"update_hook", "version", "wal_hook",
|
||||
0
|
||||
"authorizer", "backup", "bind_fallback",
|
||||
"busy", "cache", "changes",
|
||||
"close", "collate", "collation_needed",
|
||||
"commit_hook", "complete", "copy",
|
||||
"deserialize", "enable_load_extension", "errorcode",
|
||||
"eval", "exists", "function",
|
||||
"incrblob", "interrupt", "last_insert_rowid",
|
||||
"nullvalue", "onecolumn", "preupdate",
|
||||
"profile", "progress", "rekey",
|
||||
"restore", "rollback_hook", "serialize",
|
||||
"status", "timeout", "total_changes",
|
||||
"trace", "trace_v2", "transaction",
|
||||
"unlock_notify", "update_hook", "version",
|
||||
"wal_hook", 0
|
||||
};
|
||||
enum DB_enum {
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
|
||||
DB_CACHE, DB_CHANGES, DB_CLOSE,
|
||||
DB_COLLATE, DB_COLLATION_NEEDED, DB_COMMIT_HOOK,
|
||||
DB_COMPLETE, DB_COPY, DB_DESERIALIZE,
|
||||
DB_ENABLE_LOAD_EXTENSION, DB_ERRORCODE, DB_EVAL,
|
||||
DB_EXISTS, DB_FUNCTION, DB_INCRBLOB,
|
||||
DB_INTERRUPT, DB_LAST_INSERT_ROWID, DB_NULLVALUE,
|
||||
DB_ONECOLUMN, DB_PREUPDATE, DB_PROFILE,
|
||||
DB_PROGRESS, DB_REKEY, DB_RESTORE,
|
||||
DB_ROLLBACK_HOOK, DB_SERIALIZE, DB_STATUS,
|
||||
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
|
||||
DB_TRACE_V2, DB_TRANSACTION, DB_UNLOCK_NOTIFY,
|
||||
DB_UPDATE_HOOK, DB_VERSION, DB_WAL_HOOK
|
||||
DB_AUTHORIZER, DB_BACKUP, DB_BIND_FALLBACK,
|
||||
DB_BUSY, DB_CACHE, DB_CHANGES,
|
||||
DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED,
|
||||
DB_COMMIT_HOOK, DB_COMPLETE, DB_COPY,
|
||||
DB_DESERIALIZE, DB_ENABLE_LOAD_EXTENSION,DB_ERRORCODE,
|
||||
DB_EVAL, DB_EXISTS, DB_FUNCTION,
|
||||
DB_INCRBLOB, DB_INTERRUPT, DB_LAST_INSERT_ROWID,
|
||||
DB_NULLVALUE, DB_ONECOLUMN, DB_PREUPDATE,
|
||||
DB_PROFILE, DB_PROGRESS, DB_REKEY,
|
||||
DB_RESTORE, DB_ROLLBACK_HOOK, DB_SERIALIZE,
|
||||
DB_STATUS, DB_TIMEOUT, DB_TOTAL_CHANGES,
|
||||
DB_TRACE, DB_TRACE_V2, DB_TRANSACTION,
|
||||
DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK, DB_VERSION,
|
||||
DB_WAL_HOOK
|
||||
};
|
||||
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
|
||||
|
||||
@@ -2037,6 +2065,49 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db bind_fallback ?CALLBACK?
|
||||
**
|
||||
** When resolving bind parameters in an SQL statement, if the parameter
|
||||
** cannot be associated with a TCL variable then invoke CALLBACK with a
|
||||
** single argument that is the name of the parameter and use the return
|
||||
** value of the CALLBACK as the binding. If CALLBACK returns something
|
||||
** other than TCL_OK or TCL_ERROR then bind a NULL.
|
||||
**
|
||||
** If CALLBACK is an empty string, then revert to the default behavior
|
||||
** which is to set the binding to NULL.
|
||||
**
|
||||
** If CALLBACK returns an error, that causes the statement execution to
|
||||
** abort. Hence, to configure a connection so that it throws an error
|
||||
** on an attempt to bind an unknown variable, do something like this:
|
||||
**
|
||||
** proc bind_error {name} {error "no such variable: $name"}
|
||||
** db bind_fallback bind_error
|
||||
*/
|
||||
case DB_BIND_FALLBACK: {
|
||||
if( objc>3 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
|
||||
return TCL_ERROR;
|
||||
}else if( objc==2 ){
|
||||
if( pDb->zBindFallback ){
|
||||
Tcl_AppendResult(interp, pDb->zBindFallback, (char*)0);
|
||||
}
|
||||
}else{
|
||||
char *zCallback;
|
||||
int len;
|
||||
if( pDb->zBindFallback ){
|
||||
Tcl_Free(pDb->zBindFallback);
|
||||
}
|
||||
zCallback = Tcl_GetStringFromObj(objv[2], &len);
|
||||
if( zCallback && len>0 ){
|
||||
pDb->zBindFallback = Tcl_Alloc( len + 1 );
|
||||
memcpy(pDb->zBindFallback, zCallback, len+1);
|
||||
}else{
|
||||
pDb->zBindFallback = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* $db busy ?CALLBACK?
|
||||
**
|
||||
** Invoke the given callback if an SQL statement attempts to open
|
||||
|
Reference in New Issue
Block a user