wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
using System.Diagnostics;

using i64 = System.Int64;
using u32 = System.UInt32;

namespace Community.CsharpSqlite
{
#if TCLSH
  using tcl.lang;
  using Tcl_CmdProc = tcl.lang.Interp.dxObjCmdProc;
  using Tcl_Interp = tcl.lang.Interp;
  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.
    **
    *************************************************************************
    ** Code for testing the btree.c module in SQLite.  This code
    ** is not included in the SQLite library.  It is used for automated
    ** testing of the SQLite library.
    *************************************************************************
    **  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 "sqliteInt.h"
    //#include "btreeInt.h"
    //#include "tcl.h"
    //#include <stdlib.h>
    //#include <string.h>

    /*
    ** Interpret an SQLite error number
    */
    static string errorName( int rc )
    {
      string zName;
      switch ( rc )
      {
        case SQLITE_OK:
          zName = "SQLITE_OK";
          break;
        case SQLITE_ERROR:
          zName = "SQLITE_ERROR";
          break;
        case SQLITE_PERM:
          zName = "SQLITE_PERM";
          break;
        case SQLITE_ABORT:
          zName = "SQLITE_ABORT";
          break;
        case SQLITE_BUSY:
          zName = "SQLITE_BUSY";
          break;
        case SQLITE_NOMEM:
          zName = "SQLITE_NOMEM";
          break;
        case SQLITE_READONLY:
          zName = "SQLITE_READONLY";
          break;
        case SQLITE_INTERRUPT:
          zName = "SQLITE_INTERRUPT";
          break;
        case SQLITE_IOERR:
          zName = "SQLITE_IOERR";
          break;
        case SQLITE_CORRUPT:
          zName = "SQLITE_CORRUPT";
          break;
        case SQLITE_FULL:
          zName = "SQLITE_FULL";
          break;
        case SQLITE_CANTOPEN:
          zName = "SQLITE_CANTOPEN";
          break;
        case SQLITE_PROTOCOL:
          zName = "SQLITE_PROTOCOL";
          break;
        case SQLITE_EMPTY:
          zName = "SQLITE_EMPTY";
          break;
        case SQLITE_LOCKED:
          zName = "SQLITE_LOCKED";
          break;
        default:
          zName = "SQLITE_Unknown";
          break;
      }
      return zName;
    }

    /*
    ** A bogus sqlite3 connection structure for use in the btree
    ** tests.
    */
    static sqlite3 sDb = new sqlite3();
    static int nRefSqlite3 = 0;

