modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test3.c (about)

     1  /*
     2  ** 2001 September 15
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  ** Code for testing the btree.c module in SQLite.  This code
    13  ** is not included in the SQLite library.  It is used for automated
    14  ** testing of the SQLite library.
    15  */
    16  #include "sqliteInt.h"
    17  #include "btreeInt.h"
    18  #if defined(INCLUDE_SQLITE_TCL_H)
    19  #  include "sqlite_tcl.h"
    20  #else
    21  #  include "tcl.h"
    22  #endif
    23  #include <stdlib.h>
    24  #include <string.h>
    25  
    26  extern const char *sqlite3ErrName(int);
    27  
    28  /*
    29  ** A bogus sqlite3 connection structure for use in the btree
    30  ** tests.
    31  */
    32  static sqlite3 sDb;
    33  static int nRefSqlite3 = 0;
    34  
    35  /*
    36  ** Usage:   btree_open FILENAME NCACHE
    37  **
    38  ** Open a new database
    39  */
    40  static int SQLITE_TCLAPI btree_open(
    41    void *NotUsed,
    42    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    43    int argc,              /* Number of arguments */
    44    const char **argv      /* Text of each argument */
    45  ){
    46    Btree *pBt;
    47    int rc, nCache;
    48    char zBuf[100];
    49    int n;
    50    char *zFilename;
    51    if( argc!=3 ){
    52      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
    53         " FILENAME NCACHE FLAGS\"", 0);
    54      return TCL_ERROR;
    55    }
    56    if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
    57    nRefSqlite3++;
    58    if( nRefSqlite3==1 ){
    59      sDb.pVfs = sqlite3_vfs_find(0);
    60      sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
    61      sqlite3_mutex_enter(sDb.mutex);
    62    }
    63    n = (int)strlen(argv[1]);
    64    zFilename = sqlite3_malloc( n+2 );
    65    if( zFilename==0 ) return TCL_ERROR;
    66    memcpy(zFilename, argv[1], n+1);
    67    zFilename[n+1] = 0;
    68    rc = sqlite3BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0, 
    69       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
    70    sqlite3_free(zFilename);
    71    if( rc!=SQLITE_OK ){
    72      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
    73      return TCL_ERROR;
    74    }
    75    sqlite3BtreeSetCacheSize(pBt, nCache);
    76    sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
    77    Tcl_AppendResult(interp, zBuf, 0);
    78    return TCL_OK;
    79  }
    80  
    81  /*
    82  ** Usage:   btree_close ID
    83  **
    84  ** Close the given database.
    85  */
    86  static int SQLITE_TCLAPI btree_close(
    87    void *NotUsed,
    88    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    89    int argc,              /* Number of arguments */
    90    const char **argv      /* Text of each argument */
    91  ){
    92    Btree *pBt;
    93    int rc;
    94    if( argc!=2 ){
    95      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
    96         " ID\"", 0);
    97      return TCL_ERROR;
    98    }
    99    pBt = sqlite3TestTextToPtr(argv[1]);
   100    rc = sqlite3BtreeClose(pBt);
   101    if( rc!=SQLITE_OK ){
   102      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   103      return TCL_ERROR;
   104    }
   105    nRefSqlite3--;
   106    if( nRefSqlite3==0 ){
   107      sqlite3_mutex_leave(sDb.mutex);
   108      sqlite3_mutex_free(sDb.mutex);
   109      sDb.mutex = 0;
   110      sDb.pVfs = 0;
   111    }
   112    return TCL_OK;
   113  }
   114  
   115  
   116  /*
   117  ** Usage:   btree_begin_transaction ID
   118  **
   119  ** Start a new transaction
   120  */
   121  static int SQLITE_TCLAPI btree_begin_transaction(
   122    void *NotUsed,
   123    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   124    int argc,              /* Number of arguments */
   125    const char **argv      /* Text of each argument */
   126  ){
   127    Btree *pBt;
   128    int rc;
   129    if( argc!=2 ){
   130      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   131         " ID\"", 0);
   132      return TCL_ERROR;
   133    }
   134    pBt = sqlite3TestTextToPtr(argv[1]);
   135    sqlite3BtreeEnter(pBt);
   136    rc = sqlite3BtreeBeginTrans(pBt, 1);
   137    sqlite3BtreeLeave(pBt);
   138    if( rc!=SQLITE_OK ){
   139      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   140      return TCL_ERROR;
   141    }
   142    return TCL_OK;
   143  }
   144  
   145  /*
   146  ** Usage:   btree_pager_stats ID
   147  **
   148  ** Returns pager statistics
   149  */
   150  static int SQLITE_TCLAPI btree_pager_stats(
   151    void *NotUsed,
   152    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   153    int argc,              /* Number of arguments */
   154    const char **argv      /* Text of each argument */
   155  ){
   156    Btree *pBt;
   157    int i;
   158    int *a;
   159  
   160    if( argc!=2 ){
   161      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   162         " ID\"", 0);
   163      return TCL_ERROR;
   164    }
   165    pBt = sqlite3TestTextToPtr(argv[1]);
   166   
   167    /* Normally in this file, with a b-tree handle opened using the 
   168    ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
   169    ** But this function is sometimes called with a btree handle obtained
   170    ** from an open SQLite connection (using [btree_from_db]). In this case
   171    ** we need to obtain the mutex for the controlling SQLite handle before
   172    ** it is safe to call sqlite3BtreeEnter().
   173    */
   174    sqlite3_mutex_enter(pBt->db->mutex);
   175  
   176    sqlite3BtreeEnter(pBt);
   177    a = sqlite3PagerStats(sqlite3BtreePager(pBt));
   178    for(i=0; i<11; i++){
   179      static char *zName[] = {
   180        "ref", "page", "max", "size", "state", "err",
   181        "hit", "miss", "ovfl", "read", "write"
   182      };
   183      char zBuf[100];
   184      Tcl_AppendElement(interp, zName[i]);
   185      sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);
   186      Tcl_AppendElement(interp, zBuf);
   187    }
   188    sqlite3BtreeLeave(pBt);
   189  
   190    /* Release the mutex on the SQLite handle that controls this b-tree */
   191    sqlite3_mutex_leave(pBt->db->mutex);
   192    return TCL_OK;
   193  }
   194  
   195  /*
   196  ** Usage:   btree_cursor ID TABLENUM WRITEABLE
   197  **
   198  ** Create a new cursor.  Return the ID for the cursor.
   199  */
   200  static int SQLITE_TCLAPI btree_cursor(
   201    void *NotUsed,
   202    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   203    int argc,              /* Number of arguments */
   204    const char **argv      /* Text of each argument */
   205  ){
   206    Btree *pBt;
   207    int iTable;
   208    BtCursor *pCur;
   209    int rc = SQLITE_OK;
   210    int wrFlag;
   211    char zBuf[30];
   212  
   213    if( argc!=4 ){
   214      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   215         " ID TABLENUM WRITEABLE\"", 0);
   216      return TCL_ERROR;
   217    }
   218    pBt = sqlite3TestTextToPtr(argv[1]);
   219    if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
   220    if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
   221    if( wrFlag ) wrFlag = BTREE_WRCSR;
   222    pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
   223    memset(pCur, 0, sqlite3BtreeCursorSize());
   224    sqlite3_mutex_enter(pBt->db->mutex);
   225    sqlite3BtreeEnter(pBt);
   226  #ifndef SQLITE_OMIT_SHARED_CACHE
   227    rc = sqlite3BtreeLockTable(pBt, iTable, !!wrFlag);
   228  #endif
   229    if( rc==SQLITE_OK ){
   230      rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
   231    }
   232    sqlite3BtreeLeave(pBt);
   233    sqlite3_mutex_leave(pBt->db->mutex);
   234    if( rc ){
   235      ckfree((char *)pCur);
   236      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   237      return TCL_ERROR;
   238    }
   239    sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
   240    Tcl_AppendResult(interp, zBuf, 0);
   241    return SQLITE_OK;
   242  }
   243  
   244  /*
   245  ** Usage:   btree_close_cursor ID
   246  **
   247  ** Close a cursor opened using btree_cursor.
   248  */
   249  static int SQLITE_TCLAPI btree_close_cursor(
   250    void *NotUsed,
   251    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   252    int argc,              /* Number of arguments */
   253    const char **argv      /* Text of each argument */
   254  ){
   255    BtCursor *pCur;
   256    int rc;
   257  
   258    if( argc!=2 ){
   259      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   260         " ID\"", 0);
   261      return TCL_ERROR;
   262    }
   263    pCur = sqlite3TestTextToPtr(argv[1]);
   264  #if SQLITE_THREADSAFE>0
   265    {
   266      Btree *pBt = pCur->pBtree;
   267      sqlite3_mutex_enter(pBt->db->mutex);
   268      sqlite3BtreeEnter(pBt);
   269      rc = sqlite3BtreeCloseCursor(pCur);
   270      sqlite3BtreeLeave(pBt);
   271      sqlite3_mutex_leave(pBt->db->mutex);
   272    }
   273  #else
   274    rc = sqlite3BtreeCloseCursor(pCur);
   275  #endif
   276    ckfree((char *)pCur);
   277    if( rc ){
   278      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   279      return TCL_ERROR;
   280    }
   281    return SQLITE_OK;
   282  }
   283  
   284  /*
   285  ** Usage:   btree_next ID
   286  **
   287  ** Move the cursor to the next entry in the table.  Return 0 on success
   288  ** or 1 if the cursor was already on the last entry in the table or if
   289  ** the table is empty.
   290  */
   291  static int SQLITE_TCLAPI btree_next(
   292    void *NotUsed,
   293    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   294    int argc,              /* Number of arguments */
   295    const char **argv      /* Text of each argument */
   296  ){
   297    BtCursor *pCur;
   298    int rc;
   299    int res = 0;
   300    char zBuf[100];
   301  
   302    if( argc!=2 ){
   303      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   304         " ID\"", 0);
   305      return TCL_ERROR;
   306    }
   307    pCur = sqlite3TestTextToPtr(argv[1]);
   308    sqlite3BtreeEnter(pCur->pBtree);
   309    rc = sqlite3BtreeNext(pCur, 0);
   310    if( rc==SQLITE_DONE ){
   311      res = 1;
   312      rc = SQLITE_OK;
   313    }
   314    sqlite3BtreeLeave(pCur->pBtree);
   315    if( rc ){
   316      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   317      return TCL_ERROR;
   318    }
   319    sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
   320    Tcl_AppendResult(interp, zBuf, 0);
   321    return SQLITE_OK;
   322  }
   323  
   324  /*
   325  ** Usage:   btree_first ID
   326  **
   327  ** Move the cursor to the first entry in the table.  Return 0 if the
   328  ** cursor was left point to something and 1 if the table is empty.
   329  */
   330  static int SQLITE_TCLAPI btree_first(
   331    void *NotUsed,
   332    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   333    int argc,              /* Number of arguments */
   334    const char **argv      /* Text of each argument */
   335  ){
   336    BtCursor *pCur;
   337    int rc;
   338    int res = 0;
   339    char zBuf[100];
   340  
   341    if( argc!=2 ){
   342      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   343         " ID\"", 0);
   344      return TCL_ERROR;
   345    }
   346    pCur = sqlite3TestTextToPtr(argv[1]);
   347    sqlite3BtreeEnter(pCur->pBtree);
   348    rc = sqlite3BtreeFirst(pCur, &res);
   349    sqlite3BtreeLeave(pCur->pBtree);
   350    if( rc ){
   351      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   352      return TCL_ERROR;
   353    }
   354    sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
   355    Tcl_AppendResult(interp, zBuf, 0);
   356    return SQLITE_OK;
   357  }
   358  
   359  /*
   360  ** Usage:   btree_eof ID
   361  **
   362  ** Return TRUE if the given cursor is not pointing at a valid entry.
   363  ** Return FALSE if the cursor does point to a valid entry.
   364  */
   365  static int SQLITE_TCLAPI btree_eof(
   366    void *NotUsed,
   367    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   368    int argc,              /* Number of arguments */
   369    const char **argv      /* Text of each argument */
   370  ){
   371    BtCursor *pCur;
   372    int rc;
   373    char zBuf[50];
   374  
   375    if( argc!=2 ){
   376      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   377         " ID\"", 0);
   378      return TCL_ERROR;
   379    }
   380    pCur = sqlite3TestTextToPtr(argv[1]);
   381    sqlite3BtreeEnter(pCur->pBtree);
   382    rc = sqlite3BtreeEof(pCur);
   383    sqlite3BtreeLeave(pCur->pBtree);
   384    sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
   385    Tcl_AppendResult(interp, zBuf, 0);
   386    return SQLITE_OK;
   387  }
   388  
   389  /*
   390  ** Usage:   btree_payload_size ID
   391  **
   392  ** Return the number of bytes of payload
   393  */
   394  static int SQLITE_TCLAPI btree_payload_size(
   395    void *NotUsed,
   396    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   397    int argc,              /* Number of arguments */
   398    const char **argv      /* Text of each argument */
   399  ){
   400    BtCursor *pCur;
   401    u32 n;
   402    char zBuf[50];
   403  
   404    if( argc!=2 ){
   405      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   406         " ID\"", 0);
   407      return TCL_ERROR;
   408    }
   409    pCur = sqlite3TestTextToPtr(argv[1]);
   410    sqlite3BtreeEnter(pCur->pBtree);
   411    n = sqlite3BtreePayloadSize(pCur);
   412    sqlite3BtreeLeave(pCur->pBtree);
   413    sqlite3_snprintf(sizeof(zBuf),zBuf, "%u", n);
   414    Tcl_AppendResult(interp, zBuf, 0);
   415    return SQLITE_OK;
   416  }
   417  
   418  /*
   419  ** usage:   varint_test  START  MULTIPLIER  COUNT  INCREMENT
   420  **
   421  ** This command tests the putVarint() and getVarint()
   422  ** routines, both for accuracy and for speed.
   423  **
   424  ** An integer is written using putVarint() and read back with
   425  ** getVarint() and varified to be unchanged.  This repeats COUNT
   426  ** times.  The first integer is START*MULTIPLIER.  Each iteration
   427  ** increases the integer by INCREMENT.
   428  **
   429  ** This command returns nothing if it works.  It returns an error message
   430  ** if something goes wrong.
   431  */
   432  static int SQLITE_TCLAPI btree_varint_test(
   433    void *NotUsed,
   434    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   435    int argc,              /* Number of arguments */
   436    const char **argv      /* Text of each argument */
   437  ){
   438    u32 start, mult, count, incr;
   439    u64 in, out;
   440    int n1, n2, i, j;
   441    unsigned char zBuf[100];
   442    if( argc!=5 ){
   443      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   444         " START MULTIPLIER COUNT INCREMENT\"", 0);
   445      return TCL_ERROR;
   446    }
   447    if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
   448    if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
   449    if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
   450    if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
   451    in = start;
   452    in *= mult;
   453    for(i=0; i<(int)count; i++){
   454      char zErr[200];
   455      n1 = putVarint(zBuf, in);
   456      if( n1>9 || n1<1 ){
   457        sqlite3_snprintf(sizeof(zErr), zErr,
   458           "putVarint returned %d - should be between 1 and 9", n1);
   459        Tcl_AppendResult(interp, zErr, 0);
   460        return TCL_ERROR;
   461      }
   462      n2 = getVarint(zBuf, &out);
   463      if( n1!=n2 ){
   464        sqlite3_snprintf(sizeof(zErr), zErr,
   465            "putVarint returned %d and getVarint returned %d", n1, n2);
   466        Tcl_AppendResult(interp, zErr, 0);
   467        return TCL_ERROR;
   468      }
   469      if( in!=out ){
   470        sqlite3_snprintf(sizeof(zErr), zErr,
   471            "Wrote 0x%016llx and got back 0x%016llx", in, out);
   472        Tcl_AppendResult(interp, zErr, 0);
   473        return TCL_ERROR;
   474      }
   475      if( (in & 0xffffffff)==in ){
   476        u32 out32;
   477        n2 = getVarint32(zBuf, out32);
   478        out = out32;
   479        if( n1!=n2 ){
   480          sqlite3_snprintf(sizeof(zErr), zErr,
   481            "putVarint returned %d and GetVarint32 returned %d", 
   482                    n1, n2);
   483          Tcl_AppendResult(interp, zErr, 0);
   484          return TCL_ERROR;
   485        }
   486        if( in!=out ){
   487          sqlite3_snprintf(sizeof(zErr), zErr,
   488            "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
   489              in, out);
   490          Tcl_AppendResult(interp, zErr, 0);
   491          return TCL_ERROR;
   492        }
   493      }
   494  
   495      /* In order to get realistic timings, run getVarint 19 more times.
   496      ** This is because getVarint is called about 20 times more often
   497      ** than putVarint.
   498      */
   499      for(j=0; j<19; j++){
   500        getVarint(zBuf, &out);
   501      }
   502      in += incr;
   503    }
   504    return TCL_OK;
   505  }
   506  
   507  /*
   508  ** usage:   btree_from_db  DB-HANDLE
   509  **
   510  ** This command returns the btree handle for the main database associated
   511  ** with the database-handle passed as the argument. Example usage:
   512  **
   513  ** sqlite3 db test.db
   514  ** set bt [btree_from_db db]
   515  */
   516  static int SQLITE_TCLAPI btree_from_db(
   517    void *NotUsed,
   518    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   519    int argc,              /* Number of arguments */
   520    const char **argv      /* Text of each argument */
   521  ){
   522    char zBuf[100];
   523    Tcl_CmdInfo info;
   524    sqlite3 *db;
   525    Btree *pBt;
   526    int iDb = 0;
   527  
   528    if( argc!=2 && argc!=3 ){
   529      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   530         " DB-HANDLE ?N?\"", 0);
   531      return TCL_ERROR;
   532    }
   533  
   534    if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
   535      Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
   536      return TCL_ERROR;
   537    }
   538    if( argc==3 ){
   539      iDb = atoi(argv[2]);
   540    }
   541  
   542    db = *((sqlite3 **)info.objClientData);
   543    assert( db );
   544  
   545    pBt = db->aDb[iDb].pBt;
   546    sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
   547    Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
   548    return TCL_OK;
   549  }
   550  
   551  /*
   552  ** Usage:   btree_ismemdb ID
   553  **
   554  ** Return true if the B-Tree is currently stored entirely in memory.
   555  */
   556  static int SQLITE_TCLAPI btree_ismemdb(
   557    void *NotUsed,
   558    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   559    int argc,              /* Number of arguments */
   560    const char **argv      /* Text of each argument */
   561  ){
   562    Btree *pBt;
   563    int res;
   564    sqlite3_file *pFile;
   565  
   566    if( argc!=2 ){
   567      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
   568         " ID\"", 0);
   569      return TCL_ERROR;
   570    }
   571    pBt = sqlite3TestTextToPtr(argv[1]);
   572    sqlite3_mutex_enter(pBt->db->mutex);
   573    sqlite3BtreeEnter(pBt);
   574    pFile = sqlite3PagerFile(sqlite3BtreePager(pBt));
   575    res = (pFile->pMethods==0);
   576    sqlite3BtreeLeave(pBt);
   577    sqlite3_mutex_leave(pBt->db->mutex);
   578    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
   579    return SQLITE_OK;
   580  }
   581  
   582  /*
   583  ** usage:   btree_set_cache_size ID NCACHE
   584  **
   585  ** Set the size of the cache used by btree $ID.
   586  */
   587  static int SQLITE_TCLAPI btree_set_cache_size(
   588    void *NotUsed,
   589    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   590    int argc,              /* Number of arguments */
   591    const char **argv      /* Text of each argument */
   592  ){
   593    int nCache;
   594    Btree *pBt;
   595    
   596    if( argc!=3 ){
   597      Tcl_AppendResult(
   598          interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0);
   599      return TCL_ERROR;
   600    }
   601    pBt = sqlite3TestTextToPtr(argv[1]);
   602    if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
   603  
   604    sqlite3_mutex_enter(pBt->db->mutex);
   605    sqlite3BtreeEnter(pBt);
   606    sqlite3BtreeSetCacheSize(pBt, nCache);
   607    sqlite3BtreeLeave(pBt);
   608    sqlite3_mutex_leave(pBt->db->mutex);
   609    return TCL_OK;
   610  }      
   611  
   612  /*
   613  ** usage:   btree_insert CSR ?KEY? VALUE
   614  **
   615  ** Set the size of the cache used by btree $ID.
   616  */
   617  static int SQLITE_TCLAPI btree_insert(
   618    ClientData clientData,
   619    Tcl_Interp *interp,
   620    int objc,
   621    Tcl_Obj *const objv[]
   622  ){
   623    BtCursor *pCur;
   624    int rc;
   625    BtreePayload x;
   626  
   627    if( objc!=4 && objc!=3 ){
   628      Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE");
   629      return TCL_ERROR;
   630    }
   631  
   632    memset(&x, 0, sizeof(x));
   633    if( objc==4 ){
   634      if( Tcl_GetIntFromObj(interp, objv[2], &rc) ) return TCL_ERROR;
   635      x.nKey = rc;
   636      x.pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &x.nData);
   637    }else{
   638      x.pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &rc);
   639      x.nKey = rc;
   640    }
   641    pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
   642  
   643    sqlite3_mutex_enter(pCur->pBtree->db->mutex);
   644    sqlite3BtreeEnter(pCur->pBtree);
   645    rc = sqlite3BtreeInsert(pCur, &x, 0, 0);
   646    sqlite3BtreeLeave(pCur->pBtree);
   647    sqlite3_mutex_leave(pCur->pBtree->db->mutex);
   648  
   649    Tcl_ResetResult(interp);
   650    if( rc ){
   651      Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
   652      return TCL_ERROR;
   653    }
   654    return TCL_OK;
   655  }
   656  
   657  
   658  /*
   659  ** Register commands with the TCL interpreter.
   660  */
   661  int Sqlitetest3_Init(Tcl_Interp *interp){
   662    static struct {
   663       char *zName;
   664       Tcl_CmdProc *xProc;
   665    } aCmd[] = {
   666       { "btree_open",               (Tcl_CmdProc*)btree_open               },
   667       { "btree_close",              (Tcl_CmdProc*)btree_close              },
   668       { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
   669       { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
   670       { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
   671       { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
   672       { "btree_next",               (Tcl_CmdProc*)btree_next               },
   673       { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
   674       { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
   675       { "btree_first",              (Tcl_CmdProc*)btree_first              },
   676       { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
   677       { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
   678       { "btree_ismemdb",            (Tcl_CmdProc*)btree_ismemdb            },
   679       { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     }
   680    };
   681    int i;
   682  
   683    for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
   684      Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
   685    }
   686  
   687    Tcl_CreateObjCommand(interp, "btree_insert", btree_insert, 0, 0);
   688  
   689    return TCL_OK;
   690  }