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