persistentstorage/sqlite3api/TEST/SRC/test3.c
changeset 0 08ec8eefde2f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sqlite3api/TEST/SRC/test3.c	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1605 @@
+/*
+** 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.
+**
+** $Id: test3.c,v 1.101 2008/08/13 19:11:48 drh Exp $
+*/
+#include "sqliteInt.h"
+#include "btreeInt.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+** Interpret an SQLite error number
+*/
+static char *errorName(int rc){
+  char *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;
+static int nRefSqlite3 = 0;
+
+/*
+** Usage:   btree_open FILENAME NCACHE FLAGS
+**
+** Open a new database
+*/
+static int btree_open(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc, nCache, flags;
+  char zBuf[100];
+  if( argc!=4 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " FILENAME NCACHE FLAGS\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR;
+  nRefSqlite3++;
+  if( nRefSqlite3==1 ){
+    sDb.pVfs = sqlite3_vfs_find(0);
+    sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
+    sqlite3_mutex_enter(sDb.mutex);
+  }
+  rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags,
+     SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3BtreeSetCacheSize(pBt, nCache);
+  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_close ID
+**
+** Close the given database.
+*/
+static int btree_close(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  rc = sqlite3BtreeClose(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  nRefSqlite3--;
+  if( nRefSqlite3==0 ){
+    sqlite3_mutex_leave(sDb.mutex);
+    sqlite3_mutex_free(sDb.mutex);
+    sDb.mutex = 0;
+    sDb.pVfs = 0;
+  }
+  return TCL_OK;
+}
+
+
+/*
+** Usage:   btree_begin_transaction ID
+**
+** Start a new transaction
+*/
+static int btree_begin_transaction(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeBeginTrans(pBt, 1);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_rollback ID
+**
+** Rollback changes
+*/
+static int btree_rollback(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeRollback(pBt);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_commit ID
+**
+** Commit all changes
+*/
+static int btree_commit(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeCommit(pBt);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_begin_statement ID
+**
+** Start a new statement transaction
+*/
+static int btree_begin_statement(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeBeginStmt(pBt);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_rollback_statement ID
+**
+** Rollback changes
+*/
+static int btree_rollback_statement(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeRollbackStmt(pBt);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_commit_statement ID
+**
+** Commit all changes
+*/
+static int btree_commit_statement(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeCommitStmt(pBt);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_create_table ID FLAGS
+**
+** Create a new table in the database
+*/
+static int btree_create_table(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc, iTable, flags;
+  char zBuf[30];
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID FLAGS\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &flags) ) return TCL_ERROR;
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeCreateTable(pBt, &iTable, flags);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iTable);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_drop_table ID TABLENUM
+**
+** Delete an entire table from the database
+*/
+static int btree_drop_table(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int iTable;
+  int rc;
+  int notUsed1;
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID TABLENUM\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeDropTable(pBt, iTable, &notUsed1);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_clear_table ID TABLENUM
+**
+** Remove all entries from the given table but keep the table around.
+*/
+static int btree_clear_table(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int iTable;
+  int rc;
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID TABLENUM\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeClearTable(pBt, iTable);
+  sqlite3BtreeLeave(pBt);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_get_meta ID
+**
+** Return meta data
+*/
+static int btree_get_meta(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  int i;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  for(i=0; i<SQLITE_N_BTREE_META; i++){
+    char zBuf[30];
+    u32 v;
+    sqlite3BtreeEnter(pBt);
+    rc = sqlite3BtreeGetMeta(pBt, i, &v);
+    sqlite3BtreeLeave(pBt);
+    if( rc!=SQLITE_OK ){
+      Tcl_AppendResult(interp, errorName(rc), 0);
+      return TCL_ERROR;
+    }
+    sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",v);
+    Tcl_AppendElement(interp, zBuf);
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_update_meta ID METADATA...
+**
+** Return meta data
+*/
+static int btree_update_meta(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int rc;
+  int i;
+  int aMeta[SQLITE_N_BTREE_META];
+
+  if( argc!=2+SQLITE_N_BTREE_META ){
+    char zBuf[30];
+    sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",SQLITE_N_BTREE_META);
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID METADATA...\" (METADATA is ", zBuf, " integers)", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  for(i=1; i<SQLITE_N_BTREE_META; i++){
+    if( Tcl_GetInt(interp, argv[i+2], &aMeta[i]) ) return TCL_ERROR;
+  }
+  for(i=1; i<SQLITE_N_BTREE_META; i++){
+    sqlite3BtreeEnter(pBt);
+    rc = sqlite3BtreeUpdateMeta(pBt, i, aMeta[i]);
+    sqlite3BtreeLeave(pBt);
+    if( rc!=SQLITE_OK ){
+      Tcl_AppendResult(interp, errorName(rc), 0);
+      return TCL_ERROR;
+    }
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_pager_stats ID
+**
+** Returns pager statistics
+*/
+static int btree_pager_stats(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int i;
+  int *a;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+ 
+  /* 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++){
+    static char *zName[] = {
+      "ref", "page", "max", "size", "state", "err",
+      "hit", "miss", "ovfl", "read", "write"
+    };
+    char zBuf[100];
+    Tcl_AppendElement(interp, zName[i]);
+    sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);
+    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_OK;
+}
+
+/*
+** Usage:   btree_integrity_check ID ROOT ...
+**
+** Look through every page of the given BTree file to verify correct
+** formatting and linkage.  Return a line of text for each problem found.
+** Return an empty string if everything worked.
+*/
+static int btree_integrity_check(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int nRoot;
+  int *aRoot;
+  int i;
+  int nErr;
+  char *zResult;
+
+  if( argc<3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID ROOT ...\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  nRoot = argc-2;
+  aRoot = (int*)sqlite3_malloc( sizeof(int)*(argc-2) );
+  for(i=0; i<argc-2; i++){
+    if( Tcl_GetInt(interp, argv[i+2], &aRoot[i]) ) return TCL_ERROR;
+  }
+#ifndef SQLITE_OMIT_INTEGRITY_CHECK
+  sqlite3BtreeEnter(pBt);
+  zResult = sqlite3BtreeIntegrityCheck(pBt, aRoot, nRoot, 10000, &nErr);
+  sqlite3BtreeLeave(pBt);
+#else
+  zResult = 0;
+#endif
+  sqlite3_free((void*)aRoot);
+  if( zResult ){
+    Tcl_AppendResult(interp, zResult, 0);
+    sqlite3_free(zResult); 
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_cursor_list ID
+**
+** Print information about all cursors to standard output for debugging.
+*/
+static int btree_cursor_list(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pBt);
+  sqlite3BtreeCursorList(pBt);
+  sqlite3BtreeLeave(pBt);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_cursor ID TABLENUM WRITEABLE
+**
+** Create a new cursor.  Return the ID for the cursor.
+*/
+static int btree_cursor(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int iTable;
+  BtCursor *pCur;
+  int rc;
+  int wrFlag;
+  char zBuf[30];
+
+  if( argc!=4 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID TABLENUM WRITEABLE\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
+  if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
+  pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
+  memset(pCur, 0, sqlite3BtreeCursorSize());
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
+  sqlite3BtreeLeave(pBt);
+  if( rc ){
+    ckfree((char *)pCur);
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_close_cursor ID
+**
+** Close a cursor opened using btree_cursor.
+*/
+static int btree_close_cursor(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  Btree *pBt;
+  int rc;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  pBt = pCur->pBtree;
+  sqlite3BtreeEnter(pBt);
+  rc = sqlite3BtreeCloseCursor(pCur);
+  sqlite3BtreeLeave(pBt);
+  ckfree((char *)pCur);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_move_to ID KEY
+**
+** Move the cursor to the entry with the given key.
+*/
+static int btree_move_to(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res;
+  char zBuf[20];
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID KEY\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+    int iKey;
+    if( Tcl_GetInt(interp, argv[2], &iKey) ){
+      sqlite3BtreeLeave(pCur->pBtree);
+      return TCL_ERROR;
+    }
+    rc = sqlite3BtreeMovetoUnpacked(pCur, 0, iKey, 0, &res);
+  }else{
+    rc = sqlite3BtreeMoveto(pCur, argv[2], strlen(argv[2]), 0, &res);  
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  if( res<0 ) res = -1;
+  if( res>0 ) res = 1;
+  sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_delete ID
+**
+** Delete the entry that the cursor is pointing to
+*/
+static int btree_delete(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeDelete(pCur);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_insert ID KEY DATA ?NZERO?
+**
+** Create a new entry with the given key and data.  If an entry already
+** exists with the same key the old entry is overwritten.
+*/
+static int btree_insert(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  BtCursor *pCur;
+  int rc;
+  int nZero;
+
+  if( objc!=4 && objc!=5 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?");
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
+  if( objc==5 ){
+    if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR;
+  }else{
+    nZero = 0;
+  }
+  sqlite3BtreeEnter(pCur->pBtree);
+  if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+    i64 iKey;
+    int len;
+    unsigned char *pBuf;
+    if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ){
+      sqlite3BtreeLeave(pCur->pBtree);
+      return TCL_ERROR;
+    }
+    pBuf = Tcl_GetByteArrayFromObj(objv[3], &len);
+    rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0);
+  }else{
+    int keylen;
+    int dlen;
+    unsigned char *pKBuf;
+    unsigned char *pDBuf;
+    pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen);
+    pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen);
+    rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0);
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return 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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeNext(pCur, &res);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_prev ID
+**
+** Move the cursor to the previous entry in the table.  Return 0 on
+** success and 1 if the cursor was already on the first entry in
+** the table or if the table was empty.
+*/
+static int btree_prev(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreePrevious(pCur, &res);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeFirst(pCur, &res);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_last ID
+**
+** Move the cursor to the last entry in the table.  Return 0 if the
+** cursor was left point to something and 1 if the table is empty.
+*/
+static int btree_last(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int res = 0;
+  char zBuf[100];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeLast(pCur, &res);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
+  Tcl_AppendResult(interp, zBuf, 0);
+  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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  char zBuf[50];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeEof(pCur);
+  sqlite3BtreeLeave(pCur->pBtree);
+  sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_keysize ID
+**
+** Return the number of bytes of key.  For an INTKEY table, this
+** returns the key itself.
+*/
+static int btree_keysize(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  u64 n;
+  char zBuf[50];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  sqlite3BtreeKeySize(pCur, (i64*)&n);
+  sqlite3BtreeLeave(pCur->pBtree);
+  sqlite3_snprintf(sizeof(zBuf),zBuf, "%llu", n);
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_key ID
+**
+** Return the key for the entry at which the cursor is pointing.
+*/
+static int btree_key(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  u64 n;
+  char *zBuf;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  sqlite3BtreeKeySize(pCur, (i64*)&n);
+  if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+    char zBuf2[60];
+    sqlite3_snprintf(sizeof(zBuf2),zBuf2, "%llu", n);
+    Tcl_AppendResult(interp, zBuf2, 0);
+  }else{
+    zBuf = sqlite3_malloc( n+1 );
+    rc = sqlite3BtreeKey(pCur, 0, n, zBuf);
+    if( rc ){
+      sqlite3BtreeLeave(pCur->pBtree);
+      Tcl_AppendResult(interp, errorName(rc), 0);
+      return TCL_ERROR;
+    }
+    zBuf[n] = 0;
+    Tcl_AppendResult(interp, zBuf, 0);
+    sqlite3_free(zBuf);
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_data ID ?N?
+**
+** Return the data for the entry at which the cursor is pointing.
+*/
+static int btree_data(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  u32 n;
+  char *zBuf;
+
+  if( argc!=2 && argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  if( argc==2 ){
+    sqlite3BtreeDataSize(pCur, &n);
+  }else{
+    n = atoi(argv[2]);
+  }
+  zBuf = sqlite3_malloc( n+1 );
+  rc = sqlite3BtreeData(pCur, 0, n, zBuf);
+  sqlite3BtreeLeave(pCur->pBtree);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    sqlite3_free(zBuf);
+    return TCL_ERROR;
+  }
+  zBuf[n] = 0;
+  Tcl_AppendResult(interp, zBuf, 0);
+  sqlite3_free(zBuf);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_fetch_key ID AMT
+**
+** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key.
+** If sqlite3BtreeKeyFetch() fails, return an empty string.
+*/
+static int btree_fetch_key(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int n;
+  int amt;
+  u64 nKey;
+  const char *zBuf;
+  char zStatic[1000];
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID AMT\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+  sqlite3BtreeEnter(pCur->pBtree);
+  sqlite3BtreeKeySize(pCur, (i64*)&nKey);
+  zBuf = sqlite3BtreeKeyFetch(pCur, &amt);
+  if( zBuf && amt>=n ){
+    assert( nKey<sizeof(zStatic) );
+    if( n>0 ) nKey = n;
+    memcpy(zStatic, zBuf, (int)nKey); 
+    zStatic[nKey] = 0;
+    Tcl_AppendResult(interp, zStatic, 0);
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_fetch_data ID AMT
+**
+** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key.
+** If sqlite3BtreeDataFetch() fails, return an empty string.
+*/
+static int btree_fetch_data(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int n;
+  int amt;
+  u32 nData;
+  const char *zBuf;
+  char zStatic[1000];
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID AMT\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
+  sqlite3BtreeEnter(pCur->pBtree);
+  sqlite3BtreeDataSize(pCur, &nData);
+  zBuf = sqlite3BtreeDataFetch(pCur, &amt);
+  if( zBuf && amt>=n ){
+    assert( nData<sizeof(zStatic) );
+    if( n>0 ) nData = n;
+    memcpy(zStatic, zBuf, (int)nData); 
+    zStatic[nData] = 0;
+    Tcl_AppendResult(interp, zStatic, 0);
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_payload_size ID
+**
+** Return the number of bytes of payload
+*/
+static int btree_payload_size(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int n2;
+  u64 n1;
+  char zBuf[50];
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  sqlite3BtreeEnter(pCur->pBtree);
+  if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){
+    n1 = 0;
+  }else{
+    sqlite3BtreeKeySize(pCur, (i64*)&n1);
+  }
+  sqlite3BtreeDataSize(pCur, (u32*)&n2);
+  sqlite3BtreeLeave(pCur->pBtree);
+  sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
+  Tcl_AppendResult(interp, zBuf, 0);
+  return SQLITE_OK;
+}
+
+/*
+** Usage:   btree_cursor_info ID ?UP-CNT?
+**
+** Return integers containing information about the entry the
+** cursor is pointing to:
+**
+**   aResult[0] =  The page number
+**   aResult[1] =  The entry number
+**   aResult[2] =  Total number of entries on this page
+**   aResult[3] =  Cell size (local payload + header)
+**   aResult[4] =  Number of free bytes on this page
+**   aResult[5] =  Number of free blocks on the page
+**   aResult[6] =  Total payload size (local + overflow)
+**   aResult[7] =  Header size in bytes
+**   aResult[8] =  Local payload size
+**   aResult[9] =  Parent page number
+**   aResult[10]=  Page number of the first overflow page
+*/
+static int btree_cursor_info(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  BtCursor *pCur;
+  int rc;
+  int i, j;
+  int up;
+  int aResult[11];
+  char zBuf[400];
+
+  if( argc!=2 && argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID ?UP-CNT?\"", 0);
+    return TCL_ERROR;
+  }
+  pCur = sqlite3TestTextToPtr(argv[1]);
+  if( argc==3 ){
+    if( Tcl_GetInt(interp, argv[2], &up) ) return TCL_ERROR;
+  }else{
+    up = 0;
+  }
+  sqlite3BtreeEnter(pCur->pBtree);
+  rc = sqlite3BtreeCursorInfo(pCur, aResult, up);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    sqlite3BtreeLeave(pCur->pBtree);
+    return TCL_ERROR;
+  }
+  j = 0;
+  for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){
+    sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]);
+    j += strlen(&zBuf[j]);
+  }
+  sqlite3BtreeLeave(pCur->pBtree);
+  Tcl_AppendResult(interp, &zBuf[1], 0);
+  return SQLITE_OK;
+}
+
+/*
+** Copied from btree.c:
+*/
+static u32 t4Get4byte(unsigned char *p){
+  return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+**   btree_ovfl_info  BTREE  CURSOR
+**
+** Given a cursor, return the sequence of pages number that form the
+** overflow pages for the data of the entry that the cursor is point
+** to.
+*/ 
+static int btree_ovfl_info(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  BtCursor *pCur;
+  Pager *pPager;
+  int rc;
+  int n;
+  int dataSize;
+  u32 pgno;
+  void *pPage;
+  int aResult[11];
+  char zElem[100];
+  Tcl_DString str;
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
+                    " BTREE CURSOR", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  pCur = sqlite3TestTextToPtr(argv[2]);
+  if( (*(void**)pCur) != (void*)pBt ){
+    Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ",
+       argv[1], 0);
+    return TCL_ERROR;
+  }
+  sqlite3BtreeEnter(pBt);
+  pPager = sqlite3BtreePager(pBt);
+  rc = sqlite3BtreeCursorInfo(pCur, aResult, 0);
+  if( rc ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    sqlite3BtreeLeave(pBt);
+    return TCL_ERROR;
+  }
+  dataSize = pBt->pBt->usableSize;
+  Tcl_DStringInit(&str);
+  n = aResult[6] - aResult[8];
+  n = (n + dataSize - 1)/dataSize;
+  pgno = (u32)aResult[10];
+  while( pgno && n-- ){
+    DbPage *pDbPage;
+    sprintf(zElem, "%d", pgno);
+    Tcl_DStringAppendElement(&str, zElem);
+    if( sqlite3PagerGet(pPager, pgno, &pDbPage)!=SQLITE_OK ){
+      Tcl_DStringFree(&str);
+      Tcl_AppendResult(interp, "unable to get page ", zElem, 0);
+      sqlite3BtreeLeave(pBt);
+      return TCL_ERROR;
+    }
+    pPage = sqlite3PagerGetData(pDbPage);
+    pgno = t4Get4byte((unsigned char*)pPage);
+    sqlite3PagerUnref(pDbPage);
+  }
+  sqlite3BtreeLeave(pBt);
+  Tcl_DStringResult(interp, &str);
+  return SQLITE_OK;
+}
+
+/*
+** The command is provided for the purpose of setting breakpoints.
+** in regression test scripts.
+**
+** By setting a GDB breakpoint on this procedure and executing the
+** btree_breakpoint command in a test script, we can stop GDB at
+** the point in the script where the btree_breakpoint command is
+** inserted.  This is useful for debugging.
+*/
+static int btree_breakpoint(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  return TCL_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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  u32 start, mult, count, incr;
+  u64 in, out;
+  int n1, n2, i, j;
+  unsigned char zBuf[100];
+  if( argc!=5 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " START MULTIPLIER COUNT INCREMENT\"", 0);
+    return TCL_ERROR;
+  }
+  if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
+  if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
+  in = start;
+  in *= mult;
+  for(i=0; i<count; i++){
+    char zErr[200];
+    n1 = putVarint(zBuf, in);
+    if( n1>9 || n1<1 ){
+      sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1);
+      Tcl_AppendResult(interp, zErr, 0);
+      return TCL_ERROR;
+    }
+    n2 = getVarint(zBuf, &out);
+    if( n1!=n2 ){
+      sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2);
+      Tcl_AppendResult(interp, zErr, 0);
+      return TCL_ERROR;
+    }
+    if( in!=out ){
+      sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out);
+      Tcl_AppendResult(interp, zErr, 0);
+      return TCL_ERROR;
+    }
+    if( (in & 0xffffffff)==in ){
+      u32 out32;
+      n2 = getVarint32(zBuf, out32);
+      out = out32;
+      if( n1!=n2 ){
+        sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", 
+                  n1, n2);
+        Tcl_AppendResult(interp, zErr, 0);
+        return TCL_ERROR;
+      }
+      if( in!=out ){
+        sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
+            in, out);
+        Tcl_AppendResult(interp, zErr, 0);
+        return TCL_ERROR;
+      }
+    }
+
+    /* In order to get realistic timings, run getVarint 19 more times.
+    ** This is because getVarint is called about 20 times more often
+    ** than putVarint.
+    */
+    for(j=0; j<19; j++){
+      getVarint(zBuf, &out);
+    }
+    in += incr;
+  }
+  return 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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  char zBuf[100];
+  Tcl_CmdInfo info;
+  sqlite3 *db;
+  Btree *pBt;
+  int iDb = 0;
+
+  if( argc!=2 && argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " DB-HANDLE ?N?\"", 0);
+    return TCL_ERROR;
+  }
+
+  if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
+    Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
+    return TCL_ERROR;
+  }
+  if( argc==3 ){
+    iDb = atoi(argv[2]);
+  }
+
+  db = *((sqlite3 **)info.objClientData);
+  assert( db );
+
+  pBt = db->aDb[iDb].pBt;
+  sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
+  Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
+  return 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(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  int nCache;
+  Btree *pBt;
+
+  if( argc!=3 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " BT NCACHE\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
+
+  sqlite3_mutex_enter(pBt->db->mutex);
+  sqlite3BtreeEnter(pBt);
+  sqlite3BtreeSetCacheSize(pBt, nCache);
+  sqlite3BtreeLeave(pBt);
+  sqlite3_mutex_leave(pBt->db->mutex);
+
+  return TCL_OK;
+}
+
+/*
+** Usage:   btree_ismemdb ID
+**
+** Return true if the B-Tree is in-memory.
+*/
+static int btree_ismemdb(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  const char **argv      /* Text of each argument */
+){
+  Btree *pBt;
+  int res;
+
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+       " ID\"", 0);
+    return TCL_ERROR;
+  }
+  pBt = sqlite3TestTextToPtr(argv[1]);
+  sqlite3_mutex_enter(pBt->db->mutex);
+  sqlite3BtreeEnter(pBt);
+  res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
+  sqlite3BtreeLeave(pBt);
+  sqlite3_mutex_leave(pBt->db->mutex);
+  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
+  return SQLITE_OK;
+}
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetest3_Init(Tcl_Interp *interp){
+  static struct {
+     char *zName;
+     Tcl_CmdProc *xProc;
+  } aCmd[] = {
+     { "btree_open",               (Tcl_CmdProc*)btree_open               },
+     { "btree_close",              (Tcl_CmdProc*)btree_close              },
+     { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
+     { "btree_commit",             (Tcl_CmdProc*)btree_commit             },
+     { "btree_rollback",           (Tcl_CmdProc*)btree_rollback           },
+     { "btree_create_table",       (Tcl_CmdProc*)btree_create_table       },
+     { "btree_drop_table",         (Tcl_CmdProc*)btree_drop_table         },
+     { "btree_clear_table",        (Tcl_CmdProc*)btree_clear_table        },
+     { "btree_get_meta",           (Tcl_CmdProc*)btree_get_meta           },
+     { "btree_update_meta",        (Tcl_CmdProc*)btree_update_meta        },
+     { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
+     { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
+     { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
+     { "btree_move_to",            (Tcl_CmdProc*)btree_move_to            },
+     { "btree_delete",             (Tcl_CmdProc*)btree_delete             },
+     { "btree_next",               (Tcl_CmdProc*)btree_next               },
+     { "btree_prev",               (Tcl_CmdProc*)btree_prev               },
+     { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
+     { "btree_keysize",            (Tcl_CmdProc*)btree_keysize            },
+     { "btree_key",                (Tcl_CmdProc*)btree_key                },
+     { "btree_data",               (Tcl_CmdProc*)btree_data               },
+     { "btree_fetch_key",          (Tcl_CmdProc*)btree_fetch_key          },
+     { "btree_fetch_data",         (Tcl_CmdProc*)btree_fetch_data         },
+     { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
+     { "btree_first",              (Tcl_CmdProc*)btree_first              },
+     { "btree_last",               (Tcl_CmdProc*)btree_last               },
+     { "btree_integrity_check",    (Tcl_CmdProc*)btree_integrity_check    },
+     { "btree_breakpoint",         (Tcl_CmdProc*)btree_breakpoint         },
+     { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
+     { "btree_begin_statement",    (Tcl_CmdProc*)btree_begin_statement    },
+     { "btree_commit_statement",   (Tcl_CmdProc*)btree_commit_statement   },
+     { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement },
+     { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
+     { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     },
+     { "btree_cursor_info",        (Tcl_CmdProc*)btree_cursor_info        },
+     { "btree_ovfl_info",          (Tcl_CmdProc*)btree_ovfl_info          },
+     { "btree_cursor_list",        (Tcl_CmdProc*)btree_cursor_list        },
+     { "btree_ismemdb",            (Tcl_CmdProc*)btree_ismemdb            },
+  };
+  int i;
+
+  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+  }
+
+  /* The btree_insert command is implemented using the tcl 'object'
+  ** interface, not the string interface like the other commands in this
+  ** file. This is so binary data can be inserted into btree tables.
+  */
+  Tcl_CreateObjCommand(interp, "btree_insert", btree_insert, 0, 0);
+  return TCL_OK;
+}