/trunk/testfixture/src/tclsqlite_c.cs |
@@ -0,0 +1,4427 @@ |
using System; |
using System.Diagnostics; |
using sqlite_int64 = System.Int64; |
using sqlite_u3264 = System.UInt64; |
using u32 = System.UInt32; |
|
namespace Community.CsharpSqlite |
{ |
#if TCLSH |
using tcl.lang; |
using ClientData = System.Object; |
|
#if !SQLITE_OMIT_INCRBLOB |
using sqlite3_blob = sqlite.Incrblob; |
#endif |
using sqlite3_stmt = Sqlite3.Vdbe; |
using Tcl_DString = tcl.lang.TclString; |
using Tcl_Interp = tcl.lang.Interp; |
using Tcl_Obj = tcl.lang.TclObject; |
using Tcl_WideInt = System.Int64; |
|
using sqlite3_value = Sqlite3.Mem; |
using System.IO; |
using System.Text; |
|
public partial class Sqlite3 |
{ |
/* |
** 2001 September 15 |
** |
** The author disclaims copyright to this source code. In place of |
** a legal notice, here is a blessing: |
** |
** May you do good and not evil. |
** May you find forgiveness for yourself and forgive others. |
** May you share freely, never taking more than you give. |
** |
************************************************************************* |
** A TCL Interface to SQLite. Append this file to sqlite3.c and |
** compile the whole thing to build a TCL-enabled version of SQLite. |
** |
** Compile-time options: |
** |
** -DTCLSH=1 Add a "main()" routine that works as a tclsh. |
** |
** -DSQLITE_TCLMD5 When used in conjuction with -DTCLSH=1, add |
** four new commands to the TCL interpreter for |
** generating MD5 checksums: md5, md5file, |
** md5-10x8, and md5file-10x8. |
** |
** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add |
** hundreds of new commands used for testing |
** SQLite. This option implies -DSQLITE_TCLMD5. |
************************************************************************* |
** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
** C#-SQLite is an independent reimplementation of the SQLite software library |
** |
** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 |
** |
************************************************************************* |
*/ |
//#include "tcl.h" |
//#include <errno.h> |
|
/* |
** Some additional include files are needed if this file is not |
** appended to the amalgamation. |
*/ |
#if !SQLITE_AMALGAMATION |
//# include "sqlite3.h" |
//# include <stdlib.h> |
//# include <string.h> |
//# include <Debug.Assert.h> |
// typedef unsigned char u8; |
#endif |
|
/* |
* Windows needs to know which symbols to export. Unix does not. |
* BUILD_sqlite should be undefined for Unix. |
*/ |
#if BUILD_sqlite |
//#undef TCL.Tcl_STORAGE_CLASS |
//#define TCL.Tcl_STORAGE_CLASS DLLEXPORT |
#endif // * BUILD_sqlite */ |
|
const int NUM_PREPARED_STMTS = 10;//#define NUM_PREPARED_STMTS 10 |
const int MAX_PREPARED_STMTS = 100;//#define MAX_PREPARED_STMTS 100 |
|
/* |
** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we |
** have to do a translation when going between the two. Set the |
** UTF_TRANSLATION_NEEDED macro to indicate that we need to do |
** this translation. |
*/ |
#if Tcl_UTF_MAX && !SQLITE_UTF8 |
//# define UTF_TRANSLATION_NEEDED 1 |
#endif |
|
/* |
** New SQL functions can be created as TCL scripts. Each such function |
** is described by an instance of the following structure. |
*/ |
//typedef struct SqlFunc SqlFunc; |
public class SqlFunc |
{ |
public Tcl_Interp interp; /* The TCL interpret to execute the function */ |
public Tcl_Obj pScript; /* The Tcl_Obj representation of the script */ |
public int useEvalObjv; /* True if it is safe to use TCL.Tcl_EvalObjv */ |
public string zName; /* Name of this function */ |
public SqlFunc pNext; /* Next function on the list of them all */ |
} |
|
/* |
** New collation sequences function can be created as TCL scripts. Each such |
** function is described by an instance of the following structure. |
*/ |
//typedef struct SqlCollate SqlCollate; |
public class SqlCollate |
{ |
public Tcl_Interp interp; /* The TCL interpret to execute the function */ |
public string zScript; /* The script to be run */ |
public SqlCollate pNext; /* Next function on the list of them all */ |
} |
|
/* |
** Prepared statements are cached for faster execution. Each prepared |
** statement is described by an instance of the following structure. |
*/ |
//typedef struct SqlPreparedStmt SqlPreparedStmt; |
public class SqlPreparedStmt |
{ |
public SqlPreparedStmt pNext; /* Next in linked list */ |
public SqlPreparedStmt pPrev; /* Previous on the list */ |
public sqlite3_stmt pStmt; /* The prepared statement */ |
public Mem[] aMem; /* Original Memory Values to be reused */ |
public int nSql; /* chars in zSql[] */ |
public string zSql; /* Text of the SQL statement */ |
public int nParm; /* Size of apParm array */ |
public Tcl_Obj[] apParm; /* Array of referenced object pointers */ |
} |
|
//typedef struct IncrblobChannel IncrblobChannel; |
|
/* |
** There is one instance of this structure for each SQLite database |
** that has been opened by the SQLite TCL interface. |
*/ |
//typedef struct SqliteDb SqliteDb; |
public class SqliteDb : object |
{ |
public sqlite3 db; /* The "real" database structure. MUST BE FIRST */ |
public Tcl_Interp interp; /* The interpreter used for this database */ |
public string zBusy; /* The busy callback routine */ |
public string zCommit; /* The commit hook callback routine */ |
public string zTrace; /* The trace callback routine */ |
public string zProfile; /* The profile callback routine */ |
public string zProgress; /* The progress callback routine */ |
public string zAuth; /* The authorization callback routine */ |
public int disableAuth; /* Disable the authorizer if it exists */ |
public string zNull = ""; /* Text to substitute for an SQL NULL value */ |
public SqlFunc pFunc; /* List of SQL functions */ |
public Tcl_Obj pUpdateHook; /* Update hook script (if any) */ |
public Tcl_Obj pRollbackHook; /* Rollback hook script (if any) */ |
public Tcl_Obj pWalHook; /* WAL hook script (if any) */ |
public Tcl_Obj pUnlockNotify; /* Unlock notify script (if any) */ |
public SqlCollate pCollate; /* List of SQL collation functions */ |
public int rc; /* Return code of most recent sqlite3_exec() */ |
public Tcl_Obj pCollateNeeded; /* Collation needed script */ |
public SqlPreparedStmt stmtList; /* List of prepared statements*/ |
public SqlPreparedStmt stmtLast; /* Last statement in the list */ |
public int maxStmt; /* The next maximum number of stmtList */ |
public int nStmt; /* Number of statements in stmtList */ |
#if !SQLITE_OMIT_INCRBLOB |
public IncrblobChannel pIncrblob; /* Linked list of open incrblob channels */ |
#endif |
public int nStep, nSort, nIndex; /* Statistics for most recent operation */ |
public int nTransaction; /* Number of nested [transaction] methods */ |
} |
|
#if !SQLITE_OMIT_INCRBLOB |
class IncrblobChannel |
{ |
public sqlite3_blob pBlob; /* sqlite3 blob handle */ |
public SqliteDb pDb; /* Associated database connection */ |
public int iSeek; /* Current seek offset */ |
public Tcl_Channel channel; /* Channel identifier */ |
public IncrblobChannel pNext; /* Linked list of all open incrblob channels */ |
public IncrblobChannel pPrev; /* Linked list of all open incrblob channels */ |
} |
#endif |
|
|
/* |
** Compute a string length that is limited to what can be stored in |
** lower 30 bits of a 32-bit signed integer. |
*/ |
static int strlen30( StringBuilder z ) |
{ |
//string z2 = z; |
//while( *z2 ){ z2++; } |
return 0x3fffffff & z.Length; |
} |
|
static int strlen30( string z ) |
{ |
//string z2 = z; |
//while( *z2 ){ z2++; } |
return 0x3fffffff & z.Length; |
} |
|
|
#if !SQLITE_OMIT_INCRBLOB |
/* |
** Close all incrblob channels opened using database connection pDb. |
** This is called when shutting down the database connection. |
*/ |
static void closeIncrblobChannels( SqliteDb pDb ) |
{ |
IncrblobChannel p; |
IncrblobChannel pNext; |
|
for ( p = pDb.pIncrblob ; p != null ; p = pNext ) |
{ |
pNext = p.pNext; |
|
/* Note: Calling unregister here call TCL.Tcl_Close on the incrblob channel, |
** which deletes the IncrblobChannel structure at p. So do not |
** call TCL.Tcl_Free() here. |
*/ |
TCL.Tcl_UnregisterChannel( pDb.interp, p.channel ); |
} |
} |
|
/* |
** Close an incremental blob channel. |
*/ |
//static int incrblobClose(object instanceData, Tcl_Interp interp){ |
// IncrblobChannel p = (IncrblobChannel )instanceData; |
// int rc = sqlite3_blob_close(p.pBlob); |
// sqlite3 db = p.pDb.db; |
|
// /* Remove the channel from the SqliteDb.pIncrblob list. */ |
// if( p.pNext ){ |
// p.pNext.pPrev = p.pPrev; |
// } |
// if( p.pPrev ){ |
// p.pPrev.pNext = p.pNext; |
// } |
// if( p.pDb.pIncrblob==p ){ |
// p.pDb.pIncrblob = p.pNext; |
// } |
|
// /* Free the IncrblobChannel structure */ |
// TCL.Tcl_Free((char )p); |
|
// if( rc!=SQLITE_OK ){ |
// TCL.Tcl_SetResult(interp, (char )sqlite3_errmsg(db), TCL.Tcl_VOLATILE); |
// return TCL.TCL_ERROR; |
// } |
// return TCL.TCL_OK; |
//} |
|
/* |
** Read data from an incremental blob channel. |
*/ |
//static int incrblobInput( |
// object instanceData, |
// char *buf, |
// int bufSize, |
// int *errorCodePtr |
//){ |
// IncrblobChannel p = (IncrblobChannel )instanceData; |
// int nRead = bufSize; /* Number of bytes to read */ |
// int nBlob; /* Total size of the blob */ |
// int rc; /* sqlite error code */ |
|
// nBlob = sqlite3_blob_bytes(p.pBlob); |
// if( (p.iSeek+nRead)>nBlob ){ |
// nRead = nBlob-p.iSeek; |
// } |
// if( nRead<=0 ){ |
// return 0; |
// } |
|
// rc = sqlite3_blob_read(p.pBlob, (void )buf, nRead, p.iSeek); |
// if( rc!=SQLITE_OK ){ |
// *errorCodePtr = rc; |
// return -1; |
// } |
|
// p.iSeek += nRead; |
// return nRead; |
//} |
|
/* |
** Write data to an incremental blob channel. |
*/ |
//static int incrblobOutput( |
// object instanceData, |
// string buf, |
// int toWrite, |
// int *errorCodePtr |
//){ |
// IncrblobChannel p = (IncrblobChannel )instanceData; |
// int nWrite = toWrite; /* Number of bytes to write */ |
// int nBlob; /* Total size of the blob */ |
// int rc; /* sqlite error code */ |
|
// nBlob = sqlite3_blob_bytes(p.pBlob); |
// if( (p.iSeek+nWrite)>nBlob ){ |
// *errorCodePtr = EINVAL; |
// return -1; |
// } |
// if( nWrite<=0 ){ |
// return 0; |
// } |
|
// rc = sqlite3_blob_write(p.pBlob, (void )buf, nWrite, p.iSeek); |
// if( rc!=SQLITE_OK ){ |
// *errorCodePtr = EIO; |
// return -1; |
// } |
|
// p.iSeek += nWrite; |
// return nWrite; |
//} |
|
/* |
** Seek an incremental blob channel. |
*/ |
//static int incrblobSeek( |
// object instanceData, |
// long offset, |
// int seekMode, |
// int *errorCodePtr |
//){ |
// IncrblobChannel p = (IncrblobChannel )instanceData; |
|
// switch( seekMode ){ |
// case SEEK_SET: |
// p.iSeek = offset; |
// break; |
// case SEEK_CUR: |
// p.iSeek += offset; |
// break; |
// case SEEK_END: |
// p.iSeek = sqlite3_blob_bytes(p.pBlob) + offset; |
// break; |
|
// default: Debug.Assert(!"Bad seekMode"); |
// } |
|
// return p.iSeek; |
//} |
|
|
//static void incrblobWatch(object instanceData, int mode){ |
// /* NO-OP */ |
//} |
//static int incrblobHandle(object instanceData, int dir, object *hPtr){ |
// return TCL.TCL_ERROR; |
//} |
|
static TCL.Tcl_ChannelType IncrblobChannelType = { |
"incrblob", /* typeName */ |
TCL.Tcl_CHANNEL_VERSION_2, /* version */ |
incrblobClose, /* closeProc */ |
incrblobInput, /* inputProc */ |
incrblobOutput, /* outputProc */ |
incrblobSeek, /* seekProc */ |
0, /* setOptionProc */ |
0, /* getOptionProc */ |
incrblobWatch, /* watchProc (this is a no-op) */ |
incrblobHandle, /* getHandleProc (always returns error) */ |
0, /* close2Proc */ |
0, /* blockModeProc */ |
0, /* flushProc */ |
0, /* handlerProc */ |
0, /* wideSeekProc */ |
}; |
|
/* |
** Create a new incrblob channel. |
*/ |
static int count = 0; |
static int createIncrblobChannel( |
Tcl_Interp interp, |
SqliteDb pDb, |
string zDb, |
string zTable, |
string zColumn, |
sqlite_int64 iRow, |
int isReadonly |
){ |
IncrblobChannel p; |
sqlite3 db = pDb.db; |
sqlite3_blob pBlob; |
int rc; |
int flags = TCL.Tcl_READABLE|(isReadonly ? 0 : TCL.Tcl_WRITABLE); |
|
/* This variable is used to name the channels: "incrblob_[incr count]" */ |
//static int count = 0; |
string zChannel = "";//string[64]; |
|
rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, pBlob); |
if( rc!=SQLITE_OK ){ |
TCL.Tcl_SetResult(interp, sqlite3_errmsg(pDb.db), TCL.Tcl_VOLATILE); |
return TCL.TCL_ERROR; |
} |
|
p = new IncrblobChannel();//(IncrblobChannel )Tcl_Alloc(sizeof(IncrblobChannel)); |
p.iSeek = 0; |
p.pBlob = pBlob; |
|
sqlite3_snprintf(64, zChannel, "incrblob_%d", ++count); |
p.channel = TCL.Tcl_CreateChannel(IncrblobChannelType, zChannel, p, flags); |
TCL.Tcl_RegisterChannel(interp, p.channel); |
|
/* Link the new channel into the SqliteDb.pIncrblob list. */ |
p.pNext = pDb.pIncrblob; |
p.pPrev = null; |
if( p.pNext!=null ){ |
p.pNext.pPrev = p; |
} |
pDb.pIncrblob = p; |
p.pDb = pDb; |
|
TCL.Tcl_SetResult(interp, Tcl_GetChannelName(p.channel), TCL.Tcl_VOLATILE); |
return TCL.TCL_OK; |
} |
#else // * else clause for "#if !SQLITE_OMIT_INCRBLOB" */ |
//#define closeIncrblobChannels(pDb) |
static void closeIncrblobChannels( SqliteDb pDb ) |
{ |
} |
#endif |
|
/* |
** Look at the script prefix in pCmd. We will be executing this script |
** after first appending one or more arguments. This routine analyzes |
** the script to see if it is safe to use TCL.Tcl_EvalObjv() on the script |
** rather than the more general TCL.Tcl_EvalEx(). TCL.Tcl_EvalObjv() is much |
** faster. |
** |
** Scripts that are safe to use with TCL.Tcl_EvalObjv() consists of a |
** command name followed by zero or more arguments with no [...] or $ |
** or {...} or ; to be seen anywhere. Most callback scripts consist |
** of just a single procedure name and they meet this requirement. |
*/ |
static int safeToUseEvalObjv( Tcl_Interp interp, Tcl_Obj pCmd ) |
{ |
/* We could try to do something with TCL.Tcl_Parse(). But we will instead |
** just do a search for forbidden characters. If any of the forbidden |
** characters appear in pCmd, we will report the string as unsafe. |
*/ |
string z; |
int n = 0; |
z = TCL.Tcl_GetStringFromObj( pCmd, out n ); |
while ( n-- > 0 ) |
{ |
int c = z[n];// *( z++ ); |
if ( c == '$' || c == '[' || c == ';' ) |
return 0; |
} |
return 1; |
} |
|
/* |
** Find an SqlFunc structure with the given name. Or create a new |
** one if an existing one cannot be found. Return a pointer to the |
** structure. |
*/ |
static SqlFunc findSqlFunc( SqliteDb pDb, string zName ) |
{ |
SqlFunc p, pNew; |
int i; |
pNew = new SqlFunc();//(SqlFunc)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 ); |
//pNew.zName = (char)&pNew[1]; |
//for(i=0; zName[i]; i++){ pNew.zName[i] = tolower(zName[i]); } |
//pNew.zName[i] = 0; |
pNew.zName = zName.ToLower(); |
for ( p = pDb.pFunc; p != null; p = p.pNext ) |
{ |
if ( p.zName == pNew.zName ) |
{ |
//Tcl_Free((char)pNew); |
return p; |
} |
} |
pNew.interp = pDb.interp; |
pNew.pScript = null; |
pNew.pNext = pDb.pFunc; |
pDb.pFunc = pNew; |
return pNew; |
} |
|
/* |
** Finalize and free a list of prepared statements |
*/ |
static void flushStmtCache( SqliteDb pDb ) |
{ |
SqlPreparedStmt pPreStmt; |
|
while ( pDb.stmtList != null ) |
{ |
sqlite3_finalize( pDb.stmtList.pStmt ); |
pPreStmt = pDb.stmtList; |
pDb.stmtList = pDb.stmtList.pNext; |
TCL.Tcl_Free( ref pPreStmt ); |
} |
pDb.nStmt = 0; |
pDb.stmtLast = null; |
} |
|
/* |
** TCL calls this procedure when an sqlite3 database command is |
** deleted. |
*/ |
static void DbDeleteCmd( ref object db ) |
{ |
SqliteDb pDb = (SqliteDb)db; |
flushStmtCache( pDb ); |
closeIncrblobChannels( pDb ); |
sqlite3_close( pDb.db ); |
while ( pDb.pFunc != null ) |
{ |
SqlFunc pFunc = pDb.pFunc; |
pDb.pFunc = pFunc.pNext; |
TCL.Tcl_DecrRefCount( ref pFunc.pScript ); |
TCL.Tcl_Free( ref pFunc ); |
} |
while ( pDb.pCollate != null ) |
{ |
SqlCollate pCollate = pDb.pCollate; |
pDb.pCollate = pCollate.pNext; |
TCL.Tcl_Free( ref pCollate ); |
} |
if ( pDb.zBusy != null ) |
{ |
TCL.Tcl_Free( ref pDb.zBusy ); |
} |
if ( pDb.zTrace != null ) |
{ |
TCL.Tcl_Free( ref pDb.zTrace ); |
} |
if ( pDb.zProfile != null ) |
{ |
TCL.Tcl_Free( ref pDb.zProfile ); |
} |
if ( pDb.zAuth != null ) |
{ |
TCL.Tcl_Free( ref pDb.zAuth ); |
} |
if ( pDb.zNull != null ) |
{ |
TCL.Tcl_Free( ref pDb.zNull ); |
} |
if ( pDb.pUpdateHook != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pDb.pUpdateHook ); |
} |
if ( pDb.pRollbackHook != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pDb.pRollbackHook ); |
} |
if ( pDb.pWalHook != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pDb.pWalHook ); |
} |
if ( pDb.pCollateNeeded != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pDb.pCollateNeeded ); |
} |
TCL.Tcl_Free( ref pDb ); |
} |
|
/* |
** This routine is called when a database file is locked while trying |
** to execute SQL. |
*/ |
static int DbBusyHandler( object cd, int nTries ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
int rc; |
StringBuilder zVal = new StringBuilder( 30 );//char zVal[30]; |
|
sqlite3_snprintf( 30, zVal, "%d", nTries ); |
rc = TCL.Tcl_VarEval( pDb.interp, pDb.zBusy, " ", zVal.ToString(), null ); |
if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
{ |
return 0; |
} |
return 1; |
} |
|
#if !SQLITE_OMIT_PROGRESS_CALLBACK |
/* |
** This routine is invoked as the 'progress callback' for the database. |
*/ |
static int DbProgressHandler( object cd ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
int rc; |
|
Debug.Assert( pDb.zProgress != null ); |
rc = TCL.Tcl_Eval( pDb.interp, pDb.zProgress ); |
if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
{ |
return 1; |
} |
return 0; |
} |
#endif |
|
#if !SQLITE_OMIT_TRACE |
/* |
** This routine is called by the SQLite trace handler whenever a new |
** block of SQL is executed. The TCL script in pDb.zTrace is executed. |
*/ |
static void DbTraceHandler( object cd, string zSql ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
TclObject str = null; |
|
TCL.Tcl_DStringInit( out str ); |
TCL.Tcl_DStringAppendElement( str, pDb.zTrace ); |
TCL.Tcl_DStringAppendElement( str, " {" + zSql + "}" ); |
TCL.Tcl_EvalObjEx( pDb.interp, str, 0 );// TCL.Tcl_Eval( pDb.interp, TCL.Tcl_DStringValue( ref str ) ); |
TCL.Tcl_DStringFree( ref str ); |
TCL.Tcl_ResetResult( pDb.interp ); |
} |
#endif |
|
#if !SQLITE_OMIT_TRACE |
/* |
** This routine is called by the SQLite profile handler after a statement |
** SQL has executed. The TCL script in pDb.zProfile is evaluated. |
*/ |
static void DbProfileHandler( object cd, string zSql, sqlite_int64 tm ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
TclObject str = null; |
StringBuilder zTm = new StringBuilder( 100 );//char zTm[100]; |
|
sqlite3_snprintf( 100, zTm, "%lld", tm ); |
TCL.Tcl_DStringInit( out str ); |
TCL.Tcl_DStringAppendElement( str, pDb.zProfile ); |
TCL.Tcl_DStringAppendElement( str, " {" + zSql + "}" ); |
TCL.Tcl_DStringAppendElement( str, " {" + zTm.ToString() + "}" ); |
TCL.Tcl_Eval( pDb.interp, str.ToString() ); |
TCL.Tcl_DStringFree( ref str ); |
TCL.Tcl_ResetResult( pDb.interp ); |
} |
#endif |
|
/* |
** This routine is called when a transaction is committed. The |
** TCL script in pDb.zCommit is executed. If it returns non-zero or |
** if it throws an exception, the transaction is rolled back instead |
** of being committed. |
*/ |
static int DbCommitHandler( object cd ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
int rc; |
|
rc = TCL.Tcl_Eval( pDb.interp, pDb.zCommit ); |
if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
{ |
return 1; |
} |
return 0; |
} |
|
static void DbRollbackHandler( object _object ) |
{ |
SqliteDb pDb = (SqliteDb)_object; |
Debug.Assert( pDb.pRollbackHook != null ); |
if ( TCL.TCL_OK != TCL.Tcl_EvalObjEx( pDb.interp, pDb.pRollbackHook, 0 ) ) |
{ |
TCL.Tcl_BackgroundError( pDb.interp ); |
} |
} |
|
/* |
** This procedure handles wal_hook callbacks. |
*/ |
static int DbWalHandler( |
object clientData, |
sqlite3 db, |
string zDb, |
int nEntry |
) |
{ |
int ret = SQLITE_OK; |
Tcl_Obj p; |
SqliteDb pDb = (SqliteDb)clientData; |
Tcl_Interp interp = pDb.interp; |
Debug.Assert( pDb.pWalHook != null ); |
|
p = TCL.Tcl_DuplicateObj( pDb.pWalHook ); |
TCL.Tcl_IncrRefCount( p ); |
TCL.Tcl_ListObjAppendElement( interp, p, TCL.Tcl_NewStringObj( zDb, -1 ) ); |
TCL.Tcl_ListObjAppendElement( interp, p, TCL.Tcl_NewIntObj( nEntry ) ); |
if ( TCL.TCL_OK != TCL.Tcl_EvalObjEx( interp, p, 0 ) |
|| TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, TCL.Tcl_GetObjResult( interp ), out ret ) |
) |
{ |
TCL.Tcl_BackgroundError( interp ); |
} |
TCL.Tcl_DecrRefCount( ref p ); |
|
return ret; |
} |
|
#if (SQLITE_TEST) && (SQLITE_ENABLE_UNLOCK_NOTIFY) |
static void setTestUnlockNotifyVars(Tcl_Interp interp, int iArg, int nArg){ |
char zBuf[64]; |
sprintf(zBuf, "%d", iArg); |
Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); |
sprintf(zBuf, "%d", nArg); |
Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); |
} |
#else |
//# define setTestUnlockNotifyVars(x,y,z) |
#endif |
|
#if SQLITE_ENABLE_UNLOCK_NOTIFY |
static void DbUnlockNotify(void **apArg, int nArg){ |
int i; |
for(i=0; i<nArg; i++){ |
const int flags = (TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); |
SqliteDb *pDb = (SqliteDb )apArg[i]; |
setTestUnlockNotifyVars(pDb.interp, i, nArg); |
Debug.Assert( pDb.pUnlockNotify); |
Tcl_EvalObjEx(pDb.interp, pDb.pUnlockNotify, flags); |
Tcl_DecrRefCount(pDb.pUnlockNotify); |
pDb.pUnlockNotify = 0; |
} |
} |
#endif |
|
static void DbUpdateHandler( |
object p, |
int op, |
string zDb, |
string zTbl, |
sqlite_int64 rowid |
) |
{ |
SqliteDb pDb = (SqliteDb)p; |
Tcl_Obj pCmd; |
|
Debug.Assert( pDb.pUpdateHook != null ); |
Debug.Assert( op == SQLITE_INSERT || op == SQLITE_UPDATE || op == SQLITE_DELETE ); |
|
pCmd = TCL.Tcl_DuplicateObj( pDb.pUpdateHook ); |
TCL.Tcl_IncrRefCount( pCmd ); |
TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( |
( ( op == SQLITE_INSERT ) ? "INSERT" : ( op == SQLITE_UPDATE ) ? "UPDATE" : "DELETE" ), -1 ) ); |
TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( zDb, -1 ) ); |
TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( zTbl, -1 ) ); |
TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewWideIntObj( rowid ) ); |
TCL.Tcl_EvalObjEx( pDb.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
TCL.Tcl_DecrRefCount( ref pCmd ); |
} |
|
static void tclCollateNeeded( |
object pCtx, |
sqlite3 db, |
int enc, |
string zName |
) |
{ |
SqliteDb pDb = (SqliteDb)pCtx; |
Tcl_Obj pScript = TCL.Tcl_DuplicateObj( pDb.pCollateNeeded ); |
TCL.Tcl_IncrRefCount( pScript ); |
TCL.Tcl_ListObjAppendElement( null, pScript, TCL.Tcl_NewStringObj( zName, -1 ) ); |
TCL.Tcl_EvalObjEx( pDb.interp, pScript, 0 ); |
TCL.Tcl_DecrRefCount( ref pScript ); |
} |
|
/* |
** This routine is called to evaluate an SQL collation function implemented |
** using TCL script. |
*/ |
static int tclSqlCollate( |
object pCtx, |
int nA, |
string zA, |
int nB, |
string zB |
) |
{ |
SqlCollate p = (SqlCollate)pCtx; |
Tcl_Obj pCmd; |
|
pCmd = TCL.Tcl_NewStringObj( p.zScript, -1 ); |
TCL.Tcl_IncrRefCount( pCmd ); |
TCL.Tcl_ListObjAppendElement( p.interp, pCmd, TCL.Tcl_NewStringObj( zA, nA ) ); |
TCL.Tcl_ListObjAppendElement( p.interp, pCmd, TCL.Tcl_NewStringObj( zB, nB ) ); |
TCL.Tcl_EvalObjEx( p.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
TCL.Tcl_DecrRefCount( ref pCmd ); |
return ( atoi( TCL.Tcl_GetStringResult( p.interp ) ) ); |
} |
|
/* |
** This routine is called to evaluate an SQL function implemented |
** using TCL script. |
*/ |
static void tclSqlFunc( sqlite3_context context, int argc, sqlite3_value[] argv ) |
{ |
SqlFunc p = (SqlFunc)sqlite3_user_data( context ); |
Tcl_Obj pCmd = null; |
int i; |
int rc; |
|
if ( argc == 0 ) |
{ |
/* If there are no arguments to the function, call TCL.Tcl_EvalObjEx on the |
** script object directly. This allows the TCL compiler to generate |
** bytecode for the command on the first invocation and thus make |
** subsequent invocations much faster. */ |
pCmd = p.pScript; |
TCL.Tcl_IncrRefCount( pCmd ); |
rc = TCL.Tcl_EvalObjEx( p.interp, pCmd, 0 ); |
TCL.Tcl_DecrRefCount( ref pCmd ); |
} |
else |
{ |
/* If there are arguments to the function, make a shallow copy of the |
** script object, lappend the arguments, then evaluate the copy. |
** |
** By "shallow" copy, we mean a only the outer list Tcl_Obj is duplicated. |
** The new Tcl_Obj contains pointers to the original list elements. |
** That way, when TCL.Tcl_EvalObjv() is run and shimmers the first element |
** of the list to tclCmdNameType, that alternate representation will |
** be preserved and reused on the next invocation. |
*/ |
Tcl_Obj[] aArg = null; |
int nArg = 0; |
if ( TCL.Tcl_ListObjGetElements( p.interp, p.pScript, out nArg, out aArg ) ) |
{ |
sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
return; |
} |
pCmd = TCL.Tcl_NewListObj( nArg, aArg ); |
TCL.Tcl_IncrRefCount( pCmd ); |
for ( i = 0; i < argc; i++ ) |
{ |
sqlite3_value pIn = argv[i]; |
Tcl_Obj pVal; |
|
/* Set pVal to contain the i'th column of this row. */ |
switch ( sqlite3_value_type( pIn ) ) |
{ |
case SQLITE_BLOB: |
{ |
int bytes = sqlite3_value_bytes( pIn ); |
pVal = TCL.Tcl_NewByteArrayObj( sqlite3_value_blob( pIn ), bytes ); |
break; |
} |
case SQLITE_INTEGER: |
{ |
sqlite_int64 v = sqlite3_value_int64( pIn ); |
if ( v >= -2147483647 && v <= 2147483647 ) |
{ |
pVal = TCL.Tcl_NewIntObj( (int)v ); |
} |
else |
{ |
pVal = TCL.Tcl_NewWideIntObj( v ); |
} |
break; |
} |
case SQLITE_FLOAT: |
{ |
double r = sqlite3_value_double( pIn ); |
pVal = TCL.Tcl_NewDoubleObj( r ); |
break; |
} |
case SQLITE_NULL: |
{ |
pVal = TCL.Tcl_NewStringObj( "", 0 ); |
break; |
} |
default: |
{ |
int bytes = sqlite3_value_bytes( pIn ); |
pVal = TCL.Tcl_NewStringObj( sqlite3_value_text( pIn ), bytes ); |
break; |
} |
} |
rc = TCL.Tcl_ListObjAppendElement( p.interp, pCmd, pVal ) ? 1 : 0; |
if ( rc != 0 ) |
{ |
TCL.Tcl_DecrRefCount( ref pCmd ); |
sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
return; |
} |
} |
if ( p.useEvalObjv == 0 ) |
{ |
/* TCL.Tcl_EvalObjEx() will automatically call TCL.Tcl_EvalObjv() if pCmd |
** is a list without a string representation. To prevent this from |
** happening, make sure pCmd has a valid string representation */ |
TCL.Tcl_GetString( pCmd ); |
} |
rc = TCL.Tcl_EvalObjEx( p.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
TCL.Tcl_DecrRefCount( ref pCmd ); |
} |
|
if ( rc != 0 && rc != TCL.TCL_RETURN ) |
{ |
sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
} |
else |
{ |
Tcl_Obj pVar = TCL.Tcl_GetObjResult( p.interp ); |
int n = 0; |
string data = ""; |
Tcl_WideInt v = 0; |
double r = 0; |
string zType = pVar.typePtr;//string zType = (pVar.typePtr ? pVar.typePtr.name : ""); |
if ( zType == "bytearray" ) |
{ //&& pVar.bytes==0 ){ |
/* Only return a BLOB type if the Tcl variable is a bytearray and |
** has no string representation. */ |
data = Encoding.UTF8.GetString( TCL.Tcl_GetByteArrayFromObj( pVar, out n ) ); |
sqlite3_result_blob( context, data, n, null ); |
} |
else if ( zType == "boolean" ) |
{ |
TCL.Tcl_GetIntFromObj( null, pVar, out n ); |
sqlite3_result_int( context, n ); |
} |
else if ( zType == "wideint" || |
zType == "int" || Int64.TryParse( pVar.ToString(), out v ) ) |
{ |
TCL.Tcl_GetWideIntFromObj( null, pVar, out v ); |
sqlite3_result_int64( context, v ); |
} |
else if ( zType == "double" || Double.TryParse( pVar.ToString(), out r ) ) |
{ |
TCL.Tcl_GetDoubleFromObj( null, pVar, out r ); |
sqlite3_result_double( context, r ); |
} |
else |
{ |
data = TCL.Tcl_GetStringFromObj( pVar, n ); |
n = data.Length; |
sqlite3_result_text( context, data, n, SQLITE_TRANSIENT ); |
} |
} |
} |
|
#if !SQLITE_OMIT_AUTHORIZATION |
/* |
** This is the authentication function. It appends the authentication |
** type code and the two arguments to zCmd[] then invokes the result |
** on the interpreter. The reply is examined to determine if the |
** authentication fails or succeeds. |
*/ |
static int auth_callback( |
object pArg, |
int code, |
const string zArg1, |
const string zArg2, |
const string zArg3, |
const string zArg4 |
){ |
string zCode; |
TCL.Tcl_DString str; |
int rc; |
const string zReply; |
SqliteDb pDb = (SqliteDb)pArg; |
if( pdb.disableAuth ) return SQLITE_OK; |
|
switch( code ){ |
case SQLITE_COPY : zCode="SQLITE_COPY"; break; |
case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; |
case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; |
case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; |
case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; |
case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; |
case SQLITE_CREATE_TEMP_VIEW : zCode="SQLITE_CREATE_TEMP_VIEW"; break; |
case SQLITE_CREATE_TRIGGER : zCode="SQLITE_CREATE_TRIGGER"; break; |
case SQLITE_CREATE_VIEW : zCode="SQLITE_CREATE_VIEW"; break; |
case SQLITE_DELETE : zCode="SQLITE_DELETE"; break; |
case SQLITE_DROP_INDEX : zCode="SQLITE_DROP_INDEX"; break; |
case SQLITE_DROP_TABLE : zCode="SQLITE_DROP_TABLE"; break; |
case SQLITE_DROP_TEMP_INDEX : zCode="SQLITE_DROP_TEMP_INDEX"; break; |
case SQLITE_DROP_TEMP_TABLE : zCode="SQLITE_DROP_TEMP_TABLE"; break; |
case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break; |
case SQLITE_DROP_TEMP_VIEW : zCode="SQLITE_DROP_TEMP_VIEW"; break; |
case SQLITE_DROP_TRIGGER : zCode="SQLITE_DROP_TRIGGER"; break; |
case SQLITE_DROP_VIEW : zCode="SQLITE_DROP_VIEW"; break; |
case SQLITE_INSERT : zCode="SQLITE_INSERT"; break; |
case SQLITE_PRAGMA : zCode="SQLITE_PRAGMA"; break; |
case SQLITE_READ : zCode="SQLITE_READ"; break; |
case SQLITE_SELECT : zCode="SQLITE_SELECT"; break; |
case SQLITE_TRANSACTION : zCode="SQLITE_TRANSACTION"; break; |
case SQLITE_UPDATE : zCode="SQLITE_UPDATE"; break; |
case SQLITE_ATTACH : zCode="SQLITE_ATTACH"; break; |
case SQLITE_DETACH : zCode="SQLITE_DETACH"; break; |
case SQLITE_ALTER_TABLE : zCode="SQLITE_ALTER_TABLE"; break; |
case SQLITE_REINDEX : zCode="SQLITE_REINDEX"; break; |
case SQLITE_ANALYZE : zCode="SQLITE_ANALYZE"; break; |
case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break; |
case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break; |
case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break; |
case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break; |
default : zCode="????"; break; |
} |
TCL.Tcl_DStringInit(&str); |
TCL.Tcl_DStringAppend(&str, pDb.zAuth, -1); |
TCL.Tcl_DStringAppendElement(&str, zCode); |
TCL.Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : ""); |
TCL.Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : ""); |
TCL.Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : ""); |
TCL.Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); |
rc = TCL.Tcl_GlobalEval(pDb.interp, TCL.Tcl_DStringValue(&str)); |
TCL.Tcl_DStringFree(&str); |
zReply = TCL.Tcl_GetStringResult(pDb.interp); |
if( strcmp(zReply,"SQLITE_OK")==0 ){ |
rc = SQLITE_OK; |
}else if( strcmp(zReply,"SQLITE_DENY")==0 ){ |
rc = SQLITE_DENY; |
}else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){ |
rc = SQLITE_IGNORE; |
}else{ |
rc = 999; |
} |
return rc; |
} |
#endif // * SQLITE_OMIT_AUTHORIZATION */ |
|
/* |
** zText is a pointer to text obtained via an sqlite3_result_text() |
** or similar interface. This routine returns a Tcl string object, |
** reference count set to 0, containing the text. If a translation |
** between iso8859 and UTF-8 is required, it is preformed. |
*/ |
static Tcl_Obj dbTextToObj( string zText ) |
{ |
Tcl_Obj pVal; |
#if UTF_TRANSLATION_NEEDED |
//TCL.Tcl_DString dCol; |
//TCL.Tcl_DStringInit(&dCol); |
//TCL.Tcl_ExternalToUtfDString(NULL, zText, -1, dCol); |
//pVal = TCL.Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); |
//TCL.Tcl_DStringFree(ref dCol); |
if (zText.Length == Encoding.UTF8.GetByteCount(zText)) pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
else pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
#else |
pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
#endif |
return pVal; |
} |
|
/* |
** This routine reads a line of text from FILE in, stores |
** the text in memory obtained from malloc() and returns a pointer |
** to the text. NULL is returned at end of file, or if malloc() |
** fails. |
** |
** The interface is like "readline" but no command-line editing |
** is done. |
** |
** copied from shell.c from '.import' command |
*/ |
//static char *local_getline(string zPrompt, FILE *in){ |
// string zLine; |
// int nLine; |
// int n; |
// int eol; |
|
// nLine = 100; |
// zLine = malloc( nLine ); |
// if( zLine==0 ) return 0; |
// n = 0; |
// eol = 0; |
// while( !eol ){ |
// if( n+100>nLine ){ |
// nLine = nLine*2 + 100; |
// zLine = realloc(zLine, nLine); |
// if( zLine==0 ) return 0; |
// } |
// if( fgets(&zLine[n], nLine - n, in)==0 ){ |
// if( n==0 ){ |
// free(zLine); |
// return 0; |
// } |
// zLine[n] = 0; |
// eol = 1; |
// break; |
// } |
// while( zLine[n] ){ n++; } |
// if( n>0 && zLine[n-1]=='\n' ){ |
// n--; |
// zLine[n] = 0; |
// eol = 1; |
// } |
// } |
// zLine = realloc( zLine, n+1 ); |
// return zLine; |
//} |
|
|
/* |
** This function is part of the implementation of the command: |
** |
** $db transaction [-deferred|-immediate|-exclusive] SCRIPT |
** |
** It is invoked after evaluating the script SCRIPT to commit or rollback |
** the transaction or savepoint opened by the [transaction] command. |
*/ |
static int DbTransPostCmd( |
object data, /* data[0] is the Sqlite3Db* for $db */ |
Tcl_Interp interp, /* Tcl interpreter */ |
int result /* Result of evaluating SCRIPT */ |
) |
{ |
string[] azEnd = { |
"RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */ |
"COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */ |
"ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction", |
"ROLLBACK" /* rc==TCL_ERROR, nTransaction==0 */ |
}; |
SqliteDb pDb = (SqliteDb)data; |
int rc = result; |
string zEnd; |
|
pDb.nTransaction--; |
zEnd = azEnd[( ( rc == TCL.TCL_ERROR ) ? 1 : 0 ) * 2 + ( ( pDb.nTransaction == 0 ) ? 1 : 0 )]; |
|
pDb.disableAuth++; |
if ( sqlite3_exec( pDb.db, zEnd, 0, 0, 0 ) != 0 ) |
{ |
/* This is a tricky scenario to handle. The most likely cause of an |
** error is that the exec() above was an attempt to commit the |
** top-level transaction that returned SQLITE_BUSY. Or, less likely, |
** that an IO-error has occured. In either case, throw a Tcl exception |
** and try to rollback the transaction. |
** |
** But it could also be that the user executed one or more BEGIN, |
** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing |
** this method's logic. Not clear how this would be best handled. |
*/ |
if ( rc != TCL.TCL_ERROR ) |
{ |
TCL.Tcl_AppendResult( interp, sqlite3_errmsg( pDb.db ), 0 ); |
rc = TCL.TCL_ERROR; |
} |
sqlite3_exec( pDb.db, "ROLLBACK", 0, 0, 0 ); |
} |
pDb.disableAuth--; |
|
return rc; |
} |
|
/* |
** Search the cache for a prepared-statement object that implements the |
** first SQL statement in the buffer pointed to by parameter zIn. If |
** no such prepared-statement can be found, allocate and prepare a new |
** one. In either case, bind the current values of the relevant Tcl |
** variables to any $var, :var or @var variables in the statement. Before |
** returning, set *ppPreStmt to point to the prepared-statement object. |
** |
** Output parameter *pzOut is set to point to the next SQL statement in |
** buffer zIn, or to the '\0' byte at the end of zIn if there is no |
** next statement. |
** |
** If successful, TCL_OK is returned. Otherwise, TCL_ERROR is returned |
** and an error message loaded into interpreter pDb.interp. |
*/ |
static int dbPrepareAndBind( |
SqliteDb pDb, /* Database object */ |
string zIn, /* SQL to compile */ |
ref string pzOut, /* OUT: Pointer to next SQL statement */ |
ref SqlPreparedStmt ppPreStmt /* OUT: Object used to cache statement */ |
) |
{ |
string zSql = zIn; /* Pointer to first SQL statement in zIn */ |
sqlite3_stmt pStmt = null; /* Prepared statement object */ |
SqlPreparedStmt pPreStmt; /* Pointer to cached statement */ |
int nSql; /* Length of zSql in bytes */ |
int nVar = 0; /* Number of variables in statement */ |
int iParm = 0; /* Next free entry in apParm */ |
int i; |
Tcl_Interp interp = pDb.interp; |
|
pzOut = null; |
ppPreStmt = null; |
|
/* Trim spaces from the start of zSql and calculate the remaining length. */ |
zSql = zSql.TrimStart(); //while ( isspace( zSql[0] ) ) { zSql++; } |
nSql = strlen30( zSql ); |
|
for ( pPreStmt = pDb.stmtList; pPreStmt != null; pPreStmt = pPreStmt.pNext ) |
{ |
int n = pPreStmt.nSql; |
if ( nSql >= n |
&& zSql.StartsWith(pPreStmt.zSql) |
&& ( nSql == n /* zSql[n]==0 */|| zSql[n - 1] == ';' ) |
) |
{ |
pStmt = pPreStmt.pStmt; |
/* Restore aMem values */ |
if ( pStmt.aMem.Length < pPreStmt.aMem.Length ) |
Array.Resize( ref pStmt.aMem, pPreStmt.aMem.Length ); |
for ( int ix = 0; ix < pPreStmt.aMem.Length; ix++ ) |
{ |
pPreStmt.aMem[ix].CopyTo( ref pStmt.aMem[ix] ); |
} |
|
pzOut = zSql.Substring( pPreStmt.nSql ); |
|
/* When a prepared statement is found, unlink it from the |
** cache list. It will later be added back to the beginning |
** of the cache list in order to implement LRU replacement. |
*/ |
if ( pPreStmt.pPrev != null ) |
{ |
pPreStmt.pPrev.pNext = pPreStmt.pNext; |
} |
else |
{ |
pDb.stmtList = pPreStmt.pNext; |
} |
if ( pPreStmt.pNext != null ) |
{ |
pPreStmt.pNext.pPrev = pPreStmt.pPrev; |
} |
else |
{ |
pDb.stmtLast = pPreStmt.pPrev; |
} |
pDb.nStmt--; |
nVar = sqlite3_bind_parameter_count( pStmt ); |
break; |
} |
} |
|
/* If no prepared statement was found. Compile the SQL text. Also allocate |
** a new SqlPreparedStmt structure. */ |
if ( pPreStmt == null ) |
{ |
int nByte; |
|
if ( SQLITE_OK != sqlite3_prepare_v2( pDb.db, zSql, -1, ref pStmt, ref pzOut ) ) |
{ |
TCL.Tcl_SetObjResult( interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
pPreStmt = new SqlPreparedStmt();// (SqlPreparedStmt)Tcl_Alloc( nByte ); |
return TCL.TCL_ERROR; |
} |
if ( pStmt == null ) |
{ |
if ( SQLITE_OK != sqlite3_errcode( pDb.db ) ) |
{ |
/* A compile-time error in the statement. */ |
TCL.Tcl_SetObjResult( interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
/* The statement was a no-op. Continue to the next statement |
** in the SQL string. |
*/ |
return TCL.TCL_OK; |
} |
} |
|
Debug.Assert( pPreStmt == null ); |
nVar = sqlite3_bind_parameter_count( pStmt ); |
//nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj ); |
pPreStmt = new SqlPreparedStmt();// (SqlPreparedStmt)Tcl_Alloc( nByte ); |
//memset(pPreStmt, 0, nByte); |
|
pPreStmt.pStmt = pStmt; |
pPreStmt.nSql = ( zSql.Length - pzOut.Length ); |
pPreStmt.zSql = sqlite3_sql( pStmt ); |
pPreStmt.apParm = new TclObject[nVar];//pPreStmt[1]; |
} |
Debug.Assert( pPreStmt != null ); |
Debug.Assert( strlen30( pPreStmt.zSql ) == pPreStmt.nSql ); |
Debug.Assert( zSql.StartsWith( pPreStmt.zSql ) ); |
|
/* Bind values to parameters that begin with $ or : */ |
for ( i = 1; i <= nVar; i++ ) |
{ |
string zVar = sqlite3_bind_parameter_name( pStmt, i ); |
if ( !String.IsNullOrEmpty( zVar ) && ( zVar[0] == '$' || zVar[0] == ':' || zVar[0] == '@' ) ) |
{ |
Tcl_Obj pVar = TCL.Tcl_GetVar2Ex( interp, zVar.Substring( 1 ), null, 0 ); |
if ( pVar != null && pVar.typePtr != "null" ) |
{ |
int n = 0; |
string data; |
string zType = pVar.typePtr; |
//char c = zType[0]; |
if ( zVar[0] == '@' || |
( zType == "bytearray" ) )// TODO -- && pVar.bytes == 0 ) ) |
{ |
/* Load a BLOB type if the Tcl variable is a bytearray and |
** it has no string representation or the host |
** parameter name begins with "@". */ |
if ( zVar[0] == '@' || pVar.stringRep == null ) |
sqlite3_bind_blob( pStmt, i, TCL.Tcl_GetByteArrayFromObj( pVar, out n ), n, SQLITE_STATIC ); |
else |
sqlite3_bind_text( pStmt, i, TCL.Tcl_GetStringFromObj( pVar, out n ), n, SQLITE_STATIC ); |
TCL.Tcl_IncrRefCount( pVar ); |
pPreStmt.apParm[iParm++] = pVar; |
} |
else if ( zType == "boolean" ) |
{ |
TCL.Tcl_GetIntFromObj( interp, pVar, out n ); |
sqlite3_bind_int( pStmt, i, n ); |
} |
else if ( zType == "double" ) |
{ |
double r = 0; |
TCL.Tcl_GetDoubleFromObj( interp, pVar, out r ); |
sqlite3_bind_double( pStmt, i, r ); |
} |
else if ( zType == "wideint" || |
zType == "int" ) |
{ |
Tcl_WideInt v = 0; |
TCL.Tcl_GetWideIntFromObj( interp, pVar, out v ); |
sqlite3_bind_int64( pStmt, i, v ); |
} |
else |
{ |
data = TCL.Tcl_GetStringFromObj( pVar, out n ); |
sqlite3_bind_text( pStmt, i, data, n, SQLITE_STATIC ); |
TCL.Tcl_IncrRefCount( pVar ); |
pPreStmt.apParm[iParm++] = pVar; |
} |
} |
else |
{ |
sqlite3_bind_null( pStmt, i ); |
} |
} |
} |
pPreStmt.nParm = iParm; |
/* save aMem values for later reuse */ |
pPreStmt.aMem = new Mem[pPreStmt.pStmt.aMem.Length]; |
for ( int ix = 0; ix < pPreStmt.pStmt.aMem.Length; ix++ ) |
{ |
pPreStmt.pStmt.aMem[ix].CopyTo( ref pPreStmt.aMem[ix] ); |
} |
ppPreStmt = pPreStmt; |
|
return TCL.TCL_OK; |
} |
|
|
/* |
** Release a statement reference obtained by calling dbPrepareAndBind(). |
** There should be exactly one call to this function for each call to |
** dbPrepareAndBind(). |
** |
** If the discard parameter is non-zero, then the statement is deleted |
** immediately. Otherwise it is added to the LRU list and may be returned |
** by a subsequent call to dbPrepareAndBind(). |
*/ |
static void dbReleaseStmt( |
SqliteDb pDb, /* Database handle */ |
SqlPreparedStmt pPreStmt, /* Prepared statement handle to release */ |
int discard /* True to delete (not cache) the pPreStmt */ |
) |
{ |
int i; |
|
/* Free the bound string and blob parameters */ |
for ( i = 0; i < pPreStmt.nParm; i++ ) |
{ |
TCL.Tcl_DecrRefCount( ref pPreStmt.apParm[i] ); |
} |
pPreStmt.nParm = 0; |
|
if ( pDb.maxStmt <= 0 || discard != 0 ) |
{ |
/* If the cache is turned off, deallocated the statement */ |
sqlite3_finalize( pPreStmt.pStmt ); |
TCL.Tcl_Free( ref pPreStmt ); |
} |
else |
{ |
/* Add the prepared statement to the beginning of the cache list. */ |
pPreStmt.pNext = pDb.stmtList; |
pPreStmt.pPrev = null; |
if ( pDb.stmtList != null ) |
{ |
pDb.stmtList.pPrev = pPreStmt; |
} |
pDb.stmtList = pPreStmt; |
if ( pDb.stmtLast == null ) |
{ |
Debug.Assert( pDb.nStmt == 0 ); |
pDb.stmtLast = pPreStmt; |
} |
else |
{ |
Debug.Assert( pDb.nStmt > 0 ); |
} |
pDb.nStmt++; |
|
/* If we have too many statement in cache, remove the surplus from |
** the end of the cache list. */ |
while ( pDb.nStmt > pDb.maxStmt ) |
{ |
sqlite3_finalize( pDb.stmtLast.pStmt ); |
pDb.stmtLast = pDb.stmtLast.pPrev; |
TCL.Tcl_Free( ref pDb.stmtLast.pNext ); |
pDb.stmtLast.pNext = null; |
pDb.nStmt--; |
} |
} |
} |
|
/* |
** Structure used with dbEvalXXX() functions: |
** |
** dbEvalInit() |
** dbEvalStep() |
** dbEvalFinalize() |
** dbEvalRowInfo() |
** dbEvalColumnValue() |
*/ |
//typedef struct DbEvalContext DbEvalContext; |
public class DbEvalContext |
{ |
public SqliteDb pDb; /* Database handle */ |
public Tcl_Obj pSql; /* Object holding string zSql */ |
public string zSql; /* Remaining SQL to execute */ |
public SqlPreparedStmt pPreStmt; /* Current statement */ |
public int nCol; /* Number of columns returned by pStmt */ |
public Tcl_Obj pArray; /* Name of array variable */ |
public Tcl_Obj[] apColName; /* Array of column names */ |
|
public void Clear() |
{ |
pDb = null; |
pSql = null; |
zSql = null; |
pPreStmt = null; |
pArray = null; |
apColName = null; |
|
} |
}; |
|
/* |
** Release any cache of column names currently held as part of |
** the DbEvalContext structure passed as the first argument. |
*/ |
static void dbReleaseColumnNames( DbEvalContext p ) |
{ |
if ( p.apColName != null ) |
{ |
int i; |
for ( i = 0; i < p.nCol; i++ ) |
{ |
TCL.Tcl_DecrRefCount( ref p.apColName[i] ); |
} |
TCL.Tcl_Free( ref p.apColName ); |
p.apColName = null; |
} |
p.nCol = 0; |
} |
|
/* |
** Initialize a DbEvalContext structure. |
** |
** If pArray is not NULL, then it contains the name of a Tcl array |
** variable. The "*" member of this array is set to a list containing |
** the names of the columns returned by the statement as part of each |
** call to dbEvalStep(), in order from left to right. e.g. if the names |
** of the returned columns are a, b and c, it does the equivalent of the |
** tcl command: |
** |
** set ${pArray}() {a b c} |
*/ |
static void dbEvalInit( |
DbEvalContext p, /* Pointer to structure to initialize */ |
SqliteDb pDb, /* Database handle */ |
Tcl_Obj pSql, /* Object containing SQL script */ |
Tcl_Obj pArray /* Name of Tcl array to set () element of */ |
) |
{ |
if ( p != null ) |
p.Clear();// memset( p, 0, sizeof( DbEvalContext ) ); |
p.pDb = pDb; |
p.zSql = TCL.Tcl_GetString( pSql ); |
p.pSql = pSql; |
TCL.Tcl_IncrRefCount( pSql ); |
if ( pArray != null ) |
{ |
p.pArray = pArray; |
TCL.Tcl_IncrRefCount( pArray ); |
} |
} |
|
/* |
** Obtain information about the row that the DbEvalContext passed as the |
** first argument currently points to. |
*/ |
static void dbEvalRowInfo( |
DbEvalContext p, /* Evaluation context */ |
out int pnCol, /* OUT: Number of column names */ |
out Tcl_Obj[] papColName /* OUT: Array of column names */ |
) |
{ |
/* Compute column names */ |
if ( null == p.apColName ) |
{ |
sqlite3_stmt pStmt = p.pPreStmt.pStmt; |
int i; /* Iterator variable */ |
int nCol; /* Number of columns returned by pStmt */ |
Tcl_Obj[] apColName = null; /* Array of column names */ |
|
p.nCol = nCol = sqlite3_column_count( pStmt ); |
if ( nCol > 0 )// && ( papColName != null || p.pArray != null ) ) |
{ |
apColName = new TclObject[nCol];// (Tcl_Obj*)Tcl_Alloc( sizeof( Tcl_Obj* ) * nCol ); |
for ( i = 0; i < nCol; i++ ) |
{ |
apColName[i] = dbTextToObj( sqlite3_column_name( pStmt, i ) ); |
TCL.Tcl_IncrRefCount( apColName[i] ); |
} |
p.apColName = apColName; |
} |
|
/* If results are being stored in an array variable, then create |
** the array() entry for that array |
*/ |
if ( p.pArray != null ) |
{ |
Tcl_Interp interp = p.pDb.interp; |
Tcl_Obj pColList = TCL.Tcl_NewObj(); |
Tcl_Obj pStar = TCL.Tcl_NewStringObj( "*", -1 ); |
|
for ( i = 0; i < nCol; i++ ) |
{ |
TCL.Tcl_ListObjAppendElement( interp, pColList, apColName[i] ); |
} |
TCL.Tcl_IncrRefCount( pStar ); |
TCL.Tcl_ObjSetVar2( interp, p.pArray, pStar, pColList, 0 ); |
TCL.Tcl_DecrRefCount( ref pStar ); |
} |
} |
|
//if ( papColName != null ) |
{ |
papColName = p.apColName; |
} |
//if ( pnCol !=0) |
{ |
pnCol = p.nCol; |
} |
} |
|
/* |
** Return one of TCL_OK, TCL_BREAK or TCL_ERROR. If TCL_ERROR is |
** returned, then an error message is stored in the interpreter before |
** returning. |
** |
** A return value of TCL_OK means there is a row of data available. The |
** data may be accessed using dbEvalRowInfo() and dbEvalColumnValue(). This |
** is analogous to a return of SQLITE_ROW from sqlite3_step(). If TCL_BREAK |
** is returned, then the SQL script has finished executing and there are |
** no further rows available. This is similar to SQLITE_DONE. |
*/ |
static int dbEvalStep( DbEvalContext p ) |
{ |
while ( !String.IsNullOrEmpty( p.zSql ) || p.pPreStmt != null ) |
{ |
int rc; |
if ( p.pPreStmt == null ) |
{ |
rc = dbPrepareAndBind( p.pDb, p.zSql, ref p.zSql, ref p.pPreStmt ); |
if ( rc != TCL.TCL_OK ) |
return rc; |
} |
else |
{ |
int rcs; |
SqliteDb pDb = p.pDb; |
SqlPreparedStmt pPreStmt = p.pPreStmt; |
sqlite3_stmt pStmt = pPreStmt.pStmt; |
|
rcs = sqlite3_step( pStmt ); |
if ( rcs == SQLITE_ROW ) |
{ |
return TCL.TCL_OK; |
} |
if ( p.pArray != null ) |
{ |
TclObject[] pDummy; |
int iDummy; |
dbEvalRowInfo( p, out iDummy, out pDummy ); |
} |
rcs = sqlite3_reset( pStmt ); |
|
pDb.nStep = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, 1 ); |
pDb.nSort = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_SORT, 1 ); |
pDb.nIndex = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_AUTOINDEX, 1 ); |
dbReleaseColumnNames( p ); |
p.pPreStmt = null; |
|
if ( rcs != SQLITE_OK ) |
{ |
/* If a run-time error occurs, report the error and stop reading |
** the SQL. */ |
TCL.Tcl_SetObjResult( pDb.interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
dbReleaseStmt( pDb, pPreStmt, 1 ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
dbReleaseStmt( pDb, pPreStmt, 0 ); |
} |
} |
} |
|
/* Finished */ |
return TCL.TCL_BREAK; |
} |
|
/* |
** Free all resources currently held by the DbEvalContext structure passed |
** as the first argument. There should be exactly one call to this function |
** for each call to dbEvalInit(). |
*/ |
static void dbEvalFinalize( DbEvalContext p ) |
{ |
if ( p.pPreStmt != null ) |
{ |
sqlite3_reset( p.pPreStmt.pStmt ); |
dbReleaseStmt( p.pDb, p.pPreStmt, 1 ); |
p.pPreStmt = null; |
} |
if ( p.pArray != null ) |
{ |
TCL.Tcl_DecrRefCount( ref p.pArray ); |
p.pArray = null; |
} |
TCL.Tcl_DecrRefCount( ref p.pSql ); |
dbReleaseColumnNames( p ); |
} |
|
/* |
** Return a pointer to a Tcl_Obj structure with ref-count 0 that contains |
** the value for the iCol'th column of the row currently pointed to by |
** the DbEvalContext structure passed as the first argument. |
*/ |
static Tcl_Obj dbEvalColumnValue( DbEvalContext p, int iCol ) |
{ |
sqlite3_stmt pStmt = p.pPreStmt.pStmt; |
switch ( sqlite3_column_type( pStmt, iCol ) ) |
{ |
case SQLITE_BLOB: |
{ |
int bytes = sqlite3_column_bytes( pStmt, iCol ); |
byte[] zBlob = sqlite3_column_blob( pStmt, iCol ); |
if ( null == zBlob ) |
bytes = 0; |
return TCL.Tcl_NewByteArrayObj( zBlob, bytes ); |
} |
case SQLITE_INTEGER: |
{ |
sqlite_int64 v = sqlite3_column_int64( pStmt, iCol ); |
if ( v >= -2147483647 && v <= 2147483647 ) |
{ |
return TCL.Tcl_NewIntObj( (int)v ); |
} |
else |
{ |
return TCL.Tcl_NewWideIntObj( v ); |
} |
} |
case SQLITE_FLOAT: |
{ |
return TCL.Tcl_NewDoubleObj( sqlite3_column_double( pStmt, iCol ) ); |
} |
case SQLITE_NULL: |
{ |
return dbTextToObj( p.pDb.zNull ); |
} |
} |
|
return dbTextToObj( sqlite3_column_text( pStmt, 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] |
** 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. |
*/ |
#if TCL_MAJOR_VERSION//>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) |
//# define SQLITE_TCL_NRE 1 |
static int DbUseNre(void){ |
int major, minor; |
Tcl_GetVersion(&major, &minor, 0, 0); |
return( (major==8 && minor>=6) || major>8 ); |
} |
#else |
/* |
** Compiling using headers earlier than 8.6. In this case NR cannot be |
** used, so DbUseNre() to always return zero. Add #defines for the other |
** Tcl_NRxxx() functions to prevent them from causing compilation errors, |
** even though the only invocations of them are within conditional blocks |
** of the form: |
** |
** if( DbUseNre() ) { ... } |
*/ |
const int SQLITE_TCL_NRE = 0; //# define SQLITE_TCL_NRE 0 |
static bool DbUseNre() |
{ |
return false; |
} //# define DbUseNre() 0 |
//# define Tcl_NRAddCallback(a,b,c,d,e,f) 0 |
//# define Tcl_NREvalObj(a,b,c) 0 |
//# define Tcl_NRCreateCommand(a,b,c,d,e,f) 0 |
#endif |
|
/* |
** This function is part of the implementation of the command: |
** |
** $db eval SQL ?ARRAYNAME? SCRIPT |
*/ |
static int DbEvalNextCmd( |
object[] data, /* data[0] is the (DbEvalContext) */ |
Tcl_Interp interp, /* Tcl interpreter */ |
int result /* Result so far */ |
) |
{ |
int rc = result; /* Return code */ |
|
/* The first element of the data[] array is a pointer to a DbEvalContext |
** structure allocated using TCL.Tcl_Alloc(). The second element of data[] |
** is a pointer to a TCL.Tcl_Obj containing the script to run for each row |
** returned by the queries encapsulated in data[0]. */ |
DbEvalContext p = (DbEvalContext)data[0]; |
Tcl_Obj pScript = (Tcl_Obj)data[1]; |
Tcl_Obj pArray = p.pArray; |
|
while ( ( rc == TCL.TCL_OK || rc == TCL.TCL_CONTINUE ) && TCL.TCL_OK == ( rc = dbEvalStep( p ) ) ) |
{ |
int i; |
int nCol; |
Tcl_Obj[] apColName; |
dbEvalRowInfo( p, out nCol, out apColName ); |
for ( i = 0; i < nCol; i++ ) |
{ |
Tcl_Obj pVal = dbEvalColumnValue( p, i ); |
if ( pArray == null ) |
{ |
TCL.Tcl_ObjSetVar2( interp, apColName[i], null, pVal, 0 ); |
} |
else |
{ |
TCL.Tcl_ObjSetVar2( interp, pArray, apColName[i], pVal, 0 ); |
} |
} |
|
/* The required interpreter variables are now populated with the data |
** from the current row. If using NRE, schedule callbacks to evaluate |
** script pScript, then to invoke this function again to fetch the next |
** row (or clean up if there is no next row or the script throws an |
** exception). After scheduling the callbacks, return control to the |
** caller. |
** |
** If not using NRE, evaluate pScript directly and continue with the |
** next iteration of this while(...) loop. */ |
if ( DbUseNre() ) |
{ |
Debugger.Break(); |
//TCL.Tcl_NRAddCallback(interp, DbEvalNextCmd, (void)p, (void)pScript, 0, 0); |
//return TCL.Tcl_NREvalObj(interp, pScript, 0); |
} |
else |
{ |
rc = TCL.Tcl_EvalObjEx( interp, pScript, 0 ); |
} |
} |
|
TCL.Tcl_DecrRefCount( ref pScript ); |
dbEvalFinalize( p ); |
TCL.Tcl_Free( ref p ); |
|
if ( rc == TCL.TCL_OK || rc == TCL.TCL_BREAK ) |
{ |
TCL.Tcl_ResetResult( interp ); |
rc = TCL.TCL_OK; |
} |
return rc; |
} |
|
/* |
** The "sqlite" command below creates a new Tcl command for each |
** connection it opens to an SQLite database. This routine is invoked |
** whenever one of those connection-specific commands is executed |
** in Tcl. For example, if you run Tcl code like this: |
** |
** sqlite3 db1 "my_database" |
** db1 close |
** |
** The first command opens a connection to the "my_database" database |
** and calls that connection "db1". The second command causes this |
** subroutine to be invoked. |
*/ |
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_ENABLE_LOAD_EXTENSION, |
DB_ERRORCODE, |
DB_EVAL, |
DB_EXISTS, |
DB_FUNCTION, |
DB_INCRBLOB, |
DB_INTERRUPT, |
DB_LAST_INSERT_ROWID, |
DB_NULLVALUE, |
DB_ONECOLUMN, |
DB_PROFILE, |
DB_PROGRESS, |
DB_REKEY, |
DB_RESTORE, |
DB_ROLLBACK_HOOK, |
DB_STATUS, |
DB_TIMEOUT, |
DB_TOTAL_CHANGES, |
DB_TRACE, |
DB_TRANSACTION, |
DB_UNLOCK_NOTIFY, |
DB_UPDATE_HOOK, |
DB_VERSION, |
DB_WAL_HOOK |
}; |
|
enum TTYPE_enum |
{ |
TTYPE_DEFERRED, |
TTYPE_EXCLUSIVE, |
TTYPE_IMMEDIATE |
}; |
|
static int DbObjCmd( object cd, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) |
{ |
SqliteDb pDb = (SqliteDb)cd; |
int choice = 0; |
int rc = TCL.TCL_OK; |
string[] DB_strs = { |
"authorizer", "backup", "busy", |
"cache", "changes", "close", |
"collate", "collation_needed", "commit_hook", |
"complete", "copy", "enable_load_extension", |
"errorcode", "eval", "exists", |
"function", "incrblob", "interrupt", |
"last_insert_rowid", "nullvalue", "onecolumn", |
"profile", "progress", "rekey", |
"restore", "rollback_hook", "status", |
"timeout", "total_changes", "trace", |
"transaction", "unlock_notify", "update_hook", |
"version", "wal_hook" |
}; |
|
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ |
if ( objc < 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 1, objv, "SUBCOMMAND ..." ); |
return TCL.TCL_ERROR; |
} |
if ( TCL.Tcl_GetIndexFromObj( interp, objv[1], DB_strs, "option", 0, out choice ) ) |
{ |
return TCL.TCL_ERROR; |
} |
|
switch ( choice ) |
{ |
|
/* $db authorizer ?CALLBACK? |
** |
** Invoke the given callback to authorize each SQL operation as it is |
** compiled. 5 arguments are appended to the callback before it is |
** invoked: |
** |
** (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...) |
** (2) First descriptive name (depends on authorization type) |
** (3) Second descriptive name |
** (4) Name of the database (ex: "main", "temp") |
** (5) Name of trigger that is doing the access |
** |
** The callback should return on of the following strings: SQLITE_OK, |
** SQLITE_IGNORE, or SQLITE_DENY. Any other return value is an error. |
** |
** If this method is invoked with no arguments, the current authorization |
** callback string is returned. |
*/ |
case (int)DB_enum.DB_AUTHORIZER: |
{ |
#if SQLITE_OMIT_AUTHORIZATION |
TCL.Tcl_AppendResult( interp, "authorization not available in this build" ); |
return TCL.TCL_RETURN; |
#else |
if( objc>3 ){ |
TCL.Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); |
return TCL.TCL_ERROR; |
}else if( objc==2 ){ |
if( pDb.zAuth ){ |
TCL.Tcl_AppendResult(interp, pDb.zAuth); |
} |
}else{ |
string zAuth; |
int len; |
if( pDb.zAuth ){ |
TCL.Tcl_Free(pDb.zAuth); |
} |
zAuth = TCL.Tcl_GetStringFromObj(objv[2], len); |
if( zAuth && len>0 ){ |
pDb.zAuth = TCL.Tcl_Alloc( len + 1 ); |
memcpy(pDb.zAuth, zAuth, len+1); |
}else{ |
pDb.zAuth = 0; |
} |
if( pDb.zAuth ){ |
pDb.interp = interp; |
sqlite3_set_authorizer(pDb.db, auth_callback, pDb); |
}else{ |
sqlite3_set_authorizer(pDb.db, 0, 0); |
} |
} |
break; |
#endif |
} |
|
/* $db backup ?DATABASE? FILENAME |
** |
** Open or create a database file named FILENAME. Transfer the |
** content of local database DATABASE (default: "main") into the |
** FILENAME database. |
*/ |
case (int)DB_enum.DB_BACKUP: |
{ |
string zDestFile; |
string zSrcDb; |
sqlite3 pDest = null; |
sqlite3_backup pBackup; |
|
if ( objc == 3 ) |
{ |
zSrcDb = "main"; |
zDestFile = TCL.Tcl_GetString( objv[2] ); |
} |
else if ( objc == 4 ) |
{ |
zSrcDb = TCL.Tcl_GetString( objv[2] ); |
zDestFile = TCL.Tcl_GetString( objv[3] ); |
} |
else |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?DATABASE? FILENAME" ); |
return TCL.TCL_ERROR; |
} |
rc = sqlite3_open( zDestFile, out pDest ); |
if ( rc != SQLITE_OK ) |
{ |
TCL.Tcl_AppendResult( interp, "cannot open target database: ", |
sqlite3_errmsg( pDest ) ); |
sqlite3_close( pDest ); |
return TCL.TCL_ERROR; |
} |
pBackup = sqlite3_backup_init( pDest, "main", pDb.db, zSrcDb ); |
if ( pBackup == null ) |
{ |
TCL.Tcl_AppendResult( interp, "backup failed: ", |
sqlite3_errmsg( pDest ) ); |
sqlite3_close( pDest ); |
return TCL.TCL_ERROR; |
} |
#if SQLITE_HAS_CODEC |
if ( pBackup.pSrc.pBt.pPager.pCodec != null ) |
{ |
pBackup.pDest.pBt.pPager.xCodec = pBackup.pSrc.pBt.pPager.xCodec; |
pBackup.pDest.pBt.pPager.xCodecFree = pBackup.pSrc.pBt.pPager.xCodecFree; |
pBackup.pDest.pBt.pPager.xCodecSizeChng = pBackup.pSrc.pBt.pPager.xCodecSizeChng; |
pBackup.pDest.pBt.pPager.pCodec = pBackup.pSrc.pBt.pPager.pCodec.Copy(); |
} |
#endif |
while ( ( rc = sqlite3_backup_step( pBackup, 100 ) ) == SQLITE_OK ) |
{ |
} |
sqlite3_backup_finish( pBackup ); |
if ( rc == SQLITE_DONE ) |
{ |
rc = TCL.TCL_OK; |
} |
else |
{ |
TCL.Tcl_AppendResult( interp, "backup failed: ", |
sqlite3_errmsg( pDest ) ); |
rc = TCL.TCL_ERROR; |
} |
sqlite3_close( pDest ); |
break; |
} |
|
// /* $db busy ?CALLBACK? |
// ** |
// ** Invoke the given callback if an SQL statement attempts to open |
// ** a locked database file. |
// */ |
case (int)DB_enum.DB_BUSY: |
{ |
if ( objc > 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "CALLBACK" ); |
return TCL.TCL_ERROR; |
} |
else if ( objc == 2 ) |
{ |
if ( pDb.zBusy != null ) |
{ |
TCL.Tcl_AppendResult( interp, pDb.zBusy ); |
} |
} |
else |
{ |
string zBusy; |
int len = 0; |
if ( pDb.zBusy != null ) |
{ |
TCL.Tcl_Free( ref pDb.zBusy ); |
} |
zBusy = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
if ( zBusy != null && len > 0 ) |
{ |
//pDb.zBusy = TCL.Tcl_Alloc( len + 1 ); |
pDb.zBusy = zBusy;// memcpy( pDb.zBusy, zBusy, len + 1 ); |
} |
else |
{ |
pDb.zBusy = null; |
} |
if ( pDb.zBusy != null ) |
{ |
pDb.interp = interp; |
sqlite3_busy_handler( pDb.db, (dxBusy)DbBusyHandler, pDb ); |
} |
else |
{ |
sqlite3_busy_handler( pDb.db, null, null ); |
} |
} |
break; |
} |
|
// /* $db cache flush |
// ** $db cache size n |
// ** |
// ** Flush the prepared statement cache, or set the maximum number of |
// ** cached statements. |
// */ |
case (int)DB_enum.DB_CACHE: |
{ |
string subCmd; |
int n = 0; |
|
if ( objc <= 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 1, objv, "cache option ?arg?" ); |
return TCL.TCL_ERROR; |
} |
subCmd = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
if ( subCmd == "flush" ) |
{ |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "flush" ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
flushStmtCache( pDb ); |
} |
} |
else if ( subCmd == "size" ) |
{ |
if ( objc != 4 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "size n" ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
if ( TCL.TCL_ERROR == ( TCL.Tcl_GetIntFromObj( interp, objv[3], out n ) != TCL.TCL_OK ? TCL.TCL_ERROR : TCL.TCL_OK ) ) |
{ |
TCL.Tcl_AppendResult( interp, "cannot convert \"", |
TCL.Tcl_GetStringFromObj( objv[3], 0 ), "\" to integer", 0 ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
if ( n < 0 ) |
{ |
flushStmtCache( pDb ); |
n = 0; |
} |
else if ( n > MAX_PREPARED_STMTS ) |
{ |
n = MAX_PREPARED_STMTS; |
} |
pDb.maxStmt = n; |
} |
} |
} |
else |
{ |
TCL.Tcl_AppendResult( interp, "bad option \"", |
TCL.Tcl_GetStringFromObj( objv[2], 0 ), "\": must be flush or size", null ); |
return TCL.TCL_ERROR; |
} |
break; |
} |
|
/* $db changes |
** |
** Return the number of rows that were modified, inserted, or deleted by |
** the most recent INSERT, UPDATE or DELETE statement, not including |
** any changes made by trigger programs. |
*/ |
case (int)DB_enum.DB_CHANGES: |
{ |
Tcl_Obj pResult; |
if ( objc != 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
return TCL.TCL_ERROR; |
} |
pResult = TCL.Tcl_GetObjResult( interp ); |
TCL.Tcl_SetResult( interp, sqlite3_changes( pDb.db ).ToString(), 0 ); |
break; |
} |
|
/* $db close |
** |
** Shutdown the database |
*/ |
case (int)DB_enum.DB_CLOSE: |
{ |
TCL.Tcl_DeleteCommand( interp, TCL.Tcl_GetStringFromObj( objv[0], 0 ) ); |
break; |
} |
|
/* |
** $db collate NAME SCRIPT |
** |
** Create a new SQL collation function called NAME. Whenever |
** that function is called, invoke SCRIPT to evaluate the function. |
*/ |
case (int)DB_enum.DB_COLLATE: |
{ |
SqlCollate pCollate; |
string zName; |
string zScript; |
int nScript = 0; |
if ( objc != 4 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "NAME SCRIPT" ); |
return TCL.TCL_ERROR; |
} |
zName = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
zScript = TCL.Tcl_GetStringFromObj( objv[3], nScript ); |
pCollate = new SqlCollate();//(SqlCollate)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 ); |
//if ( pCollate == null ) return TCL.TCL_ERROR; |
pCollate.interp = interp; |
pCollate.pNext = pDb.pCollate; |
pCollate.zScript = zScript; // pCollate[1]; |
pDb.pCollate = pCollate; |
//memcpy( pCollate.zScript, zScript, nScript + 1 ); |
if ( sqlite3_create_collation( pDb.db, zName, SQLITE_UTF8, |
pCollate, (dxCompare)tclSqlCollate ) != 0 ) |
{ |
TCL.Tcl_SetResult( interp, sqlite3_errmsg( pDb.db ), TCL.TCL_VOLATILE ); |
return TCL.TCL_ERROR; |
} |
break; |
} |
|
/* |
** $db collation_needed SCRIPT |
** |
** Create a new SQL collation function called NAME. Whenever |
** that function is called, invoke SCRIPT to evaluate the function. |
*/ |
case (int)DB_enum.DB_COLLATION_NEEDED: |
{ |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "SCRIPT" ); |
return TCL.TCL_ERROR; |
} |
if ( pDb.pCollateNeeded != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pDb.pCollateNeeded ); |
} |
pDb.pCollateNeeded = TCL.Tcl_DuplicateObj( objv[2] ); |
TCL.Tcl_IncrRefCount( pDb.pCollateNeeded ); |
sqlite3_collation_needed( pDb.db, (object)pDb, (dxCollNeeded)tclCollateNeeded ); |
break; |
} |
|
/* $db commit_hook ?CALLBACK? |
** |
** Invoke the given callback just before committing every SQL transaction. |
** If the callback throws an exception or returns non-zero, then the |
** transaction is aborted. If CALLBACK is an empty string, the callback |
** is disabled. |
*/ |
case (int)DB_enum.DB_COMMIT_HOOK: |
{ |
if ( objc > 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
return TCL.TCL_ERROR; |
} |
else if ( objc == 2 ) |
{ |
if ( pDb.zCommit != null ) |
{ |
TCL.Tcl_AppendResult( interp, pDb.zCommit ); |
} |
} |
else |
{ |
string zCommit; |
int len = 0; |
if ( pDb.zCommit != null ) |
{ |
TCL.Tcl_Free( ref pDb.zCommit ); |
} |
zCommit = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
if ( zCommit != null && len > 0 ) |
{ |
pDb.zCommit = zCommit;// TCL.Tcl_Alloc( len + 1 ); |
//memcpy( pDb.zCommit, zCommit, len + 1 ); |
} |
else |
{ |
pDb.zCommit = null; |
} |
if ( pDb.zCommit != null ) |
{ |
pDb.interp = interp; |
sqlite3_commit_hook( pDb.db, DbCommitHandler, pDb ); |
} |
else |
{ |
sqlite3_commit_hook( pDb.db, null, null ); |
} |
} |
break; |
} |
|
/* $db complete SQL |
** |
** Return TRUE if SQL is a complete SQL statement. Return FALSE if |
** additional lines of input are needed. This is similar to the |
** built-in "info complete" command of Tcl. |
*/ |
case (int)DB_enum.DB_COMPLETE: |
{ |
#if !SQLITE_OMIT_COMPLETE |
Tcl_Obj pResult; |
int isComplete; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL" ); |
return TCL.TCL_ERROR; |
} |
isComplete = sqlite3_complete( TCL.Tcl_GetStringFromObj( objv[2], 0 ) ); |
pResult = TCL.Tcl_GetObjResult( interp ); |
TCL.Tcl_SetBooleanObj( pResult, isComplete ); |
#endif |
break; |
} |
|
/* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? |
** |
** Copy data into table from filename, optionally using SEPARATOR |
** as column separators. If a column contains a null string, or the |
** value of NULLINDICATOR, a NULL is inserted for the column. |
** conflict-algorithm is one of the sqlite conflict algorithms: |
** rollback, abort, fail, ignore, replace |
** On success, return the number of lines processed, not necessarily same |
** as 'db changes' due to conflict-algorithm selected. |
** |
** This code is basically an implementation/enhancement of |
** the sqlite3 shell.c ".import" command. |
** |
** 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 |
*/ |
case (int)DB_enum.DB_COPY: |
{ |
string zTable; /* Insert data into this table */ |
string zFile; /* The file from which to extract data */ |
string zConflict; /* The conflict algorithm to use */ |
sqlite3_stmt pStmt = null; /* A statement */ |
int nCol; /* Number of columns in the table */ |
int nByte; /* Number of bytes in an SQL string */ |
int i, j; /* Loop counters */ |
int nSep; /* Number of bytes in zSep[] */ |
int nNull; /* Number of bytes in zNull[] */ |
StringBuilder zSql = new StringBuilder( 200 ); /* An SQL statement */ |
string zLine; /* A single line of input from the file */ |
string[] azCol; /* zLine[] broken up into columns */ |
string zCommit; /* How to commit changes */ |
TextReader _in; /* The input file */ |
int lineno = 0; /* Line number of input file */ |
StringBuilder zLineNum = new StringBuilder( 80 ); /* Line number print buffer */ |
Tcl_Obj pResult; /* interp result */ |
|
string zSep; |
string zNull; |
if ( objc < 5 || objc > 7 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, |
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?" ); |
return TCL.TCL_ERROR; |
} |
if ( objc >= 6 ) |
{ |
zSep = TCL.Tcl_GetStringFromObj( objv[5], 0 ); |
} |
else |
{ |
zSep = "\t"; |
} |
if ( objc >= 7 ) |
{ |
zNull = TCL.Tcl_GetStringFromObj( objv[6], 0 ); |
} |
else |
{ |
zNull = ""; |
} |
zConflict = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
zTable = TCL.Tcl_GetStringFromObj( objv[3], 0 ); |
zFile = TCL.Tcl_GetStringFromObj( objv[4], 0 ); |
nSep = strlen30( zSep ); |
nNull = strlen30( zNull ); |
if ( nSep == 0 ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: non-null separator required for copy" ); |
return TCL.TCL_ERROR; |
} |
if ( zConflict != "rollback" && |
zConflict != "abort" && |
zConflict != "fail" && |
zConflict != "ignore" && |
zConflict != "replace" ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: \"", zConflict, |
"\", conflict-algorithm must be one of: rollback, " + |
"abort, fail, ignore, or replace", 0 ); |
return TCL.TCL_ERROR; |
} |
zSql.Append( sqlite3_mprintf( "SELECT * FROM '%q'", zTable ) ); |
if ( zSql == null ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: no such table: ", zTable ); |
return TCL.TCL_ERROR; |
} |
nByte = strlen30( zSql ); |
rc = sqlite3_prepare( pDb.db, zSql.ToString(), -1, ref pStmt, 0 ); |
sqlite3DbFree( null, ref zSql ); |
if ( rc != 0 ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
nCol = 0; |
} |
else |
{ |
nCol = sqlite3_column_count( pStmt ); |
} |
sqlite3_finalize( pStmt ); |
if ( nCol == 0 ) |
{ |
return TCL.TCL_ERROR; |
} |
//zSql.Append(malloc( nByte + 50 + nCol*2 ); |
//if( zSql==0 ) { |
// TCL.Tcl_AppendResult(interp, "Error: can't malloc()"); |
// return TCL.TCL_ERROR; |
//} |
sqlite3_snprintf( nByte + 50, zSql, "INSERT OR %q INTO '%q' VALUES(?", |
zConflict, zTable ); |
j = strlen30( zSql ); |
for ( i = 1; i < nCol; i++ ) |
{ |
//zSql+=[j++] = ','; |
//zSql[j++] = '?'; |
zSql.Append( ",?" ); |
} |
//zSql[j++] = ')'; |
//zSql[j] = ""; |
zSql.Append( ")" ); |
rc = sqlite3_prepare( pDb.db, zSql.ToString(), -1, ref pStmt, 0 ); |
//free(zSql); |
if ( rc != 0 ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
sqlite3_finalize( pStmt ); |
return TCL.TCL_ERROR; |
} |
_in = new StreamReader( zFile );//fopen(zFile, "rb"); |
if ( _in == null ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: cannot open file: ", zFile ); |
sqlite3_finalize( pStmt ); |
return TCL.TCL_ERROR; |
} |
azCol = new string[nCol + 1];//malloc( sizeof(azCol[0])*(nCol+1) ); |
if ( azCol == null ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: can't malloc()" ); |
_in.Close();//fclose(_in); |
return TCL.TCL_ERROR; |
} |
sqlite3_exec( pDb.db, "BEGIN", 0, 0, 0 ); |
zCommit = "COMMIT"; |
while ( ( zLine = _in.ReadLine() ) != null )//local_getline(0, _in))!=0 ) |
{ |
string z; |
i = 0; |
lineno++; |
azCol = zLine.Split( zSep[0] ); |
//for(i=0, z=zLine; *z; z++){ |
// if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ |
// *z = 0; |
// i++; |
// if( i<nCol ){ |
// azCol[i] = z[nSep]; |
// z += nSep-1; |
// } |
// } |
//} |
if ( azCol.Length != nCol ) |
{ |
StringBuilder zErr = new StringBuilder( 200 ); |
int nErr = strlen30( zFile ) + 200; |
//zErr = malloc(nErr); |
//if( zErr ){ |
sqlite3_snprintf( nErr, zErr, |
"Error: %s line %d: expected %d columns of data but found %d", |
zFile, lineno, nCol, i + 1 ); |
TCL.Tcl_AppendResult( interp, zErr ); |
// free(zErr); |
//} |
zCommit = "ROLLBACK"; |
break; |
} |
for ( i = 0; i < nCol; i++ ) |
{ |
/* check for null data, if so, bind as null */ |
if ( ( nNull > 0 && azCol[i] == zNull ) |
|| strlen30( azCol[i] ) == 0 |
) |
{ |
sqlite3_bind_null( pStmt, i + 1 ); |
} |
else |
{ |
sqlite3_bind_text( pStmt, i + 1, azCol[i], -1, SQLITE_STATIC ); |
} |
} |
sqlite3_step( pStmt ); |
rc = sqlite3_reset( pStmt ); |
//free(zLine); |
if ( rc != SQLITE_OK ) |
{ |
TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
zCommit = "ROLLBACK"; |
break; |
} |
} |
//free(azCol); |
_in.Close();// fclose( _in ); |
sqlite3_finalize( pStmt ); |
sqlite3_exec( pDb.db, zCommit, 0, 0, 0 ); |
|
if ( zCommit[0] == 'C' ) |
{ |
/* success, set result as number of lines processed */ |
pResult = TCL.Tcl_GetObjResult( interp ); |
TCL.Tcl_SetIntObj( pResult, lineno ); |
rc = TCL.TCL_OK; |
} |
else |
{ |
/* failure, append lineno where failed */ |
sqlite3_snprintf( 80, zLineNum, "%d", lineno ); |
TCL.Tcl_AppendResult( interp, ", failed while processing line: ", zLineNum ); |
rc = TCL.TCL_ERROR; |
} |
break; |
} |
|
/* |
** $db enable_load_extension BOOLEAN |
** |
** Turn the extension loading feature on or off. It if off by |
** default. |
*/ |
case (int)DB_enum.DB_ENABLE_LOAD_EXTENSION: |
{ |
#if !SQLITE_OMIT_LOAD_EXTENSION |
bool onoff = false; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "BOOLEAN" ); |
return TCL.TCL_ERROR; |
} |
if ( TCL.Tcl_GetBooleanFromObj( interp, objv[2], out onoff ) ) |
{ |
return TCL.TCL_ERROR; |
} |
sqlite3_enable_load_extension( pDb.db, onoff ? 1 : 0 ); |
break; |
#else |
TCL.Tcl_AppendResult(interp, "extension loading is turned off at compile-time", |
0); |
return TCL.TCL_ERROR; |
#endif |
} |
|
/* |
** $db errorcode |
** |
** Return the numeric error code that was returned by the most recent |
** call to sqlite3_exec(). |
*/ |
case (int)DB_enum.DB_ERRORCODE: |
{ |
TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewIntObj( sqlite3_errcode( pDb.db ) ) ); |
break; |
} |
|
/* |
** $db exists $sql |
** $db onecolumn $sql |
** |
** The onecolumn method is the equivalent of: |
** lindex [$db eval $sql] 0 |
*/ |
case (int)DB_enum.DB_EXISTS: |
case (int)DB_enum.DB_ONECOLUMN: |
{ |
DbEvalContext sEval = new DbEvalContext(); |
; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL" ); |
return TCL.TCL_ERROR; |
} |
dbEvalInit( sEval, pDb, objv[2], null ); |
rc = dbEvalStep( sEval ); |
if ( choice == (int)DB_enum.DB_ONECOLUMN ) |
{ |
if ( rc == TCL.TCL_OK ) |
{ |
TCL.Tcl_SetObjResult( interp, dbEvalColumnValue( sEval, 0 ) ); |
} |
} |
else if ( rc == TCL.TCL_BREAK || rc == TCL.TCL_OK ) |
{ |
TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewBooleanObj( ( rc == TCL.TCL_OK ? 1 : 0 ) ) ); |
} |
dbEvalFinalize( sEval ); |
|
if ( rc == TCL.TCL_BREAK ) |
{ |
rc = TCL.TCL_OK; |
} |
break; |
} |
|
/* |
** $db eval $sql ?array? ?{ ...code... }? |
** |
** The SQL statement in $sql is evaluated. For each row, the values are |
** placed in elements of the array named "array" and ...code... is executed. |
** If "array" and "code" are omitted, then no callback is every invoked. |
** If "array" is an empty string, then the values are placed in variables |
** that have the same name as the fields extracted by the query. |
*/ |
case (int)DB_enum.DB_EVAL: |
{ |
if ( objc < 3 || objc > 5 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?" ); |
return TCL.TCL_ERROR; |
} |
|
if ( objc == 3 ) |
{ |
DbEvalContext sEval = new DbEvalContext(); |
Tcl_Obj pRet = TCL.Tcl_NewObj(); |
TCL.Tcl_IncrRefCount( pRet ); |
dbEvalInit( sEval, pDb, objv[2], null ); |
//Console.WriteLine( objv[2].ToString() ); |
while ( TCL.TCL_OK == ( rc = dbEvalStep( sEval ) ) ) |
{ |
int i; |
int nCol; |
TclObject[] pDummy; |
dbEvalRowInfo( sEval, out nCol, out pDummy ); |
for ( i = 0; i < nCol; i++ ) |
{ |
TCL.Tcl_ListObjAppendElement( interp, pRet, dbEvalColumnValue( sEval, i ) ); |
} |
} |
dbEvalFinalize( sEval ); |
if ( rc == TCL.TCL_BREAK ) |
{ |
TCL.Tcl_SetObjResult( interp, pRet ); |
rc = TCL.TCL_OK; |
} |
TCL.Tcl_DecrRefCount( ref pRet ); |
} |
else |
{ |
cd = new object[2]; |
DbEvalContext p; |
Tcl_Obj pArray = null; |
Tcl_Obj pScript; |
|
if ( objc == 5 && !String.IsNullOrEmpty( TCL.Tcl_GetString( objv[3] ) ) ) |
{ |
pArray = objv[3]; |
} |
pScript = objv[objc - 1]; |
TCL.Tcl_IncrRefCount( pScript ); |
|
p = new DbEvalContext();// (DbEvalContext)Tcl_Alloc( sizeof( DbEvalContext ) ); |
dbEvalInit( p, pDb, objv[2], pArray ); |
|
( (object[])cd )[0] = p; |
( (object[])cd )[1] = pScript; |
rc = DbEvalNextCmd( (object[])cd, interp, TCL.TCL_OK ); |
} |
break; |
} |
|
/* |
** $db function NAME [-argcount N] SCRIPT |
** |
** Create a new SQL function called NAME. Whenever that function is |
** called, invoke SCRIPT to evaluate the function. |
*/ |
case (int)DB_enum.DB_FUNCTION: |
{ |
SqlFunc pFunc; |
Tcl_Obj pScript; |
string zName; |
int nArg = -1; |
if ( objc == 6 ) |
{ |
string z = TCL.Tcl_GetString( objv[3] ); |
int n = strlen30( z ); |
if ( n > 2 && z.StartsWith( "-argcount" ) )//strncmp( z, "-argcount", n ) == 0 ) |
{ |
if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[4], out nArg ) ) |
return TCL.TCL_ERROR; |
if ( nArg < 0 ) |
{ |
TCL.Tcl_AppendResult( interp, "number of arguments must be non-negative" ); |
return TCL.TCL_ERROR; |
} |
} |
pScript = objv[5]; |
} |
else if ( objc != 4 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "NAME [-argcount N] SCRIPT" ); |
return TCL.TCL_ERROR; |
} |
else |
{ |
pScript = objv[3]; |
} |
zName = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
pFunc = findSqlFunc( pDb, zName ); |
if ( pFunc == null ) |
return TCL.TCL_ERROR; |
if ( pFunc.pScript != null ) |
{ |
TCL.Tcl_DecrRefCount( ref pFunc.pScript ); |
} |
pFunc.pScript = pScript; |
TCL.Tcl_IncrRefCount( pScript ); |
pFunc.useEvalObjv = safeToUseEvalObjv( interp, pScript ); |
rc = sqlite3_create_function( pDb.db, zName, nArg, SQLITE_UTF8, |
pFunc, tclSqlFunc, null, null ); |
if ( rc != SQLITE_OK ) |
{ |
rc = TCL.TCL_ERROR; |
TCL.Tcl_SetResult( interp, sqlite3_errmsg( pDb.db ), TCL.TCL_VOLATILE ); |
} |
break; |
} |
|
/* |
** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID |
*/ |
case (int)DB_enum.DB_INCRBLOB: |
{ |
#if SQLITE_OMIT_INCRBLOB |
TCL.Tcl_AppendResult( interp, "incrblob not available in this build" ); |
return TCL.TCL_ERROR; |
#else |
int isReadonly = 0; |
string zDb = "main" ; |
string zTable; |
string zColumn; |
long iRow = 0; |
|
/* Check for the -readonly option */ |
if ( objc > 3 && TCL.Tcl_GetString( objv[2] ) == "-readonly" ) |
{ |
isReadonly = 1; |
} |
|
if ( objc != ( 5 + isReadonly ) && objc != ( 6 + isReadonly ) ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID" ); |
return TCL.TCL_ERROR; |
} |
|
if ( objc == ( 6 + isReadonly ) ) |
{ |
zDb = TCL.Tcl_GetString( objv[2] ) ; |
} |
zTable = TCL.Tcl_GetString( objv[objc - 3] ); |
zColumn = TCL.Tcl_GetString( objv[objc - 2] ) ; |
rc = TCL.Tcl_GetWideIntFromObj( interp, objv[objc - 1], out iRow ) ? 1 : 0; |
|
if ( rc == TCL.TCL_OK ) |
{ |
rc = createIncrblobChannel( |
interp, pDb, zDb, zTable, zColumn, iRow, isReadonly |
); |
} |
break; |
#endif |
} |
/* |
** $db interrupt |
** |
** Interrupt the execution of the inner-most SQL interpreter. This |
** causes the SQL statement to return an error of SQLITE_INTERRUPT. |
*/ |
case (int)DB_enum.DB_INTERRUPT: |
{ |
sqlite3_interrupt( pDb.db ); |
break; |
} |
|
/* |
** $db nullvalue ?STRING? |
** |
** Change text used when a NULL comes back from the database. If ?STRING? |
** is not present, then the current string used for NULL is returned. |
** If STRING is present, then STRING is returned. |
** |
*/ |
case (int)DB_enum.DB_NULLVALUE: |
{ |
if ( objc != 2 && objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "NULLVALUE" ); |
return TCL.TCL_ERROR; |
} |
if ( objc == 3 ) |
{ |
int len = 0; |
string zNull = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
if ( pDb.zNull != null ) |
{ |
TCL.Tcl_Free( ref pDb.zNull ); |
} |
if ( zNull != null && len > 0 ) |
{ |
pDb.zNull = zNull; |
//pDb.zNull = TCL.Tcl_Alloc( len + 1 ); |
//memcpy(pDb->zNull, zNull, len); |
//pDb.zNull[len] = '\0'; |
} |
else |
{ |
pDb.zNull = null; |
} |
} |
TCL.Tcl_SetObjResult( interp, dbTextToObj( pDb.zNull ) ); |
break; |
} |
|
/* |
** $db last_insert_rowid |
** |
** Return an integer which is the ROWID for the most recent insert. |
*/ |
case (int)DB_enum.DB_LAST_INSERT_ROWID: |
{ |
Tcl_Obj pResult; |
Tcl_WideInt rowid; |
if ( objc != 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
return TCL.TCL_ERROR; |
} |
rowid = sqlite3_last_insert_rowid( pDb.db ); |
pResult = TCL.Tcl_GetObjResult( interp ); |
TCL.Tcl_SetLongObj( pResult, rowid ); |
break; |
} |
|
/* |
** The DB_ONECOLUMN method is implemented together with DB_EXISTS. |
*/ |
|
/* $db progress ?N CALLBACK? |
** |
** Invoke the given callback every N virtual machine opcodes while executing |
** queries. |
*/ |
case (int)DB_enum.DB_PROGRESS: |
{ |
if ( objc == 2 ) |
{ |
if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
{ |
TCL.Tcl_AppendResult( interp, pDb.zProgress ); |
} |
} |
else if ( objc == 4 ) |
{ |
string zProgress; |
int len = 0; |
int N = 0; |
if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[2], out N ) ) |
{ |
return TCL.TCL_ERROR; |
}; |
if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
{ |
TCL.Tcl_Free( ref pDb.zProgress ); |
} |
zProgress = TCL.Tcl_GetStringFromObj( objv[3], len ); |
if ( !String.IsNullOrEmpty( zProgress ) ) |
{ |
//pDb.zProgress = TCL.Tcl_Alloc( len + 1 ); |
//memcpy( pDb.zProgress, zProgress, len + 1 ); |
pDb.zProgress = zProgress; |
} |
else |
{ |
pDb.zProgress = null; |
} |
#if !SQLITE_OMIT_PROGRESS_CALLBACK |
if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
{ |
pDb.interp = interp; |
sqlite3_progress_handler( pDb.db, N, DbProgressHandler, pDb ); |
} |
else |
{ |
sqlite3_progress_handler( pDb.db, 0, null, 0 ); |
} |
#endif |
} |
else |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "N CALLBACK" ); |
return TCL.TCL_ERROR; |
} |
break; |
} |
|
/* $db profile ?CALLBACK? |
** |
** Make arrangements to invoke the CALLBACK routine after each SQL statement |
** that has run. The text of the SQL and the amount of elapse time are |
** appended to CALLBACK before the script is run. |
*/ |
case (int)DB_enum.DB_PROFILE: |
{ |
if ( objc > 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
return TCL.TCL_ERROR; |
} |
else if ( objc == 2 ) |
{ |
if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
{ |
TCL.Tcl_AppendResult( interp, pDb.zProfile ); |
} |
} |
else |
{ |
string zProfile; |
int len = 0; |
if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
{ |
TCL.Tcl_Free( ref pDb.zProfile ); |
} |
zProfile = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
if ( !String.IsNullOrEmpty( zProfile ) && len > 0 ) |
{ |
//pDb.zProfile = TCL.Tcl_Alloc( len + 1 ); |
//memcpy( pDb.zProfile, zProfile, len + 1 ); |
pDb.zProfile = zProfile; |
} |
else |
{ |
pDb.zProfile = null; |
} |
#if !SQLITE_OMIT_TRACE && !(SQLITE_OMIT_FLOATING_POINT) |
if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
{ |
pDb.interp = interp; |
sqlite3_profile( pDb.db, DbProfileHandler, pDb ); |
} |
else |
{ |
sqlite3_profile( pDb.db, null, null ); |
} |
#endif |
} |
break; |
} |
|
/* |
** $db rekey KEY |
** |
** Change the encryption key on the currently open database. |
*/ |
case (int)DB_enum.DB_REKEY: |
{ |
int nKey = 0; |
string pKey; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "KEY" ); |
return TCL.TCL_ERROR; |
} |
pKey = TCL.Tcl_GetStringFromObj( objv[2], out nKey ); |
#if SQLITE_HAS_CODEC |
rc = sqlite3_rekey( pDb.db, pKey, nKey ); |
if ( rc != 0 ) |
{ |
TCL.Tcl_AppendResult( interp, sqlite3ErrStr( rc ) ); |
rc = TCL.TCL_ERROR; |
} |
#endif |
break; |
} |
|
/* $db restore ?DATABASE? FILENAME |
** |
** Open a database file named FILENAME. Transfer the content |
** of FILENAME into the local database DATABASE (default: "main"). |
*/ |
case (int)DB_enum.DB_RESTORE: |
{ |
string zSrcFile; |
string zDestDb; |
sqlite3 pSrc = null; |
sqlite3_backup pBackup; |
int nTimeout = 0; |
|
if ( objc == 3 ) |
{ |
zDestDb = "main"; |
zSrcFile = TCL.Tcl_GetString( objv[2] ); |
} |
else if ( objc == 4 ) |
{ |
zDestDb = TCL.Tcl_GetString( objv[2] ); |
zSrcFile = TCL.Tcl_GetString( objv[3] ); |
} |
else |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?DATABASE? FILENAME" ); |
return TCL.TCL_ERROR; |
} |
rc = sqlite3_open_v2( zSrcFile, out pSrc, SQLITE_OPEN_READONLY, null ); |
if ( rc != SQLITE_OK ) |
{ |
TCL.Tcl_AppendResult( interp, "cannot open source database: ", |
sqlite3_errmsg( pSrc ) ); |
sqlite3_close( pSrc ); |
return TCL.TCL_ERROR; |
} |
pBackup = sqlite3_backup_init( pDb.db, zDestDb, pSrc, "main" ); |
if ( pBackup == null ) |
{ |
TCL.Tcl_AppendResult( interp, "restore failed: ", |
sqlite3_errmsg( pDb.db ) ); |
sqlite3_close( pSrc ); |
return TCL.TCL_ERROR; |
} |
|
#if SQLITE_HAS_CODEC |
if ( pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec != null ) |
{ |
pBackup.pSrc.pBt.pPager.xCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodec; |
pBackup.pSrc.pBt.pPager.xCodecFree = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecFree; |
pBackup.pSrc.pBt.pPager.xCodecSizeChng = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecSizeChng; |
pBackup.pSrc.pBt.pPager.pCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec.Copy(); |
if ( pBackup.pDest.GetHashCode() != pBackup.pDestDb.aDb[0].GetHashCode() ) // Not Main Database |
{ |
pBackup.pDest.pBt.pPager.xCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodec; |
pBackup.pDest.pBt.pPager.xCodecFree = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecFree; |
pBackup.pDest.pBt.pPager.xCodecSizeChng = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecSizeChng; |
pBackup.pDest.pBt.pPager.pCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec.Copy(); |
} |
} |
#endif |
while ( ( rc = sqlite3_backup_step( pBackup, 100 ) ) == SQLITE_OK |
|| rc == SQLITE_BUSY ) |
{ |
if ( rc == SQLITE_BUSY ) |
{ |
if ( nTimeout++ >= 3 ) |
break; |
sqlite3_sleep( 100 ); |
} |
} |
sqlite3_backup_finish( pBackup ); |
if ( rc == SQLITE_DONE ) |
{ |
rc = TCL.TCL_OK; |
} |
else if ( rc == SQLITE_BUSY || rc == SQLITE_LOCKED ) |
{ |
TCL.Tcl_AppendResult( interp, "restore failed: source database busy" |
); |
rc = TCL.TCL_ERROR; |
} |
else |
{ |
TCL.Tcl_AppendResult( interp, "restore failed: ", |
sqlite3_errmsg( pDb.db ) ); |
rc = TCL.TCL_ERROR; |
} |
sqlite3_close( pSrc ); |
break; |
} |
|
/* |
** $db status (step|sort|autoindex) |
** |
** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or |
** SQLITE_STMTSTATUS_SORT for the most recent eval. |
*/ |
case (int)DB_enum.DB_STATUS: |
{ |
int v; |
string zOp; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "(step|sort|autoindex)" ); |
return TCL.TCL_ERROR; |
} |
zOp = TCL.Tcl_GetString( objv[2] ); |
if ( zOp == "step" ) |
{ |
v = pDb.nStep; |
} |
else if ( zOp == "sort" ) |
{ |
v = pDb.nSort; |
} |
else if ( zOp == "autoindex" ) |
{ |
v = pDb.nIndex; |
} |
else |
{ |
TCL.Tcl_AppendResult( interp, "bad argument: should be autoindex, step or sort" ); |
return TCL.TCL_ERROR; |
} |
TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewIntObj( v ) ); |
break; |
} |
|
/* |
** $db timeout MILLESECONDS |
** |
** Delay for the number of milliseconds specified when a file is locked. |
*/ |
case (int)DB_enum.DB_TIMEOUT: |
{ |
int ms = 0; |
if ( objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "MILLISECONDS" ); |
return TCL.TCL_ERROR; |
} |
if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[2], out ms ) ) |
return TCL.TCL_ERROR; |
sqlite3_busy_timeout( pDb.db, ms ); |
break; |
} |
|
/* |
** $db total_changes |
** |
** Return the number of rows that were modified, inserted, or deleted |
** since the database handle was created. |
*/ |
case (int)DB_enum.DB_TOTAL_CHANGES: |
{ |
Tcl_Obj pResult; |
if ( objc != 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
return TCL.TCL_ERROR; |
} |
pResult = TCL.Tcl_GetObjResult( interp ); |
TCL.Tcl_SetIntObj( pResult, sqlite3_total_changes( pDb.db ) ); |
break; |
} |
|
/* $db trace ?CALLBACK? |
** |
** Make arrangements to invoke the CALLBACK routine for each SQL statement |
** that is executed. The text of the SQL is appended to CALLBACK before |
** it is executed. |
*/ |
case (int)DB_enum.DB_TRACE: |
{ |
if ( objc > 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
return TCL.TCL_ERROR; |
} |
else if ( objc == 2 ) |
{ |
if ( pDb.zTrace != null ) |
{ |
TCL.Tcl_AppendResult( interp, pDb.zTrace ); |
} |
} |
else |
{ |
string zTrace; |
int len = 0; |
if ( pDb.zTrace != null ) |
{ |
TCL.Tcl_Free( ref pDb.zTrace ); |
} |
zTrace = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
if ( zTrace != null && len > 0 ) |
{ |
//pDb.zTrace = TCL.Tcl_Alloc( len + 1 ); |
pDb.zTrace = zTrace;//memcpy( pDb.zTrace, zTrace, len + 1 ); |
} |
else |
{ |
pDb.zTrace = null; |
} |
#if !SQLITE_OMIT_TRACE && !(SQLITE_OMIT_FLOATING_POINT) |
if ( pDb.zTrace != null ) |
{ |
pDb.interp = interp; |
sqlite3_trace( pDb.db, (dxTrace)DbTraceHandler, pDb ); |
} |
else |
{ |
sqlite3_trace( pDb.db, null, null ); |
} |
#endif |
} |
break; |
} |
|
// /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT |
// ** |
// ** 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. |
// ** pass the exception on up the stack. |
// ** |
// ** This command was inspired by Dave Thomas's talk on Ruby at the |
// ** 2005 O'Reilly Open Source Convention (OSCON). |
// */ |
case (int)DB_enum.DB_TRANSACTION: |
{ |
Tcl_Obj pScript; |
string zBegin = "SAVEPOINT _tcl_transaction"; |
if ( objc != 3 && objc != 4 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "[TYPE] SCRIPT" ); |
return TCL.TCL_ERROR; |
} |
if ( pDb.nTransaction == 0 && objc == 4 ) |
{ |
string[] TTYPE_strs = { "deferred", "exclusive", "immediate", null }; |
|
int ttype = 0; |
if ( TCL.Tcl_GetIndexFromObj( interp, objv[2], TTYPE_strs, "transaction type", |
0, out ttype ) ) |
{ |
return TCL.TCL_ERROR; |
} |
switch ( ttype ) |
{ |
case (int)TTYPE_enum.TTYPE_DEFERRED: /* no-op */ |
; |
break; |
case (int)TTYPE_enum.TTYPE_EXCLUSIVE: |
zBegin = "BEGIN EXCLUSIVE"; |
break; |
case (int)TTYPE_enum.TTYPE_IMMEDIATE: |
zBegin = "BEGIN IMMEDIATE"; |
break; |
} |
} |
pScript = objv[objc - 1]; |
|
/* Run the SQLite BEGIN command to open a transaction or savepoint. */ |
pDb.disableAuth++; |
rc = sqlite3_exec( pDb.db, zBegin, 0, 0, 0 ); |
pDb.disableAuth--; |
if ( rc != SQLITE_OK ) |
{ |
TCL.Tcl_AppendResult( interp, sqlite3_errmsg( pDb.db ) ); |
return TCL.TCL_ERROR; |
} |
pDb.nTransaction++; |
/* If using NRE, schedule a callback to invoke the script pScript, then |
** a second callback to commit (or rollback) the transaction or savepoint |
** opened above. If not using NRE, evaluate the script directly, then |
** call function DbTransPostCmd() to commit (or rollback) the transaction |
** or savepoint. */ |
if ( DbUseNre() ) |
{ |
Debugger.Break(); |
//Tcl_NRAddCallback( interp, DbTransPostCmd, cd, 0, 0, 0 ); |
//Tcl_NREvalObj(interp, pScript, 0); |
} |
else |
{ |
rc = DbTransPostCmd( cd, interp, TCL.Tcl_EvalObjEx( interp, pScript, 0 ) ); |
} |
break; |
} |
|
/* |
** $db unlock_notify ?script? |
*/ |
case (int)DB_enum.DB_UNLOCK_NOTIFY: |
{ |
#if !SQLITE_ENABLE_UNLOCK_NOTIFY |
TCL.Tcl_AppendResult( interp, "unlock_notify not available in this build", 0 ); |
rc = TCL.TCL_ERROR; |
#else |
if( objc!=2 && objc!=3 ){ |
Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); |
rc = TCL.Tcl_ERROR; |
}else{ |
void (*xNotify)(void **, int) = 0; |
void *pNotifyArg = 0; |
|
if( pDb.pUnlockNotify ){ |
Tcl_DecrRefCount(pDb.pUnlockNotify); |
pDb.pUnlockNotify = 0; |
} |
|
if( objc==3 ){ |
xNotify = DbUnlockNotify; |
pNotifyArg = (void )pDb; |
pDb.pUnlockNotify = objv[2]; |
Tcl_IncrRefCount(pDb.pUnlockNotify); |
} |
|
if( sqlite3_unlock_notify(pDb.db, xNotify, pNotifyArg) ){ |
Tcl_AppendResult(interp, sqlite3_errmsg(pDb.db), 0); |
rc = TCL.Tcl_ERROR; |
} |
} |
#endif |
break; |
} |
/* |
** $db wal_hook ?script? |
** $db update_hook ?script? |
** $db rollback_hook ?script? |
*/ |
case (int)DB_enum.DB_WAL_HOOK: |
case (int)DB_enum.DB_UPDATE_HOOK: |
case (int)DB_enum.DB_ROLLBACK_HOOK: |
{ |
|
/* set ppHook to point at pUpdateHook or pRollbackHook, depending on |
** whether [$db update_hook] or [$db rollback_hook] was invoked. |
*/ |
Tcl_Obj ppHook; |
if ( choice == (int)DB_enum.DB_UPDATE_HOOK ) |
{ |
ppHook = pDb.pUpdateHook; |
} |
else if ( choice == (int)DB_enum.DB_WAL_HOOK ) |
{ |
ppHook = pDb.pWalHook; |
} |
else |
{ |
ppHook = pDb.pRollbackHook; |
} |
|
if ( objc != 2 && objc != 3 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 2, objv, "?SCRIPT?" ); |
return TCL.TCL_ERROR; |
} |
if ( ppHook != null ) |
{ |
TCL.Tcl_SetObjResult( interp, ppHook ); |
if ( objc == 3 ) |
{ |
TCL.Tcl_DecrRefCount( ref ppHook ); |
ppHook = null; |
} |
} |
if ( objc == 3 ) |
{ |
Debug.Assert( null == ppHook ); |
if ( objv[2] != null )//TCL.Tcl_GetCharLength( objv[2] ) > 0 ) |
{ |
ppHook = objv[2]; |
TCL.Tcl_IncrRefCount( ppHook ); |
} |
} |
if ( choice == (int)DB_enum.DB_UPDATE_HOOK ) |
{ |
pDb.pUpdateHook = ppHook; |
} |
else |
{ |
pDb.pRollbackHook = ppHook; |
} |
sqlite3_update_hook( pDb.db, ( pDb.pUpdateHook != null ? (dxUpdateCallback)DbUpdateHandler : null ), pDb ); |
sqlite3_rollback_hook( pDb.db, ( pDb.pRollbackHook != null ? (dxRollbackCallback)DbRollbackHandler : null ), pDb ); |
sqlite3_wal_hook( pDb.db, ( pDb.pWalHook != null ? (dxWalCallback)DbWalHandler : null ), pDb ); |
|
break; |
} |
|
/* $db version |
** |
** Return the version string for this database. |
*/ |
case (int)DB_enum.DB_VERSION: |
{ |
TCL.Tcl_SetResult( interp, sqlite3_libversion(), TCL.TCL_STATIC ); |
break; |
} |
|
default: |
Debug.Assert( false, "Missing switch:" + objv[1].ToString() ); |
break; |
} /* End of the SWITCH statement */ |
return rc; |
} |
|
#if SQLITE_TCL_NRE |
/* |
** Adaptor that provides an objCmd interface to the NRE-enabled |
** interface implementation. |
*/ |
static int DbObjCmdAdaptor( |
void *cd, |
Tcl_Interp interp, |
int objc, |
Tcl_Obj *const*objv |
){ |
return TCL.TCL_NRCallObjProc(interp, DbObjCmd, cd, objc, objv); |
} |
#endif //* SQLITE_TCL_NRE */ |
/* |
** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? |
** ?-create BOOLEAN? ?-nomutex BOOLEAN? |
** |
** This is the main Tcl command. When the "sqlite" Tcl command is |
** invoked, this routine runs to process that command. |
** |
** The first argument, DBNAME, is an arbitrary name for a new |
** database connection. This command creates a new command named |
** DBNAME that is used to control that connection. The database |
** connection is deleted when the DBNAME command is deleted. |
** |
** The second argument is the name of the database file. |
** |
*/ |
static int DbMain( object cd, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) |
{ |
SqliteDb p; |
string pKey = null; |
int nKey = 0; |
string zArg; |
string zErrMsg; |
int i; |
string zFile; |
string zVfs = null; |
int flags; |
Tcl_DString translatedFilename; |
/* In normal use, each TCL interpreter runs in a single thread. So |
** by default, we can turn of mutexing on SQLite database connections. |
** However, for testing purposes it is useful to have mutexes turned |
** on. So, by default, mutexes default off. But if compiled with |
** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on. |
*/ |
#if SQLITE_TCL_DEFAULT_FULLMUTEX |
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; |
#else |
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; |
#endif |
if ( objc == 2 ) |
{ |
zArg = TCL.Tcl_GetStringFromObj( objv[1], 0 ); |
if ( zArg == "-version" ) |
{ |
TCL.Tcl_AppendResult( interp, sqlite3_version, null ); |
return TCL.TCL_OK; |
} |
if ( zArg == "-has-codec" ) |
{ |
#if SQLITE_HAS_CODEC |
TCL.Tcl_AppendResult( interp, "1" ); |
#else |
TCL.Tcl_AppendResult( interp, "0", null ); |
#endif |
return TCL.TCL_OK; |
} |
if ( zArg == "-tcl-uses-utf" ) |
{ |
TCL.Tcl_AppendResult( interp, "1", null ); |
return TCL.TCL_OK; |
} |
} |
for ( i = 3; i + 1 < objc; i += 2 ) |
{ |
zArg = TCL.Tcl_GetString( objv[i] ); |
if ( zArg == "-key" ) |
{ |
pKey = TCL.Tcl_GetStringFromObj( objv[i + 1], out nKey ); |
} |
else if ( zArg == "-vfs" ) |
{ |
zVfs = TCL.Tcl_GetString( objv[i + 1] ); |
} |
else if ( zArg == "-readonly" ) |
{ |
bool b = false; |
if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
return TCL.TCL_ERROR; |
if ( b ) |
{ |
flags &= ~( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ); |
flags |= SQLITE_OPEN_READONLY; |
} |
else |
{ |
flags &= ~SQLITE_OPEN_READONLY; |
flags |= SQLITE_OPEN_READWRITE; |
} |
} |
else if ( zArg == "-create" ) |
{ |
bool b = false; |
if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
return TCL.TCL_ERROR; |
if ( b && ( flags & SQLITE_OPEN_READONLY ) == 0 ) |
{ |
flags |= SQLITE_OPEN_CREATE; |
} |
else |
{ |
flags &= ~SQLITE_OPEN_CREATE; |
} |
} |
else if ( zArg == "-nomutex" ) |
{ |
bool b = false; |
if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
return TCL.TCL_ERROR; |
if ( b ) |
{ |
flags |= SQLITE_OPEN_NOMUTEX; |
flags &= ~SQLITE_OPEN_FULLMUTEX; |
} |
else |
{ |
flags &= ~SQLITE_OPEN_NOMUTEX; |
} |
} |
else if ( zArg == "-fullmutex" )//strcmp( zArg, "-fullmutex" ) == 0 ) |
{ |
bool b = false; |
if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
return TCL.TCL_ERROR; |
if ( b ) |
{ |
flags |= SQLITE_OPEN_FULLMUTEX; |
flags &= ~SQLITE_OPEN_NOMUTEX; |
} |
else |
{ |
flags &= ~SQLITE_OPEN_FULLMUTEX; |
} |
} |
else |
{ |
TCL.Tcl_AppendResult( interp, "unknown option: ", zArg, null ); |
return TCL.TCL_ERROR; |
} |
} |
if ( objc < 3 || ( objc & 1 ) != 1 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 1, objv, |
"HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN?" |
#if SQLITE_HAS_CODEC |
+ " ?-key CODECKEY?" |
#endif |
); |
return TCL.TCL_ERROR; |
} |
zErrMsg = ""; |
p = new SqliteDb();//(SqliteDb)Tcl_Alloc( sizeof(*p) ); |
if ( p == null ) |
{ |
TCL.Tcl_SetResult( interp, "malloc failed", TCL.TCL_STATIC ); |
return TCL.TCL_ERROR; |
} |
//memset(p, 0, sizeof(*p)); |
zFile = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
//zFile = TCL.Tcl_TranslateFileName( interp, zFile, ref translatedFilename ); |
sqlite3_open_v2( zFile, out p.db, flags, zVfs ); |
//Tcl_DStringFree( ref translatedFilename ); |
if ( SQLITE_OK != sqlite3_errcode( p.db ) ) |
{ |
zErrMsg = sqlite3_errmsg( p.db );// sqlite3_mprintf( "%s", sqlite3_errmsg( p.db ) ); |
sqlite3_close( p.db ); |
p.db = null; |
} |
#if SQLITE_HAS_CODEC |
if ( p.db != null ) |
{ |
sqlite3_key( p.db, pKey, nKey ); |
} |
#endif |
if ( p.db == null ) |
{ |
TCL.Tcl_SetResult( interp, zErrMsg, TCL.TCL_VOLATILE ); |
TCL.Tcl_Free( ref p ); |
zErrMsg = "";// sqlite3DbFree( db, ref zErrMsg ); |
return TCL.TCL_ERROR; |
} |
p.maxStmt = NUM_PREPARED_STMTS; |
p.interp = interp; |
zArg = TCL.Tcl_GetStringFromObj( objv[1], 0 ); |
if ( DbUseNre() ) |
{ |
Debugger.Break(); |
//Tcl_NRCreateCommand(interp, zArg, DbObjCmdAdaptor, DbObjCmd, |
// p, DbDeleteCmd); |
} |
else |
{ |
TCL.Tcl_CreateObjCommand( interp, zArg, (Interp.dxObjCmdProc)DbObjCmd, p, (Interp.dxCmdDeleteProc)DbDeleteCmd ); |
} |
return TCL.TCL_OK; |
} |
|
/* |
** Provide a dummy TCL.Tcl_InitStubs if we are using this as a static |
** library. |
*/ |
#if !USE_TCL_STUBS |
//# undef TCL.Tcl_InitStubs |
static void Tcl_InitStubs( Tcl_Interp interp, string s, int i ) |
{ |
} |
#endif |
|
/* |
** Make sure we have a PACKAGE_VERSION macro defined. This will be |
** defined automatically by the TEA makefile. But other makefiles |
** do not define it. |
*/ |
#if !PACKAGE_VERSION |
public static string PACKAGE_VERSION;//# define PACKAGE_VERSION SQLITE_VERSION |
#endif |
|
|
/* |
** Initialize this module. |
** |
** This Tcl module contains only a single new Tcl command named "sqlite". |
** (Hence there is no namespace. There is no point in using a namespace |
** if the extension only supplies one new name!) The "sqlite" command is |
** used to open a new SQLite database. See the DbMain() routine above |
** for additional information. |
** |
** The EXTERN macros are required by TCL in order to work on windows. |
*/ |
//int Sqlite3_Init(Tcl_Interp interp){ |
static public int Sqlite3_Init( Tcl_Interp interp ) |
{ |
PACKAGE_VERSION = SQLITE_VERSION; |
Tcl_InitStubs( interp, "tclsharp 8.4", 0 ); |
TCL.Tcl_CreateObjCommand( interp, "sqlite3", (Interp.dxObjCmdProc)DbMain, null, null ); |
TCL.Tcl_PkgProvide( interp, "sqlite3", PACKAGE_VERSION ); |
|
#if !SQLITE_3_SUFFIX_ONLY |
/* The "sqlite" alias is undocumented. It is here only to support |
** legacy scripts. All new scripts should use only the "sqlite3" |
** command. |
*/ |
TCL.Tcl_CreateObjCommand( interp, "sqlite", (Interp.dxObjCmdProc)DbMain, null, null ); |
#endif |
return TCL.TCL_OK; |
} |
//int Tclsqlite3_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
//int Sqlite3_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
//int Tclsqlite3_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
//int Sqlite3_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Tclsqlite3_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Sqlite3_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Tclsqlite3_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK;} |
|
|
#if !SQLITE_3_SUFFIX_ONLY |
//int Sqlite_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
//int Tclsqlite_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
//int Sqlite_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
//int Tclsqlite_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
//int Sqlite_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Tclsqlite_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Sqlite_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
//int Tclsqlite_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK;} |
#endif |
|
#if TCLSH |
/***************************************************************************** |
** All of the code that follows is used to build standalone TCL interpreters |
** that are statically linked with SQLite. Enable these by compiling |
** with -DTCLSH=n where n can be 1 or 2. An n of 1 generates a standard |
** tclsh but with SQLite built in. An n of 2 generates the SQLite space |
** analysis program. |
*/ |
|
#if (SQLITE_TEST) || (SQLITE_TCLMD5) |
/* |
* This code implements the MD5 message-digest algorithm. |
* The algorithm is due to Ron Rivest. This code was |
* written by Colin Plumb in 1993, no copyright is claimed. |
* This code is in the public domain; do with it what you wish. |
* |
* Equivalent code is available from RSA Data Security, Inc. |
* This code has been tested against that, and is equivalent, |
* except that you don't need to include two pages of legalese |
* with every copy. |
* |
* To compute the message digest of a chunk of bytes, declare an |
* MD5Context structure, pass it to MD5Init, call MD5Update as |
* needed on buffers full of bytes, and then call MD5Final, which |
* will fill a supplied 16-byte array with the digest. |
*/ |
|
/* |
* If compiled on a machine that doesn't have a 32-bit integer, |
* you just set "uint32" to the appropriate datatype for an |
* unsigned 32-bit integer. For example: |
* |
* cc -Duint32='unsigned long' md5.c |
* |
*/ |
//#if !uint32 |
//# define uint32 unsigned int |
//#endif |
|
//struct MD5Context { |
// int isInit; |
// uint32 buf[4]; |
// uint32 bits[2]; |
// unsigned char in[64]; |
//}; |
//typedef struct MD5Context MD5Context; |
class MD5Context |
{ |
public bool isInit; |
public u32[] buf = new u32[4]; |
public u32[] bits = new u32[2]; |
public u32[] _in = new u32[64]; |
public Mem _Mem; |
}; |
|
/* |
* Note: this code is harmless on little-endian machines. |
*/ |
//static void byteReverse (unsigned char *buf, unsigned longs){ |
// uint32 t; |
// do { |
// t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | |
// ((unsigned)buf[1]<<8 | buf[0]); |
// *(uint32 )buf = t; |
// buf += 4; |
// } while (--longs); |
//} |
|
/* The four core functions - F1 is optimized somewhat */ |
|
delegate u32 dxF1234( u32 x, u32 y, u32 z ); |
|
//* #define F1(x, y, z) (x & y | ~x & z) */ |
//#define F1(x, y, z) (z ^ (x & (y ^ z))) |
static u32 F1( u32 x, u32 y, u32 z ) |
{ |
return ( z ^ ( x & ( y ^ z ) ) ); |
} |
|
//#define F2(x, y, z) F1(z, x, y) |
static u32 F2( u32 x, u32 y, u32 z ) |
{ |
return F1( z, x, y ); |
} |
|
//#define F3(x, y, z) (x ^ y ^ z) |
static u32 F3( u32 x, u32 y, u32 z ) |
{ |
return ( x ^ y ^ z ); |
} |
|
//#define F4(x, y, z) (y ^ (x | ~z)) |
static u32 F4( u32 x, u32 y, u32 z ) |
{ |
return ( y ^ ( x | ~z ) ); |
} |
|
///* This is the central step in the MD5 algorithm. */ |
//#define MD5STEP(f, w, x, y, z, data, s) \ |
// ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) |
static void MD5STEP( dxF1234 f, ref u32 w, u32 x, u32 y, u32 z, u32 data, byte s ) |
{ |
w += f( x, y, z ) + data; |
w = w << s | w >> ( 32 - s ); |
w += x; |
} |
|
/* |
* The core of the MD5 algorithm, this alters an existing MD5 hash to |
* reflect the addition of 16 longwords of new data. MD5Update blocks |
* the data and converts bytes into longwords for this routine. |
*/ |
static void MD5Transform( u32[] buf, u32[] _in ) |
{ |
u32 a, b, c, d; |
|
a = buf[0]; |
b = buf[1]; |
c = buf[2]; |
d = buf[3]; |
|
MD5STEP( F1, ref a, b, c, d, _in[0] + 0xd76aa478, 7 ); |
MD5STEP( F1, ref d, a, b, c, _in[1] + 0xe8c7b756, 12 ); |
MD5STEP( F1, ref c, d, a, b, _in[2] + 0x242070db, 17 ); |
MD5STEP( F1, ref b, c, d, a, _in[3] + 0xc1bdceee, 22 ); |
MD5STEP( F1, ref a, b, c, d, _in[4] + 0xf57c0faf, 7 ); |
MD5STEP( F1, ref d, a, b, c, _in[5] + 0x4787c62a, 12 ); |
MD5STEP( F1, ref c, d, a, b, _in[6] + 0xa8304613, 17 ); |
MD5STEP( F1, ref b, c, d, a, _in[7] + 0xfd469501, 22 ); |
MD5STEP( F1, ref a, b, c, d, _in[8] + 0x698098d8, 7 ); |
MD5STEP( F1, ref d, a, b, c, _in[9] + 0x8b44f7af, 12 ); |
MD5STEP( F1, ref c, d, a, b, _in[10] + 0xffff5bb1, 17 ); |
MD5STEP( F1, ref b, c, d, a, _in[11] + 0x895cd7be, 22 ); |
MD5STEP( F1, ref a, b, c, d, _in[12] + 0x6b901122, 7 ); |
MD5STEP( F1, ref d, a, b, c, _in[13] + 0xfd987193, 12 ); |
MD5STEP( F1, ref c, d, a, b, _in[14] + 0xa679438e, 17 ); |
MD5STEP( F1, ref b, c, d, a, _in[15] + 0x49b40821, 22 ); |
|
MD5STEP( F2, ref a, b, c, d, _in[1] + 0xf61e2562, 5 ); |
MD5STEP( F2, ref d, a, b, c, _in[6] + 0xc040b340, 9 ); |
MD5STEP( F2, ref c, d, a, b, _in[11] + 0x265e5a51, 14 ); |
MD5STEP( F2, ref b, c, d, a, _in[0] + 0xe9b6c7aa, 20 ); |
MD5STEP( F2, ref a, b, c, d, _in[5] + 0xd62f105d, 5 ); |
MD5STEP( F2, ref d, a, b, c, _in[10] + 0x02441453, 9 ); |
MD5STEP( F2, ref c, d, a, b, _in[15] + 0xd8a1e681, 14 ); |
MD5STEP( F2, ref b, c, d, a, _in[4] + 0xe7d3fbc8, 20 ); |
MD5STEP( F2, ref a, b, c, d, _in[9] + 0x21e1cde6, 5 ); |
MD5STEP( F2, ref d, a, b, c, _in[14] + 0xc33707d6, 9 ); |
MD5STEP( F2, ref c, d, a, b, _in[3] + 0xf4d50d87, 14 ); |
MD5STEP( F2, ref b, c, d, a, _in[8] + 0x455a14ed, 20 ); |
MD5STEP( F2, ref a, b, c, d, _in[13] + 0xa9e3e905, 5 ); |
MD5STEP( F2, ref d, a, b, c, _in[2] + 0xfcefa3f8, 9 ); |
MD5STEP( F2, ref c, d, a, b, _in[7] + 0x676f02d9, 14 ); |
MD5STEP( F2, ref b, c, d, a, _in[12] + 0x8d2a4c8a, 20 ); |
|
MD5STEP( F3, ref a, b, c, d, _in[5] + 0xfffa3942, 4 ); |
MD5STEP( F3, ref d, a, b, c, _in[8] + 0x8771f681, 11 ); |
MD5STEP( F3, ref c, d, a, b, _in[11] + 0x6d9d6122, 16 ); |
MD5STEP( F3, ref b, c, d, a, _in[14] + 0xfde5380c, 23 ); |
MD5STEP( F3, ref a, b, c, d, _in[1] + 0xa4beea44, 4 ); |
MD5STEP( F3, ref d, a, b, c, _in[4] + 0x4bdecfa9, 11 ); |
MD5STEP( F3, ref c, d, a, b, _in[7] + 0xf6bb4b60, 16 ); |
MD5STEP( F3, ref b, c, d, a, _in[10] + 0xbebfbc70, 23 ); |
MD5STEP( F3, ref a, b, c, d, _in[13] + 0x289b7ec6, 4 ); |
MD5STEP( F3, ref d, a, b, c, _in[0] + 0xeaa127fa, 11 ); |
MD5STEP( F3, ref c, d, a, b, _in[3] + 0xd4ef3085, 16 ); |
MD5STEP( F3, ref b, c, d, a, _in[6] + 0x04881d05, 23 ); |
MD5STEP( F3, ref a, b, c, d, _in[9] + 0xd9d4d039, 4 ); |
MD5STEP( F3, ref d, a, b, c, _in[12] + 0xe6db99e5, 11 ); |
MD5STEP( F3, ref c, d, a, b, _in[15] + 0x1fa27cf8, 16 ); |
MD5STEP( F3, ref b, c, d, a, _in[2] + 0xc4ac5665, 23 ); |
|
MD5STEP( F4, ref a, b, c, d, _in[0] + 0xf4292244, 6 ); |
MD5STEP( F4, ref d, a, b, c, _in[7] + 0x432aff97, 10 ); |
MD5STEP( F4, ref c, d, a, b, _in[14] + 0xab9423a7, 15 ); |
MD5STEP( F4, ref b, c, d, a, _in[5] + 0xfc93a039, 21 ); |
MD5STEP( F4, ref a, b, c, d, _in[12] + 0x655b59c3, 6 ); |
MD5STEP( F4, ref d, a, b, c, _in[3] + 0x8f0ccc92, 10 ); |
MD5STEP( F4, ref c, d, a, b, _in[10] + 0xffeff47d, 15 ); |
MD5STEP( F4, ref b, c, d, a, _in[1] + 0x85845dd1, 21 ); |
MD5STEP( F4, ref a, b, c, d, _in[8] + 0x6fa87e4f, 6 ); |
MD5STEP( F4, ref d, a, b, c, _in[15] + 0xfe2ce6e0, 10 ); |
MD5STEP( F4, ref c, d, a, b, _in[6] + 0xa3014314, 15 ); |
MD5STEP( F4, ref b, c, d, a, _in[13] + 0x4e0811a1, 21 ); |
MD5STEP( F4, ref a, b, c, d, _in[4] + 0xf7537e82, 6 ); |
MD5STEP( F4, ref d, a, b, c, _in[11] + 0xbd3af235, 10 ); |
MD5STEP( F4, ref c, d, a, b, _in[2] + 0x2ad7d2bb, 15 ); |
MD5STEP( F4, ref b, c, d, a, _in[9] + 0xeb86d391, 21 ); |
|
buf[0] += a; |
buf[1] += b; |
buf[2] += c; |
buf[3] += d; |
} |
|
/* |
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious |
* initialization constants. |
*/ |
static void MD5Init( MD5Context ctx ) |
{ |
ctx.isInit = true; |
ctx.buf[0] = 0x67452301; |
ctx.buf[1] = 0xefcdab89; |
ctx.buf[2] = 0x98badcfe; |
ctx.buf[3] = 0x10325476; |
ctx.bits[0] = 0; |
ctx.bits[1] = 0; |
} |
|
/* |
* Update context to reflect the concatenation of another buffer full |
* of bytes. |
*/ |
static void MD5Update( MD5Context pCtx, byte[] buf, int len ) |
{ |
|
MD5Context ctx = (MD5Context)pCtx; |
int t; |
|
/* Update bitcount */ |
|
t = (int)ctx.bits[0]; |
if ( ( ctx.bits[0] = (u32)( t + ( (u32)len << 3 ) ) ) < t ) |
ctx.bits[1]++; /* Carry from low to high */ |
ctx.bits[1] += (u32)( len >> 29 ); |
|
t = ( t >> 3 ) & 0x3f; /* Bytes already in shsInfo.data */ |
|
/* Handle any leading odd-sized chunks */ |
|
int _buf = 0; // Offset into buffer |
int p = t; //Offset into ctx._in |
if ( t != 0 ) |
{ |
//byte p = (byte)ctx._in + t; |
t = 64 - t; |
if ( len < t ) |
{ |
Buffer.BlockCopy( buf, _buf, ctx._in, p, len );// memcpy( p, buf, len ); |
return; |
} |
Buffer.BlockCopy( buf, _buf, ctx._in, p, t ); //memcpy( p, buf, t ); |
//byteReverse(ctx._in, 16); |
MD5Transform( ctx.buf, ctx._in ); |
_buf += t;// buf += t; |
len -= t; |
} |
|
/* Process data in 64-byte chunks */ |
|
while ( len >= 64 ) |
{ |
Buffer.BlockCopy( buf, _buf, ctx._in, 0, 64 );//memcpy( ctx._in, buf, 64 ); |
//byteReverse(ctx._in, 16); |
MD5Transform( ctx.buf, ctx._in ); |
_buf += 64;// buf += 64; |
len -= 64; |
} |
|
/* Handle any remaining bytes of data. */ |
|
Buffer.BlockCopy( buf, _buf, ctx._in, 0, len ); //memcpy( ctx._in, buf, len ); |
} |
|
/* |
* Final wrapup - pad to 64-byte boundary with the bit pattern |
* 1 0* (64-bit count of bits processed, MSB-first) |
*/ |
|
static void MD5Final( byte[] digest, MD5Context pCtx ) |
{ |
MD5Context ctx = pCtx; |
int count; |
int p; |
|
/* Compute number of bytes mod 64 */ |
count = (int)( ctx.bits[0] >> 3 ) & 0x3F; |
|
/* Set the first char of padding to 0x80. This is safe since there is |
always at least one byte free */ |
p = count; |
ctx._in[p++] = 0x80; |
|
/* Bytes of padding needed to make 64 bytes */ |
count = 64 - 1 - count; |
|
/* Pad out to 56 mod 64 */ |
if ( count < 8 ) |
{ |
/* Two lots of padding: Pad the first block to 64 bytes */ |
Array.Clear( ctx._in, p, count );//memset(p, 0, count); |
//byteReverse( ctx._in, 16 ); |
MD5Transform( ctx.buf, ctx._in ); |
|
/* Now fill the next block with 56 bytes */ |
Array.Clear( ctx._in, 0, 56 );//memset(ctx._in, 0, 56); |
} |
else |
{ |
/* Pad block to 56 bytes */ |
Array.Clear( ctx._in, p, count - 8 );//memset(p, 0, count-8); |
} |
//byteReverse( ctx._in, 14 ); |
|
/* Append length in bits and transform */ |
ctx._in[14] = (byte)ctx.bits[0]; |
ctx._in[15] = (byte)ctx.bits[1]; |
|
MD5Transform( ctx.buf, ctx._in ); |
//byteReverse( ctx.buf, 4 ); |
Buffer.BlockCopy( ctx.buf, 0, digest, 0, 16 );//memcpy(digest, ctx.buf, 16); |
//memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */ |
Array.Clear( ctx._in, 0, ctx._in.Length ); |
Array.Clear( ctx.bits, 0, ctx.bits.Length ); |
Array.Clear( ctx.buf, 0, ctx.buf.Length ); |
ctx._Mem = null; |
} |
|
/* |
** Convert a 128-bit MD5 digest into a 32-digit base-16 number. |
*/ |
static void DigestToBase16( byte[] digest, byte[] zBuf ) |
{ |
string zEncode = "0123456789abcdef"; |
int i, j; |
|
for ( j = i = 0; i < 16; i++ ) |
{ |
int a = digest[i]; |
zBuf[j++] = (byte)zEncode[( a >> 4 ) & 0xf]; |
zBuf[j++] = (byte)zEncode[a & 0xf]; |
} |
if ( j < zBuf.Length ) |
zBuf[j] = 0; |
} |
|
|
/* |
** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers |
** each representing 16 bits of the digest and separated from each |
** other by a "-" character. |
*/ |
//static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ |
// int i, j; |
// unsigned int x; |
// for(i=j=0; i<16; i+=2){ |
// x = digest[i]*256 + digest[i+1]; |
// if( i>0 ) zDigest[j++] = '-'; |
// sprintf(&zDigest[j], "%05u", x); |
// j += 5; |
// } |
// zDigest[j] = 0; |
//} |
|
/* |
** A TCL command for md5. The argument is the text to be hashed. The |
** Result is the hash in base64. |
*/ |
static int md5_cmd( object cd, Tcl_Interp interp, int argc, Tcl_Obj[] argv ) |
{ |
MD5Context ctx = new MD5Context(); |
byte[] digest = new byte[16]; |
byte[] zBuf = new byte[32]; |
|
|
if ( argc != 2 ) |
{ |
TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], |
" TEXT\"" ); |
return TCL.TCL_ERROR; |
} |
MD5Init( ctx ); |
MD5Update( ctx, Encoding.UTF8.GetBytes( argv[1].ToString() ), Encoding.UTF8.GetByteCount( argv[1].ToString() ) ); |
MD5Final( digest, ctx ); |
DigestToBase16( digest, zBuf ); |
TCL.Tcl_AppendResult( interp, Encoding.UTF8.GetString( zBuf, 0, zBuf.Length ) ); |
return TCL.TCL_OK; |
} |
|
|
/* |
** A TCL command to take the md5 hash of a file. The argument is the |
** name of the file. |
*/ |
static int md5file_cmd( object cd, Tcl_Interp interp, int argc, Tcl_Obj[] argv ) |
{ |
StreamReader _in = null; |
byte[] digest = new byte[16]; |
StringBuilder zBuf = new StringBuilder( 10240 ); |
|
if ( argc != 2 ) |
{ |
TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], |
" FILENAME\"", 0 ); |
return TCL.TCL_ERROR; |
} |
Debugger.Break(); // TODO -- _in = fopen( argv[1], "rb" ); |
if ( _in == null ) |
{ |
TCL.Tcl_AppendResult( interp, "unable to open file \"", argv[1], |
"\" for reading", 0 ); |
return TCL.TCL_ERROR; |
} |
Debugger.Break(); // TODO |
//MD5Init( ctx ); |
//for(;;){ |
// int n; |
// n = fread(zBuf, 1, zBuf.Capacity, _in); |
// if( n<=0 ) break; |
// MD5Update(ctx, zBuf.ToString(), (unsigned)n); |
//} |
//fclose(_in); |
//MD5Final(digest, ctx); |
// DigestToBase16(digest, zBuf); |
//Tcl_AppendResult( interp, zBuf ); |
return TCL.TCL_OK; |
} |
|
/* |
** Register the four new TCL commands for generating MD5 checksums |
** with the TCL interpreter. |
*/ |
static public int Md5_Init( Tcl_Interp interp ) |
{ |
TCL.Tcl_CreateCommand( interp, "md5", md5_cmd, null, null ); |
//Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc)md5_cmd, |
// MD5DigestToBase10x8, 0); |
|
TCL.Tcl_CreateCommand( interp, "md5file", md5file_cmd, null, null ); |
|
//Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc)md5file_cmd, |
// MD5DigestToBase10x8, 0); |
return TCL.TCL_OK; |
} |
#endif //* defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) */ |
|
#if (SQLITE_TEST) |
/* |
** During testing, the special md5sum() aggregate function is available. |
** inside SQLite. The following routines implement that function. |
*/ |
static void md5step( sqlite3_context context, int argc, sqlite3_value[] argv ) |
{ |
MD5Context p = null; |
int i; |
if ( argc < 1 ) |
return; |
Mem pMem = sqlite3_aggregate_context( context, 1 );//sizeof(*p)); |
if ( pMem._MD5Context == null ) |
{ |
pMem._MD5Context = new MD5Context(); |
( (MD5Context)pMem._MD5Context )._Mem = pMem; |
} |
p = (MD5Context)pMem._MD5Context; |
if ( p == null ) |
return; |
if ( !p.isInit ) |
{ |
MD5Init( p ); |
} |
for ( i = 0; i < argc; i++ ) |
{ |
byte[] zData = sqlite3_value_text( argv[i] ) == null ? null : Encoding.UTF8.GetBytes( sqlite3_value_text( argv[i] ) ); |
if ( zData != null ) |
{ |
MD5Update( p, zData, zData.Length ); |
} |
} |
} |
|
static void md5finalize( sqlite3_context context ) |
{ |
MD5Context p; |
byte[] digest = new byte[16]; |
byte[] zBuf = new byte[33]; |
Mem pMem = sqlite3_aggregate_context( context, 0 ); |
if ( pMem != null ) |
{ |
p = (MD5Context)pMem._MD5Context; |
MD5Final( digest, p ); |
} |
DigestToBase16( digest, zBuf ); |
sqlite3_result_text( context, Encoding.UTF8.GetString( zBuf, 0, zBuf.Length ), -1, SQLITE_TRANSIENT ); |
} |
|
static int Md5_Register( sqlite3 db, ref string dummy1, sqlite3_api_routines dummy2 ) |
{ |
int rc = sqlite3_create_function( db, "md5sum", -1, SQLITE_UTF8, 0, null, |
md5step, md5finalize ); |
sqlite3_overload_function( db, "md5sum", -1 ); /* To exercise this API */ |
return rc; |
} |
|
#endif //* defined(SQLITE_TEST) */ |
|
|
/* |
** If the macro TCLSH is one, then put in code this for the |
** "main" routine that will initialize Tcl and take input from |
** standard input, or if a file is named on the command line |
** the TCL interpreter reads and evaluates that file. |
*/ |
#if TCLSH//==1 |
//static char zMainloop[] = |
// "set line {}\n" |
// "while {![eof stdin]} {\n" |
// "if {$line!=\"\"} {\n" |
// "puts -nonewline \"> \"\n" |
// "} else {\n" |
// "puts -nonewline \"% \"\n" |
// "}\n" |
// "flush stdout\n" |
// "append line [gets stdin]\n" |
// "if {[info complete $line]} {\n" |
// "if {[catch {uplevel #0 $line} result]} {\n" |
// "puts stderr \"Error: $result\"\n" |
// "} elseif {$result!=\"\"} {\n" |
// "puts $result\n" |
// "}\n" |
// "set line {}\n" |
// "} else {\n" |
// "append line \\n\n" |
// "}\n" |
// "}\n" |
//; |
#endif |
|
//#if TCLSH==2 |
//static char zMainloop[] = |
//#include "spaceanal_tcl.h" |
//; |
//#endif |
//#define TCLSH_MAIN main /* Needed to fake out mktclapp */ |
#if SQLITE_TEST |
//static void init_all(Tcl_Interp ); |
static int init_all_cmd( |
ClientData cd, |
Tcl_Interp interp, |
int objc, |
Tcl_Obj[] objv |
) |
{ |
|
Tcl_Interp slave; |
if ( objc != 2 ) |
{ |
TCL.Tcl_WrongNumArgs( interp, 1, objv, "SLAVE" ); |
return TCL.TCL_ERROR; |
} |
|
slave = null;// TCL.Tcl_GetSlave( interp, TCL.Tcl_GetString( objv[1] ) ); |
if ( slave == null ) |
{ |
return TCL.TCL_ERROR; |
} |
|
init_all( slave ); |
return TCL.TCL_OK; |
} |
#endif |
|
/* |
** Configure the interpreter passed as the first argument to have access |
** to the commands and linked variables that make up: |
** |
** * the [sqlite3] extension itself, |
** |
** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and |
** |
** * If SQLITE_TEST is set, the various test interfaces used by the Tcl |
** test suite. |
*/ |
static void init_all( Tcl_Interp interp ) |
{ |
Sqlite3_Init( interp ); |
#if (SQLITE_TEST) || (SQLITE_TCLMD5) |
Md5_Init( interp ); |
#endif |
#if SQLITE_TEST |
//{ |
//extern int Sqliteconfig_Init(Tcl_Interp); |
//extern int Sqlitetest1_Init(Tcl_Interp); |
//extern int Sqlitetest2_Init(Tcl_Interp); |
//extern int Sqlitetest3_Init(Tcl_Interp); |
//extern int Sqlitetest4_Init(Tcl_Interp); |
//extern int Sqlitetest5_Init(Tcl_Interp); |
//extern int Sqlitetest6_Init(Tcl_Interp); |
//extern int Sqlitetest7_Init(Tcl_Interp); |
//extern int Sqlitetest8_Init(Tcl_Interp); |
//extern int Sqlitetest9_Init(Tcl_Interp); |
//extern int Sqlitetestasync_Init(Tcl_Interp); |
//extern int Sqlitetest_autoext_Init(Tcl_Interp); |
//extern int Sqlitetest_demovfs_Init(Tcl_Interp ); |
//extern int Sqlitetest_func_Init(Tcl_Interp); |
//extern int Sqlitetest_hexio_Init(Tcl_Interp); |
//extern int Sqlitetest_malloc_Init(Tcl_Interp); |
//extern int Sqlitetest_mutex_Init(Tcl_Interp); |
//extern int Sqlitetestschema_Init(Tcl_Interp); |
//extern int Sqlitetestsse_Init(Tcl_Interp); |
//extern int Sqlitetesttclvar_Init(Tcl_Interp); |
//extern int SqlitetestThread_Init(Tcl_Interp); |
//extern int SqlitetestOnefile_Init(); |
//extern int SqlitetestOsinst_Init(Tcl_Interp); |
//extern int Sqlitetestbackup_Init(Tcl_Interp); |
//extern int Sqlitetestintarray_Init(Tcl_Interp); |
//extern int Sqlitetestvfs_Init(Tcl_Interp ); |
//extern int SqlitetestStat_Init(Tcl_Interp); |
//extern int Sqlitetestrtree_Init(Tcl_Interp); |
//extern int Sqlitequota_Init(Tcl_Interp); |
//extern int Sqlitemultiplex_Init(Tcl_Interp); |
//extern int SqliteSuperlock_Init(Tcl_Interp); |
//extern int SqlitetestSyscall_Init(Tcl_Interp); |
//extern int Sqlitetestfuzzer_Init(Tcl_Interp); |
//extern int Sqlitetestwholenumber_Init(Tcl_Interp); |
|
#if (SQLITE_ENABLE_FTS3) || (SQLITE_ENABLE_FTS4) |
//extern int Sqlitetestfts3_Init(Tcl_Interp interp); |
#endif |
|
#if SQLITE_ENABLE_ZIPVFS |
// extern int Zipvfs_Init(Tcl_Interp); |
// Zipvfs_Init(interp); |
#endif |
|
Sqliteconfig_Init( interp ); |
Sqlitetest1_Init( interp ); |
Sqlitetest2_Init( interp ); |
Sqlitetest3_Init( interp ); |
|
//Threads not implemented under C# |
//Sqlitetest4_Init(interp); |
|
//TODO implemented under C# |
//Sqlitetest5_Init(interp); |
|
//Simulated Crashtests not implemented under C# |
//Sqlitetest6_Init(interp); |
|
//client/server version (Unix Only) not implemented under C# |
//Sqlitetest7_Init(interp); |
|
//virtual table interface not implemented under C# |
//Sqlitetest8_Init(interp); |
|
Sqlitetest9_Init( interp ); |
|
//asynchronous IO extension interface not implemented under C# |
//Sqlitetestasync_Init(interp); |
|
//sqlite3_auto_extension() function not implemented under C# |
//Sqlitetest_autoext_Init(interp); |
|
//VFS not implemented under C# |
//Sqlitetest_demovfs_Init(interp); |
|
Sqlitetest_func_Init( interp ); |
Sqlitetest_hexio_Init( interp ); |
Sqlitetest_malloc_Init( interp ); |
Sqlitetest_mutex_Init( interp ); |
|
//virtual table interfaces not implemented under C# |
//Sqlitetestschema_Init(interp); |
|
//virtual table interfaces not implemented under C# |
//Sqlitetesttclvar_Init(interp); |
|
//Threads not implemented under C# |
//SqlitetestThread_Init(interp); |
|
//VFS not implemented under C# |
//SqlitetestOnefile_Init(interp); |
|
//VFS not implemented under C# |
//SqlitetestOsinst_Init(interp); |
|
Sqlitetestbackup_Init( interp ); |
|
//virtual table interfaces not implemented under C# |
//Sqlitetestintarray_Init(interp); |
|
//VFS not implemented under C# |
//Sqlitetestvfs_Init(interp); |
|
//virtual table interfaces not implemented under C# |
//SqlitetestStat_Init(interp); |
//Sqlitetestrtree_Init( interp ); |
//Sqlitequota_Init( interp ); |
//Sqlitemultiplex_Init( interp ); |
//SqliteSuperlock_Init( interp ); |
//SqlitetestSyscall_Init( interp ); |
Sqlitetestfuzzer_Init( interp ); |
//Sqlitetestwholenumber_Init( interp ); |
|
#if (SQLITE_ENABLE_FTS3) || (SQLITE_ENABLE_FTS4) |
//Sqlitetestfts3_Init(interp); |
#endif |
TCL.Tcl_CreateObjCommand( interp, "load_testfixture_extensions", init_all_cmd, 0, null ); |
|
#if SQLITE_SSE |
Sqlitetestsse_Init(interp); |
#endif |
} |
#endif |
} |
|
#if FALSE |
//#define TCLSH_MAIN main /* Needed to fake out mktclapp */ |
int TCLSH_MAIN(int argc, char **argv){ |
Tcl_Interp interp; |
|
/* Call sqlite3_shutdown() once before doing anything else. This is to |
** test that sqlite3_shutdown() can be safely called by a process before |
** sqlite3_initialize() is. */ |
sqlite3_shutdown(); |
|
#if TCLSH//TCLSH==2 |
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); |
#endif |
Tcl_FindExecutable(argv[0]); |
|
interp = TCL.Tcl_CreateInterp(); |
init_all(interp); |
if( argc>=2 ){ |
int i; |
char zArgc[32]; |
sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); |
Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); |
Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); |
Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); |
for(i=3-TCLSH; i<argc; i++){ |
Tcl_SetVar(interp, "argv", argv[i], |
TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE); |
} |
if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){ |
string zInfo = TCL.Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); |
if( zInfo==0 ) zInfo = TCL.Tcl_GetStringResult(interp); |
fprintf(stderr,"%s: %s\n", *argv, zInfo); |
return 1; |
} |
} |
if( TCLSH==2 || argc<=1 ){ |
Tcl_GlobalEval(interp, zMainloop); |
} |
return 0; |
} |
#endif |
#endif // * TCLSH */ |
#endif // NO_TCL |
} |
|