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