sl@0: /* sl@0: ** 2001 September 15 sl@0: ** sl@0: ** Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: ** sl@0: ** The author disclaims copyright to this source code. In place of sl@0: ** a legal notice, here is a blessing: sl@0: ** sl@0: ** May you do good and not evil. sl@0: ** May you find forgiveness for yourself and forgive others. sl@0: ** May you share freely, never taking more than you give. sl@0: ** sl@0: ************************************************************************* sl@0: ** A TCL Interface to SQLite. Append this file to sqlite3.c and sl@0: ** compile the whole thing to build a TCL-enabled version of SQLite. sl@0: ** sl@0: ** $Id: tclsqlite.c,v 1.226 2008/09/23 10:12:15 drh Exp $ sl@0: */ sl@0: #include "tcl.h" sl@0: #include sl@0: sl@0: /* sl@0: ** Some additional include files are needed if this file is not sl@0: ** appended to the amalgamation. sl@0: */ sl@0: #ifndef SQLITE_AMALGAMATION sl@0: # include "sqliteInt.h" sl@0: # include sl@0: # include sl@0: # include sl@0: # include sl@0: #endif sl@0: sl@0: #ifdef __SYMBIAN32__ sl@0: int CopyTestFiles(void); sl@0: int DeleteTestFiles(void); sl@0: int PrintText(void*, Tcl_Interp*, int objc, Tcl_Obj* const* objv); sl@0: void PrintS(const char* aText); sl@0: #endif sl@0: sl@0: /* sl@0: * Windows needs to know which symbols to export. Unix does not. sl@0: * BUILD_sqlite should be undefined for Unix. sl@0: */ sl@0: #ifdef BUILD_sqlite sl@0: #undef TCL_STORAGE_CLASS sl@0: #define TCL_STORAGE_CLASS DLLEXPORT sl@0: #endif /* BUILD_sqlite */ sl@0: sl@0: #define NUM_PREPARED_STMTS 10 sl@0: #define MAX_PREPARED_STMTS 100 sl@0: sl@0: /* sl@0: ** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we sl@0: ** have to do a translation when going between the two. Set the sl@0: ** UTF_TRANSLATION_NEEDED macro to indicate that we need to do sl@0: ** this translation. sl@0: */ sl@0: #if defined(TCL_UTF_MAX) && !defined(SQLITE_UTF8) sl@0: # define UTF_TRANSLATION_NEEDED 1 sl@0: #endif sl@0: sl@0: /* sl@0: ** New SQL functions can be created as TCL scripts. Each such function sl@0: ** is described by an instance of the following structure. sl@0: */ sl@0: typedef struct SqlFunc SqlFunc; sl@0: struct SqlFunc { sl@0: Tcl_Interp *interp; /* The TCL interpret to execute the function */ sl@0: Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */ sl@0: int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */ sl@0: char *zName; /* Name of this function */ sl@0: SqlFunc *pNext; /* Next function on the list of them all */ sl@0: }; sl@0: sl@0: /* sl@0: ** New collation sequences function can be created as TCL scripts. Each such sl@0: ** function is described by an instance of the following structure. sl@0: */ sl@0: typedef struct SqlCollate SqlCollate; sl@0: struct SqlCollate { sl@0: Tcl_Interp *interp; /* The TCL interpret to execute the function */ sl@0: char *zScript; /* The script to be run */ sl@0: SqlCollate *pNext; /* Next function on the list of them all */ sl@0: }; sl@0: sl@0: /* sl@0: ** Prepared statements are cached for faster execution. Each prepared sl@0: ** statement is described by an instance of the following structure. sl@0: */ sl@0: typedef struct SqlPreparedStmt SqlPreparedStmt; sl@0: struct SqlPreparedStmt { sl@0: SqlPreparedStmt *pNext; /* Next in linked list */ sl@0: SqlPreparedStmt *pPrev; /* Previous on the list */ sl@0: sqlite3_stmt *pStmt; /* The prepared statement */ sl@0: int nSql; /* chars in zSql[] */ sl@0: const char *zSql; /* Text of the SQL statement */ sl@0: }; sl@0: sl@0: typedef struct IncrblobChannel IncrblobChannel; sl@0: sl@0: /* sl@0: ** There is one instance of this structure for each SQLite database sl@0: ** that has been opened by the SQLite TCL interface. sl@0: */ sl@0: typedef struct SqliteDb SqliteDb; sl@0: struct SqliteDb { sl@0: sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ sl@0: Tcl_Interp *interp; /* The interpreter used for this database */ sl@0: char *zBusy; /* The busy callback routine */ sl@0: char *zCommit; /* The commit hook callback routine */ sl@0: char *zTrace; /* The trace callback routine */ sl@0: char *zProfile; /* The profile callback routine */ sl@0: char *zProgress; /* The progress callback routine */ sl@0: char *zAuth; /* The authorization callback routine */ sl@0: int disableAuth; /* Disable the authorizer if it exists */ sl@0: char *zNull; /* Text to substitute for an SQL NULL value */ sl@0: SqlFunc *pFunc; /* List of SQL functions */ sl@0: Tcl_Obj *pUpdateHook; /* Update hook script (if any) */ sl@0: Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */ sl@0: SqlCollate *pCollate; /* List of SQL collation functions */ sl@0: int rc; /* Return code of most recent sqlite3_exec() */ sl@0: Tcl_Obj *pCollateNeeded; /* Collation needed script */ sl@0: SqlPreparedStmt *stmtList; /* List of prepared statements*/ sl@0: SqlPreparedStmt *stmtLast; /* Last statement in the list */ sl@0: int maxStmt; /* The next maximum number of stmtList */ sl@0: int nStmt; /* Number of statements in stmtList */ sl@0: IncrblobChannel *pIncrblob;/* Linked list of open incrblob channels */ sl@0: }; sl@0: sl@0: struct IncrblobChannel { sl@0: sqlite3_blob *pBlob; /* sqlite3 blob handle */ sl@0: SqliteDb *pDb; /* Associated database connection */ sl@0: int iSeek; /* Current seek offset */ sl@0: Tcl_Channel channel; /* Channel identifier */ sl@0: IncrblobChannel *pNext; /* Linked list of all open incrblob channels */ sl@0: IncrblobChannel *pPrev; /* Linked list of all open incrblob channels */ sl@0: }; sl@0: sl@0: #ifndef SQLITE_OMIT_INCRBLOB sl@0: /* sl@0: ** Close all incrblob channels opened using database connection pDb. sl@0: ** This is called when shutting down the database connection. sl@0: */ sl@0: static void closeIncrblobChannels(SqliteDb *pDb){ sl@0: IncrblobChannel *p; sl@0: IncrblobChannel *pNext; sl@0: sl@0: for(p=pDb->pIncrblob; p; p=pNext){ sl@0: pNext = p->pNext; sl@0: sl@0: /* Note: Calling unregister here call Tcl_Close on the incrblob channel, sl@0: ** which deletes the IncrblobChannel structure at *p. So do not sl@0: ** call Tcl_Free() here. sl@0: */ sl@0: Tcl_UnregisterChannel(pDb->interp, p->channel); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: ** Close an incremental blob channel. sl@0: */ sl@0: static int incrblobClose(ClientData instanceData, Tcl_Interp *interp){ sl@0: IncrblobChannel *p = (IncrblobChannel *)instanceData; sl@0: int rc = sqlite3_blob_close(p->pBlob); sl@0: sqlite3 *db = p->pDb->db; sl@0: sl@0: /* Remove the channel from the SqliteDb.pIncrblob list. */ sl@0: if( p->pNext ){ sl@0: p->pNext->pPrev = p->pPrev; sl@0: } sl@0: if( p->pPrev ){ sl@0: p->pPrev->pNext = p->pNext; sl@0: } sl@0: if( p->pDb->pIncrblob==p ){ sl@0: p->pDb->pIncrblob = p->pNext; sl@0: } sl@0: sl@0: /* Free the IncrblobChannel structure */ sl@0: Tcl_Free((char *)p); sl@0: sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Read data from an incremental blob channel. sl@0: */ sl@0: static int incrblobInput( sl@0: ClientData instanceData, sl@0: char *buf, sl@0: int bufSize, sl@0: int *errorCodePtr sl@0: ){ sl@0: IncrblobChannel *p = (IncrblobChannel *)instanceData; sl@0: int nRead = bufSize; /* Number of bytes to read */ sl@0: int nBlob; /* Total size of the blob */ sl@0: int rc; /* sqlite error code */ sl@0: sl@0: nBlob = sqlite3_blob_bytes(p->pBlob); sl@0: if( (p->iSeek+nRead)>nBlob ){ sl@0: nRead = nBlob-p->iSeek; sl@0: } sl@0: if( nRead<=0 ){ sl@0: return 0; sl@0: } sl@0: sl@0: rc = sqlite3_blob_read(p->pBlob, (void *)buf, nRead, p->iSeek); sl@0: if( rc!=SQLITE_OK ){ sl@0: *errorCodePtr = rc; sl@0: return -1; sl@0: } sl@0: sl@0: p->iSeek += nRead; sl@0: return nRead; sl@0: } sl@0: sl@0: /* sl@0: ** Write data to an incremental blob channel. sl@0: */ sl@0: static int incrblobOutput( sl@0: ClientData instanceData, sl@0: CONST char *buf, sl@0: int toWrite, sl@0: int *errorCodePtr sl@0: ){ sl@0: IncrblobChannel *p = (IncrblobChannel *)instanceData; sl@0: int nWrite = toWrite; /* Number of bytes to write */ sl@0: int nBlob; /* Total size of the blob */ sl@0: int rc; /* sqlite error code */ sl@0: sl@0: nBlob = sqlite3_blob_bytes(p->pBlob); sl@0: if( (p->iSeek+nWrite)>nBlob ){ sl@0: *errorCodePtr = EINVAL; sl@0: return -1; sl@0: } sl@0: if( nWrite<=0 ){ sl@0: return 0; sl@0: } sl@0: sl@0: rc = sqlite3_blob_write(p->pBlob, (void *)buf, nWrite, p->iSeek); sl@0: if( rc!=SQLITE_OK ){ sl@0: *errorCodePtr = EIO; sl@0: return -1; sl@0: } sl@0: sl@0: p->iSeek += nWrite; sl@0: return nWrite; sl@0: } sl@0: sl@0: /* sl@0: ** Seek an incremental blob channel. sl@0: */ sl@0: static int incrblobSeek( sl@0: ClientData instanceData, sl@0: long offset, sl@0: int seekMode, sl@0: int *errorCodePtr sl@0: ){ sl@0: IncrblobChannel *p = (IncrblobChannel *)instanceData; sl@0: sl@0: switch( seekMode ){ sl@0: case SEEK_SET: sl@0: p->iSeek = offset; sl@0: break; sl@0: case SEEK_CUR: sl@0: p->iSeek += offset; sl@0: break; sl@0: case SEEK_END: sl@0: p->iSeek = sqlite3_blob_bytes(p->pBlob) + offset; sl@0: break; sl@0: sl@0: default: assert(0); /* Bad seekMode */ sl@0: } sl@0: sl@0: return p->iSeek; sl@0: } sl@0: sl@0: sl@0: static void incrblobWatch(ClientData instanceData, int mode){ sl@0: /* NO-OP */ sl@0: } sl@0: static int incrblobHandle(ClientData instanceData, int dir, ClientData *hPtr){ sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: static Tcl_ChannelType IncrblobChannelType = { sl@0: "incrblob", /* typeName */ sl@0: TCL_CHANNEL_VERSION_2, /* version */ sl@0: incrblobClose, /* closeProc */ sl@0: incrblobInput, /* inputProc */ sl@0: incrblobOutput, /* outputProc */ sl@0: incrblobSeek, /* seekProc */ sl@0: 0, /* setOptionProc */ sl@0: 0, /* getOptionProc */ sl@0: incrblobWatch, /* watchProc (this is a no-op) */ sl@0: incrblobHandle, /* getHandleProc (always returns error) */ sl@0: 0, /* close2Proc */ sl@0: 0, /* blockModeProc */ sl@0: 0, /* flushProc */ sl@0: 0, /* handlerProc */ sl@0: 0, /* wideSeekProc */ sl@0: }; sl@0: sl@0: /* sl@0: ** Create a new incrblob channel. sl@0: */ sl@0: static int createIncrblobChannel( sl@0: Tcl_Interp *interp, sl@0: SqliteDb *pDb, sl@0: const char *zDb, sl@0: const char *zTable, sl@0: const char *zColumn, sl@0: sqlite_int64 iRow, sl@0: int isReadonly sl@0: ){ sl@0: IncrblobChannel *p; sl@0: sqlite3 *db = pDb->db; sl@0: sqlite3_blob *pBlob; sl@0: int rc; sl@0: int flags = TCL_READABLE|(isReadonly ? 0 : TCL_WRITABLE); sl@0: sl@0: /* This variable is used to name the channels: "incrblob_[incr count]" */ sl@0: static int count = 0; sl@0: char zChannel[64]; sl@0: sl@0: rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, &pBlob); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: p = (IncrblobChannel *)Tcl_Alloc(sizeof(IncrblobChannel)); sl@0: p->iSeek = 0; sl@0: p->pBlob = pBlob; sl@0: sl@0: sqlite3_snprintf(sizeof(zChannel), zChannel, "incrblob_%d", ++count); sl@0: p->channel = Tcl_CreateChannel(&IncrblobChannelType, zChannel, p, flags); sl@0: Tcl_RegisterChannel(interp, p->channel); sl@0: sl@0: /* Link the new channel into the SqliteDb.pIncrblob list. */ sl@0: p->pNext = pDb->pIncrblob; sl@0: p->pPrev = 0; sl@0: if( p->pNext ){ sl@0: p->pNext->pPrev = p; sl@0: } sl@0: pDb->pIncrblob = p; sl@0: p->pDb = pDb; sl@0: sl@0: Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: #else /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */ sl@0: #define closeIncrblobChannels(pDb) sl@0: #endif sl@0: sl@0: /* sl@0: ** Look at the script prefix in pCmd. We will be executing this script sl@0: ** after first appending one or more arguments. This routine analyzes sl@0: ** the script to see if it is safe to use Tcl_EvalObjv() on the script sl@0: ** rather than the more general Tcl_EvalEx(). Tcl_EvalObjv() is much sl@0: ** faster. sl@0: ** sl@0: ** Scripts that are safe to use with Tcl_EvalObjv() consists of a sl@0: ** command name followed by zero or more arguments with no [...] or $ sl@0: ** or {...} or ; to be seen anywhere. Most callback scripts consist sl@0: ** of just a single procedure name and they meet this requirement. sl@0: */ sl@0: static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){ sl@0: /* We could try to do something with Tcl_Parse(). But we will instead sl@0: ** just do a search for forbidden characters. If any of the forbidden sl@0: ** characters appear in pCmd, we will report the string as unsafe. sl@0: */ sl@0: const char *z; sl@0: int n; sl@0: z = Tcl_GetStringFromObj(pCmd, &n); sl@0: while( n-- > 0 ){ sl@0: int c = *(z++); sl@0: if( c=='$' || c=='[' || c==';' ) return 0; sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: ** Find an SqlFunc structure with the given name. Or create a new sl@0: ** one if an existing one cannot be found. Return a pointer to the sl@0: ** structure. sl@0: */ sl@0: static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){ sl@0: SqlFunc *p, *pNew; sl@0: int i; sl@0: pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen(zName) + 1 ); sl@0: pNew->zName = (char*)&pNew[1]; sl@0: for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); } sl@0: pNew->zName[i] = 0; sl@0: for(p=pDb->pFunc; p; p=p->pNext){ sl@0: if( strcmp(p->zName, pNew->zName)==0 ){ sl@0: Tcl_Free((char*)pNew); sl@0: return p; sl@0: } sl@0: } sl@0: pNew->interp = pDb->interp; sl@0: pNew->pScript = 0; sl@0: pNew->pNext = pDb->pFunc; sl@0: pDb->pFunc = pNew; sl@0: return pNew; sl@0: } sl@0: sl@0: /* sl@0: ** Finalize and free a list of prepared statements sl@0: */ sl@0: static void flushStmtCache( SqliteDb *pDb ){ sl@0: SqlPreparedStmt *pPreStmt; sl@0: sl@0: while( pDb->stmtList ){ sl@0: sqlite3_finalize( pDb->stmtList->pStmt ); sl@0: pPreStmt = pDb->stmtList; sl@0: pDb->stmtList = pDb->stmtList->pNext; sl@0: Tcl_Free( (char*)pPreStmt ); sl@0: } sl@0: pDb->nStmt = 0; sl@0: pDb->stmtLast = 0; sl@0: } sl@0: sl@0: /* sl@0: ** TCL calls this procedure when an sqlite3 database command is sl@0: ** deleted. sl@0: */ sl@0: static void DbDeleteCmd(void *db){ sl@0: SqliteDb *pDb = (SqliteDb*)db; sl@0: flushStmtCache(pDb); sl@0: closeIncrblobChannels(pDb); sl@0: sqlite3_close(pDb->db); sl@0: while( pDb->pFunc ){ sl@0: SqlFunc *pFunc = pDb->pFunc; sl@0: pDb->pFunc = pFunc->pNext; sl@0: Tcl_DecrRefCount(pFunc->pScript); sl@0: Tcl_Free((char*)pFunc); sl@0: } sl@0: while( pDb->pCollate ){ sl@0: SqlCollate *pCollate = pDb->pCollate; sl@0: pDb->pCollate = pCollate->pNext; sl@0: Tcl_Free((char*)pCollate); sl@0: } sl@0: if( pDb->zBusy ){ sl@0: Tcl_Free(pDb->zBusy); sl@0: } sl@0: if( pDb->zTrace ){ sl@0: Tcl_Free(pDb->zTrace); sl@0: } sl@0: if( pDb->zProfile ){ sl@0: Tcl_Free(pDb->zProfile); sl@0: } sl@0: if( pDb->zAuth ){ sl@0: Tcl_Free(pDb->zAuth); sl@0: } sl@0: if( pDb->zNull ){ sl@0: Tcl_Free(pDb->zNull); sl@0: } sl@0: if( pDb->pUpdateHook ){ sl@0: Tcl_DecrRefCount(pDb->pUpdateHook); sl@0: } sl@0: if( pDb->pRollbackHook ){ sl@0: Tcl_DecrRefCount(pDb->pRollbackHook); sl@0: } sl@0: if( pDb->pCollateNeeded ){ sl@0: Tcl_DecrRefCount(pDb->pCollateNeeded); sl@0: } sl@0: Tcl_Free((char*)pDb); sl@0: } sl@0: sl@0: /* sl@0: ** This routine is called when a database file is locked while trying sl@0: ** to execute SQL. sl@0: */ sl@0: static int DbBusyHandler(void *cd, int nTries){ sl@0: SqliteDb *pDb = (SqliteDb*)cd; sl@0: int rc; sl@0: char zVal[30]; sl@0: sl@0: sqlite3_snprintf(sizeof(zVal), zVal, "%d", nTries); sl@0: rc = Tcl_VarEval(pDb->interp, pDb->zBusy, " ", zVal, (char*)0); sl@0: if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ sl@0: return 0; sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: #ifndef SQLITE_OMIT_PROGRESS_CALLBACK sl@0: /* sl@0: ** This routine is invoked as the 'progress callback' for the database. sl@0: */ sl@0: static int DbProgressHandler(void *cd){ sl@0: SqliteDb *pDb = (SqliteDb*)cd; sl@0: int rc; sl@0: sl@0: assert( pDb->zProgress ); sl@0: rc = Tcl_Eval(pDb->interp, pDb->zProgress); sl@0: if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ sl@0: return 1; sl@0: } sl@0: return 0; sl@0: } sl@0: #endif sl@0: sl@0: #ifndef SQLITE_OMIT_TRACE sl@0: /* sl@0: ** This routine is called by the SQLite trace handler whenever a new sl@0: ** block of SQL is executed. The TCL script in pDb->zTrace is executed. sl@0: */ sl@0: static void DbTraceHandler(void *cd, const char *zSql){ sl@0: SqliteDb *pDb = (SqliteDb*)cd; sl@0: Tcl_DString str; sl@0: sl@0: Tcl_DStringInit(&str); sl@0: Tcl_DStringAppend(&str, pDb->zTrace, -1); sl@0: Tcl_DStringAppendElement(&str, zSql); sl@0: Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); sl@0: Tcl_DStringFree(&str); sl@0: Tcl_ResetResult(pDb->interp); sl@0: } sl@0: #endif sl@0: sl@0: #ifndef SQLITE_OMIT_TRACE sl@0: /* sl@0: ** This routine is called by the SQLite profile handler after a statement sl@0: ** SQL has executed. The TCL script in pDb->zProfile is evaluated. sl@0: */ sl@0: static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){ sl@0: SqliteDb *pDb = (SqliteDb*)cd; sl@0: Tcl_DString str; sl@0: char zTm[100]; sl@0: sl@0: sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm); sl@0: Tcl_DStringInit(&str); sl@0: Tcl_DStringAppend(&str, pDb->zProfile, -1); sl@0: Tcl_DStringAppendElement(&str, zSql); sl@0: Tcl_DStringAppendElement(&str, zTm); sl@0: Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); sl@0: Tcl_DStringFree(&str); sl@0: Tcl_ResetResult(pDb->interp); sl@0: } sl@0: #endif sl@0: sl@0: /* sl@0: ** This routine is called when a transaction is committed. The sl@0: ** TCL script in pDb->zCommit is executed. If it returns non-zero or sl@0: ** if it throws an exception, the transaction is rolled back instead sl@0: ** of being committed. sl@0: */ sl@0: static int DbCommitHandler(void *cd){ sl@0: SqliteDb *pDb = (SqliteDb*)cd; sl@0: int rc; sl@0: sl@0: rc = Tcl_Eval(pDb->interp, pDb->zCommit); sl@0: if( rc!=TCL_OK || atoi(Tcl_GetStringResult(pDb->interp)) ){ sl@0: return 1; sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: static void DbRollbackHandler(void *clientData){ sl@0: SqliteDb *pDb = (SqliteDb*)clientData; sl@0: assert(pDb->pRollbackHook); sl@0: if( TCL_OK!=Tcl_EvalObjEx(pDb->interp, pDb->pRollbackHook, 0) ){ sl@0: Tcl_BackgroundError(pDb->interp); sl@0: } sl@0: } sl@0: sl@0: static void DbUpdateHandler( sl@0: void *p, sl@0: int op, sl@0: const char *zDb, sl@0: const char *zTbl, sl@0: sqlite_int64 rowid sl@0: ){ sl@0: SqliteDb *pDb = (SqliteDb *)p; sl@0: Tcl_Obj *pCmd; sl@0: sl@0: assert( pDb->pUpdateHook ); sl@0: assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE ); sl@0: sl@0: pCmd = Tcl_DuplicateObj(pDb->pUpdateHook); sl@0: Tcl_IncrRefCount(pCmd); sl@0: Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj( sl@0: ( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1)); sl@0: Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); sl@0: Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); sl@0: Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid)); sl@0: Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); sl@0: } sl@0: sl@0: static void tclCollateNeeded( sl@0: void *pCtx, sl@0: sqlite3 *db, sl@0: int enc, sl@0: const char *zName sl@0: ){ sl@0: SqliteDb *pDb = (SqliteDb *)pCtx; sl@0: Tcl_Obj *pScript = Tcl_DuplicateObj(pDb->pCollateNeeded); sl@0: Tcl_IncrRefCount(pScript); sl@0: Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zName, -1)); sl@0: Tcl_EvalObjEx(pDb->interp, pScript, 0); sl@0: Tcl_DecrRefCount(pScript); sl@0: } sl@0: sl@0: /* sl@0: ** This routine is called to evaluate an SQL collation function implemented sl@0: ** using TCL script. sl@0: */ sl@0: static int tclSqlCollate( sl@0: void *pCtx, sl@0: int nA, sl@0: const void *zA, sl@0: int nB, sl@0: const void *zB sl@0: ){ sl@0: SqlCollate *p = (SqlCollate *)pCtx; sl@0: Tcl_Obj *pCmd; sl@0: sl@0: pCmd = Tcl_NewStringObj(p->zScript, -1); sl@0: Tcl_IncrRefCount(pCmd); sl@0: Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA)); sl@0: Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB)); sl@0: Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); sl@0: Tcl_DecrRefCount(pCmd); sl@0: return (atoi(Tcl_GetStringResult(p->interp))); sl@0: } sl@0: sl@0: /* sl@0: ** This routine is called to evaluate an SQL function implemented sl@0: ** using TCL script. sl@0: */ sl@0: static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){ sl@0: SqlFunc *p = sqlite3_user_data(context); sl@0: Tcl_Obj *pCmd; sl@0: int i; sl@0: int rc; sl@0: sl@0: if( argc==0 ){ sl@0: /* If there are no arguments to the function, call Tcl_EvalObjEx on the sl@0: ** script object directly. This allows the TCL compiler to generate sl@0: ** bytecode for the command on the first invocation and thus make sl@0: ** subsequent invocations much faster. */ sl@0: pCmd = p->pScript; sl@0: Tcl_IncrRefCount(pCmd); sl@0: rc = Tcl_EvalObjEx(p->interp, pCmd, 0); sl@0: Tcl_DecrRefCount(pCmd); sl@0: }else{ sl@0: /* If there are arguments to the function, make a shallow copy of the sl@0: ** script object, lappend the arguments, then evaluate the copy. sl@0: ** sl@0: ** By "shallow" copy, we mean a only the outer list Tcl_Obj is duplicated. sl@0: ** The new Tcl_Obj contains pointers to the original list elements. sl@0: ** That way, when Tcl_EvalObjv() is run and shimmers the first element sl@0: ** of the list to tclCmdNameType, that alternate representation will sl@0: ** be preserved and reused on the next invocation. sl@0: */ sl@0: Tcl_Obj **aArg; sl@0: int nArg; sl@0: if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ sl@0: sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); sl@0: return; sl@0: } sl@0: pCmd = Tcl_NewListObj(nArg, aArg); sl@0: Tcl_IncrRefCount(pCmd); sl@0: for(i=0; i=-2147483647 && v<=2147483647 ){ sl@0: pVal = Tcl_NewIntObj(v); sl@0: }else{ sl@0: pVal = Tcl_NewWideIntObj(v); sl@0: } sl@0: break; sl@0: } sl@0: case SQLITE_FLOAT: { sl@0: double r = sqlite3_value_double(pIn); sl@0: pVal = Tcl_NewDoubleObj(r); sl@0: break; sl@0: } sl@0: case SQLITE_NULL: { sl@0: pVal = Tcl_NewStringObj("", 0); sl@0: break; sl@0: } sl@0: default: { sl@0: int bytes = sqlite3_value_bytes(pIn); sl@0: pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes); sl@0: break; sl@0: } sl@0: } sl@0: rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal); sl@0: if( rc ){ sl@0: Tcl_DecrRefCount(pCmd); sl@0: sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); sl@0: return; sl@0: } sl@0: } sl@0: if( !p->useEvalObjv ){ sl@0: /* Tcl_EvalObjEx() will automatically call Tcl_EvalObjv() if pCmd sl@0: ** is a list without a string representation. To prevent this from sl@0: ** happening, make sure pCmd has a valid string representation */ sl@0: Tcl_GetString(pCmd); sl@0: } sl@0: rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT); sl@0: Tcl_DecrRefCount(pCmd); sl@0: } sl@0: sl@0: if( rc && rc!=TCL_RETURN ){ sl@0: sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); sl@0: }else{ sl@0: Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); sl@0: int n; sl@0: u8 *data; sl@0: char *zType = pVar->typePtr ? pVar->typePtr->name : ""; sl@0: char c = zType[0]; sl@0: if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ sl@0: /* Only return a BLOB type if the Tcl variable is a bytearray and sl@0: ** has no string representation. */ sl@0: data = Tcl_GetByteArrayFromObj(pVar, &n); sl@0: sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT); sl@0: }else if( c=='b' && strcmp(zType,"boolean")==0 ){ sl@0: Tcl_GetIntFromObj(0, pVar, &n); sl@0: sqlite3_result_int(context, n); sl@0: }else if( c=='d' && strcmp(zType,"double")==0 ){ sl@0: double r; sl@0: Tcl_GetDoubleFromObj(0, pVar, &r); sl@0: sqlite3_result_double(context, r); sl@0: }else if( (c=='w' && strcmp(zType,"wideInt")==0) || sl@0: (c=='i' && strcmp(zType,"int")==0) ){ sl@0: Tcl_WideInt v; sl@0: Tcl_GetWideIntFromObj(0, pVar, &v); sl@0: sqlite3_result_int64(context, v); sl@0: }else{ sl@0: data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); sl@0: sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT); sl@0: } sl@0: } sl@0: } sl@0: sl@0: #ifndef SQLITE_OMIT_AUTHORIZATION sl@0: /* sl@0: ** This is the authentication function. It appends the authentication sl@0: ** type code and the two arguments to zCmd[] then invokes the result sl@0: ** on the interpreter. The reply is examined to determine if the sl@0: ** authentication fails or succeeds. sl@0: */ sl@0: static int auth_callback( sl@0: void *pArg, sl@0: int code, sl@0: const char *zArg1, sl@0: const char *zArg2, sl@0: const char *zArg3, sl@0: const char *zArg4 sl@0: ){ sl@0: char *zCode; sl@0: Tcl_DString str; sl@0: int rc; sl@0: const char *zReply; sl@0: SqliteDb *pDb = (SqliteDb*)pArg; sl@0: if( pDb->disableAuth ) return SQLITE_OK; sl@0: sl@0: switch( code ){ sl@0: case SQLITE_COPY : zCode="SQLITE_COPY"; break; sl@0: case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; sl@0: case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; sl@0: case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; sl@0: case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; sl@0: case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; sl@0: case SQLITE_CREATE_TEMP_VIEW : zCode="SQLITE_CREATE_TEMP_VIEW"; break; sl@0: case SQLITE_CREATE_TRIGGER : zCode="SQLITE_CREATE_TRIGGER"; break; sl@0: case SQLITE_CREATE_VIEW : zCode="SQLITE_CREATE_VIEW"; break; sl@0: case SQLITE_DELETE : zCode="SQLITE_DELETE"; break; sl@0: case SQLITE_DROP_INDEX : zCode="SQLITE_DROP_INDEX"; break; sl@0: case SQLITE_DROP_TABLE : zCode="SQLITE_DROP_TABLE"; break; sl@0: case SQLITE_DROP_TEMP_INDEX : zCode="SQLITE_DROP_TEMP_INDEX"; break; sl@0: case SQLITE_DROP_TEMP_TABLE : zCode="SQLITE_DROP_TEMP_TABLE"; break; sl@0: case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break; sl@0: case SQLITE_DROP_TEMP_VIEW : zCode="SQLITE_DROP_TEMP_VIEW"; break; sl@0: case SQLITE_DROP_TRIGGER : zCode="SQLITE_DROP_TRIGGER"; break; sl@0: case SQLITE_DROP_VIEW : zCode="SQLITE_DROP_VIEW"; break; sl@0: case SQLITE_INSERT : zCode="SQLITE_INSERT"; break; sl@0: case SQLITE_PRAGMA : zCode="SQLITE_PRAGMA"; break; sl@0: case SQLITE_READ : zCode="SQLITE_READ"; break; sl@0: case SQLITE_SELECT : zCode="SQLITE_SELECT"; break; sl@0: case SQLITE_TRANSACTION : zCode="SQLITE_TRANSACTION"; break; sl@0: case SQLITE_UPDATE : zCode="SQLITE_UPDATE"; break; sl@0: case SQLITE_ATTACH : zCode="SQLITE_ATTACH"; break; sl@0: case SQLITE_DETACH : zCode="SQLITE_DETACH"; break; sl@0: case SQLITE_ALTER_TABLE : zCode="SQLITE_ALTER_TABLE"; break; sl@0: case SQLITE_REINDEX : zCode="SQLITE_REINDEX"; break; sl@0: case SQLITE_ANALYZE : zCode="SQLITE_ANALYZE"; break; sl@0: case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break; sl@0: case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break; sl@0: case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break; sl@0: default : zCode="????"; break; sl@0: } sl@0: Tcl_DStringInit(&str); sl@0: Tcl_DStringAppend(&str, pDb->zAuth, -1); sl@0: Tcl_DStringAppendElement(&str, zCode); sl@0: Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : ""); sl@0: Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : ""); sl@0: Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : ""); sl@0: Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); sl@0: rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str)); sl@0: Tcl_DStringFree(&str); sl@0: zReply = Tcl_GetStringResult(pDb->interp); sl@0: if( strcmp(zReply,"SQLITE_OK")==0 ){ sl@0: rc = SQLITE_OK; sl@0: }else if( strcmp(zReply,"SQLITE_DENY")==0 ){ sl@0: rc = SQLITE_DENY; sl@0: }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){ sl@0: rc = SQLITE_IGNORE; sl@0: }else{ sl@0: rc = 999; sl@0: } sl@0: return rc; sl@0: } sl@0: #endif /* SQLITE_OMIT_AUTHORIZATION */ sl@0: sl@0: /* sl@0: ** zText is a pointer to text obtained via an sqlite3_result_text() sl@0: ** or similar interface. This routine returns a Tcl string object, sl@0: ** reference count set to 0, containing the text. If a translation sl@0: ** between iso8859 and UTF-8 is required, it is preformed. sl@0: */ sl@0: static Tcl_Obj *dbTextToObj(char const *zText){ sl@0: Tcl_Obj *pVal; sl@0: #ifdef UTF_TRANSLATION_NEEDED sl@0: Tcl_DString dCol; sl@0: Tcl_DStringInit(&dCol); sl@0: Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol); sl@0: pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); sl@0: Tcl_DStringFree(&dCol); sl@0: #else sl@0: pVal = Tcl_NewStringObj(zText, -1); sl@0: #endif sl@0: return pVal; sl@0: } sl@0: sl@0: /* sl@0: ** This routine reads a line of text from FILE in, stores sl@0: ** the text in memory obtained from malloc() and returns a pointer sl@0: ** to the text. NULL is returned at end of file, or if malloc() sl@0: ** fails. sl@0: ** sl@0: ** The interface is like "readline" but no command-line editing sl@0: ** is done. sl@0: ** sl@0: ** copied from shell.c from '.import' command sl@0: */ sl@0: static char *local_getline(char *zPrompt, FILE *in){ sl@0: char *zLine; sl@0: int nLine; sl@0: int n; sl@0: int eol; sl@0: sl@0: nLine = 100; sl@0: zLine = malloc( nLine ); sl@0: if( zLine==0 ) return 0; sl@0: n = 0; sl@0: eol = 0; sl@0: while( !eol ){ sl@0: if( n+100>nLine ){ sl@0: nLine = nLine*2 + 100; sl@0: zLine = realloc(zLine, nLine); sl@0: if( zLine==0 ) return 0; sl@0: } sl@0: if( fgets(&zLine[n], nLine - n, in)==0 ){ sl@0: if( n==0 ){ sl@0: free(zLine); sl@0: return 0; sl@0: } sl@0: zLine[n] = 0; sl@0: eol = 1; sl@0: break; sl@0: } sl@0: while( zLine[n] ){ n++; } sl@0: if( n>0 && zLine[n-1]=='\n' ){ sl@0: n--; sl@0: zLine[n] = 0; sl@0: eol = 1; sl@0: } sl@0: } sl@0: zLine = realloc( zLine, n+1 ); sl@0: return zLine; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Figure out the column names for the data returned by the statement sl@0: ** passed as the second argument. sl@0: ** sl@0: ** If parameter papColName is not NULL, then *papColName is set to point sl@0: ** at an array allocated using Tcl_Alloc(). It is the callers responsibility sl@0: ** to free this array using Tcl_Free(), and to decrement the reference sl@0: ** count of each Tcl_Obj* member of the array. sl@0: ** sl@0: ** The return value of this function is the number of columns of data sl@0: ** returned by pStmt (and hence the size of the *papColName array). sl@0: ** sl@0: ** If pArray is not NULL, then it contains the name of a Tcl array sl@0: ** variable. The "*" member of this array is set to a list containing sl@0: ** the names of the columns returned by the statement, in order from sl@0: ** left to right. e.g. if the names of the returned columns are a, b and sl@0: ** c, it does the equivalent of the tcl command: sl@0: ** sl@0: ** set ${pArray}(*) {a b c} sl@0: */ sl@0: static int sl@0: computeColumnNames( sl@0: Tcl_Interp *interp, sl@0: sqlite3_stmt *pStmt, /* SQL statement */ sl@0: Tcl_Obj ***papColName, /* OUT: Array of column names */ sl@0: Tcl_Obj *pArray /* Name of array variable (may be null) */ sl@0: ){ sl@0: int nCol; sl@0: sl@0: /* Compute column names */ sl@0: nCol = sqlite3_column_count(pStmt); sl@0: if( papColName ){ sl@0: int i; sl@0: Tcl_Obj **apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); sl@0: for(i=0; i3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); sl@0: return TCL_ERROR; sl@0: }else if( objc==2 ){ sl@0: if( pDb->zAuth ){ sl@0: Tcl_AppendResult(interp, pDb->zAuth, 0); sl@0: } sl@0: }else{ sl@0: char *zAuth; sl@0: int len; sl@0: if( pDb->zAuth ){ sl@0: Tcl_Free(pDb->zAuth); sl@0: } sl@0: zAuth = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( zAuth && len>0 ){ sl@0: pDb->zAuth = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zAuth, zAuth, len+1); sl@0: }else{ sl@0: pDb->zAuth = 0; sl@0: } sl@0: if( pDb->zAuth ){ sl@0: pDb->interp = interp; sl@0: sqlite3_set_authorizer(pDb->db, auth_callback, pDb); sl@0: }else{ sl@0: sqlite3_set_authorizer(pDb->db, 0, 0); sl@0: } sl@0: } sl@0: #endif sl@0: break; sl@0: } sl@0: sl@0: /* $db busy ?CALLBACK? sl@0: ** sl@0: ** Invoke the given callback if an SQL statement attempts to open sl@0: ** a locked database file. sl@0: */ sl@0: case DB_BUSY: { sl@0: if( objc>3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "CALLBACK"); sl@0: return TCL_ERROR; sl@0: }else if( objc==2 ){ sl@0: if( pDb->zBusy ){ sl@0: Tcl_AppendResult(interp, pDb->zBusy, 0); sl@0: } sl@0: }else{ sl@0: char *zBusy; sl@0: int len; sl@0: if( pDb->zBusy ){ sl@0: Tcl_Free(pDb->zBusy); sl@0: } sl@0: zBusy = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( zBusy && len>0 ){ sl@0: pDb->zBusy = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zBusy, zBusy, len+1); sl@0: }else{ sl@0: pDb->zBusy = 0; sl@0: } sl@0: if( pDb->zBusy ){ sl@0: pDb->interp = interp; sl@0: sqlite3_busy_handler(pDb->db, DbBusyHandler, pDb); sl@0: }else{ sl@0: sqlite3_busy_handler(pDb->db, 0, 0); sl@0: } sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* $db cache flush sl@0: ** $db cache size n sl@0: ** sl@0: ** Flush the prepared statement cache, or set the maximum number of sl@0: ** cached statements. sl@0: */ sl@0: case DB_CACHE: { sl@0: char *subCmd; sl@0: int n; sl@0: sl@0: if( objc<=2 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "cache option ?arg?"); sl@0: return TCL_ERROR; sl@0: } sl@0: subCmd = Tcl_GetStringFromObj( objv[2], 0 ); sl@0: if( *subCmd=='f' && strcmp(subCmd,"flush")==0 ){ sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "flush"); sl@0: return TCL_ERROR; sl@0: }else{ sl@0: flushStmtCache( pDb ); sl@0: } sl@0: }else if( *subCmd=='s' && strcmp(subCmd,"size")==0 ){ sl@0: if( objc!=4 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "size n"); sl@0: return TCL_ERROR; sl@0: }else{ sl@0: if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){ sl@0: Tcl_AppendResult( interp, "cannot convert \"", sl@0: Tcl_GetStringFromObj(objv[3],0), "\" to integer", 0); sl@0: return TCL_ERROR; sl@0: }else{ sl@0: if( n<0 ){ sl@0: flushStmtCache( pDb ); sl@0: n = 0; sl@0: }else if( n>MAX_PREPARED_STMTS ){ sl@0: n = MAX_PREPARED_STMTS; sl@0: } sl@0: pDb->maxStmt = n; sl@0: } sl@0: } sl@0: }else{ sl@0: Tcl_AppendResult( interp, "bad option \"", sl@0: Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* $db changes sl@0: ** sl@0: ** Return the number of rows that were modified, inserted, or deleted by sl@0: ** the most recent INSERT, UPDATE or DELETE statement, not including sl@0: ** any changes made by trigger programs. sl@0: */ sl@0: case DB_CHANGES: { sl@0: Tcl_Obj *pResult; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: pResult = Tcl_GetObjResult(interp); sl@0: Tcl_SetIntObj(pResult, sqlite3_changes(pDb->db)); sl@0: break; sl@0: } sl@0: sl@0: /* $db close sl@0: ** sl@0: ** Shutdown the database sl@0: */ sl@0: case DB_CLOSE: { sl@0: Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0)); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db collate NAME SCRIPT sl@0: ** sl@0: ** Create a new SQL collation function called NAME. Whenever sl@0: ** that function is called, invoke SCRIPT to evaluate the function. sl@0: */ sl@0: case DB_COLLATE: { sl@0: SqlCollate *pCollate; sl@0: char *zName; sl@0: char *zScript; sl@0: int nScript; sl@0: if( objc!=4 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); sl@0: return TCL_ERROR; sl@0: } sl@0: zName = Tcl_GetStringFromObj(objv[2], 0); sl@0: zScript = Tcl_GetStringFromObj(objv[3], &nScript); sl@0: pCollate = (SqlCollate*)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 ); sl@0: if( pCollate==0 ) return TCL_ERROR; sl@0: pCollate->interp = interp; sl@0: pCollate->pNext = pDb->pCollate; sl@0: pCollate->zScript = (char*)&pCollate[1]; sl@0: pDb->pCollate = pCollate; sl@0: memcpy(pCollate->zScript, zScript, nScript+1); sl@0: if( sqlite3_create_collation(pDb->db, zName, SQLITE_UTF8, sl@0: pCollate, tclSqlCollate) ){ sl@0: Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); sl@0: return TCL_ERROR; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db collation_needed SCRIPT sl@0: ** sl@0: ** Create a new SQL collation function called NAME. Whenever sl@0: ** that function is called, invoke SCRIPT to evaluate the function. sl@0: */ sl@0: case DB_COLLATION_NEEDED: { sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "SCRIPT"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( pDb->pCollateNeeded ){ sl@0: Tcl_DecrRefCount(pDb->pCollateNeeded); sl@0: } sl@0: pDb->pCollateNeeded = Tcl_DuplicateObj(objv[2]); sl@0: Tcl_IncrRefCount(pDb->pCollateNeeded); sl@0: sqlite3_collation_needed(pDb->db, pDb, tclCollateNeeded); sl@0: break; sl@0: } sl@0: sl@0: /* $db commit_hook ?CALLBACK? sl@0: ** sl@0: ** Invoke the given callback just before committing every SQL transaction. sl@0: ** If the callback throws an exception or returns non-zero, then the sl@0: ** transaction is aborted. If CALLBACK is an empty string, the callback sl@0: ** is disabled. sl@0: */ sl@0: case DB_COMMIT_HOOK: { sl@0: if( objc>3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); sl@0: return TCL_ERROR; sl@0: }else if( objc==2 ){ sl@0: if( pDb->zCommit ){ sl@0: Tcl_AppendResult(interp, pDb->zCommit, 0); sl@0: } sl@0: }else{ sl@0: char *zCommit; sl@0: int len; sl@0: if( pDb->zCommit ){ sl@0: Tcl_Free(pDb->zCommit); sl@0: } sl@0: zCommit = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( zCommit && len>0 ){ sl@0: pDb->zCommit = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zCommit, zCommit, len+1); sl@0: }else{ sl@0: pDb->zCommit = 0; sl@0: } sl@0: if( pDb->zCommit ){ sl@0: pDb->interp = interp; sl@0: sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb); sl@0: }else{ sl@0: sqlite3_commit_hook(pDb->db, 0, 0); sl@0: } sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* $db complete SQL sl@0: ** sl@0: ** Return TRUE if SQL is a complete SQL statement. Return FALSE if sl@0: ** additional lines of input are needed. This is similar to the sl@0: ** built-in "info complete" command of Tcl. sl@0: */ sl@0: case DB_COMPLETE: { sl@0: #ifndef SQLITE_OMIT_COMPLETE sl@0: Tcl_Obj *pResult; sl@0: int isComplete; sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "SQL"); sl@0: return TCL_ERROR; sl@0: } sl@0: isComplete = sqlite3_complete( Tcl_GetStringFromObj(objv[2], 0) ); sl@0: pResult = Tcl_GetObjResult(interp); sl@0: Tcl_SetBooleanObj(pResult, isComplete); sl@0: #endif sl@0: break; sl@0: } sl@0: sl@0: /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? sl@0: ** sl@0: ** Copy data into table from filename, optionally using SEPARATOR sl@0: ** as column separators. If a column contains a null string, or the sl@0: ** value of NULLINDICATOR, a NULL is inserted for the column. sl@0: ** conflict-algorithm is one of the sqlite conflict algorithms: sl@0: ** rollback, abort, fail, ignore, replace sl@0: ** On success, return the number of lines processed, not necessarily same sl@0: ** as 'db changes' due to conflict-algorithm selected. sl@0: ** sl@0: ** This code is basically an implementation/enhancement of sl@0: ** the sqlite3 shell.c ".import" command. sl@0: ** sl@0: ** This command usage is equivalent to the sqlite2.x COPY statement, sl@0: ** which imports file data into a table using the PostgreSQL COPY file format: sl@0: ** $db copy $conflit_algo $table_name $filename \t \\N sl@0: */ sl@0: case DB_COPY: { sl@0: char *zTable; /* Insert data into this table */ sl@0: char *zFile; /* The file from which to extract data */ sl@0: char *zConflict; /* The conflict algorithm to use */ sl@0: sqlite3_stmt *pStmt; /* A statement */ sl@0: int nCol; /* Number of columns in the table */ sl@0: int nByte; /* Number of bytes in an SQL string */ sl@0: int i, j; /* Loop counters */ sl@0: int nSep; /* Number of bytes in zSep[] */ sl@0: int nNull; /* Number of bytes in zNull[] */ sl@0: char *zSql; /* An SQL statement */ sl@0: char *zLine; /* A single line of input from the file */ sl@0: char **azCol; /* zLine[] broken up into columns */ sl@0: char *zCommit; /* How to commit changes */ sl@0: FILE *in; /* The input file */ sl@0: int lineno = 0; /* Line number of input file */ sl@0: char zLineNum[80]; /* Line number print buffer */ sl@0: Tcl_Obj *pResult; /* interp result */ sl@0: sl@0: char *zSep; sl@0: char *zNull; sl@0: if( objc<5 || objc>7 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, sl@0: "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( objc>=6 ){ sl@0: zSep = Tcl_GetStringFromObj(objv[5], 0); sl@0: }else{ sl@0: zSep = "\t"; sl@0: } sl@0: if( objc>=7 ){ sl@0: zNull = Tcl_GetStringFromObj(objv[6], 0); sl@0: }else{ sl@0: zNull = ""; sl@0: } sl@0: zConflict = Tcl_GetStringFromObj(objv[2], 0); sl@0: zTable = Tcl_GetStringFromObj(objv[3], 0); sl@0: zFile = Tcl_GetStringFromObj(objv[4], 0); sl@0: nSep = strlen(zSep); sl@0: nNull = strlen(zNull); sl@0: if( nSep==0 ){ sl@0: Tcl_AppendResult(interp,"Error: non-null separator required for copy",0); sl@0: return TCL_ERROR; sl@0: } sl@0: if(strcmp(zConflict, "rollback") != 0 && sl@0: strcmp(zConflict, "abort" ) != 0 && sl@0: strcmp(zConflict, "fail" ) != 0 && sl@0: strcmp(zConflict, "ignore" ) != 0 && sl@0: strcmp(zConflict, "replace" ) != 0 ) { sl@0: Tcl_AppendResult(interp, "Error: \"", zConflict, sl@0: "\", conflict-algorithm must be one of: rollback, " sl@0: "abort, fail, ignore, or replace", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); sl@0: if( zSql==0 ){ sl@0: Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: nByte = strlen(zSql); sl@0: rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0); sl@0: sqlite3_free(zSql); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); sl@0: nCol = 0; sl@0: }else{ sl@0: nCol = sqlite3_column_count(pStmt); sl@0: } sl@0: sqlite3_finalize(pStmt); sl@0: if( nCol==0 ) { sl@0: return TCL_ERROR; sl@0: } sl@0: zSql = malloc( nByte + 50 + nCol*2 ); sl@0: if( zSql==0 ) { sl@0: Tcl_AppendResult(interp, "Error: can't malloc()", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?", sl@0: zConflict, zTable); sl@0: j = strlen(zSql); sl@0: for(i=1; idb, zSql, -1, &pStmt, 0); sl@0: free(zSql); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); sl@0: sqlite3_finalize(pStmt); sl@0: return TCL_ERROR; sl@0: } sl@0: in = fopen(zFile, "rb"); sl@0: if( in==0 ){ sl@0: Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL); sl@0: sqlite3_finalize(pStmt); sl@0: return TCL_ERROR; sl@0: } sl@0: azCol = malloc( sizeof(azCol[0])*(nCol+1) ); sl@0: if( azCol==0 ) { sl@0: Tcl_AppendResult(interp, "Error: can't malloc()", 0); sl@0: fclose(in); sl@0: return TCL_ERROR; sl@0: } sl@0: (void)sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0); sl@0: zCommit = "COMMIT"; sl@0: while( (zLine = local_getline(0, in))!=0 ){ sl@0: char *z; sl@0: i = 0; sl@0: lineno++; sl@0: azCol[0] = zLine; sl@0: for(i=0, z=zLine; *z; z++){ sl@0: if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ sl@0: *z = 0; sl@0: i++; sl@0: if( i0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) { sl@0: sqlite3_bind_null(pStmt, i+1); sl@0: }else{ sl@0: sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); sl@0: } sl@0: } sl@0: sqlite3_step(pStmt); sl@0: rc = sqlite3_reset(pStmt); sl@0: free(zLine); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0); sl@0: zCommit = "ROLLBACK"; sl@0: break; sl@0: } sl@0: } sl@0: free(azCol); sl@0: fclose(in); sl@0: sqlite3_finalize(pStmt); sl@0: (void)sqlite3_exec(pDb->db, zCommit, 0, 0, 0); sl@0: sl@0: if( zCommit[0] == 'C' ){ sl@0: /* success, set result as number of lines processed */ sl@0: pResult = Tcl_GetObjResult(interp); sl@0: Tcl_SetIntObj(pResult, lineno); sl@0: rc = TCL_OK; sl@0: }else{ sl@0: /* failure, append lineno where failed */ sl@0: sqlite3_snprintf(sizeof(zLineNum), zLineNum,"%d",lineno); sl@0: Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0); sl@0: rc = TCL_ERROR; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db enable_load_extension BOOLEAN sl@0: ** sl@0: ** Turn the extension loading feature on or off. It if off by sl@0: ** default. sl@0: */ sl@0: case DB_ENABLE_LOAD_EXTENSION: { sl@0: #ifndef SQLITE_OMIT_LOAD_EXTENSION sl@0: int onoff; sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "BOOLEAN"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetBooleanFromObj(interp, objv[2], &onoff) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_enable_load_extension(pDb->db, onoff); sl@0: break; sl@0: #else sl@0: Tcl_AppendResult(interp, "extension loading is turned off at compile-time", sl@0: 0); sl@0: return TCL_ERROR; sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: ** $db errorcode sl@0: ** sl@0: ** Return the numeric error code that was returned by the most recent sl@0: ** call to sqlite3_exec(). sl@0: */ sl@0: case DB_ERRORCODE: { sl@0: Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db))); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db eval $sql ?array? ?{ ...code... }? sl@0: ** $db onecolumn $sql sl@0: ** sl@0: ** The SQL statement in $sql is evaluated. For each row, the values are sl@0: ** placed in elements of the array named "array" and ...code... is executed. sl@0: ** If "array" and "code" are omitted, then no callback is every invoked. sl@0: ** If "array" is an empty string, then the values are placed in variables sl@0: ** that have the same name as the fields extracted by the query. sl@0: ** sl@0: ** The onecolumn method is the equivalent of: sl@0: ** lindex [$db eval $sql] 0 sl@0: */ sl@0: case DB_ONECOLUMN: sl@0: case DB_EVAL: sl@0: case DB_EXISTS: { sl@0: char const *zSql; /* Next SQL statement to execute */ sl@0: char const *zLeft; /* What is left after first stmt in zSql */ sl@0: sqlite3_stmt *pStmt; /* Compiled SQL statment */ sl@0: Tcl_Obj *pArray; /* Name of array into which results are written */ sl@0: Tcl_Obj *pScript; /* Script to run for each result set */ sl@0: Tcl_Obj **apParm; /* Parameters that need a Tcl_DecrRefCount() */ sl@0: int nParm; /* Number of entries used in apParm[] */ sl@0: Tcl_Obj *aParm[10]; /* Static space for apParm[] in the common case */ sl@0: Tcl_Obj *pRet; /* Value to be returned */ sl@0: SqlPreparedStmt *pPreStmt; /* Pointer to a prepared statement */ sl@0: int rc2; sl@0: sl@0: if( choice==DB_EVAL ){ sl@0: if( objc<3 || objc>5 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?"); sl@0: return TCL_ERROR; sl@0: } sl@0: pRet = Tcl_NewObj(); sl@0: Tcl_IncrRefCount(pRet); sl@0: }else{ sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "SQL"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( choice==DB_EXISTS ){ sl@0: pRet = Tcl_NewBooleanObj(0); sl@0: Tcl_IncrRefCount(pRet); sl@0: }else{ sl@0: pRet = 0; sl@0: } sl@0: } sl@0: if( objc==3 ){ sl@0: pArray = pScript = 0; sl@0: }else if( objc==4 ){ sl@0: pArray = 0; sl@0: pScript = objv[3]; sl@0: }else{ sl@0: pArray = objv[3]; sl@0: if( Tcl_GetString(pArray)[0]==0 ) pArray = 0; sl@0: pScript = objv[4]; sl@0: } sl@0: sl@0: Tcl_IncrRefCount(objv[2]); sl@0: zSql = Tcl_GetStringFromObj(objv[2], 0); sl@0: while( rc==TCL_OK && zSql[0] ){ sl@0: int i; /* Loop counter */ sl@0: int nVar; /* Number of bind parameters in the pStmt */ sl@0: int nCol = -1; /* Number of columns in the result set */ sl@0: Tcl_Obj **apColName = 0; /* Array of column names */ sl@0: int len; /* String length of zSql */ sl@0: sl@0: /* Try to find a SQL statement that has already been compiled and sl@0: ** which matches the next sequence of SQL. sl@0: */ sl@0: pStmt = 0; sl@0: len = strlen(zSql); sl@0: for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ sl@0: int n = pPreStmt->nSql; sl@0: if( len>=n sl@0: && memcmp(pPreStmt->zSql, zSql, n)==0 sl@0: && (zSql[n]==0 || zSql[n-1]==';') sl@0: ){ sl@0: pStmt = pPreStmt->pStmt; sl@0: zLeft = &zSql[pPreStmt->nSql]; sl@0: sl@0: /* When a prepared statement is found, unlink it from the sl@0: ** cache list. It will later be added back to the beginning sl@0: ** of the cache list in order to implement LRU replacement. sl@0: */ sl@0: if( pPreStmt->pPrev ){ sl@0: pPreStmt->pPrev->pNext = pPreStmt->pNext; sl@0: }else{ sl@0: pDb->stmtList = pPreStmt->pNext; sl@0: } sl@0: if( pPreStmt->pNext ){ sl@0: pPreStmt->pNext->pPrev = pPreStmt->pPrev; sl@0: }else{ sl@0: pDb->stmtLast = pPreStmt->pPrev; sl@0: } sl@0: pDb->nStmt--; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: /* If no prepared statement was found. Compile the SQL text sl@0: */ sl@0: if( pStmt==0 ){ sl@0: if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, &zLeft) ){ sl@0: Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); sl@0: rc = TCL_ERROR; sl@0: break; sl@0: } sl@0: if( pStmt==0 ){ sl@0: if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ sl@0: /* A compile-time error in the statement sl@0: */ sl@0: Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); sl@0: rc = TCL_ERROR; sl@0: break; sl@0: }else{ sl@0: /* The statement was a no-op. Continue to the next statement sl@0: ** in the SQL string. sl@0: */ sl@0: zSql = zLeft; sl@0: continue; sl@0: } sl@0: } sl@0: assert( pPreStmt==0 ); sl@0: } sl@0: sl@0: /* Bind values to parameters that begin with $ or : sl@0: */ sl@0: nVar = sqlite3_bind_parameter_count(pStmt); sl@0: nParm = 0; sl@0: if( nVar>sizeof(aParm)/sizeof(aParm[0]) ){ sl@0: apParm = (Tcl_Obj**)Tcl_Alloc(nVar*sizeof(apParm[0])); sl@0: }else{ sl@0: apParm = aParm; sl@0: } sl@0: for(i=1; i<=nVar; i++){ sl@0: const char *zVar = sqlite3_bind_parameter_name(pStmt, i); sl@0: if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ sl@0: Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0); sl@0: if( pVar ){ sl@0: int n; sl@0: u8 *data; sl@0: char *zType = pVar->typePtr ? pVar->typePtr->name : ""; sl@0: char c = zType[0]; sl@0: if( zVar[0]=='@' || sl@0: (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ sl@0: /* Load a BLOB type if the Tcl variable is a bytearray and sl@0: ** it has no string representation or the host sl@0: ** parameter name begins with "@". */ sl@0: data = Tcl_GetByteArrayFromObj(pVar, &n); sl@0: sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); sl@0: Tcl_IncrRefCount(pVar); sl@0: apParm[nParm++] = pVar; sl@0: }else if( c=='b' && strcmp(zType,"boolean")==0 ){ sl@0: Tcl_GetIntFromObj(interp, pVar, &n); sl@0: sqlite3_bind_int(pStmt, i, n); sl@0: }else if( c=='d' && strcmp(zType,"double")==0 ){ sl@0: double r; sl@0: Tcl_GetDoubleFromObj(interp, pVar, &r); sl@0: sqlite3_bind_double(pStmt, i, r); sl@0: }else if( (c=='w' && strcmp(zType,"wideInt")==0) || sl@0: (c=='i' && strcmp(zType,"int")==0) ){ sl@0: Tcl_WideInt v; sl@0: Tcl_GetWideIntFromObj(interp, pVar, &v); sl@0: sqlite3_bind_int64(pStmt, i, v); sl@0: }else{ sl@0: data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); sl@0: sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC); sl@0: Tcl_IncrRefCount(pVar); sl@0: apParm[nParm++] = pVar; sl@0: } sl@0: }else{ sl@0: sqlite3_bind_null( pStmt, i ); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* Execute the SQL sl@0: */ sl@0: while( rc==TCL_OK && pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){ sl@0: sl@0: /* Compute column names. This must be done after the first successful sl@0: ** call to sqlite3_step(), in case the query is recompiled and the sl@0: ** number or names of the returned columns changes. sl@0: */ sl@0: assert(!pArray||pScript); sl@0: if (nCol < 0) { sl@0: Tcl_Obj ***ap = (pScript?&apColName:0); sl@0: nCol = computeColumnNames(interp, pStmt, ap, pArray); sl@0: } sl@0: sl@0: for(i=0; i=-2147483647 && v<=2147483647 ){ sl@0: pVal = Tcl_NewIntObj(v); sl@0: }else{ sl@0: pVal = Tcl_NewWideIntObj(v); sl@0: } sl@0: break; sl@0: } sl@0: case SQLITE_FLOAT: { sl@0: double r = sqlite3_column_double(pStmt, i); sl@0: pVal = Tcl_NewDoubleObj(r); sl@0: break; sl@0: } sl@0: case SQLITE_NULL: { sl@0: pVal = dbTextToObj(pDb->zNull); sl@0: break; sl@0: } sl@0: default: { sl@0: pVal = dbTextToObj((char *)sqlite3_column_text(pStmt, i)); sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if( pScript ){ sl@0: if( pArray==0 ){ sl@0: Tcl_ObjSetVar2(interp, apColName[i], 0, pVal, 0); sl@0: }else{ sl@0: Tcl_ObjSetVar2(interp, pArray, apColName[i], pVal, 0); sl@0: } sl@0: }else if( choice==DB_ONECOLUMN ){ sl@0: assert( pRet==0 ); sl@0: if( pRet==0 ){ sl@0: pRet = pVal; sl@0: Tcl_IncrRefCount(pRet); sl@0: } sl@0: rc = TCL_BREAK; sl@0: i = nCol; sl@0: }else if( choice==DB_EXISTS ){ sl@0: Tcl_DecrRefCount(pRet); sl@0: pRet = Tcl_NewBooleanObj(1); sl@0: Tcl_IncrRefCount(pRet); sl@0: rc = TCL_BREAK; sl@0: i = nCol; sl@0: }else{ sl@0: Tcl_ListObjAppendElement(interp, pRet, pVal); sl@0: } sl@0: } sl@0: sl@0: if( pScript ){ sl@0: rc = Tcl_EvalObjEx(interp, pScript, 0); sl@0: if( rc==TCL_CONTINUE ){ sl@0: rc = TCL_OK; sl@0: } sl@0: } sl@0: } sl@0: if( rc==TCL_BREAK ){ sl@0: rc = TCL_OK; sl@0: } sl@0: sl@0: /* Free the column name objects */ sl@0: if( pScript ){ sl@0: /* If the query returned no rows, but an array variable was sl@0: ** specified, call computeColumnNames() now to populate the sl@0: ** arrayname(*) variable. sl@0: */ sl@0: if (pArray && nCol < 0) { sl@0: Tcl_Obj ***ap = (pScript?&apColName:0); sl@0: nCol = computeColumnNames(interp, pStmt, ap, pArray); sl@0: } sl@0: for(i=0; idb))); sl@0: sqlite3_finalize(pStmt); sl@0: rc = TCL_ERROR; sl@0: if( pPreStmt ) Tcl_Free((char*)pPreStmt); sl@0: break; sl@0: }else if( pDb->maxStmt<=0 ){ sl@0: /* If the cache is turned off, deallocated the statement */ sl@0: if( pPreStmt ) Tcl_Free((char*)pPreStmt); sl@0: sqlite3_finalize(pStmt); sl@0: }else{ sl@0: /* Everything worked and the cache is operational. sl@0: ** Create a new SqlPreparedStmt structure if we need one. sl@0: ** (If we already have one we can just reuse it.) sl@0: */ sl@0: if( pPreStmt==0 ){ sl@0: len = zLeft - zSql; sl@0: pPreStmt = (SqlPreparedStmt*)Tcl_Alloc( sizeof(*pPreStmt) ); sl@0: if( pPreStmt==0 ) return TCL_ERROR; sl@0: pPreStmt->pStmt = pStmt; sl@0: pPreStmt->nSql = len; sl@0: pPreStmt->zSql = sqlite3_sql(pStmt); sl@0: assert( strlen(pPreStmt->zSql)==len ); sl@0: assert( 0==memcmp(pPreStmt->zSql, zSql, len) ); sl@0: } sl@0: sl@0: /* Add the prepared statement to the beginning of the cache list sl@0: */ sl@0: pPreStmt->pNext = pDb->stmtList; sl@0: pPreStmt->pPrev = 0; sl@0: if( pDb->stmtList ){ sl@0: pDb->stmtList->pPrev = pPreStmt; sl@0: } sl@0: pDb->stmtList = pPreStmt; sl@0: if( pDb->stmtLast==0 ){ sl@0: assert( pDb->nStmt==0 ); sl@0: pDb->stmtLast = pPreStmt; sl@0: }else{ sl@0: assert( pDb->nStmt>0 ); sl@0: } sl@0: pDb->nStmt++; sl@0: sl@0: /* If we have too many statement in cache, remove the surplus from the sl@0: ** end of the cache list. sl@0: */ sl@0: while( pDb->nStmt>pDb->maxStmt ){ sl@0: sqlite3_finalize(pDb->stmtLast->pStmt); sl@0: pDb->stmtLast = pDb->stmtLast->pPrev; sl@0: Tcl_Free((char*)pDb->stmtLast->pNext); sl@0: pDb->stmtLast->pNext = 0; sl@0: pDb->nStmt--; sl@0: } sl@0: } sl@0: sl@0: /* Proceed to the next statement */ sl@0: zSql = zLeft; sl@0: } sl@0: Tcl_DecrRefCount(objv[2]); sl@0: sl@0: if( pRet ){ sl@0: if( rc==TCL_OK ){ sl@0: Tcl_SetObjResult(interp, pRet); sl@0: } sl@0: Tcl_DecrRefCount(pRet); sl@0: }else if( rc==TCL_OK ){ sl@0: Tcl_ResetResult(interp); sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db function NAME [-argcount N] SCRIPT sl@0: ** sl@0: ** Create a new SQL function called NAME. Whenever that function is sl@0: ** called, invoke SCRIPT to evaluate the function. sl@0: */ sl@0: case DB_FUNCTION: { sl@0: SqlFunc *pFunc; sl@0: Tcl_Obj *pScript; sl@0: char *zName; sl@0: int nArg = -1; sl@0: if( objc==6 ){ sl@0: const char *z = Tcl_GetString(objv[3]); sl@0: int n = strlen(z); sl@0: if( n>2 && strncmp(z, "-argcount",n)==0 ){ sl@0: if( Tcl_GetIntFromObj(interp, objv[4], &nArg) ) return TCL_ERROR; sl@0: if( nArg<0 ){ sl@0: Tcl_AppendResult(interp, "number of arguments must be non-negative", sl@0: (char*)0); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: pScript = objv[5]; sl@0: }else if( objc!=4 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "NAME [-argcount N] SCRIPT"); sl@0: return TCL_ERROR; sl@0: }else{ sl@0: pScript = objv[3]; sl@0: } sl@0: zName = Tcl_GetStringFromObj(objv[2], 0); sl@0: pFunc = findSqlFunc(pDb, zName); sl@0: if( pFunc==0 ) return TCL_ERROR; sl@0: if( pFunc->pScript ){ sl@0: Tcl_DecrRefCount(pFunc->pScript); sl@0: } sl@0: pFunc->pScript = pScript; sl@0: Tcl_IncrRefCount(pScript); sl@0: pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript); sl@0: rc = sqlite3_create_function(pDb->db, zName, nArg, SQLITE_UTF8, sl@0: pFunc, tclSqlFunc, 0, 0); sl@0: if( rc!=SQLITE_OK ){ sl@0: rc = TCL_ERROR; sl@0: Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE); sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID sl@0: */ sl@0: case DB_INCRBLOB: { sl@0: #ifdef SQLITE_OMIT_INCRBLOB sl@0: Tcl_AppendResult(interp, "incrblob not available in this build", 0); sl@0: return TCL_ERROR; sl@0: #else sl@0: int isReadonly = 0; sl@0: const char *zDb = "main"; sl@0: const char *zTable; sl@0: const char *zColumn; sl@0: sqlite_int64 iRow; sl@0: sl@0: /* Check for the -readonly option */ sl@0: if( objc>3 && strcmp(Tcl_GetString(objv[2]), "-readonly")==0 ){ sl@0: isReadonly = 1; sl@0: } sl@0: sl@0: if( objc!=(5+isReadonly) && objc!=(6+isReadonly) ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID"); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if( objc==(6+isReadonly) ){ sl@0: zDb = Tcl_GetString(objv[2]); sl@0: } sl@0: zTable = Tcl_GetString(objv[objc-3]); sl@0: zColumn = Tcl_GetString(objv[objc-2]); sl@0: rc = Tcl_GetWideIntFromObj(interp, objv[objc-1], &iRow); sl@0: sl@0: if( rc==TCL_OK ){ sl@0: rc = createIncrblobChannel( sl@0: interp, pDb, zDb, zTable, zColumn, iRow, isReadonly sl@0: ); sl@0: } sl@0: #endif sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db interrupt sl@0: ** sl@0: ** Interrupt the execution of the inner-most SQL interpreter. This sl@0: ** causes the SQL statement to return an error of SQLITE_INTERRUPT. sl@0: */ sl@0: case DB_INTERRUPT: { sl@0: sqlite3_interrupt(pDb->db); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db nullvalue ?STRING? sl@0: ** sl@0: ** Change text used when a NULL comes back from the database. If ?STRING? sl@0: ** is not present, then the current string used for NULL is returned. sl@0: ** If STRING is present, then STRING is returned. sl@0: ** sl@0: */ sl@0: case DB_NULLVALUE: { sl@0: if( objc!=2 && objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( objc==3 ){ sl@0: int len; sl@0: char *zNull = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( pDb->zNull ){ sl@0: Tcl_Free(pDb->zNull); sl@0: } sl@0: if( zNull && len>0 ){ sl@0: pDb->zNull = Tcl_Alloc( len + 1 ); sl@0: strncpy(pDb->zNull, zNull, len); sl@0: pDb->zNull[len] = '\0'; sl@0: }else{ sl@0: pDb->zNull = 0; sl@0: } sl@0: } sl@0: Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db last_insert_rowid sl@0: ** sl@0: ** Return an integer which is the ROWID for the most recent insert. sl@0: */ sl@0: case DB_LAST_INSERT_ROWID: { sl@0: Tcl_Obj *pResult; sl@0: Tcl_WideInt rowid; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: rowid = sqlite3_last_insert_rowid(pDb->db); sl@0: pResult = Tcl_GetObjResult(interp); sl@0: Tcl_SetWideIntObj(pResult, rowid); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** The DB_ONECOLUMN method is implemented together with DB_EVAL. sl@0: */ sl@0: sl@0: /* $db progress ?N CALLBACK? sl@0: ** sl@0: ** Invoke the given callback every N virtual machine opcodes while executing sl@0: ** queries. sl@0: */ sl@0: case DB_PROGRESS: { sl@0: if( objc==2 ){ sl@0: if( pDb->zProgress ){ sl@0: Tcl_AppendResult(interp, pDb->zProgress, 0); sl@0: } sl@0: }else if( objc==4 ){ sl@0: char *zProgress; sl@0: int len; sl@0: int N; sl@0: if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &N) ){ sl@0: return TCL_ERROR; sl@0: }; sl@0: if( pDb->zProgress ){ sl@0: Tcl_Free(pDb->zProgress); sl@0: } sl@0: zProgress = Tcl_GetStringFromObj(objv[3], &len); sl@0: if( zProgress && len>0 ){ sl@0: pDb->zProgress = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zProgress, zProgress, len+1); sl@0: }else{ sl@0: pDb->zProgress = 0; sl@0: } sl@0: #ifndef SQLITE_OMIT_PROGRESS_CALLBACK sl@0: if( pDb->zProgress ){ sl@0: pDb->interp = interp; sl@0: sqlite3_progress_handler(pDb->db, N, DbProgressHandler, pDb); sl@0: }else{ sl@0: sqlite3_progress_handler(pDb->db, 0, 0, 0); sl@0: } sl@0: #endif sl@0: }else{ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "N CALLBACK"); sl@0: return TCL_ERROR; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* $db profile ?CALLBACK? sl@0: ** sl@0: ** Make arrangements to invoke the CALLBACK routine after each SQL statement sl@0: ** that has run. The text of the SQL and the amount of elapse time are sl@0: ** appended to CALLBACK before the script is run. sl@0: */ sl@0: case DB_PROFILE: { sl@0: if( objc>3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); sl@0: return TCL_ERROR; sl@0: }else if( objc==2 ){ sl@0: if( pDb->zProfile ){ sl@0: Tcl_AppendResult(interp, pDb->zProfile, 0); sl@0: } sl@0: }else{ sl@0: char *zProfile; sl@0: int len; sl@0: if( pDb->zProfile ){ sl@0: Tcl_Free(pDb->zProfile); sl@0: } sl@0: zProfile = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( zProfile && len>0 ){ sl@0: pDb->zProfile = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zProfile, zProfile, len+1); sl@0: }else{ sl@0: pDb->zProfile = 0; sl@0: } sl@0: #ifndef SQLITE_OMIT_TRACE sl@0: if( pDb->zProfile ){ sl@0: pDb->interp = interp; sl@0: sqlite3_profile(pDb->db, DbProfileHandler, pDb); sl@0: }else{ sl@0: sqlite3_profile(pDb->db, 0, 0); sl@0: } sl@0: #endif sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db rekey KEY sl@0: ** sl@0: ** Change the encryption key on the currently open database. sl@0: */ sl@0: case DB_REKEY: { sl@0: int nKey; sl@0: void *pKey; sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "KEY"); sl@0: return TCL_ERROR; sl@0: } sl@0: pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); sl@0: #ifdef SQLITE_HAS_CODEC sl@0: rc = sqlite3_rekey(pDb->db, pKey, nKey); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0); sl@0: rc = TCL_ERROR; sl@0: } sl@0: #endif sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db timeout MILLESECONDS sl@0: ** sl@0: ** Delay for the number of milliseconds specified when a file is locked. sl@0: */ sl@0: case DB_TIMEOUT: { sl@0: int ms; sl@0: if( objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR; sl@0: sqlite3_busy_timeout(pDb->db, ms); sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db total_changes sl@0: ** sl@0: ** Return the number of rows that were modified, inserted, or deleted sl@0: ** since the database handle was created. sl@0: */ sl@0: case DB_TOTAL_CHANGES: { sl@0: Tcl_Obj *pResult; sl@0: if( objc!=2 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, ""); sl@0: return TCL_ERROR; sl@0: } sl@0: pResult = Tcl_GetObjResult(interp); sl@0: Tcl_SetIntObj(pResult, sqlite3_total_changes(pDb->db)); sl@0: break; sl@0: } sl@0: sl@0: /* $db trace ?CALLBACK? sl@0: ** sl@0: ** Make arrangements to invoke the CALLBACK routine for each SQL statement sl@0: ** that is executed. The text of the SQL is appended to CALLBACK before sl@0: ** it is executed. sl@0: */ sl@0: case DB_TRACE: { sl@0: if( objc>3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); sl@0: return TCL_ERROR; sl@0: }else if( objc==2 ){ sl@0: if( pDb->zTrace ){ sl@0: Tcl_AppendResult(interp, pDb->zTrace, 0); sl@0: } sl@0: }else{ sl@0: char *zTrace; sl@0: int len; sl@0: if( pDb->zTrace ){ sl@0: Tcl_Free(pDb->zTrace); sl@0: } sl@0: zTrace = Tcl_GetStringFromObj(objv[2], &len); sl@0: if( zTrace && len>0 ){ sl@0: pDb->zTrace = Tcl_Alloc( len + 1 ); sl@0: memcpy(pDb->zTrace, zTrace, len+1); sl@0: }else{ sl@0: pDb->zTrace = 0; sl@0: } sl@0: #ifndef SQLITE_OMIT_TRACE sl@0: if( pDb->zTrace ){ sl@0: pDb->interp = interp; sl@0: sqlite3_trace(pDb->db, DbTraceHandler, pDb); sl@0: }else{ sl@0: sqlite3_trace(pDb->db, 0, 0); sl@0: } sl@0: #endif sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT sl@0: ** sl@0: ** Start a new transaction (if we are not already in the midst of a sl@0: ** transaction) and execute the TCL script SCRIPT. After SCRIPT sl@0: ** completes, either commit the transaction or roll it back if SCRIPT sl@0: ** throws an exception. Or if no new transation was started, do nothing. sl@0: ** pass the exception on up the stack. sl@0: ** sl@0: ** This command was inspired by Dave Thomas's talk on Ruby at the sl@0: ** 2005 O'Reilly Open Source Convention (OSCON). sl@0: */ sl@0: case DB_TRANSACTION: { sl@0: int inTrans; sl@0: Tcl_Obj *pScript; sl@0: const char *zBegin = "BEGIN"; sl@0: if( objc!=3 && objc!=4 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( objc==3 ){ sl@0: pScript = objv[2]; sl@0: } else { sl@0: static const char *TTYPE_strs[] = { sl@0: "deferred", "exclusive", "immediate", 0 sl@0: }; sl@0: enum TTYPE_enum { sl@0: TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE sl@0: }; sl@0: int ttype; sl@0: if( Tcl_GetIndexFromObj(interp, objv[2], TTYPE_strs, "transaction type", sl@0: 0, &ttype) ){ sl@0: return TCL_ERROR; sl@0: } sl@0: switch( (enum TTYPE_enum)ttype ){ sl@0: case TTYPE_DEFERRED: /* no-op */; break; sl@0: case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break; sl@0: case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break; sl@0: } sl@0: pScript = objv[3]; sl@0: } sl@0: inTrans = !sqlite3_get_autocommit(pDb->db); sl@0: if( !inTrans ){ sl@0: pDb->disableAuth++; sl@0: (void)sqlite3_exec(pDb->db, zBegin, 0, 0, 0); sl@0: pDb->disableAuth--; sl@0: } sl@0: rc = Tcl_EvalObjEx(interp, pScript, 0); sl@0: if( !inTrans ){ sl@0: const char *zEnd; sl@0: if( rc==TCL_ERROR ){ sl@0: zEnd = "ROLLBACK"; sl@0: } else { sl@0: zEnd = "COMMIT"; sl@0: } sl@0: pDb->disableAuth++; sl@0: if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ sl@0: sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); sl@0: } sl@0: pDb->disableAuth--; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: ** $db update_hook ?script? sl@0: ** $db rollback_hook ?script? sl@0: */ sl@0: case DB_UPDATE_HOOK: sl@0: case DB_ROLLBACK_HOOK: { sl@0: sl@0: /* set ppHook to point at pUpdateHook or pRollbackHook, depending on sl@0: ** whether [$db update_hook] or [$db rollback_hook] was invoked. sl@0: */ sl@0: Tcl_Obj **ppHook; sl@0: if( choice==DB_UPDATE_HOOK ){ sl@0: ppHook = &pDb->pUpdateHook; sl@0: }else{ sl@0: ppHook = &pDb->pRollbackHook; sl@0: } sl@0: sl@0: if( objc!=2 && objc!=3 ){ sl@0: Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if( *ppHook ){ sl@0: Tcl_SetObjResult(interp, *ppHook); sl@0: if( objc==3 ){ sl@0: Tcl_DecrRefCount(*ppHook); sl@0: *ppHook = 0; sl@0: } sl@0: } sl@0: if( objc==3 ){ sl@0: assert( !(*ppHook) ); sl@0: if( Tcl_GetCharLength(objv[2])>0 ){ sl@0: *ppHook = objv[2]; sl@0: Tcl_IncrRefCount(*ppHook); sl@0: } sl@0: } sl@0: sl@0: sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb); sl@0: sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb); sl@0: sl@0: break; sl@0: } sl@0: sl@0: /* $db version sl@0: ** sl@0: ** Return the version string for this database. sl@0: */ sl@0: case DB_VERSION: { sl@0: Tcl_SetResult(interp, (char *)sqlite3_libversion(), TCL_STATIC); sl@0: break; sl@0: } sl@0: sl@0: sl@0: } /* End of the SWITCH statement */ sl@0: return rc; sl@0: } sl@0: sl@0: /* sl@0: ** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? sl@0: ** ?-create BOOLEAN? ?-nomutex BOOLEAN? sl@0: ** sl@0: ** This is the main Tcl command. When the "sqlite" Tcl command is sl@0: ** invoked, this routine runs to process that command. sl@0: ** sl@0: ** The first argument, DBNAME, is an arbitrary name for a new sl@0: ** database connection. This command creates a new command named sl@0: ** DBNAME that is used to control that connection. The database sl@0: ** connection is deleted when the DBNAME command is deleted. sl@0: ** sl@0: ** The second argument is the name of the database file. sl@0: ** sl@0: */ sl@0: static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ sl@0: SqliteDb *p; sl@0: void *pKey = 0; sl@0: int nKey = 0; sl@0: const char *zArg; sl@0: char *zErrMsg; sl@0: int i; sl@0: const char *zFile; sl@0: const char *zVfs = 0; sl@0: int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; sl@0: Tcl_DString translatedFilename; sl@0: if( objc==2 ){ sl@0: zArg = Tcl_GetStringFromObj(objv[1], 0); sl@0: if( strcmp(zArg,"-version")==0 ){ sl@0: Tcl_AppendResult(interp,sqlite3_version,0); sl@0: return TCL_OK; sl@0: } sl@0: if( strcmp(zArg,"-has-codec")==0 ){ sl@0: #ifdef SQLITE_HAS_CODEC sl@0: Tcl_AppendResult(interp,"1",0); sl@0: #else sl@0: Tcl_AppendResult(interp,"0",0); sl@0: #endif sl@0: return TCL_OK; sl@0: } sl@0: } sl@0: for(i=3; i+1db, flags, zVfs); sl@0: Tcl_DStringFree(&translatedFilename); sl@0: if( SQLITE_OK!=sqlite3_errcode(p->db) ){ sl@0: zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db)); sl@0: sqlite3_close(p->db); sl@0: p->db = 0; sl@0: } sl@0: #ifdef SQLITE_HAS_CODEC sl@0: if( p->db ){ sl@0: sqlite3_key(p->db, pKey, nKey); sl@0: } sl@0: #endif sl@0: if( p->db==0 ){ sl@0: Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); sl@0: Tcl_Free((char*)p); sl@0: sqlite3_free(zErrMsg); sl@0: return TCL_ERROR; sl@0: } sl@0: p->maxStmt = NUM_PREPARED_STMTS; sl@0: p->interp = interp; sl@0: zArg = Tcl_GetStringFromObj(objv[1], 0); sl@0: Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Provide a dummy Tcl_InitStubs if we are using this as a static sl@0: ** library. sl@0: */ sl@0: #ifndef USE_TCL_STUBS sl@0: # undef Tcl_InitStubs sl@0: # define Tcl_InitStubs(a,b,c) sl@0: #endif sl@0: sl@0: /* sl@0: ** Make sure we have a PACKAGE_VERSION macro defined. This will be sl@0: ** defined automatically by the TEA makefile. But other makefiles sl@0: ** do not define it. sl@0: */ sl@0: #ifndef PACKAGE_VERSION sl@0: # define PACKAGE_VERSION SQLITE_VERSION sl@0: #endif sl@0: sl@0: /* sl@0: ** Initialize this module. sl@0: ** sl@0: ** This Tcl module contains only a single new Tcl command named "sqlite". sl@0: ** (Hence there is no namespace. There is no point in using a namespace sl@0: ** if the extension only supplies one new name!) The "sqlite" command is sl@0: ** used to open a new SQLite database. See the DbMain() routine above sl@0: ** for additional information. sl@0: */ sl@0: EXTERN int Sqlite3_Init(Tcl_Interp *interp){ sl@0: Tcl_InitStubs(interp, "8.4", 0); sl@0: Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); sl@0: Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION); sl@0: Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); sl@0: Tcl_PkgProvide(interp, "sqlite", PACKAGE_VERSION); sl@0: return TCL_OK; sl@0: } sl@0: EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } sl@0: EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } sl@0: EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } sl@0: EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} sl@0: sl@0: sl@0: #ifndef SQLITE_3_SUFFIX_ONLY sl@0: EXTERN int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } sl@0: EXTERN int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } sl@0: EXTERN int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } sl@0: EXTERN int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } sl@0: EXTERN int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; } sl@0: EXTERN int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} sl@0: #endif sl@0: sl@0: #ifdef TCLSH sl@0: /***************************************************************************** sl@0: ** The code that follows is used to build standalone TCL interpreters sl@0: ** that are statically linked with SQLite. sl@0: */ sl@0: sl@0: /* sl@0: ** If the macro TCLSH is one, then put in code this for the sl@0: ** "main" routine that will initialize Tcl and take input from sl@0: ** standard input, or if a file is named on the command line sl@0: ** the TCL interpreter reads and evaluates that file. sl@0: */ sl@0: #if TCLSH==1 sl@0: static char zMainloop[] = sl@0: "set line {}\n" sl@0: "while {![eof stdin]} {\n" sl@0: "if {$line!=\"\"} {\n" sl@0: "puts -nonewline \"> \"\n" sl@0: "} else {\n" sl@0: "puts -nonewline \"% \"\n" sl@0: "}\n" sl@0: "flush stdout\n" sl@0: "append line [gets stdin]\n" sl@0: "if {[info complete $line]} {\n" sl@0: "if {[catch {uplevel #0 $line} result]} {\n" sl@0: "puts stderr \"Error: $result\"\n" sl@0: "} elseif {$result!=\"\"} {\n" sl@0: "puts $result\n" sl@0: "}\n" sl@0: "set line {}\n" sl@0: "} else {\n" sl@0: "append line \\n\n" sl@0: "}\n" sl@0: "}\n" sl@0: ; sl@0: #endif sl@0: sl@0: /* sl@0: ** If the macro TCLSH is two, then get the main loop code out of sl@0: ** the separate file "spaceanal_tcl.h". sl@0: */ sl@0: #if TCLSH==2 sl@0: static char zMainloop[] = sl@0: #include "spaceanal_tcl.h" sl@0: ; sl@0: #endif sl@0: sl@0: #define TCLSH_MAIN main /* Needed to fake out mktclapp */ sl@0: int TCLSH_MAIN(int argc, char **argv){ sl@0: Tcl_Interp *interp; sl@0: #if defined(__SYMBIAN32__) sl@0: int isChildProcess = 0; sl@0: int oldArgc = 0; sl@0: int err = 0; sl@0: #endif sl@0: sl@0: PrintS("###TclSqlite3: New instance"); sl@0: Tcl_FindExecutable(argv[0]); sl@0: PrintS("###TclSqlite3: Create interpreter"); sl@0: interp = Tcl_CreateInterp(); sl@0: #if defined(__SYMBIAN32__) sl@0: PrintS("###TclSqlite3: Child process init"); sl@0: if (ChildProcessInit(&argc, &argv)) sl@0: { sl@0: oldArgc = argc; sl@0: argc = argc-4; sl@0: isChildProcess = 1; sl@0: } sl@0: else sl@0: { sl@0: err = CopyTestFiles(); sl@0: if(err != 0) sl@0: { sl@0: PrintS("###TclSqlite3: Exit-1"); sl@0: return 1; sl@0: } sl@0: } sl@0: #endif sl@0: PrintS("###TclSqlite3: Init"); sl@0: Sqlite3_Init(interp); sl@0: #ifdef SQLITE_TEST sl@0: { sl@0: extern int Md5_Init(Tcl_Interp*); sl@0: extern int Sqliteconfig_Init(Tcl_Interp*); sl@0: extern int Sqlitetest1_Init(Tcl_Interp*); sl@0: extern int Sqlitetest2_Init(Tcl_Interp*); sl@0: extern int Sqlitetest3_Init(Tcl_Interp*); sl@0: extern int Sqlitetest4_Init(Tcl_Interp*); sl@0: extern int Sqlitetest5_Init(Tcl_Interp*); sl@0: extern int Sqlitetest6_Init(Tcl_Interp*); sl@0: extern int Sqlitetest7_Init(Tcl_Interp*); sl@0: extern int Sqlitetest8_Init(Tcl_Interp*); sl@0: extern int Sqlitetest9_Init(Tcl_Interp*); sl@0: extern int Sqlitetestasync_Init(Tcl_Interp*); sl@0: extern int Sqlitetest_autoext_Init(Tcl_Interp*); sl@0: extern int Sqlitetest_func_Init(Tcl_Interp*); sl@0: extern int Sqlitetest_hexio_Init(Tcl_Interp*); sl@0: extern int Sqlitetest_malloc_Init(Tcl_Interp*); sl@0: extern int Sqlitetest_mutex_Init(Tcl_Interp*); sl@0: extern int Sqlitetestschema_Init(Tcl_Interp*); sl@0: extern int Sqlitetestsse_Init(Tcl_Interp*); sl@0: extern int Sqlitetesttclvar_Init(Tcl_Interp*); sl@0: extern int SqlitetestThread_Init(Tcl_Interp*); sl@0: extern int SqlitetestOnefile_Init(); sl@0: extern int SqlitetestOsinst_Init(Tcl_Interp*); sl@0: sl@0: Md5_Init(interp); sl@0: Sqliteconfig_Init(interp); sl@0: Sqlitetest1_Init(interp); sl@0: Sqlitetest2_Init(interp); sl@0: Sqlitetest3_Init(interp); sl@0: Sqlitetest4_Init(interp); sl@0: Sqlitetest5_Init(interp); sl@0: Sqlitetest6_Init(interp); sl@0: Sqlitetest7_Init(interp); sl@0: Sqlitetest8_Init(interp); sl@0: Sqlitetest9_Init(interp); sl@0: Sqlitetestasync_Init(interp); sl@0: Sqlitetest_autoext_Init(interp); sl@0: Sqlitetest_func_Init(interp); sl@0: Sqlitetest_hexio_Init(interp); sl@0: Sqlitetest_malloc_Init(interp); sl@0: Sqlitetest_mutex_Init(interp); sl@0: Sqlitetestschema_Init(interp); sl@0: Sqlitetesttclvar_Init(interp); sl@0: SqlitetestThread_Init(interp); sl@0: SqlitetestOnefile_Init(interp); sl@0: SqlitetestOsinst_Init(interp); sl@0: sl@0: #ifdef SQLITE_SSE sl@0: Sqlitetestsse_Init(interp); sl@0: #endif sl@0: } sl@0: #endif sl@0: if( argc>=2 || TCLSH==2 ){ sl@0: int i; sl@0: char zArgc[32]; sl@0: sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); sl@0: Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); sl@0: Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); sl@0: Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); sl@0: for(i=3-TCLSH; iresult; sl@0: sprintf(errMsg, "###TclSqlite3: Error, argv=%s, zInfo=%s", *argv, zInfo); sl@0: PrintS(errMsg); sl@0: #ifdef __SYMBIAN32__ sl@0: ChildProcessCleanup(isChildProcess, oldArgc, argv); sl@0: #endif sl@0: PrintS("###TclSqlite3: Exit-2"); sl@0: return 1; sl@0: } sl@0: PrintS("###TclSqlite3: Tests end"); sl@0: } sl@0: if( argc<=1 || TCLSH==2 ){ sl@0: Tcl_GlobalEval(interp, zMainloop); sl@0: } sl@0: #if defined(__SYMBIAN32__) sl@0: ChildProcessCleanup(isChildProcess, oldArgc, argv); sl@0: #endif sl@0: PrintS("###TclSqlite3: Exit-3"); sl@0: return 0; sl@0: } sl@0: #endif /* TCLSH */