    /*
    ** Usage:   btree_open FILENAME NCACHE
    **
    ** Open a new database
    */
    static int btree_open(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt = null;
      int rc;
      int nCache = 0;
      StringBuilder zBuf = new StringBuilder( 100 );
      if ( argc != 3 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " FILENAME NCACHE\"", "" );
        return TCL.TCL_ERROR;
      }
      if ( TCL.Tcl_GetInt( interp, argv[2], out nCache ) )
        return TCL.TCL_ERROR;
      nRefSqlite3++;
      if ( nRefSqlite3 == 1 )
      {
        sDb.pVfs = sqlite3_vfs_find( null );
        sDb.mutex = sqlite3MutexAlloc( SQLITE_MUTEX_RECURSIVE );
        sqlite3_mutex_enter( sDb.mutex );
      }
      rc = sqlite3BtreeOpen( sDb.pVfs, argv[1].ToString(), sDb, ref pBt, 0,
      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB );
      if ( rc != SQLITE_OK )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        return TCL.TCL_ERROR;
      }
      sqlite3BtreeSetCacheSize( pBt, nCache );
      sqlite3_snprintf( 100, zBuf, "->%p", pBt );
      if ( TCL.Tcl_CreateCommandPointer( interp, zBuf, pBt ) )
      {
        return TCL.TCL_ERROR;
      }
      else
        TCL.Tcl_AppendResult( interp, zBuf, null );
      return TCL.TCL_OK;
    }

    /*
    ** Usage:   btree_close ID
    **
    ** Close the given database.
    */
    static int btree_close(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int rc;
      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"", null );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      rc = sqlite3BtreeClose( ref pBt );
      if ( rc != SQLITE_OK )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        return TCL.TCL_ERROR;
      }
      nRefSqlite3--;
      if ( nRefSqlite3 == 0 )
      {
        sqlite3_mutex_leave( sDb.mutex );
        sqlite3_mutex_free( sDb.mutex );
        sDb.mutex = null;
        sDb.pVfs = null;
      }
      return TCL.TCL_OK;
    }


    /*
    ** Usage:   btree_begin_transaction ID
    **
    ** Start a new transaction
    */
    static int btree_begin_transaction(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int rc;
      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"", null );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      sqlite3BtreeEnter( pBt );
      rc = sqlite3BtreeBeginTrans( pBt, 1 );
      sqlite3BtreeLeave( pBt );
      if ( rc != SQLITE_OK )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      return TCL.TCL_OK;
    }

    /*
    ** Usage:   btree_pager_stats ID
    **
    ** Returns pager statistics
    */
    static int btree_pager_stats(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int i;
      int[] a;

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );

      /* Normally in this file, with a b-tree handle opened using the
      ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
      ** But this function is sometimes called with a btree handle obtained
      ** from an open SQLite connection (using [btree_from_db]). In this case
      ** we need to obtain the mutex for the controlling SQLite handle before
      ** it is safe to call sqlite3BtreeEnter().
      */
      sqlite3_mutex_enter( pBt.db.mutex );

      sqlite3BtreeEnter( pBt );
      a = sqlite3PagerStats( sqlite3BtreePager( pBt ) );
      for ( i = 0; i < 11; i++ )
      {
        string[] zName = new string[]{
"ref", "page", "max", "size", "state", "err",
"hit", "miss", "ovfl", "read", "write"
};
        StringBuilder zBuf = new StringBuilder( 100 );
        TCL.Tcl_AppendElement( interp, zName[i] );
        sqlite3_snprintf( 100, zBuf, "%d", a[i] );
        TCL.Tcl_AppendElement( interp, zBuf );
      }
      sqlite3BtreeLeave( pBt );
      /* Release the mutex on the SQLite handle that controls this b-tree */
      sqlite3_mutex_leave( pBt.db.mutex );
      return TCL.TCL_OK;
    }

    /*
    ** Usage:   btree_cursor ID TABLENUM WRITEABLE
    **
    ** Create a new cursor.  Return the ID for the cursor.
    */
    static int btree_cursor(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int iTable = 0;
      BtCursor pCur;
      int rc = SQLITE_OK;
      int wrFlag = 0;
      StringBuilder zBuf = new StringBuilder( 30 );

      if ( argc != 4 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID TABLENUM WRITEABLE\"" );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      if ( TCL.Tcl_GetInt( interp, argv[2], out iTable ) )
        return TCL.TCL_ERROR;
      if ( TCL.Tcl_GetBoolean( interp, argv[3], out wrFlag ) )
        return TCL.TCL_ERROR;
      //pCur = (BtCursor )ckalloc(sqlite3BtreeCursorSize());
      pCur = new BtCursor();// memset( pCur, 0, sqlite3BtreeCursorSize() );
      sqlite3BtreeEnter( pBt );
#if !SQLITE_OMIT_SHARED_CACHE
      rc = sqlite3BtreeLockTable( pBt, iTable, wrFlag );
#endif
      if ( rc == SQLITE_OK )
      {
        rc = sqlite3BtreeCursor( pBt, iTable, wrFlag, null, pCur );
      }
      sqlite3BtreeLeave( pBt );
      if ( rc != 0 )
      {
        pCur = null;// ckfree( pCur );
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      sqlite3_snprintf( 30, zBuf, "->%p", pCur );
      if ( TCL.Tcl_CreateCommandPointer( interp, zBuf, pCur ) )
      {
        return TCL.TCL_ERROR;
      }
      else
        TCL.Tcl_AppendResult( interp, zBuf );
      return SQLITE_OK;
    }

    /*
    ** Usage:   btree_close_cursor ID
    **
    ** Close a cursor opened using btree_cursor.
    */
    static int btree_close_cursor(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      BtCursor pCur;
      Btree pBt;
      int rc;

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pCur = (BtCursor)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      pBt = pCur.pBtree;
      sqlite3BtreeEnter( pBt );
      rc = sqlite3BtreeCloseCursor( pCur );
      sqlite3BtreeLeave( pBt );
      pCur = null;//ckfree( (char*)pCur );
      if ( rc != 0 )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      return SQLITE_OK;
    }

    /*
    ** Usage:   btree_next ID
    **
    ** Move the cursor to the next entry in the table.  Return 0 on success
    ** or 1 if the cursor was already on the last entry in the table or if
    ** the table is empty.
    */
    static int btree_next(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      BtCursor pCur;
      int rc;
      int res = 0;
      StringBuilder zBuf = new StringBuilder( 100 );

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pCur = (BtCursor)sqlite3TestTextToPtr( interp, argv[1].ToString() );
#if SQLITE_TEST
      sqlite3BtreeEnter( pCur.pBtree );
#endif
      rc = sqlite3BtreeNext( pCur, ref res );
#if SQLITE_TEST
      sqlite3BtreeLeave( pCur.pBtree );
#endif
      if ( rc != 0 )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      sqlite3_snprintf( 100, zBuf, "%d", res );
      TCL.Tcl_AppendResult( interp, zBuf );
      return SQLITE_OK;
    }

    /*
    ** Usage:   btree_first ID
    **
    ** Move the cursor to the first entry in the table.  Return 0 if the
    ** cursor was left point to something and 1 if the table is empty.
    */
    static int btree_first(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      BtCursor pCur;
      int rc;
      int res = 0;
      StringBuilder zBuf = new StringBuilder( 100 );

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pCur = (BtCursor)sqlite3TestTextToPtr( interp, argv[1].ToString() );
#if SQLITE_TEST
      sqlite3BtreeEnter( pCur.pBtree );
#endif
      rc = sqlite3BtreeFirst( pCur, ref res );
#if SQLITE_TEST
      sqlite3BtreeLeave( pCur.pBtree );
#endif
      if ( rc != 0 )
      {
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      sqlite3_snprintf( 100, zBuf, "%d", res );
      TCL.Tcl_AppendResult( interp, zBuf );
      return SQLITE_OK;
    }

    /*
    ** Usage:   btree_eof ID
    **
    ** Return TRUE if the given cursor is not pointing at a valid entry.
    ** Return FALSE if the cursor does point to a valid entry.
    */
    //static int btree_eof(
    //  object NotUsed,
    // Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    //  int argc,              /* Number of arguments */
    //  TclObject[] argv      /* Text of each argument */
    //){
    //  BtCursor pCur;
    //  int rc;
    //  char zBuf[50];

    //  if( argc!=2 ){
    //   TCL.Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0].ToString(),
    //       " ID\"");
    //    return TCL.TCL_ERROR;
    //  }
    //  pCur = (BtCursor)sqlite3TestTextToPtr(interp,argv[1].ToString());
    //  sqlite3BtreeEnter(pCur.pBtree);
    //  rc = sqlite3BtreeEof(pCur);
    //  sqlite3BtreeLeave(pCur.pBtree);
    //  sqlite3_snprintf(100, ref zBuf, "%d", rc);
    // TCL.Tcl_AppendResult(interp, zBuf);
    //  return SQLITE_OK;
    //}

    /*
    ** Usage:   btree_payload_size ID
    **
    ** Return the number of bytes of payload
    */
    static int btree_payload_size(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      BtCursor pCur;
      i64 n1 = 0;
      u32 n2 = 0;
      StringBuilder zBuf = new StringBuilder( 50 );

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pCur = (BtCursor)sqlite3TestTextToPtr( interp, argv[1].ToString() );
#if SQLITE_TEST
      sqlite3BtreeEnter( pCur.pBtree );
#endif

      /* The cursor may be in "require-seek" state. If this is the case, the
  ** call to BtreeDataSize() will fix it. */
      sqlite3BtreeDataSize( pCur, ref n2 );
      if ( pCur.apPage[pCur.iPage].intKey != 0 )
      {
        n1 = 0;
      }
      else
      {
        sqlite3BtreeKeySize( pCur, ref n1 );
      }
      sqlite3BtreeLeave( pCur.pBtree );
      sqlite3_snprintf( 30, zBuf, "%d", (int)( n1 + n2 ) );
      TCL.Tcl_AppendResult( interp, zBuf );
      return SQLITE_OK;
    }

    /*
    ** usage:   varint_test  START  MULTIPLIER  COUNT  INCREMENT
    **
    ** This command tests the putVarint() and getVarint()
    ** routines, both for accuracy and for speed.
    **
    ** An integer is written using putVarint() and read back with
    ** getVarint() and varified to be unchanged.  This repeats COUNT
    ** times.  The first integer is START*MULTIPLIER.  Each iteration
    ** increases the integer by INCREMENT.
    **
    ** This command returns nothing if it works.  It returns an error message
    ** if something goes wrong.
    */
    static int btree_varint_test(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that _invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      int start = 0, mult = 0, count = 0, incr = 0;
      int _in;
      u32 _out = 0;
      int n1, n2, i, j;
      byte[] zBuf = new byte[100];
      if ( argc != 5 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " START MULTIPLIER COUNT incrEMENT\"", 0 );
        return TCL.TCL_ERROR;
      }
      if ( TCL.Tcl_GetInt( interp, argv[1], out start ) )
        return TCL.TCL_ERROR;
      if ( TCL.Tcl_GetInt( interp, argv[2], out mult ) )
        return TCL.TCL_ERROR;
      if ( TCL.Tcl_GetInt( interp, argv[3], out count ) )
        return TCL.TCL_ERROR;
      if ( TCL.Tcl_GetInt( interp, argv[4], out incr ) )
        return TCL.TCL_ERROR;
      _in = start;
      _in *= mult;
      for ( i = 0; i < count; i++ )
      {
        StringBuilder zErr = new StringBuilder( 200 );
        n1 = putVarint( zBuf, 0, _in );
        if ( n1 > 9 || n1 < 1 )
        {
          sqlite3_snprintf( 100, zErr, "putVarint returned %d - should be between 1 and 9", n1 );
          TCL.Tcl_AppendResult( interp, zErr );
          return TCL.TCL_ERROR;
        }
        n2 = getVarint( zBuf, 0, out _out );
        if ( n1 != n2 )
        {
          sqlite3_snprintf( 100, zErr, "putVarint returned %d and GetVar_int returned %d", n1, n2 );
          TCL.Tcl_AppendResult( interp, zErr );
          return TCL.TCL_ERROR;
        }
        if ( _in != (int)_out )
        {
          sqlite3_snprintf( 100, zErr, "Wrote 0x%016llx and got back 0x%016llx", _in, _out );
          TCL.Tcl_AppendResult( interp, zErr );
          return TCL.TCL_ERROR;
        }
        if ( ( _in & 0xffffffff ) == _in )
        {
          u32 _out32 = 0;
          n2 = getVarint32( zBuf, out _out32 );
          _out = _out32;
          if ( n1 != n2 )
          {
            sqlite3_snprintf( 100, zErr, "putVarint returned %d and GetVar_int32 returned %d",
            n1, n2 );
            TCL.Tcl_AppendResult( interp, zErr );
            return TCL.TCL_ERROR;
          }
          if ( _in != (int)_out )
          {
            sqlite3_snprintf( 100, zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVar_int32",
            _in, _out );
            TCL.Tcl_AppendResult( interp, zErr );
            return TCL.TCL_ERROR;
          }
        }

        /* _in order to get realistic tim_ings, run getVar_int 19 more times.
        ** This is because getVar_int is called ab_out 20 times more often
        ** than putVarint.
        */
        for ( j = 0; j < 19; j++ )
        {
          getVarint( zBuf, 0, out _out );
        }
        _in += incr;
      }
      return TCL.TCL_OK;
    }

    /*
    ** usage:   btree_from_db  DB-HANDLE
    **
    ** This command returns the btree handle for the main database associated
    ** with the database-handle passed as the argument. Example usage:
    **
    ** sqlite3 db test.db
    ** set bt [btree_from_db db]
    */
    static int btree_from_db(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      StringBuilder zBuf = new StringBuilder( 100 );
      WrappedCommand info = null;
      sqlite3 db;
      Btree pBt;
      int iDb = 0;

      if ( argc != 2 && argc != 3 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " DB-HANDLE ?N?\"" );
        return TCL.TCL_ERROR;
      }

      if ( TCL.Tcl_GetCommandInfo( interp, argv[1].ToString(), out info ) )
      {
        TCL.Tcl_AppendResult( interp, "No such db-handle: \"", argv[1], "\"" );
        return TCL.TCL_ERROR;
      }
      if ( argc == 3 )
      {
        iDb = atoi( argv[2].ToString() );
      }

      db = ( (SqliteDb)info.objClientData ).db;
      Debug.Assert( db != null );

      pBt = db.aDb[iDb].pBt;
      sqlite3_snprintf( 50, zBuf, "->%p", pBt );
      if ( TCL.Tcl_CreateCommandPointer( interp, zBuf, pBt ) )
      {
        return TCL.TCL_ERROR;
      }
      else
        TCL.Tcl_SetResult( interp, zBuf, TCL.TCL_VOLATILE );
      return TCL.TCL_OK;
    }


    /*
    ** Usage:   btree_ismemdb ID
    **
    ** Return true if the B-Tree is in-memory.
    */
    static int btree_ismemdb(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int res;

      if ( argc != 2 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0],
        " ID\"" );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      sqlite3_mutex_enter( pBt.db.mutex );
      sqlite3BtreeEnter( pBt );
      res = sqlite3PagerIsMemdb( sqlite3BtreePager( pBt ) ) ? 1 : 0;
      sqlite3BtreeLeave( pBt );
      sqlite3_mutex_leave( pBt.db.mutex );
      TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewBooleanObj( res ) );
      return TCL.TCL_OK;
    }

    /*
    ** usage:   btree_set_cache_size ID NCACHE
    **
    ** Set the size of the cache used by btree $ID.
    */
    //static int btree_set_cache_size(
    //  object NotUsed,
    // Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    //  int argc,              /* Number of arguments */
    //  TclObject[] argv      /* Text of each argument */
    //){
    //  int nCache;
    //  Btree pBt;

    //  if( argc!=3 ){
    //   TCL.Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0].ToString(),
    //       " BT NCACHE\"");
    //    return TCL.TCL_ERROR;
    //  }
    //  pBt = (Btree)sqlite3TestTextToPtr(interp,argv[1].ToString());
    //  if(TCL.Tcl_GetInt(interp, argv[2], nCache) ) return TCL.TCL_ERROR;

    //  sqlite3_mutex_enter(pBt.db.mutex);
    //  sqlite3BtreeEnter(pBt);
    //  sqlite3BtreeSetCacheSize(pBt, nCache);
    //  sqlite3BtreeLeave(pBt);
    //  sqlite3_mutex_leave(pBt.db.mutex);

    //  return TCL.TCL_OK;
    //}


    /*
    ** Register commands with the TCL interpreter.
    */
    public class _aCmd
    {
      public string zName;
      public Tcl_CmdProc xProc;

      public _aCmd( string zName, Tcl_CmdProc xProc )
      {
        this.zName = zName;
        this.xProc = xProc;
      }
    }


    public static int Sqlitetest3_Init( Tcl_Interp interp )
    {
      _aCmd[] aCmd = new _aCmd[] {
new _aCmd( "btree_open",               (Tcl_CmdProc)btree_open               ),
new _aCmd( "btree_close",              (Tcl_CmdProc)btree_close              ),
new _aCmd( "btree_begin_transaction",  (Tcl_CmdProc)btree_begin_transaction  ),
new _aCmd( "btree_pager_stats",        (Tcl_CmdProc)btree_pager_stats        ),
new _aCmd( "btree_cursor",             (Tcl_CmdProc)btree_cursor             ),
new _aCmd( "btree_close_cursor",       (Tcl_CmdProc)btree_close_cursor       ),
new _aCmd( "btree_next",               (Tcl_CmdProc)btree_next               ),
//new _aCmd( "btree_eof",                (Tcl_CmdProc)btree_eof                ),
new _aCmd( "btree_payload_size",       (Tcl_CmdProc)btree_payload_size       ),
new _aCmd( "btree_first",              (Tcl_CmdProc)btree_first              ),
new _aCmd( "btree_varint_test",        (Tcl_CmdProc)btree_varint_test        ),
new _aCmd( "btree_from_db",            (Tcl_CmdProc)btree_from_db            ),
new _aCmd( "btree_ismemdb",        (Tcl_CmdProc)btree_ismemdb       ),
//new _aCmd( "btree_set_cache_size",     (Tcl_CmdProc)btree_set_cache_size     ),
};
      int i;

      for ( i = 0; i < aCmd.Length; i++ )
      { //sizeof(aCmd)/sizeof(aCmd[0]); i++){
        TCL.Tcl_CreateCommand( interp, aCmd[i].zName, aCmd[i].xProc, null, null );
      }

      return TCL.TCL_OK;
    }
  }
#endif
}