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 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 */ 98 static 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 */ 133 static 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 */ 162 static 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 */ 212 static 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 */ 258 static 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 */ 293 static 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 */ 328 static 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 */ 363 static 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 */ 392 static 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 */ 439 static 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 */ 518 static 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 */ 558 static 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 */ 587 static 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 */ 617 int 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