--- /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, ¬Used1);
+ 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;
+}