sl@0: /* sl@0: ** 2001 September 15 sl@0: ** sl@0: ** The author disclaims copyright to this source code. In place of sl@0: ** a legal notice, here is a blessing: sl@0: ** sl@0: ** May you do good and not evil. sl@0: ** May you find forgiveness for yourself and forgive others. sl@0: ** May you share freely, never taking more than you give. sl@0: ** sl@0: ************************************************************************* sl@0: ** Code for testing the btree.c module in SQLite. This code sl@0: ** is not included in the SQLite library. It is used for automated sl@0: ** testing of the SQLite library. sl@0: ** sl@0: ** $Id: test3.c,v 1.101 2008/08/13 19:11:48 drh Exp $ sl@0: */ sl@0: #include "sqliteInt.h" sl@0: #include "btreeInt.h" sl@0: #include "tcl.h" sl@0: #include sl@0: #include sl@0: sl@0: /* sl@0: ** Interpret an SQLite error number sl@0: */ sl@0: static char *errorName(int rc){ sl@0: char *zName; sl@0: switch( rc ){ sl@0: case SQLITE_OK: zName = "SQLITE_OK"; break; sl@0: case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; sl@0: case SQLITE_PERM: zName = "SQLITE_PERM"; break; sl@0: case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; sl@0: case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; sl@0: case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; sl@0: case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; sl@0: case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; sl@0: case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; sl@0: case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; sl@0: case SQLITE_FULL: zName = "SQLITE_FULL"; break; sl@0: case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; sl@0: case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; sl@0: case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; sl@0: case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; sl@0: default: zName = "SQLITE_Unknown"; break; sl@0: } sl@0: return zName; sl@0: } sl@0: sl@0: /* sl@0: ** A bogus sqlite3 connection structure for use in the btree sl@0: ** tests. sl@0: */ sl@0: static sqlite3 sDb; sl@0: static int nRefSqlite3 = 0; sl@0: sl@0: /* sl@0: ** Usage: btree_open FILENAME NCACHE FLAGS sl@0: ** sl@0: ** Open a new database sl@0: */ sl@0: static int btree_open( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc, nCache, flags; sl@0: char zBuf[100]; sl@0: if( argc!=4 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " FILENAME NCACHE FLAGS\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; sl@0: if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR; sl@0: nRefSqlite3++; sl@0: if( nRefSqlite3==1 ){ sl@0: sDb.pVfs = sqlite3_vfs_find(0); sl@0: sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); sl@0: sqlite3_mutex_enter(sDb.mutex); sl@0: } sl@0: rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags, sl@0: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3BtreeSetCacheSize(pBt, nCache); sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_close ID sl@0: ** sl@0: ** Close the given database. sl@0: */ sl@0: static int btree_close( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: rc = sqlite3BtreeClose(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: nRefSqlite3--; sl@0: if( nRefSqlite3==0 ){ sl@0: sqlite3_mutex_leave(sDb.mutex); sl@0: sqlite3_mutex_free(sDb.mutex); sl@0: sDb.mutex = 0; sl@0: sDb.pVfs = 0; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Usage: btree_begin_transaction ID sl@0: ** sl@0: ** Start a new transaction sl@0: */ sl@0: static int btree_begin_transaction( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeBeginTrans(pBt, 1); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_rollback ID sl@0: ** sl@0: ** Rollback changes sl@0: */ sl@0: static int btree_rollback( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeRollback(pBt); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_commit ID sl@0: ** sl@0: ** Commit all changes sl@0: */ sl@0: static int btree_commit( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeCommit(pBt); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_begin_statement ID sl@0: ** sl@0: ** Start a new statement transaction sl@0: */ sl@0: static int btree_begin_statement( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeBeginStmt(pBt); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_rollback_statement ID sl@0: ** sl@0: ** Rollback changes sl@0: */ sl@0: static int btree_rollback_statement( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeRollbackStmt(pBt); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_commit_statement ID sl@0: ** sl@0: ** Commit all changes sl@0: */ sl@0: static int btree_commit_statement( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeCommitStmt(pBt); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_create_table ID FLAGS sl@0: ** sl@0: ** Create a new table in the database sl@0: */ sl@0: static int btree_create_table( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc, iTable, flags; sl@0: char zBuf[30]; sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID FLAGS\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &flags) ) return TCL_ERROR; sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeCreateTable(pBt, &iTable, flags); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", iTable); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_drop_table ID TABLENUM sl@0: ** sl@0: ** Delete an entire table from the database sl@0: */ sl@0: static int btree_drop_table( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int iTable; sl@0: int rc; sl@0: int notUsed1; sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID TABLENUM\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeDropTable(pBt, iTable, ¬Used1); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_clear_table ID TABLENUM sl@0: ** sl@0: ** Remove all entries from the given table but keep the table around. sl@0: */ sl@0: static int btree_clear_table( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int iTable; sl@0: int rc; sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID TABLENUM\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeClearTable(pBt, iTable); sl@0: sqlite3BtreeLeave(pBt); sl@0: if( rc!=SQLITE_OK ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_get_meta ID sl@0: ** sl@0: ** Return meta data sl@0: */ sl@0: static int btree_get_meta( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int rc; sl@0: int i; sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: for(i=0; idb->mutex); sl@0: sl@0: sqlite3BtreeEnter(pBt); sl@0: a = sqlite3PagerStats(sqlite3BtreePager(pBt)); sl@0: for(i=0; i<11; i++){ sl@0: static char *zName[] = { sl@0: "ref", "page", "max", "size", "state", "err", sl@0: "hit", "miss", "ovfl", "read", "write" sl@0: }; sl@0: char zBuf[100]; sl@0: Tcl_AppendElement(interp, zName[i]); sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]); sl@0: Tcl_AppendElement(interp, zBuf); sl@0: } sl@0: sqlite3BtreeLeave(pBt); sl@0: sl@0: /* Release the mutex on the SQLite handle that controls this b-tree */ sl@0: sqlite3_mutex_leave(pBt->db->mutex); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_integrity_check ID ROOT ... sl@0: ** sl@0: ** Look through every page of the given BTree file to verify correct sl@0: ** formatting and linkage. Return a line of text for each problem found. sl@0: ** Return an empty string if everything worked. sl@0: */ sl@0: static int btree_integrity_check( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int nRoot; sl@0: int *aRoot; sl@0: int i; sl@0: int nErr; sl@0: char *zResult; sl@0: sl@0: if( argc<3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID ROOT ...\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: nRoot = argc-2; sl@0: aRoot = (int*)sqlite3_malloc( sizeof(int)*(argc-2) ); sl@0: for(i=0; ipBtree; sl@0: sqlite3BtreeEnter(pBt); sl@0: rc = sqlite3BtreeCloseCursor(pCur); sl@0: sqlite3BtreeLeave(pBt); sl@0: ckfree((char *)pCur); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_move_to ID KEY sl@0: ** sl@0: ** Move the cursor to the entry with the given key. sl@0: */ sl@0: static int btree_move_to( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int res; sl@0: char zBuf[20]; sl@0: sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID KEY\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ sl@0: int iKey; sl@0: if( Tcl_GetInt(interp, argv[2], &iKey) ){ sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return TCL_ERROR; sl@0: } sl@0: rc = sqlite3BtreeMovetoUnpacked(pCur, 0, iKey, 0, &res); sl@0: }else{ sl@0: rc = sqlite3BtreeMoveto(pCur, argv[2], strlen(argv[2]), 0, &res); sl@0: } sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( res<0 ) res = -1; sl@0: if( res>0 ) res = 1; sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",res); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_delete ID sl@0: ** sl@0: ** Delete the entry that the cursor is pointing to sl@0: */ sl@0: static int btree_delete( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeDelete(pCur); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_insert ID KEY DATA ?NZERO? sl@0: ** sl@0: ** Create a new entry with the given key and data. If an entry already sl@0: ** exists with the same key the old entry is overwritten. sl@0: */ sl@0: static int btree_insert( sl@0: void * clientData, sl@0: Tcl_Interp *interp, sl@0: int objc, sl@0: Tcl_Obj *CONST objv[] sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int nZero; sl@0: sl@0: if( objc!=4 && objc!=5 ){ sl@0: Tcl_WrongNumArgs(interp, 1, objv, "ID KEY DATA ?NZERO?"); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); sl@0: if( objc==5 ){ sl@0: if( Tcl_GetIntFromObj(interp, objv[4], &nZero) ) return TCL_ERROR; sl@0: }else{ sl@0: nZero = 0; sl@0: } sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ sl@0: i64 iKey; sl@0: int len; sl@0: unsigned char *pBuf; sl@0: if( Tcl_GetWideIntFromObj(interp, objv[2], &iKey) ){ sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return TCL_ERROR; sl@0: } sl@0: pBuf = Tcl_GetByteArrayFromObj(objv[3], &len); sl@0: rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0); sl@0: }else{ sl@0: int keylen; sl@0: int dlen; sl@0: unsigned char *pKBuf; sl@0: unsigned char *pDBuf; sl@0: pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen); sl@0: pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen); sl@0: rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0); sl@0: } sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_next ID sl@0: ** sl@0: ** Move the cursor to the next entry in the table. Return 0 on success sl@0: ** or 1 if the cursor was already on the last entry in the table or if sl@0: ** the table is empty. sl@0: */ sl@0: static int btree_next( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int res = 0; sl@0: char zBuf[100]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeNext(pCur, &res); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_prev ID sl@0: ** sl@0: ** Move the cursor to the previous entry in the table. Return 0 on sl@0: ** success and 1 if the cursor was already on the first entry in sl@0: ** the table or if the table was empty. sl@0: */ sl@0: static int btree_prev( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int res = 0; sl@0: char zBuf[100]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreePrevious(pCur, &res); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_first ID sl@0: ** sl@0: ** Move the cursor to the first entry in the table. Return 0 if the sl@0: ** cursor was left point to something and 1 if the table is empty. sl@0: */ sl@0: static int btree_first( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int res = 0; sl@0: char zBuf[100]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeFirst(pCur, &res); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_last ID sl@0: ** sl@0: ** Move the cursor to the last entry in the table. Return 0 if the sl@0: ** cursor was left point to something and 1 if the table is empty. sl@0: */ sl@0: static int btree_last( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int res = 0; sl@0: char zBuf[100]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeLast(pCur, &res); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_eof ID sl@0: ** sl@0: ** Return TRUE if the given cursor is not pointing at a valid entry. sl@0: ** Return FALSE if the cursor does point to a valid entry. sl@0: */ sl@0: static int btree_eof( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: char zBuf[50]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeEof(pCur); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_keysize ID sl@0: ** sl@0: ** Return the number of bytes of key. For an INTKEY table, this sl@0: ** returns the key itself. sl@0: */ sl@0: static int btree_keysize( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: u64 n; sl@0: char zBuf[50]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: sqlite3BtreeKeySize(pCur, (i64*)&n); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf, "%llu", n); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_key ID sl@0: ** sl@0: ** Return the key for the entry at which the cursor is pointing. sl@0: */ sl@0: static int btree_key( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: u64 n; sl@0: char *zBuf; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: sqlite3BtreeKeySize(pCur, (i64*)&n); sl@0: if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ sl@0: char zBuf2[60]; sl@0: sqlite3_snprintf(sizeof(zBuf2),zBuf2, "%llu", n); sl@0: Tcl_AppendResult(interp, zBuf2, 0); sl@0: }else{ sl@0: zBuf = sqlite3_malloc( n+1 ); sl@0: rc = sqlite3BtreeKey(pCur, 0, n, zBuf); sl@0: if( rc ){ sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: return TCL_ERROR; sl@0: } sl@0: zBuf[n] = 0; sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: sqlite3_free(zBuf); sl@0: } sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_data ID ?N? sl@0: ** sl@0: ** Return the data for the entry at which the cursor is pointing. sl@0: */ sl@0: static int btree_data( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: u32 n; sl@0: char *zBuf; sl@0: sl@0: if( argc!=2 && argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: if( argc==2 ){ sl@0: sqlite3BtreeDataSize(pCur, &n); sl@0: }else{ sl@0: n = atoi(argv[2]); sl@0: } sl@0: zBuf = sqlite3_malloc( n+1 ); sl@0: rc = sqlite3BtreeData(pCur, 0, n, zBuf); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: sqlite3_free(zBuf); sl@0: return TCL_ERROR; sl@0: } sl@0: zBuf[n] = 0; sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: sqlite3_free(zBuf); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_fetch_key ID AMT sl@0: ** sl@0: ** Use the sqlite3BtreeKeyFetch() routine to get AMT bytes of the key. sl@0: ** If sqlite3BtreeKeyFetch() fails, return an empty string. sl@0: */ sl@0: static int btree_fetch_key( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int n; sl@0: int amt; sl@0: u64 nKey; sl@0: const char *zBuf; sl@0: char zStatic[1000]; sl@0: sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID AMT\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: sqlite3BtreeKeySize(pCur, (i64*)&nKey); sl@0: zBuf = sqlite3BtreeKeyFetch(pCur, &amt); sl@0: if( zBuf && amt>=n ){ sl@0: assert( nKey0 ) nKey = n; sl@0: memcpy(zStatic, zBuf, (int)nKey); sl@0: zStatic[nKey] = 0; sl@0: Tcl_AppendResult(interp, zStatic, 0); sl@0: } sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_fetch_data ID AMT sl@0: ** sl@0: ** Use the sqlite3BtreeDataFetch() routine to get AMT bytes of the key. sl@0: ** If sqlite3BtreeDataFetch() fails, return an empty string. sl@0: */ sl@0: static int btree_fetch_data( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int n; sl@0: int amt; sl@0: u32 nData; sl@0: const char *zBuf; sl@0: char zStatic[1000]; sl@0: sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID AMT\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: sqlite3BtreeDataSize(pCur, &nData); sl@0: zBuf = sqlite3BtreeDataFetch(pCur, &amt); sl@0: if( zBuf && amt>=n ){ sl@0: assert( nData0 ) nData = n; sl@0: memcpy(zStatic, zBuf, (int)nData); sl@0: zStatic[nData] = 0; sl@0: Tcl_AppendResult(interp, zStatic, 0); sl@0: } sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_payload_size ID sl@0: ** sl@0: ** Return the number of bytes of payload sl@0: */ sl@0: static int btree_payload_size( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int n2; sl@0: u64 n1; sl@0: char zBuf[50]; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: if( sqlite3BtreeFlags(pCur) & BTREE_INTKEY ){ sl@0: n1 = 0; sl@0: }else{ sl@0: sqlite3BtreeKeySize(pCur, (i64*)&n1); sl@0: } sl@0: sqlite3BtreeDataSize(pCur, (u32*)&n2); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); sl@0: Tcl_AppendResult(interp, zBuf, 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_cursor_info ID ?UP-CNT? sl@0: ** sl@0: ** Return integers containing information about the entry the sl@0: ** cursor is pointing to: sl@0: ** sl@0: ** aResult[0] = The page number sl@0: ** aResult[1] = The entry number sl@0: ** aResult[2] = Total number of entries on this page sl@0: ** aResult[3] = Cell size (local payload + header) sl@0: ** aResult[4] = Number of free bytes on this page sl@0: ** aResult[5] = Number of free blocks on the page sl@0: ** aResult[6] = Total payload size (local + overflow) sl@0: ** aResult[7] = Header size in bytes sl@0: ** aResult[8] = Local payload size sl@0: ** aResult[9] = Parent page number sl@0: ** aResult[10]= Page number of the first overflow page sl@0: */ sl@0: static int btree_cursor_info( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: BtCursor *pCur; sl@0: int rc; sl@0: int i, j; sl@0: int up; sl@0: int aResult[11]; sl@0: char zBuf[400]; sl@0: sl@0: if( argc!=2 && argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID ?UP-CNT?\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pCur = sqlite3TestTextToPtr(argv[1]); sl@0: if( argc==3 ){ sl@0: if( Tcl_GetInt(interp, argv[2], &up) ) return TCL_ERROR; sl@0: }else{ sl@0: up = 0; sl@0: } sl@0: sqlite3BtreeEnter(pCur->pBtree); sl@0: rc = sqlite3BtreeCursorInfo(pCur, aResult, up); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: sqlite3BtreeLeave(pCur->pBtree); sl@0: return TCL_ERROR; sl@0: } sl@0: j = 0; sl@0: for(i=0; ipBtree); sl@0: Tcl_AppendResult(interp, &zBuf[1], 0); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Copied from btree.c: sl@0: */ sl@0: static u32 t4Get4byte(unsigned char *p){ sl@0: return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; sl@0: } sl@0: sl@0: /* sl@0: ** btree_ovfl_info BTREE CURSOR sl@0: ** sl@0: ** Given a cursor, return the sequence of pages number that form the sl@0: ** overflow pages for the data of the entry that the cursor is point sl@0: ** to. sl@0: */ sl@0: static int btree_ovfl_info( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: BtCursor *pCur; sl@0: Pager *pPager; sl@0: int rc; sl@0: int n; sl@0: int dataSize; sl@0: u32 pgno; sl@0: void *pPage; sl@0: int aResult[11]; sl@0: char zElem[100]; sl@0: Tcl_DString str; sl@0: sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " BTREE CURSOR", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: pCur = sqlite3TestTextToPtr(argv[2]); sl@0: if( (*(void**)pCur) != (void*)pBt ){ sl@0: Tcl_AppendResult(interp, "Cursor ", argv[2], " does not belong to btree ", sl@0: argv[1], 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sqlite3BtreeEnter(pBt); sl@0: pPager = sqlite3BtreePager(pBt); sl@0: rc = sqlite3BtreeCursorInfo(pCur, aResult, 0); sl@0: if( rc ){ sl@0: Tcl_AppendResult(interp, errorName(rc), 0); sl@0: sqlite3BtreeLeave(pBt); sl@0: return TCL_ERROR; sl@0: } sl@0: dataSize = pBt->pBt->usableSize; sl@0: Tcl_DStringInit(&str); sl@0: n = aResult[6] - aResult[8]; sl@0: n = (n + dataSize - 1)/dataSize; sl@0: pgno = (u32)aResult[10]; sl@0: while( pgno && n-- ){ sl@0: DbPage *pDbPage; sl@0: sprintf(zElem, "%d", pgno); sl@0: Tcl_DStringAppendElement(&str, zElem); sl@0: if( sqlite3PagerGet(pPager, pgno, &pDbPage)!=SQLITE_OK ){ sl@0: Tcl_DStringFree(&str); sl@0: Tcl_AppendResult(interp, "unable to get page ", zElem, 0); sl@0: sqlite3BtreeLeave(pBt); sl@0: return TCL_ERROR; sl@0: } sl@0: pPage = sqlite3PagerGetData(pDbPage); sl@0: pgno = t4Get4byte((unsigned char*)pPage); sl@0: sqlite3PagerUnref(pDbPage); sl@0: } sl@0: sqlite3BtreeLeave(pBt); sl@0: Tcl_DStringResult(interp, &str); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: /* sl@0: ** The command is provided for the purpose of setting breakpoints. sl@0: ** in regression test scripts. sl@0: ** sl@0: ** By setting a GDB breakpoint on this procedure and executing the sl@0: ** btree_breakpoint command in a test script, we can stop GDB at sl@0: ** the point in the script where the btree_breakpoint command is sl@0: ** inserted. This is useful for debugging. sl@0: */ sl@0: static int btree_breakpoint( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** usage: varint_test START MULTIPLIER COUNT INCREMENT sl@0: ** sl@0: ** This command tests the putVarint() and getVarint() sl@0: ** routines, both for accuracy and for speed. sl@0: ** sl@0: ** An integer is written using putVarint() and read back with sl@0: ** getVarint() and varified to be unchanged. This repeats COUNT sl@0: ** times. The first integer is START*MULTIPLIER. Each iteration sl@0: ** increases the integer by INCREMENT. sl@0: ** sl@0: ** This command returns nothing if it works. It returns an error message sl@0: ** if something goes wrong. sl@0: */ sl@0: static int btree_varint_test( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: u32 start, mult, count, incr; sl@0: u64 in, out; sl@0: int n1, n2, i, j; sl@0: unsigned char zBuf[100]; sl@0: if( argc!=5 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " START MULTIPLIER COUNT INCREMENT\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR; sl@0: if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR; sl@0: if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR; sl@0: if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR; sl@0: in = start; sl@0: in *= mult; sl@0: for(i=0; i9 || n1<1 ){ sl@0: sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1); sl@0: Tcl_AppendResult(interp, zErr, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: n2 = getVarint(zBuf, &out); sl@0: if( n1!=n2 ){ sl@0: sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2); sl@0: Tcl_AppendResult(interp, zErr, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( in!=out ){ sl@0: sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out); sl@0: Tcl_AppendResult(interp, zErr, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( (in & 0xffffffff)==in ){ sl@0: u32 out32; sl@0: n2 = getVarint32(zBuf, out32); sl@0: out = out32; sl@0: if( n1!=n2 ){ sl@0: sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", sl@0: n1, n2); sl@0: Tcl_AppendResult(interp, zErr, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( in!=out ){ sl@0: sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", sl@0: in, out); sl@0: Tcl_AppendResult(interp, zErr, 0); sl@0: return TCL_ERROR; sl@0: } sl@0: } sl@0: sl@0: /* In order to get realistic timings, run getVarint 19 more times. sl@0: ** This is because getVarint is called about 20 times more often sl@0: ** than putVarint. sl@0: */ sl@0: for(j=0; j<19; j++){ sl@0: getVarint(zBuf, &out); sl@0: } sl@0: in += incr; sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** usage: btree_from_db DB-HANDLE sl@0: ** sl@0: ** This command returns the btree handle for the main database associated sl@0: ** with the database-handle passed as the argument. Example usage: sl@0: ** sl@0: ** sqlite3 db test.db sl@0: ** set bt [btree_from_db db] sl@0: */ sl@0: static int btree_from_db( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: char zBuf[100]; sl@0: Tcl_CmdInfo info; sl@0: sqlite3 *db; sl@0: Btree *pBt; sl@0: int iDb = 0; sl@0: sl@0: if( argc!=2 && argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " DB-HANDLE ?N?\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){ sl@0: Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: if( argc==3 ){ sl@0: iDb = atoi(argv[2]); sl@0: } sl@0: sl@0: db = *((sqlite3 **)info.objClientData); sl@0: assert( db ); sl@0: sl@0: pBt = db->aDb[iDb].pBt; sl@0: sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt); sl@0: Tcl_SetResult(interp, zBuf, TCL_VOLATILE); sl@0: return TCL_OK; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** usage: btree_set_cache_size ID NCACHE sl@0: ** sl@0: ** Set the size of the cache used by btree $ID. sl@0: */ sl@0: static int btree_set_cache_size( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: int nCache; sl@0: Btree *pBt; sl@0: sl@0: if( argc!=3 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " BT NCACHE\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; sl@0: sl@0: sqlite3_mutex_enter(pBt->db->mutex); sl@0: sqlite3BtreeEnter(pBt); sl@0: sqlite3BtreeSetCacheSize(pBt, nCache); sl@0: sqlite3BtreeLeave(pBt); sl@0: sqlite3_mutex_leave(pBt->db->mutex); sl@0: sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: ** Usage: btree_ismemdb ID sl@0: ** sl@0: ** Return true if the B-Tree is in-memory. sl@0: */ sl@0: static int btree_ismemdb( sl@0: void *NotUsed, sl@0: Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ sl@0: int argc, /* Number of arguments */ sl@0: const char **argv /* Text of each argument */ sl@0: ){ sl@0: Btree *pBt; sl@0: int res; sl@0: sl@0: if( argc!=2 ){ sl@0: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], sl@0: " ID\"", 0); sl@0: return TCL_ERROR; sl@0: } sl@0: pBt = sqlite3TestTextToPtr(argv[1]); sl@0: sqlite3_mutex_enter(pBt->db->mutex); sl@0: sqlite3BtreeEnter(pBt); sl@0: res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt)); sl@0: sqlite3BtreeLeave(pBt); sl@0: sqlite3_mutex_leave(pBt->db->mutex); sl@0: Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); sl@0: return SQLITE_OK; sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Register commands with the TCL interpreter. sl@0: */ sl@0: int Sqlitetest3_Init(Tcl_Interp *interp){ sl@0: static struct { sl@0: char *zName; sl@0: Tcl_CmdProc *xProc; sl@0: } aCmd[] = { sl@0: { "btree_open", (Tcl_CmdProc*)btree_open }, sl@0: { "btree_close", (Tcl_CmdProc*)btree_close }, sl@0: { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, sl@0: { "btree_commit", (Tcl_CmdProc*)btree_commit }, sl@0: { "btree_rollback", (Tcl_CmdProc*)btree_rollback }, sl@0: { "btree_create_table", (Tcl_CmdProc*)btree_create_table }, sl@0: { "btree_drop_table", (Tcl_CmdProc*)btree_drop_table }, sl@0: { "btree_clear_table", (Tcl_CmdProc*)btree_clear_table }, sl@0: { "btree_get_meta", (Tcl_CmdProc*)btree_get_meta }, sl@0: { "btree_update_meta", (Tcl_CmdProc*)btree_update_meta }, sl@0: { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, sl@0: { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, sl@0: { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, sl@0: { "btree_move_to", (Tcl_CmdProc*)btree_move_to }, sl@0: { "btree_delete", (Tcl_CmdProc*)btree_delete }, sl@0: { "btree_next", (Tcl_CmdProc*)btree_next }, sl@0: { "btree_prev", (Tcl_CmdProc*)btree_prev }, sl@0: { "btree_eof", (Tcl_CmdProc*)btree_eof }, sl@0: { "btree_keysize", (Tcl_CmdProc*)btree_keysize }, sl@0: { "btree_key", (Tcl_CmdProc*)btree_key }, sl@0: { "btree_data", (Tcl_CmdProc*)btree_data }, sl@0: { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, sl@0: { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, sl@0: { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, sl@0: { "btree_first", (Tcl_CmdProc*)btree_first }, sl@0: { "btree_last", (Tcl_CmdProc*)btree_last }, sl@0: { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check }, sl@0: { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint }, sl@0: { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, sl@0: { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement }, sl@0: { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement }, sl@0: { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement }, sl@0: { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, sl@0: { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }, sl@0: { "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info }, sl@0: { "btree_ovfl_info", (Tcl_CmdProc*)btree_ovfl_info }, sl@0: { "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list }, sl@0: { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, sl@0: }; sl@0: int i; sl@0: sl@0: for(i=0; i