1/* 2** 2001 September 15 3** 4** The author disclaims copyright to this source code. In place of 5** 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 code 13** is not included in the SQLite library. It is used for automated 14** testing of the SQLite library. 15*/ 16#include "sqliteInt.h" 17#include "btreeInt.h" 18#include "tcl.h" 19#include <stdlib.h> 20#include <string.h> 21 22/* 23** Interpret an SQLite error number 24*/ 25static char *errorName(int rc){ 26 char *zName; 27 switch( rc ){ 28 case SQLITE_OK: zName = "SQLITE_OK"; break; 29 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; 30 case SQLITE_PERM: zName = "SQLITE_PERM"; break; 31 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; 32 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; 33 case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; 34 case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; 35 case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; 36 case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; 37 case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; 38 case SQLITE_FULL: zName = "SQLITE_FULL"; break; 39 case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; 40 case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; 41 case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; 42 case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; 43 default: zName = "SQLITE_Unknown"; break; 44 } 45 return zName; 46} 47 48/* 49** A bogus sqlite3 connection structure for use in the btree 50** tests. 51*/ 52static sqlite3 sDb; 53static int nRefSqlite3 = 0; 54 55/* 56** Usage: btree_open FILENAME NCACHE 57** 58** Open a new database 59*/ 60static int btree_open( 61 void *NotUsed, 62 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 63 int argc, /* Number of arguments */ 64 const char **argv /* Text of each argument */ 65){ 66 Btree *pBt; 67 int rc, nCache; 68 char zBuf[100]; 69 if( argc!=3 ){ 70 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 71 " FILENAME NCACHE FLAGS\"", 0); 72 return TCL_ERROR; 73 } 74 if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; 75 nRefSqlite3++; 76 if( nRefSqlite3==1 ){ 77 sDb.pVfs = sqlite3_vfs_find(0); 78 sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); 79 sqlite3_mutex_enter(sDb.mutex); 80 } 81 rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0, 82 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); 83 if( rc!=SQLITE_OK ){ 84 Tcl_AppendResult(interp, errorName(rc), 0); 85 return TCL_ERROR; 86 } 87 sqlite3BtreeSetCacheSize(pBt, nCache); 88 sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt); 89 Tcl_AppendResult(interp, zBuf, 0); 90 return TCL_OK; 91} 92 93/* 94** Usage: btree_close ID 95** 96** Close the given database. 97*/ 98static int btree_close( 99 void *NotUsed, 100 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 101 int argc, /* Number of arguments */ 102 const char **argv /* Text of each argument */ 103){ 104 Btree *pBt; 105 int rc; 106 if( argc!=2 ){ 107 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 108 " ID\"", 0); 109 return TCL_ERROR; 110 } 111 pBt = sqlite3TestTextToPtr(argv[1]); 112 rc = sqlite3BtreeClose(pBt); 113 if( rc!=SQLITE_OK ){ 114 Tcl_AppendResult(interp, errorName(rc), 0); 115 return TCL_ERROR; 116 } 117 nRefSqlite3--; 118 if( nRefSqlite3==0 ){ 119 sqlite3_mutex_leave(sDb.mutex); 120 sqlite3_mutex_free(sDb.mutex); 121 sDb.mutex = 0; 122 sDb.pVfs = 0; 123 } 124 return TCL_OK; 125} 126 127 128/* 129** Usage: btree_begin_transaction ID 130** 131** Start a new transaction 132*/ 133static int btree_begin_transaction( 134 void *NotUsed, 135 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 136 int argc, /* Number of arguments */ 137 const char **argv /* Text of each argument */ 138){ 139 Btree *pBt; 140 int rc; 141 if( argc!=2 ){ 142 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 143 " ID\"", 0); 144 return TCL_ERROR; 145 } 146 pBt = sqlite3TestTextToPtr(argv[1]); 147 sqlite3BtreeEnter(pBt); 148 rc = sqlite3BtreeBeginTrans(pBt, 1); 149 sqlite3BtreeLeave(pBt); 150 if( rc!=SQLITE_OK ){ 151 Tcl_AppendResult(interp, errorName(rc), 0); 152 return TCL_ERROR; 153 } 154 return TCL_OK; 155} 156 157/* 158** Usage: btree_pager_stats ID 159** 160** Returns pager statistics 161*/ 162static int btree_pager_stats( 163 void *NotUsed, 164 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 165 int argc, /* Number of arguments */ 166 const char **argv /* Text of each argument */ 167){ 168 Btree *pBt; 169 int i; 170 int *a; 171 172 if( argc!=2 ){ 173 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 174 " ID\"", 0); 175 return TCL_ERROR; 176 } 177 pBt = sqlite3TestTextToPtr(argv[1]); 178 179 /* Normally in this file, with a b-tree handle opened using the 180 ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly. 181 ** But this function is sometimes called with a btree handle obtained 182 ** from an open SQLite connection (using [btree_from_db]). In this case 183 ** we need to obtain the mutex for the controlling SQLite handle before 184 ** it is safe to call sqlite3BtreeEnter(). 185 */ 186 sqlite3_mutex_enter(pBt->db->mutex); 187 188 sqlite3BtreeEnter(pBt); 189 a = sqlite3PagerStats(sqlite3BtreePager(pBt)); 190 for(i=0; i<11; i++){ 191 static char *zName[] = { 192 "ref", "page", "max", "size", "state", "err", 193 "hit", "miss", "ovfl", "read", "write" 194 }; 195 char zBuf[100]; 196 Tcl_AppendElement(interp, zName[i]); 197 sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]); 198 Tcl_AppendElement(interp, zBuf); 199 } 200 sqlite3BtreeLeave(pBt); 201 202 /* Release the mutex on the SQLite handle that controls this b-tree */ 203 sqlite3_mutex_leave(pBt->db->mutex); 204 return TCL_OK; 205} 206 207/* 208** Usage: btree_cursor ID TABLENUM WRITEABLE 209** 210** Create a new cursor. Return the ID for the cursor. 211*/ 212static int btree_cursor( 213 void *NotUsed, 214 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 215 int argc, /* Number of arguments */ 216 const char **argv /* Text of each argument */ 217){ 218 Btree *pBt; 219 int iTable; 220 BtCursor *pCur; 221 int rc = SQLITE_OK; 222 int wrFlag; 223 char zBuf[30]; 224 225 if( argc!=4 ){ 226 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 227 " ID TABLENUM WRITEABLE\"", 0); 228 return TCL_ERROR; 229 } 230 pBt = sqlite3TestTextToPtr(argv[1]); 231 if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; 232 if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR; 233 pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize()); 234 memset(pCur, 0, sqlite3BtreeCursorSize()); 235 sqlite3BtreeEnter(pBt); 236#ifndef SQLITE_OMIT_SHARED_CACHE 237 rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag); 238#endif 239 if( rc==SQLITE_OK ){ 240 rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur); 241 } 242 sqlite3BtreeLeave(pBt); 243 if( rc ){ 244 ckfree((char *)pCur); 245 Tcl_AppendResult(interp, errorName(rc), 0); 246 return TCL_ERROR; 247 } 248 sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur); 249 Tcl_AppendResult(interp, zBuf, 0); 250 return SQLITE_OK; 251} 252 253/* 254** Usage: btree_close_cursor ID 255** 256** Close a cursor opened using btree_cursor. 257*/ 258static int btree_close_cursor( 259 void *NotUsed, 260 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 261 int argc, /* Number of arguments */ 262 const char **argv /* Text of each argument */ 263){ 264 BtCursor *pCur; 265 Btree *pBt; 266 int rc; 267 268 if( argc!=2 ){ 269 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 270 " ID\"", 0); 271 return TCL_ERROR; 272 } 273 pCur = sqlite3TestTextToPtr(argv[1]); 274 pBt = pCur->pBtree; 275 sqlite3BtreeEnter(pBt); 276 rc = sqlite3BtreeCloseCursor(pCur); 277 sqlite3BtreeLeave(pBt); 278 ckfree((char *)pCur); 279 if( rc ){ 280 Tcl_AppendResult(interp, errorName(rc), 0); 281 return TCL_ERROR; 282 } 283 return SQLITE_OK; 284} 285 286/* 287** Usage: btree_next ID 288** 289** Move the cursor to the next entry in the table. Return 0 on success 290** or 1 if the cursor was already on the last entry in the table or if 291** the table is empty. 292*/ 293static int btree_next( 294 void *NotUsed, 295 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 296 int argc, /* Number of arguments */ 297 const char **argv /* Text of each argument */ 298){ 299 BtCursor *pCur; 300 int rc; 301 int res = 0; 302 char zBuf[100]; 303 304 if( argc!=2 ){ 305 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 306 " ID\"", 0); 307 return TCL_ERROR; 308 } 309 pCur = sqlite3TestTextToPtr(argv[1]); 310 sqlite3BtreeEnter(pCur->pBtree); 311 rc = sqlite3BtreeNext(pCur, &res); 312 sqlite3BtreeLeave(pCur->pBtree); 313 if( rc ){ 314 Tcl_AppendResult(interp, errorName(rc), 0); 315 return TCL_ERROR; 316 } 317 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); 318 Tcl_AppendResult(interp, zBuf, 0); 319 return SQLITE_OK; 320} 321 322/* 323** Usage: btree_first ID 324** 325** Move the cursor to the first entry in the table. Return 0 if the 326** cursor was left point to something and 1 if the table is empty. 327*/ 328static int btree_first( 329 void *NotUsed, 330 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 331 int argc, /* Number of arguments */ 332 const char **argv /* Text of each argument */ 333){ 334 BtCursor *pCur; 335 int rc; 336 int res = 0; 337 char zBuf[100]; 338 339 if( argc!=2 ){ 340 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 341 " ID\"", 0); 342 return TCL_ERROR; 343 } 344 pCur = sqlite3TestTextToPtr(argv[1]); 345 sqlite3BtreeEnter(pCur->pBtree); 346 rc = sqlite3BtreeFirst(pCur, &res); 347 sqlite3BtreeLeave(pCur->pBtree); 348 if( rc ){ 349 Tcl_AppendResult(interp, errorName(rc), 0); 350 return TCL_ERROR; 351 } 352 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); 353 Tcl_AppendResult(interp, zBuf, 0); 354 return SQLITE_OK; 355} 356 357/* 358** Usage: btree_eof ID 359** 360** Return TRUE if the given cursor is not pointing at a valid entry. 361** Return FALSE if the cursor does point to a valid entry. 362*/ 363static int btree_eof( 364 void *NotUsed, 365 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 366 int argc, /* Number of arguments */ 367 const char **argv /* Text of each argument */ 368){ 369 BtCursor *pCur; 370 int rc; 371 char zBuf[50]; 372 373 if( argc!=2 ){ 374 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 375 " ID\"", 0); 376 return TCL_ERROR; 377 } 378 pCur = sqlite3TestTextToPtr(argv[1]); 379 sqlite3BtreeEnter(pCur->pBtree); 380 rc = sqlite3BtreeEof(pCur); 381 sqlite3BtreeLeave(pCur->pBtree); 382 sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc); 383 Tcl_AppendResult(interp, zBuf, 0); 384 return SQLITE_OK; 385} 386 387/* 388** Usage: btree_payload_size ID 389** 390** Return the number of bytes of payload 391*/ 392static int btree_payload_size( 393 void *NotUsed, 394 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 395 int argc, /* Number of arguments */ 396 const char **argv /* Text of each argument */ 397){ 398 BtCursor *pCur; 399 int n2; 400 u64 n1; 401 char zBuf[50]; 402 403 if( argc!=2 ){ 404 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 405 " ID\"", 0); 406 return TCL_ERROR; 407 } 408 pCur = sqlite3TestTextToPtr(argv[1]); 409 sqlite3BtreeEnter(pCur->pBtree); 410 411 /* The cursor may be in "require-seek" state. If this is the case, the 412 ** call to BtreeDataSize() will fix it. */ 413 sqlite3BtreeDataSize(pCur, (u32*)&n2); 414 if( pCur->apPage[pCur->iPage]->intKey ){ 415 n1 = 0; 416 }else{ 417 sqlite3BtreeKeySize(pCur, (i64*)&n1); 418 } 419 sqlite3BtreeLeave(pCur->pBtree); 420 sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); 421 Tcl_AppendResult(interp, zBuf, 0); 422 return SQLITE_OK; 423} 424 425/* 426** usage: varint_test START MULTIPLIER COUNT INCREMENT 427** 428** This command tests the putVarint() and getVarint() 429** routines, both for accuracy and for speed. 430** 431** An integer is written using putVarint() and read back with 432** getVarint() and varified to be unchanged. This repeats COUNT 433** times. The first integer is START*MULTIPLIER. Each iteration 434** increases the integer by INCREMENT. 435** 436** This command returns nothing if it works. It returns an error message 437** if something goes wrong. 438*/ 439static int btree_varint_test( 440 void *NotUsed, 441 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 442 int argc, /* Number of arguments */ 443 const char **argv /* Text of each argument */ 444){ 445 u32 start, mult, count, incr; 446 u64 in, out; 447 int n1, n2, i, j; 448 unsigned char zBuf[100]; 449 if( argc!=5 ){ 450 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 451 " START MULTIPLIER COUNT INCREMENT\"", 0); 452 return TCL_ERROR; 453 } 454 if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR; 455 if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR; 456 if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR; 457 if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR; 458 in = start; 459 in *= mult; 460 for(i=0; i<count; i++){ 461 char zErr[200]; 462 n1 = putVarint(zBuf, in); 463 if( n1>9 || n1<1 ){ 464 sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1); 465 Tcl_AppendResult(interp, zErr, 0); 466 return TCL_ERROR; 467 } 468 n2 = getVarint(zBuf, &out); 469 if( n1!=n2 ){ 470 sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2); 471 Tcl_AppendResult(interp, zErr, 0); 472 return TCL_ERROR; 473 } 474 if( in!=out ){ 475 sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out); 476 Tcl_AppendResult(interp, zErr, 0); 477 return TCL_ERROR; 478 } 479 if( (in & 0xffffffff)==in ){ 480 u32 out32; 481 n2 = getVarint32(zBuf, out32); 482 out = out32; 483 if( n1!=n2 ){ 484 sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", 485 n1, n2); 486 Tcl_AppendResult(interp, zErr, 0); 487 return TCL_ERROR; 488 } 489 if( in!=out ){ 490 sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", 491 in, out); 492 Tcl_AppendResult(interp, zErr, 0); 493 return TCL_ERROR; 494 } 495 } 496 497 /* In order to get realistic timings, run getVarint 19 more times. 498 ** This is because getVarint is called about 20 times more often 499 ** than putVarint. 500 */ 501 for(j=0; j<19; j++){ 502 getVarint(zBuf, &out); 503 } 504 in += incr; 505 } 506 return TCL_OK; 507} 508 509/* 510** usage: btree_from_db DB-HANDLE 511** 512** This command returns the btree handle for the main database associated 513** with the database-handle passed as the argument. Example usage: 514** 515** sqlite3 db test.db 516** set bt [btree_from_db db] 517*/ 518static int btree_from_db( 519 void *NotUsed, 520 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 521 int argc, /* Number of arguments */ 522 const char **argv /* Text of each argument */ 523){ 524 char zBuf[100]; 525 Tcl_CmdInfo info; 526 sqlite3 *db; 527 Btree *pBt; 528 int iDb = 0; 529 530 if( argc!=2 && argc!=3 ){ 531 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 532 " DB-HANDLE ?N?\"", 0); 533 return TCL_ERROR; 534 } 535 536 if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){ 537 Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0); 538 return TCL_ERROR; 539 } 540 if( argc==3 ){ 541 iDb = atoi(argv[2]); 542 } 543 544 db = *((sqlite3 **)info.objClientData); 545 assert( db ); 546 547 pBt = db->aDb[iDb].pBt; 548 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt); 549 Tcl_SetResult(interp, zBuf, TCL_VOLATILE); 550 return TCL_OK; 551} 552 553/* 554** Usage: btree_ismemdb ID 555** 556** Return true if the B-Tree is in-memory. 557*/ 558static int btree_ismemdb( 559 void *NotUsed, 560 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 561 int argc, /* Number of arguments */ 562 const char **argv /* Text of each argument */ 563){ 564 Btree *pBt; 565 int res; 566 567 if( argc!=2 ){ 568 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 569 " ID\"", 0); 570 return TCL_ERROR; 571 } 572 pBt = sqlite3TestTextToPtr(argv[1]); 573 sqlite3_mutex_enter(pBt->db->mutex); 574 sqlite3BtreeEnter(pBt); 575 res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt)); 576 sqlite3BtreeLeave(pBt); 577 sqlite3_mutex_leave(pBt->db->mutex); 578 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); 579 return SQLITE_OK; 580} 581 582/* 583** usage: btree_set_cache_size ID NCACHE 584** 585** Set the size of the cache used by btree $ID. 586*/ 587static int btree_set_cache_size( 588 void *NotUsed, 589 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 590 int argc, /* Number of arguments */ 591 const char **argv /* Text of each argument */ 592){ 593 int nCache; 594 Btree *pBt; 595 596 if( argc!=3 ){ 597 Tcl_AppendResult( 598 interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0); 599 return TCL_ERROR; 600 } 601 pBt = sqlite3TestTextToPtr(argv[1]); 602 if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; 603 604 sqlite3_mutex_enter(pBt->db->mutex); 605 sqlite3BtreeEnter(pBt); 606 sqlite3BtreeSetCacheSize(pBt, nCache); 607 sqlite3BtreeLeave(pBt); 608 sqlite3_mutex_leave(pBt->db->mutex); 609 return TCL_OK; 610} 611 612 613 614/* 615** Register commands with the TCL interpreter. 616*/ 617int Sqlitetest3_Init(Tcl_Interp *interp){ 618 static struct { 619 char *zName; 620 Tcl_CmdProc *xProc; 621 } aCmd[] = { 622 { "btree_open", (Tcl_CmdProc*)btree_open }, 623 { "btree_close", (Tcl_CmdProc*)btree_close }, 624 { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, 625 { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, 626 { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, 627 { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, 628 { "btree_next", (Tcl_CmdProc*)btree_next }, 629 { "btree_eof", (Tcl_CmdProc*)btree_eof }, 630 { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, 631 { "btree_first", (Tcl_CmdProc*)btree_first }, 632 { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, 633 { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, 634 { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, 635 { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size } 636 }; 637 int i; 638 639 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 640 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 641 } 642 643 return TCL_OK; 644} 645