aboutsummaryrefslogtreecommitdiff
path: root/src/tclsqlite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tclsqlite.c')
-rw-r--r--src/tclsqlite.c232
1 files changed, 148 insertions, 84 deletions
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index d80c25251..d91b2fa3f 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -55,6 +55,25 @@
# include <string.h>
# include <assert.h>
typedef unsigned char u8;
+# ifndef SQLITE_PTRSIZE
+# if defined(__SIZEOF_POINTER__)
+# define SQLITE_PTRSIZE __SIZEOF_POINTER__
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
+ (defined(__APPLE__) && defined(__POWERPC__)) || \
+ (defined(__TOS_AIX__) && !defined(__64BIT__))
+# define SQLITE_PTRSIZE 4
+# else
+# define SQLITE_PTRSIZE 8
+# endif
+# endif /* SQLITE_PTRSIZE */
+# if defined(HAVE_STDINT_H)
+ typedef uintptr_t uptr;
+# elif SQLITE_PTRSIZE==4
+ typedef unsigned int uptr;
+# else
+ typedef sqlite3_uint64 uptr;
+# endif
#endif
#include <ctype.h>
@@ -181,6 +200,7 @@ struct SqliteDb {
int nVMStep; /* Another statistic for most recent operation */
int nTransaction; /* Number of nested [transaction] methods */
int openFlags; /* Flags used to open. (SQLITE_OPEN_URI) */
+ int nRef; /* Delete object when this reaches 0 */
#ifdef SQLITE_TEST
int bLegacyPrepare; /* True to use sqlite3_prepare() */
#endif
@@ -518,63 +538,83 @@ static void flushStmtCache(SqliteDb *pDb){
}
/*
+** Increment the reference counter on the SqliteDb object. The reference
+** should be released by calling delDatabaseRef().
+*/
+static void addDatabaseRef(SqliteDb *pDb){
+ pDb->nRef++;
+}
+
+/*
+** Decrement the reference counter associated with the SqliteDb object.
+** If it reaches zero, delete the object.
+*/
+static void delDatabaseRef(SqliteDb *pDb){
+ assert( pDb->nRef>0 );
+ pDb->nRef--;
+ if( pDb->nRef==0 ){
+ flushStmtCache(pDb);
+ closeIncrblobChannels(pDb);
+ sqlite3_close(pDb->db);
+ while( pDb->pFunc ){
+ SqlFunc *pFunc = pDb->pFunc;
+ pDb->pFunc = pFunc->pNext;
+ assert( pFunc->pDb==pDb );
+ Tcl_DecrRefCount(pFunc->pScript);
+ Tcl_Free((char*)pFunc);
+ }
+ while( pDb->pCollate ){
+ SqlCollate *pCollate = pDb->pCollate;
+ pDb->pCollate = pCollate->pNext;
+ Tcl_Free((char*)pCollate);
+ }
+ if( pDb->zBusy ){
+ Tcl_Free(pDb->zBusy);
+ }
+ if( pDb->zTrace ){
+ Tcl_Free(pDb->zTrace);
+ }
+ if( pDb->zTraceV2 ){
+ Tcl_Free(pDb->zTraceV2);
+ }
+ if( pDb->zProfile ){
+ Tcl_Free(pDb->zProfile);
+ }
+ if( pDb->zBindFallback ){
+ Tcl_Free(pDb->zBindFallback);
+ }
+ if( pDb->zAuth ){
+ Tcl_Free(pDb->zAuth);
+ }
+ if( pDb->zNull ){
+ Tcl_Free(pDb->zNull);
+ }
+ if( pDb->pUpdateHook ){
+ Tcl_DecrRefCount(pDb->pUpdateHook);
+ }
+ if( pDb->pPreUpdateHook ){
+ Tcl_DecrRefCount(pDb->pPreUpdateHook);
+ }
+ if( pDb->pRollbackHook ){
+ Tcl_DecrRefCount(pDb->pRollbackHook);
+ }
+ if( pDb->pWalHook ){
+ Tcl_DecrRefCount(pDb->pWalHook);
+ }
+ if( pDb->pCollateNeeded ){
+ Tcl_DecrRefCount(pDb->pCollateNeeded);
+ }
+ Tcl_Free((char*)pDb);
+ }
+}
+
+/*
** TCL calls this procedure when an sqlite3 database command is
** deleted.
*/
static void SQLITE_TCLAPI DbDeleteCmd(void *db){
SqliteDb *pDb = (SqliteDb*)db;
- flushStmtCache(pDb);
- closeIncrblobChannels(pDb);
- sqlite3_close(pDb->db);
- while( pDb->pFunc ){
- SqlFunc *pFunc = pDb->pFunc;
- pDb->pFunc = pFunc->pNext;
- assert( pFunc->pDb==pDb );
- Tcl_DecrRefCount(pFunc->pScript);
- Tcl_Free((char*)pFunc);
- }
- while( pDb->pCollate ){
- SqlCollate *pCollate = pDb->pCollate;
- pDb->pCollate = pCollate->pNext;
- Tcl_Free((char*)pCollate);
- }
- if( pDb->zBusy ){
- Tcl_Free(pDb->zBusy);
- }
- if( pDb->zTrace ){
- Tcl_Free(pDb->zTrace);
- }
- if( pDb->zTraceV2 ){
- Tcl_Free(pDb->zTraceV2);
- }
- if( pDb->zProfile ){
- Tcl_Free(pDb->zProfile);
- }
- if( pDb->zBindFallback ){
- Tcl_Free(pDb->zBindFallback);
- }
- if( pDb->zAuth ){
- Tcl_Free(pDb->zAuth);
- }
- if( pDb->zNull ){
- Tcl_Free(pDb->zNull);
- }
- if( pDb->pUpdateHook ){
- Tcl_DecrRefCount(pDb->pUpdateHook);
- }
- if( pDb->pPreUpdateHook ){
- Tcl_DecrRefCount(pDb->pPreUpdateHook);
- }
- if( pDb->pRollbackHook ){
- Tcl_DecrRefCount(pDb->pRollbackHook);
- }
- if( pDb->pWalHook ){
- Tcl_DecrRefCount(pDb->pWalHook);
- }
- if( pDb->pCollateNeeded ){
- Tcl_DecrRefCount(pDb->pCollateNeeded);
- }
- Tcl_Free((char*)pDb);
+ delDatabaseRef(pDb);
}
/*
@@ -654,7 +694,7 @@ static int DbTraceV2Handler(
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
+ Tcl_NewWideIntObj((Tcl_WideInt)(uptr)pStmt));
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewStringObj(zSql, -1));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
@@ -669,7 +709,7 @@ static int DbTraceV2Handler(
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
+ Tcl_NewWideIntObj((Tcl_WideInt)(uptr)pStmt));
Tcl_ListObjAppendElement(pDb->interp, pCmd,
Tcl_NewWideIntObj((Tcl_WideInt)ns));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
@@ -683,7 +723,7 @@ static int DbTraceV2Handler(
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
- Tcl_NewWideIntObj((Tcl_WideInt)pStmt));
+ Tcl_NewWideIntObj((Tcl_WideInt)(uptr)pStmt));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
@@ -695,7 +735,7 @@ static int DbTraceV2Handler(
pCmd = Tcl_NewStringObj(pDb->zTraceV2, -1);
Tcl_IncrRefCount(pCmd);
Tcl_ListObjAppendElement(pDb->interp, pCmd,
- Tcl_NewWideIntObj((Tcl_WideInt)db));
+ Tcl_NewWideIntObj((Tcl_WideInt)(uptr)db));
Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(pCmd);
Tcl_ResetResult(pDb->interp);
@@ -1246,6 +1286,7 @@ static int SQLITE_TCLAPI DbTransPostCmd(
}
pDb->disableAuth--;
+ delDatabaseRef(pDb);
return rc;
}
@@ -1579,6 +1620,7 @@ static void dbEvalInit(
Tcl_IncrRefCount(pArray);
}
p->evalFlags = evalFlags;
+ addDatabaseRef(p->pDb);
}
/*
@@ -1719,6 +1761,7 @@ static void dbEvalFinalize(DbEvalContext *p){
}
Tcl_DecrRefCount(p->pSql);
dbReleaseColumnNames(p);
+ delDatabaseRef(p->pDb);
}
/*
@@ -1756,7 +1799,7 @@ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){
/*
** If using Tcl version 8.6 or greater, use the NR functions to avoid
-** recursive evalution of scripts by the [db eval] and [db trans]
+** recursive evaluation of scripts by the [db eval] and [db trans]
** commands. Even if the headers used while compiling the extension
** are 8.6 or newer, the code still tests the Tcl version at runtime.
** This allows stubs-enabled builds to be used with older Tcl libraries.
@@ -1919,15 +1962,16 @@ static int SQLITE_TCLAPI DbObjCmd(
"close", "collate", "collation_needed",
"commit_hook", "complete", "config",
"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
+ "errorcode", "erroroffset", "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_BIND_FALLBACK,
@@ -1935,15 +1979,15 @@ static int SQLITE_TCLAPI DbObjCmd(
DB_CLOSE, DB_COLLATE, DB_COLLATION_NEEDED,
DB_COMMIT_HOOK, DB_COMPLETE, DB_CONFIG,
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_ERRORCODE, DB_ERROROFFSET, 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 */
@@ -2207,7 +2251,7 @@ static int SQLITE_TCLAPI DbObjCmd(
return TCL_ERROR;
}
pResult = Tcl_GetObjResult(interp);
- Tcl_SetIntObj(pResult, sqlite3_changes(pDb->db));
+ Tcl_SetWideIntObj(pResult, sqlite3_changes64(pDb->db));
break;
}
@@ -2416,7 +2460,7 @@ static int SQLITE_TCLAPI DbObjCmd(
**
** This command usage is equivalent to the sqlite2.x COPY statement,
** which imports file data into a table using the PostgreSQL COPY file format:
- ** $db copy $conflit_algo $table_name $filename \t \\N
+ ** $db copy $conflict_algorithm $table_name $filename \t \\N
*/
case DB_COPY: {
char *zTable; /* Insert data into this table */
@@ -2600,7 +2644,7 @@ static int SQLITE_TCLAPI DbObjCmd(
** Reopen DATABASE (default "main") using the content in $VALUE
*/
case DB_DESERIALIZE: {
-#ifndef SQLITE_ENABLE_DESERIALIZE
+#ifdef SQLITE_OMIT_DESERIALIZE
Tcl_AppendResult(interp, "MEMDB not available in this build",
(char*)0);
rc = TCL_ERROR;
@@ -2623,8 +2667,10 @@ static int SQLITE_TCLAPI DbObjCmd(
for(i=2; i<objc-1; i++){
const char *z = Tcl_GetString(objv[i]);
if( strcmp(z,"-maxsize")==0 && i<objc-2 ){
- rc = Tcl_GetWideIntFromObj(interp, objv[++i], &mxSize);
+ Tcl_WideInt x;
+ rc = Tcl_GetWideIntFromObj(interp, objv[++i], &x);
if( rc ) goto deserialize_error;
+ mxSize = x;
continue;
}
if( strcmp(z,"-readonly")==0 && i<objc-2 ){
@@ -2705,6 +2751,17 @@ deserialize_error:
}
/*
+ ** $db erroroffset
+ **
+ ** Return the numeric error code that was returned by the most recent
+ ** call to sqlite3_exec().
+ */
+ case DB_ERROROFFSET: {
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_error_offset(pDb->db)));
+ break;
+ }
+
+ /*
** $db exists $sql
** $db onecolumn $sql
**
@@ -2927,7 +2984,7 @@ deserialize_error:
}
if( objc==(6+isReadonly) ){
- zDb = Tcl_GetString(objv[2]);
+ zDb = Tcl_GetString(objv[2+isReadonly]);
}
zTable = Tcl_GetString(objv[objc-3]);
zColumn = Tcl_GetString(objv[objc-2]);
@@ -3016,6 +3073,9 @@ deserialize_error:
if( pDb->zProgress ){
Tcl_AppendResult(interp, pDb->zProgress, (char*)0);
}
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ sqlite3_progress_handler(pDb->db, 0, 0, 0);
+#endif
}else if( objc==4 ){
char *zProgress;
int len;
@@ -3167,7 +3227,7 @@ deserialize_error:
** Return a serialization of a database.
*/
case DB_SERIALIZE: {
-#ifndef SQLITE_ENABLE_DESERIALIZE
+#ifdef SQLITE_OMIT_DESERIALIZE
Tcl_AppendResult(interp, "MEMDB not available in this build",
(char*)0);
rc = TCL_ERROR;
@@ -3255,7 +3315,7 @@ deserialize_error:
return TCL_ERROR;
}
pResult = Tcl_GetObjResult(interp);
- Tcl_SetIntObj(pResult, sqlite3_total_changes(pDb->db));
+ Tcl_SetWideIntObj(pResult, sqlite3_total_changes64(pDb->db));
break;
}
@@ -3386,7 +3446,7 @@ deserialize_error:
** Start a new transaction (if we are not already in the midst of a
** transaction) and execute the TCL script SCRIPT. After SCRIPT
** completes, either commit the transaction or roll it back if SCRIPT
- ** throws an exception. Or if no new transation was started, do nothing.
+ ** throws an exception. Or if no new transaction was started, do nothing.
** pass the exception on up the stack.
**
** This command was inspired by Dave Thomas's talk on Ruby at the
@@ -3435,6 +3495,7 @@ deserialize_error:
** opened above. If not using NRE, evaluate the script directly, then
** call function DbTransPostCmd() to commit (or rollback) the transaction
** or savepoint. */
+ addDatabaseRef(pDb); /* DbTransPostCmd() calls delDatabaseRef() */
if( DbUseNre() ){
Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0);
(void)Tcl_NREvalObj(interp, pScript, 0);
@@ -3842,6 +3903,7 @@ static int SQLITE_TCLAPI DbMain(
}else{
Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd);
}
+ p->nRef = 1;
return TCL_OK;
}
@@ -3949,7 +4011,9 @@ static const char *tclsh_main_loop(void){
return zMainloop;
}
-#define TCLSH_MAIN main /* Needed to fake out mktclapp */
+#ifndef TCLSH_MAIN
+# define TCLSH_MAIN main
+#endif
int SQLITE_CDECL TCLSH_MAIN(int argc, char **argv){
Tcl_Interp *interp;
int i;