| /* |
| ** 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.102 2008/10/27 13:59:34 danielk1977 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, 0); |
| 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; |
| } |