123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 |
- /*
- ** 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.
- */
- #include "sqliteInt.h"
- #include "btreeInt.h"
- #include "tcl.h"
- #include <stdlib.h>
- #include <string.h>
- extern const char *sqlite3ErrName(int);
- /*
- ** A bogus sqlite3 connection structure for use in the btree
- ** tests.
- */
- static sqlite3 sDb;
- static int nRefSqlite3 = 0;
- /*
- ** Usage: btree_open FILENAME NCACHE
- **
- ** 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;
- char zBuf[100];
- int n;
- char *zFilename;
- if( argc!=3 ){
- 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;
- nRefSqlite3++;
- if( nRefSqlite3==1 ){
- sDb.pVfs = sqlite3_vfs_find(0);
- sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
- sqlite3_mutex_enter(sDb.mutex);
- }
- n = (int)strlen(argv[1]);
- zFilename = sqlite3_malloc( n+2 );
- if( zFilename==0 ) return TCL_ERROR;
- memcpy(zFilename, argv[1], n+1);
- zFilename[n+1] = 0;
- rc = sqlite3BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0,
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
- sqlite3_free(zFilename);
- if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, sqlite3ErrName(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, sqlite3ErrName(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, sqlite3ErrName(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_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 = SQLITE_OK;
- 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);
- #ifndef SQLITE_OMIT_SHARED_CACHE
- rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
- #endif
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
- }
- sqlite3BtreeLeave(pBt);
- if( rc ){
- ckfree((char *)pCur);
- Tcl_AppendResult(interp, sqlite3ErrName(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, sqlite3ErrName(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, sqlite3ErrName(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, sqlite3ErrName(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_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);
- /* The cursor may be in "require-seek" state. If this is the case, the
- ** call to BtreeDataSize() will fix it. */
- sqlite3BtreeDataSize(pCur, (u32*)&n2);
- if( pCur->apPage[pCur->iPage]->intKey ){
- n1 = 0;
- }else{
- sqlite3BtreeKeySize(pCur, (i64*)&n1);
- }
- sqlite3BtreeLeave(pCur->pBtree);
- sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
- Tcl_AppendResult(interp, zBuf, 0);
- return SQLITE_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<(int)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_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;
- }
- /*
- ** 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;
- }
- /*
- ** 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_pager_stats", (Tcl_CmdProc*)btree_pager_stats },
- { "btree_cursor", (Tcl_CmdProc*)btree_cursor },
- { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor },
- { "btree_next", (Tcl_CmdProc*)btree_next },
- { "btree_eof", (Tcl_CmdProc*)btree_eof },
- { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size },
- { "btree_first", (Tcl_CmdProc*)btree_first },
- { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test },
- { "btree_from_db", (Tcl_CmdProc*)btree_from_db },
- { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb },
- { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }
- };
- int i;
- for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
- Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
- }
- return TCL_OK;
- }
|