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