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 FLAGS 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, flags; 68 char zBuf[100]; 69 if( argc!=4 ){ 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 if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR; 76 nRefSqlite3++; 77 if( nRefSqlite3==1 ){ 78 sDb.pVfs = sqlite3_vfs_find(0); 79 sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); 80 sqlite3_mutex_enter(sDb.mutex); 81 } 82 rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags, 83 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); 84 if( rc!=SQLITE_OK ){ 85 Tcl_AppendResult(interp, errorName(rc), 0); 86 return TCL_ERROR; 87 } 88 sqlite3BtreeSetCacheSize(pBt, nCache); 89 sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt); 90 Tcl_AppendResult(interp, zBuf, 0); 91 return TCL_OK; 92 } 93 94 /* 95 ** Usage: btree_close ID 96 ** 97 ** Close the given database. 98 */ 99 static int btree_close( 100 void *NotUsed, 101 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 102 int argc, /* Number of arguments */ 103 const char **argv /* Text of each argument */ 104 ){ 105 Btree *pBt; 106 int rc; 107 if( argc!=2 ){ 108 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 109 " ID\"", 0); 110 return TCL_ERROR; 111 } 112 pBt = sqlite3TestTextToPtr(argv[1]); 113 rc = sqlite3BtreeClose(pBt); 114 if( rc!=SQLITE_OK ){ 115 Tcl_AppendResult(interp, errorName(rc), 0); 116 return TCL_ERROR; 117 } 118 nRefSqlite3--; 119 if( nRefSqlite3==0 ){ 120 sqlite3_mutex_leave(sDb.mutex); 121 sqlite3_mutex_free(sDb.mutex); 122 sDb.mutex = 0; 123 sDb.pVfs = 0; 124 } 125 return TCL_OK; 126 } 127 128 129 /* 130 ** Usage: btree_begin_transaction ID 131 ** 132 ** Start a new transaction 133 */ 134 static int btree_begin_transaction( 135 void *NotUsed, 136 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 137 int argc, /* Number of arguments */ 138 const char **argv /* Text of each argument */ 139 ){ 140 Btree *pBt; 141 int rc; 142 if( argc!=2 ){ 143 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 144 " ID\"", 0); 145 return TCL_ERROR; 146 } 147 pBt = sqlite3TestTextToPtr(argv[1]); 148 sqlite3BtreeEnter(pBt); 149 rc = sqlite3BtreeBeginTrans(pBt, 1); 150 sqlite3BtreeLeave(pBt); 151 if( rc!=SQLITE_OK ){ 152 Tcl_AppendResult(interp, errorName(rc), 0); 153 return TCL_ERROR; 154 } 155 return TCL_OK; 156 } 157 158 /* 159 ** Usage: btree_pager_stats ID 160 ** 161 ** Returns pager statistics 162 */ 163 static int btree_pager_stats( 164 void *NotUsed, 165 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 166 int argc, /* Number of arguments */ 167 const char **argv /* Text of each argument */ 168 ){ 169 Btree *pBt; 170 int i; 171 int *a; 172 173 if( argc!=2 ){ 174 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 175 " ID\"", 0); 176 return TCL_ERROR; 177 } 178 pBt = sqlite3TestTextToPtr(argv[1]); 179 180 /* Normally in this file, with a b-tree handle opened using the 181 ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly. 182 ** But this function is sometimes called with a btree handle obtained 183 ** from an open SQLite connection (using [btree_from_db]). In this case 184 ** we need to obtain the mutex for the controlling SQLite handle before 185 ** it is safe to call sqlite3BtreeEnter(). 186 */ 187 sqlite3_mutex_enter(pBt->db->mutex); 188 189 sqlite3BtreeEnter(pBt); 190 a = sqlite3PagerStats(sqlite3BtreePager(pBt)); 191 for(i=0; i<11; i++){ 192 static char *zName[] = { 193 "ref", "page", "max", "size", "state", "err", 194 "hit", "miss", "ovfl", "read", "write" 195 }; 196 char zBuf[100]; 197 Tcl_AppendElement(interp, zName[i]); 198 sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]); 199 Tcl_AppendElement(interp, zBuf); 200 } 201 sqlite3BtreeLeave(pBt); 202 203 /* Release the mutex on the SQLite handle that controls this b-tree */ 204 sqlite3_mutex_leave(pBt->db->mutex); 205 return TCL_OK; 206 } 207 208 /* 209 ** Usage: btree_cursor ID TABLENUM WRITEABLE 210 ** 211 ** Create a new cursor. Return the ID for the cursor. 212 */ 213 static int btree_cursor( 214 void *NotUsed, 215 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 216 int argc, /* Number of arguments */ 217 const char **argv /* Text of each argument */ 218 ){ 219 Btree *pBt; 220 int iTable; 221 BtCursor *pCur; 222 int rc = SQLITE_OK; 223 int wrFlag; 224 char zBuf[30]; 225 226 if( argc!=4 ){ 227 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 228 " ID TABLENUM WRITEABLE\"", 0); 229 return TCL_ERROR; 230 } 231 pBt = sqlite3TestTextToPtr(argv[1]); 232 if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; 233 if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR; 234 pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize()); 235 memset(pCur, 0, sqlite3BtreeCursorSize()); 236 sqlite3BtreeEnter(pBt); 237 #ifndef SQLITE_OMIT_SHARED_CACHE 238 rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag); 239 #endif 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 412 /* The cursor may be in "require-seek" state. If this is the case, the 413 ** call to BtreeDataSize() will fix it. */ 414 sqlite3BtreeDataSize(pCur, (u32*)&n2); 415 if( pCur->apPage[pCur->iPage]->intKey ){ 416 n1 = 0; 417 }else{ 418 sqlite3BtreeKeySize(pCur, (i64*)&n1); 419 } 420 sqlite3BtreeLeave(pCur->pBtree); 421 sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2)); 422 Tcl_AppendResult(interp, zBuf, 0); 423 return SQLITE_OK; 424 } 425 426 /* 427 ** usage: varint_test START MULTIPLIER COUNT INCREMENT 428 ** 429 ** This command tests the putVarint() and getVarint() 430 ** routines, both for accuracy and for speed. 431 ** 432 ** An integer is written using putVarint() and read back with 433 ** getVarint() and varified to be unchanged. This repeats COUNT 434 ** times. The first integer is START*MULTIPLIER. Each iteration 435 ** increases the integer by INCREMENT. 436 ** 437 ** This command returns nothing if it works. It returns an error message 438 ** if something goes wrong. 439 */ 440 static int btree_varint_test( 441 void *NotUsed, 442 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 443 int argc, /* Number of arguments */ 444 const char **argv /* Text of each argument */ 445 ){ 446 u32 start, mult, count, incr; 447 u64 in, out; 448 int n1, n2, i, j; 449 unsigned char zBuf[100]; 450 if( argc!=5 ){ 451 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 452 " START MULTIPLIER COUNT INCREMENT\"", 0); 453 return TCL_ERROR; 454 } 455 if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR; 456 if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR; 457 if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR; 458 if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR; 459 in = start; 460 in *= mult; 461 for(i=0; i<count; i++){ 462 char zErr[200]; 463 n1 = putVarint(zBuf, in); 464 if( n1>9 || n1<1 ){ 465 sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1); 466 Tcl_AppendResult(interp, zErr, 0); 467 return TCL_ERROR; 468 } 469 n2 = getVarint(zBuf, &out); 470 if( n1!=n2 ){ 471 sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2); 472 Tcl_AppendResult(interp, zErr, 0); 473 return TCL_ERROR; 474 } 475 if( in!=out ){ 476 sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out); 477 Tcl_AppendResult(interp, zErr, 0); 478 return TCL_ERROR; 479 } 480 if( (in & 0xffffffff)==in ){ 481 u32 out32; 482 n2 = getVarint32(zBuf, out32); 483 out = out32; 484 if( n1!=n2 ){ 485 sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d", 486 n1, n2); 487 Tcl_AppendResult(interp, zErr, 0); 488 return TCL_ERROR; 489 } 490 if( in!=out ){ 491 sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32", 492 in, out); 493 Tcl_AppendResult(interp, zErr, 0); 494 return TCL_ERROR; 495 } 496 } 497 498 /* In order to get realistic timings, run getVarint 19 more times. 499 ** This is because getVarint is called about 20 times more often 500 ** than putVarint. 501 */ 502 for(j=0; j<19; j++){ 503 getVarint(zBuf, &out); 504 } 505 in += incr; 506 } 507 return TCL_OK; 508 } 509 510 /* 511 ** usage: btree_from_db DB-HANDLE 512 ** 513 ** This command returns the btree handle for the main database associated 514 ** with the database-handle passed as the argument. Example usage: 515 ** 516 ** sqlite3 db test.db 517 ** set bt [btree_from_db db] 518 */ 519 static int btree_from_db( 520 void *NotUsed, 521 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 522 int argc, /* Number of arguments */ 523 const char **argv /* Text of each argument */ 524 ){ 525 char zBuf[100]; 526 Tcl_CmdInfo info; 527 sqlite3 *db; 528 Btree *pBt; 529 int iDb = 0; 530 531 if( argc!=2 && argc!=3 ){ 532 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 533 " DB-HANDLE ?N?\"", 0); 534 return TCL_ERROR; 535 } 536 537 if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){ 538 Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0); 539 return TCL_ERROR; 540 } 541 if( argc==3 ){ 542 iDb = atoi(argv[2]); 543 } 544 545 db = *((sqlite3 **)info.objClientData); 546 assert( db ); 547 548 pBt = db->aDb[iDb].pBt; 549 sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt); 550 Tcl_SetResult(interp, zBuf, TCL_VOLATILE); 551 return TCL_OK; 552 } 553 554 /* 555 ** Usage: btree_ismemdb ID 556 ** 557 ** Return true if the B-Tree is in-memory. 558 */ 559 static int btree_ismemdb( 560 void *NotUsed, 561 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 562 int argc, /* Number of arguments */ 563 const char **argv /* Text of each argument */ 564 ){ 565 Btree *pBt; 566 int res; 567 568 if( argc!=2 ){ 569 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 570 " ID\"", 0); 571 return TCL_ERROR; 572 } 573 pBt = sqlite3TestTextToPtr(argv[1]); 574 sqlite3_mutex_enter(pBt->db->mutex); 575 sqlite3BtreeEnter(pBt); 576 res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt)); 577 sqlite3BtreeLeave(pBt); 578 sqlite3_mutex_leave(pBt->db->mutex); 579 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res)); 580 return SQLITE_OK; 581 } 582 583 /* 584 ** usage: btree_set_cache_size ID NCACHE 585 ** 586 ** Set the size of the cache used by btree $ID. 587 */ 588 static int btree_set_cache_size( 589 void *NotUsed, 590 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 591 int argc, /* Number of arguments */ 592 const char **argv /* Text of each argument */ 593 ){ 594 int nCache; 595 Btree *pBt; 596 597 if( argc!=3 ){ 598 Tcl_AppendResult( 599 interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0); 600 return TCL_ERROR; 601 } 602 pBt = sqlite3TestTextToPtr(argv[1]); 603 if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; 604 605 sqlite3_mutex_enter(pBt->db->mutex); 606 sqlite3BtreeEnter(pBt); 607 sqlite3BtreeSetCacheSize(pBt, nCache); 608 sqlite3BtreeLeave(pBt); 609 sqlite3_mutex_leave(pBt->db->mutex); 610 return TCL_OK; 611 } 612 613 614 615 /* 616 ** Register commands with the TCL interpreter. 617 */ 618 int Sqlitetest3_Init(Tcl_Interp *interp){ 619 static struct { 620 char *zName; 621 Tcl_CmdProc *xProc; 622 } aCmd[] = { 623 { "btree_open", (Tcl_CmdProc*)btree_open }, 624 { "btree_close", (Tcl_CmdProc*)btree_close }, 625 { "btree_begin_transaction", (Tcl_CmdProc*)btree_begin_transaction }, 626 { "btree_pager_stats", (Tcl_CmdProc*)btree_pager_stats }, 627 { "btree_cursor", (Tcl_CmdProc*)btree_cursor }, 628 { "btree_close_cursor", (Tcl_CmdProc*)btree_close_cursor }, 629 { "btree_next", (Tcl_CmdProc*)btree_next }, 630 { "btree_eof", (Tcl_CmdProc*)btree_eof }, 631 { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, 632 { "btree_first", (Tcl_CmdProc*)btree_first }, 633 { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, 634 { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, 635 { "btree_ismemdb", (Tcl_CmdProc*)btree_ismemdb }, 636 { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size } 637 }; 638 int i; 639 640 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 641 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 642 } 643 644 return TCL_OK; 645 } 646