wasCSharpSQLite – Rev 4

Subversion Repositories:
Rev:
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
}