AryaWu/sqlite
0
1/*2** 2001 September 153**4** The author disclaims copyright to this source code. In place of5** a legal notice, here is a blessing:6**7** May you do good and not evil.8** May you find forgiveness for yourself and forgive others.9** May you share freely, never taking more than you give.10**11*************************************************************************12** Code for testing the btree.c module in SQLite. This code13** is not included in the SQLite library. It is used for automated14** testing of the SQLite library.15*/16#include "sqliteInt.h"17#include "btreeInt.h"18#include "tclsqlite.h"19#include <stdlib.h>20#include <string.h>21 22extern const char *sqlite3ErrName(int);23 24/*25** A bogus sqlite3 connection structure for use in the btree26** tests.27*/28static sqlite3 sDb;29static int nRefSqlite3 = 0;30 31/*32** Usage: btree_open FILENAME NCACHE33**34** Open a new database35*/36static int SQLITE_TCLAPI btree_open(37 void *NotUsed,38 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */39 int argc, /* Number of arguments */40 const char **argv /* Text of each argument */41){42 Btree *pBt;43 int rc, nCache;44 char zBuf[100];45 int n;46 char *zFilename;47 if( argc!=3 ){48 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],49 " FILENAME NCACHE FLAGS\"", NULL);50 return TCL_ERROR;51 }52 53 if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;54 nRefSqlite3++;55 if( nRefSqlite3==1 ){56 sDb.pVfs = sqlite3_vfs_find(0);57 sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);58 sqlite3_mutex_enter(sDb.mutex);59 }60 n = (int)strlen(argv[1]);61 zFilename = sqlite3_malloc( n+2 );62 if( zFilename==0 ) return TCL_ERROR;63 memcpy(zFilename, argv[1], n+1);64 zFilename[n+1] = 0;65 rc = sqlite3BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0, 66 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);67 sqlite3_free(zFilename);68 if( rc!=SQLITE_OK ){69 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);70 return TCL_ERROR;71 }72 sqlite3BtreeSetCacheSize(pBt, nCache);73 sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);74 Tcl_AppendResult(interp, zBuf, NULL);75 return TCL_OK;76}77 78/*79** Usage: btree_close ID80**81** Close the given database.82*/83static int SQLITE_TCLAPI btree_close(84 void *NotUsed,85 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */86 int argc, /* Number of arguments */87 const char **argv /* Text of each argument */88){89 Btree *pBt;90 int rc;91 if( argc!=2 ){92 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],93 " ID\"", NULL);94 return TCL_ERROR;95 }96 pBt = sqlite3TestTextToPtr(argv[1]);97 rc = sqlite3BtreeClose(pBt);98 if( rc!=SQLITE_OK ){99 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);100 return TCL_ERROR;101 }102 nRefSqlite3--;103 if( nRefSqlite3==0 ){104 sqlite3_mutex_leave(sDb.mutex);105 sqlite3_mutex_free(sDb.mutex);106 sDb.mutex = 0;107 sDb.pVfs = 0;108 }109 return TCL_OK;110}111 112 113/*114** Usage: btree_begin_transaction ID115**116** Start a new transaction117*/118static int SQLITE_TCLAPI btree_begin_transaction(119 void *NotUsed,120 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */121 int argc, /* Number of arguments */122 const char **argv /* Text of each argument */123){124 Btree *pBt;125 int rc;126 if( argc!=2 ){127 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],128 " ID\"", NULL);129 return TCL_ERROR;130 }131 pBt = sqlite3TestTextToPtr(argv[1]);132 sqlite3BtreeEnter(pBt);133 rc = sqlite3BtreeBeginTrans(pBt, 1, 0);134 sqlite3BtreeLeave(pBt);135 if( rc!=SQLITE_OK ){136 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);137 return TCL_ERROR;138 }139 return TCL_OK;140}141 142/*143** Usage: btree_pager_stats ID144**145** Returns pager statistics146*/147static int SQLITE_TCLAPI btree_pager_stats(148 void *NotUsed,149 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */150 int argc, /* Number of arguments */151 const char **argv /* Text of each argument */152){153 Btree *pBt;154 int i;155 int *a;156 157 if( argc!=2 ){158 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],159 " ID\"", NULL);160 return TCL_ERROR;161 }162 pBt = sqlite3TestTextToPtr(argv[1]);163 164 /* Normally in this file, with a b-tree handle opened using the 165 ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.166 ** But this function is sometimes called with a btree handle obtained167 ** from an open SQLite connection (using [btree_from_db]). In this case168 ** we need to obtain the mutex for the controlling SQLite handle before169 ** it is safe to call sqlite3BtreeEnter().170 */171 sqlite3_mutex_enter(pBt->db->mutex);172 173 sqlite3BtreeEnter(pBt);174 a = sqlite3PagerStats(sqlite3BtreePager(pBt));175 for(i=0; i<11; i++){176 static char *zName[] = {177 "ref", "page", "max", "size", "state", "err",178 "hit", "miss", "ovfl", "read", "write"179 };180 char zBuf[100];181 Tcl_AppendElement(interp, zName[i]);182 sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);183 Tcl_AppendElement(interp, zBuf);184 }185 sqlite3BtreeLeave(pBt);186 187 /* Release the mutex on the SQLite handle that controls this b-tree */188 sqlite3_mutex_leave(pBt->db->mutex);189 return TCL_OK;190}191 192/*193** Usage: btree_cursor ID TABLENUM WRITEABLE194**195** Create a new cursor. Return the ID for the cursor.196*/197static int SQLITE_TCLAPI btree_cursor(198 void *NotUsed,199 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */200 int argc, /* Number of arguments */201 const char **argv /* Text of each argument */202){203 Btree *pBt;204 int iTable;205 BtCursor *pCur;206 int rc = SQLITE_OK;207 int wrFlag;208 char zBuf[30];209 210 if( argc!=4 ){211 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],212 " ID TABLENUM WRITEABLE\"", NULL);213 return TCL_ERROR;214 }215 pBt = sqlite3TestTextToPtr(argv[1]);216 if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;217 if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;218 if( wrFlag ) wrFlag = BTREE_WRCSR;219 pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());220 memset(pCur, 0, sqlite3BtreeCursorSize());221 sqlite3_mutex_enter(pBt->db->mutex);222 sqlite3BtreeEnter(pBt);223#ifndef SQLITE_OMIT_SHARED_CACHE224 rc = sqlite3BtreeLockTable(pBt, iTable, !!wrFlag);225#endif226 if( rc==SQLITE_OK ){227 rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);228 }229 sqlite3BtreeLeave(pBt);230 sqlite3_mutex_leave(pBt->db->mutex);231 if( rc ){232 ckfree((char *)pCur);233 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);234 return TCL_ERROR;235 }236 sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);237 Tcl_AppendResult(interp, zBuf, NULL);238 return SQLITE_OK;239}240 241/*242** Usage: btree_close_cursor ID243**244** Close a cursor opened using btree_cursor.245*/246static int SQLITE_TCLAPI btree_close_cursor(247 void *NotUsed,248 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */249 int argc, /* Number of arguments */250 const char **argv /* Text of each argument */251){252 BtCursor *pCur;253 int rc;254 255 if( argc!=2 ){256 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],257 " ID\"", NULL);258 return TCL_ERROR;259 }260 pCur = sqlite3TestTextToPtr(argv[1]);261#if SQLITE_THREADSAFE>0262 {263 Btree *pBt = pCur->pBtree;264 sqlite3_mutex_enter(pBt->db->mutex);265 sqlite3BtreeEnter(pBt);266 rc = sqlite3BtreeCloseCursor(pCur);267 sqlite3BtreeLeave(pBt);268 sqlite3_mutex_leave(pBt->db->mutex);269 }270#else271 rc = sqlite3BtreeCloseCursor(pCur);272#endif273 ckfree((char *)pCur);274 if( rc ){275 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);276 return TCL_ERROR;277 }278 return SQLITE_OK;279}280 281/*282** Usage: btree_next ID283**284** Move the cursor to the next entry in the table. Return 0 on success285** or 1 if the cursor was already on the last entry in the table or if286** the table is empty.287*/288static int SQLITE_TCLAPI btree_next(289 void *NotUsed,290 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */291 int argc, /* Number of arguments */292 const char **argv /* Text of each argument */293){294 BtCursor *pCur;295 int rc;296 int res = 0;297 char zBuf[100];298 299 if( argc!=2 ){300 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],301 " ID\"", NULL);302 return TCL_ERROR;303 }304 pCur = sqlite3TestTextToPtr(argv[1]);305 sqlite3BtreeEnter(pCur->pBtree);306 rc = sqlite3BtreeNext(pCur, 0);307 if( rc==SQLITE_DONE ){308 res = 1;309 rc = SQLITE_OK;310 }311 sqlite3BtreeLeave(pCur->pBtree);312 if( rc ){313 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);314 return TCL_ERROR;315 }316 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);317 Tcl_AppendResult(interp, zBuf, NULL);318 return SQLITE_OK;319}320 321/*322** Usage: btree_first ID323**324** Move the cursor to the first entry in the table. Return 0 if the325** cursor was left point to something and 1 if the table is empty.326*/327static int SQLITE_TCLAPI btree_first(328 void *NotUsed,329 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */330 int argc, /* Number of arguments */331 const char **argv /* Text of each argument */332){333 BtCursor *pCur;334 int rc;335 int res = 0;336 char zBuf[100];337 338 if( argc!=2 ){339 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],340 " ID\"", NULL);341 return TCL_ERROR;342 }343 pCur = sqlite3TestTextToPtr(argv[1]);344 sqlite3BtreeEnter(pCur->pBtree);345 rc = sqlite3BtreeFirst(pCur, &res);346 sqlite3BtreeLeave(pCur->pBtree);347 if( rc ){348 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);349 return TCL_ERROR;350 }351 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);352 Tcl_AppendResult(interp, zBuf, NULL);353 return SQLITE_OK;354}355 356/*357** Usage: btree_eof ID358**359** Return TRUE if the given cursor is not pointing at a valid entry.360** Return FALSE if the cursor does point to a valid entry.361*/362static int SQLITE_TCLAPI btree_eof(363 void *NotUsed,364 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */365 int argc, /* Number of arguments */366 const char **argv /* Text of each argument */367){368 BtCursor *pCur;369 int rc;370 char zBuf[50];371 372 if( argc!=2 ){373 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],374 " ID\"", NULL);375 return TCL_ERROR;376 }377 pCur = sqlite3TestTextToPtr(argv[1]);378 sqlite3BtreeEnter(pCur->pBtree);379 rc = sqlite3BtreeEof(pCur);380 sqlite3BtreeLeave(pCur->pBtree);381 sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);382 Tcl_AppendResult(interp, zBuf, NULL);383 return SQLITE_OK;384}385 386/*387** Usage: btree_payload_size ID388**389** Return the number of bytes of payload390*/391static int SQLITE_TCLAPI btree_payload_size(392 void *NotUsed,393 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */394 int argc, /* Number of arguments */395 const char **argv /* Text of each argument */396){397 BtCursor *pCur;398 u32 n;399 char zBuf[50];400 401 if( argc!=2 ){402 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],403 " ID\"", NULL);404 return TCL_ERROR;405 }406 pCur = sqlite3TestTextToPtr(argv[1]);407 sqlite3BtreeEnter(pCur->pBtree);408 n = sqlite3BtreePayloadSize(pCur);409 sqlite3BtreeLeave(pCur->pBtree);410 sqlite3_snprintf(sizeof(zBuf),zBuf, "%u", n);411 Tcl_AppendResult(interp, zBuf, NULL);412 return SQLITE_OK;413}414 415/*416** usage: varint_test START MULTIPLIER COUNT INCREMENT417**418** This command tests the putVarint() and getVarint()419** routines, both for accuracy and for speed.420**421** An integer is written using putVarint() and read back with422** getVarint() and verified to be unchanged. This repeats COUNT423** times. The first integer is START*MULTIPLIER. Each iteration424** increases the integer by INCREMENT.425**426** This command returns nothing if it works. It returns an error message427** if something goes wrong.428*/429static int SQLITE_TCLAPI btree_varint_test(430 void *NotUsed,431 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */432 int argc, /* Number of arguments */433 const char **argv /* Text of each argument */434){435 u32 start, mult, count, incr;436 u64 in, out;437 int n1, n2, i, j;438 unsigned char zBuf[100];439 if( argc!=5 ){440 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],441 " START MULTIPLIER COUNT INCREMENT\"", NULL);442 return TCL_ERROR;443 }444 if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;445 if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;446 if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;447 if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;448 in = start;449 in *= mult;450 for(i=0; i<(int)count; i++){451 char zErr[200];452 n1 = putVarint(zBuf, in);453 if( n1>9 || n1<1 ){454 sqlite3_snprintf(sizeof(zErr), zErr,455 "putVarint returned %d - should be between 1 and 9", n1);456 Tcl_AppendResult(interp, zErr, NULL);457 return TCL_ERROR;458 }459 n2 = getVarint(zBuf, &out);460 if( n1!=n2 ){461 sqlite3_snprintf(sizeof(zErr), zErr,462 "putVarint returned %d and getVarint returned %d", n1, n2);463 Tcl_AppendResult(interp, zErr, NULL);464 return TCL_ERROR;465 }466 if( in!=out ){467 sqlite3_snprintf(sizeof(zErr), zErr,468 "Wrote 0x%016llx and got back 0x%016llx", in, out);469 Tcl_AppendResult(interp, zErr, NULL);470 return TCL_ERROR;471 }472 if( (in & 0xffffffff)==in ){473 u32 out32;474 n2 = getVarint32(zBuf, out32);475 out = out32;476 if( n1!=n2 ){477 sqlite3_snprintf(sizeof(zErr), zErr,478 "putVarint returned %d and GetVarint32 returned %d", 479 n1, n2);480 Tcl_AppendResult(interp, zErr, NULL);481 return TCL_ERROR;482 }483 if( in!=out ){484 sqlite3_snprintf(sizeof(zErr), zErr,485 "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",486 in, out);487 Tcl_AppendResult(interp, zErr, NULL);488 return TCL_ERROR;489 }490 }491 492 /* In order to get realistic timings, run getVarint 19 more times.493 ** This is because getVarint is called about 20 times more often494 ** than putVarint.495 */496 for(j=0; j<19; j++){497 getVarint(zBuf, &out);498 }499 in += incr;500 }501 return TCL_OK;502}503 504/*505** usage: btree_from_db DB-HANDLE506**507** This command returns the btree handle for the main database associated508** with the database-handle passed as the argument. Example usage:509**510** sqlite3 db test.db511** set bt [btree_from_db db]512*/513static int SQLITE_TCLAPI btree_from_db(514 void *NotUsed,515 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */516 int argc, /* Number of arguments */517 const char **argv /* Text of each argument */518){519 char zBuf[100];520 Tcl_CmdInfo info;521 sqlite3 *db;522 Btree *pBt;523 int iDb = 0;524 525 if( argc!=2 && argc!=3 ){526 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],527 " DB-HANDLE ?N?\"", NULL);528 return TCL_ERROR;529 }530 531 if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){532 Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", NULL);533 return TCL_ERROR;534 }535 if( argc==3 ){536 iDb = atoi(argv[2]);537 }538 539 db = *((sqlite3 **)info.objClientData);540 assert( db );541 542 pBt = db->aDb[iDb].pBt;543 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);544 Tcl_SetResult(interp, zBuf, TCL_VOLATILE);545 return TCL_OK;546}547 548/*549** Usage: btree_ismemdb ID550**551** Return true if the B-Tree is currently stored entirely in memory.552*/553static int SQLITE_TCLAPI btree_ismemdb(554 void *NotUsed,555 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */556 int argc, /* Number of arguments */557 const char **argv /* Text of each argument */558){559 Btree *pBt;560 int res;561 sqlite3_file *pFile;562 563 if( argc!=2 ){564 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],565 " ID\"", NULL);566 return TCL_ERROR;567 }568 pBt = sqlite3TestTextToPtr(argv[1]);569 sqlite3_mutex_enter(pBt->db->mutex);570 sqlite3BtreeEnter(pBt);571 pFile = sqlite3PagerFile(sqlite3BtreePager(pBt));572 res = (pFile->pMethods==0);573 sqlite3BtreeLeave(pBt);574 sqlite3_mutex_leave(pBt->db->mutex);575 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));576 return SQLITE_OK;577}578 579/*580** usage: btree_set_cache_size ID NCACHE581**582** Set the size of the cache used by btree $ID.583*/584static int SQLITE_TCLAPI btree_set_cache_size(585 void *NotUsed,586 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */587 int argc, /* Number of arguments */588 const char **argv /* Text of each argument */589){590 int nCache;591 Btree *pBt;592 593 if( argc!=3 ){594 Tcl_AppendResult(595 interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", NULL);596 return TCL_ERROR;597 }598 pBt = sqlite3TestTextToPtr(argv[1]);599 if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;600 601 sqlite3_mutex_enter(pBt->db->mutex);602 sqlite3BtreeEnter(pBt);603 sqlite3BtreeSetCacheSize(pBt, nCache);604 sqlite3BtreeLeave(pBt);605 sqlite3_mutex_leave(pBt->db->mutex);606 return TCL_OK;607} 608 609/*610** usage: btree_insert CSR ?KEY? VALUE611**612** Set the size of the cache used by btree $ID.613*/614static int SQLITE_TCLAPI btree_insert(615 ClientData clientData,616 Tcl_Interp *interp,617 int objc,618 Tcl_Obj *const objv[]619){620 BtCursor *pCur;621 int rc;622 BtreePayload x;623 Tcl_Size n;624 625 if( objc!=4 && objc!=3 ){626 Tcl_WrongNumArgs(interp, 1, objv, "?-intkey? CSR KEY VALUE");627 return TCL_ERROR;628 }629 630 memset(&x, 0, sizeof(x));631 if( objc==4 ){632 if( Tcl_GetIntFromObj(interp, objv[2], &rc) ) return TCL_ERROR;633 x.nKey = rc;634 x.pData = (void*)Tcl_GetByteArrayFromObj(objv[3], &n);635 x.nData = (int)n;636 }else{637 x.pKey = (void*)Tcl_GetByteArrayFromObj(objv[2], &n);638 x.nKey = (int)n;639 }640 pCur = (BtCursor*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));641 642 sqlite3_mutex_enter(pCur->pBtree->db->mutex);643 sqlite3BtreeEnter(pCur->pBtree);644 rc = sqlite3BtreeInsert(pCur, &x, 0, 0);645 sqlite3BtreeLeave(pCur->pBtree);646 sqlite3_mutex_leave(pCur->pBtree->db->mutex);647 648 Tcl_ResetResult(interp);649 if( rc ){650 Tcl_AppendResult(interp, sqlite3ErrName(rc), NULL);651 return TCL_ERROR;652 }653 return TCL_OK;654}655 656 657/*658** Register commands with the TCL interpreter.659*/660int Sqlitetest3_Init(Tcl_Interp *interp){661 static struct {662 char *zName;663 Tcl_CmdProc *xProc;664 } aCmd[] = {665 { "btree_open", (Tcl_CmdProc*)btree_open },666 { "btree_close", (Tcl_CmdProc*)btree_close },667 { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction },668 { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats },669 { "btree_cursor", (Tcl_CmdProc*)btree_cursor },670 { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor },671 { "btree_next", (Tcl_CmdProc*)btree_next },672 { "btree_eof", (Tcl_CmdProc*)btree_eof },673 { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size },674 { "btree_first", (Tcl_CmdProc*)btree_first },675 { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test },676 { "btree_from_db", (Tcl_CmdProc*)btree_from_db },677 { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb },678 { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }679 };680 int i;681 682 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){683 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);684 }685 686 Tcl_CreateObjCommand(interp, "btree_insert", btree_insert, 0, 0);687 688 return TCL_OK;689}690 