xref: /sqlite-3.40.0/src/test3.c (revision 6695f47e)
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;
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   rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
238   if( rc==SQLITE_OK ){
239     rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
240   }
241   sqlite3BtreeLeave(pBt);
242   if( rc ){
243     ckfree((char *)pCur);
244     Tcl_AppendResult(interp, errorName(rc), 0);
245     return TCL_ERROR;
246   }
247   sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
248   Tcl_AppendResult(interp, zBuf, 0);
249   return SQLITE_OK;
250 }
251 
252 /*
253 ** Usage:   btree_close_cursor ID
254 **
255 ** Close a cursor opened using btree_cursor.
256 */
257 static int btree_close_cursor(
258   void *NotUsed,
259   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
260   int argc,              /* Number of arguments */
261   const char **argv      /* Text of each argument */
262 ){
263   BtCursor *pCur;
264   Btree *pBt;
265   int rc;
266 
267   if( argc!=2 ){
268     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
269        " ID\"", 0);
270     return TCL_ERROR;
271   }
272   pCur = sqlite3TestTextToPtr(argv[1]);
273   pBt = pCur->pBtree;
274   sqlite3BtreeEnter(pBt);
275   rc = sqlite3BtreeCloseCursor(pCur);
276   sqlite3BtreeLeave(pBt);
277   ckfree((char *)pCur);
278   if( rc ){
279     Tcl_AppendResult(interp, errorName(rc), 0);
280     return TCL_ERROR;
281   }
282   return SQLITE_OK;
283 }
284 
285 /*
286 ** Usage:   btree_next ID
287 **
288 ** Move the cursor to the next entry in the table.  Return 0 on success
289 ** or 1 if the cursor was already on the last entry in the table or if
290 ** the table is empty.
291 */
292 static int btree_next(
293   void *NotUsed,
294   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
295   int argc,              /* Number of arguments */
296   const char **argv      /* Text of each argument */
297 ){
298   BtCursor *pCur;
299   int rc;
300   int res = 0;
301   char zBuf[100];
302 
303   if( argc!=2 ){
304     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
305        " ID\"", 0);
306     return TCL_ERROR;
307   }
308   pCur = sqlite3TestTextToPtr(argv[1]);
309   sqlite3BtreeEnter(pCur->pBtree);
310   rc = sqlite3BtreeNext(pCur, &res);
311   sqlite3BtreeLeave(pCur->pBtree);
312   if( rc ){
313     Tcl_AppendResult(interp, errorName(rc), 0);
314     return TCL_ERROR;
315   }
316   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
317   Tcl_AppendResult(interp, zBuf, 0);
318   return SQLITE_OK;
319 }
320 
321 /*
322 ** Usage:   btree_first ID
323 **
324 ** Move the cursor to the first entry in the table.  Return 0 if the
325 ** cursor was left point to something and 1 if the table is empty.
326 */
327 static int btree_first(
328   void *NotUsed,
329   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
330   int argc,              /* Number of arguments */
331   const char **argv      /* Text of each argument */
332 ){
333   BtCursor *pCur;
334   int rc;
335   int res = 0;
336   char zBuf[100];
337 
338   if( argc!=2 ){
339     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
340        " ID\"", 0);
341     return TCL_ERROR;
342   }
343   pCur = sqlite3TestTextToPtr(argv[1]);
344   sqlite3BtreeEnter(pCur->pBtree);
345   rc = sqlite3BtreeFirst(pCur, &res);
346   sqlite3BtreeLeave(pCur->pBtree);
347   if( rc ){
348     Tcl_AppendResult(interp, errorName(rc), 0);
349     return TCL_ERROR;
350   }
351   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
352   Tcl_AppendResult(interp, zBuf, 0);
353   return SQLITE_OK;
354 }
355 
356 /*
357 ** Usage:   btree_eof ID
358 **
359 ** Return TRUE if the given cursor is not pointing at a valid entry.
360 ** Return FALSE if the cursor does point to a valid entry.
361 */
362 static int btree_eof(
363   void *NotUsed,
364   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
365   int argc,              /* Number of arguments */
366   const char **argv      /* Text of each argument */
367 ){
368   BtCursor *pCur;
369   int rc;
370   char zBuf[50];
371 
372   if( argc!=2 ){
373     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
374        " ID\"", 0);
375     return TCL_ERROR;
376   }
377   pCur = sqlite3TestTextToPtr(argv[1]);
378   sqlite3BtreeEnter(pCur->pBtree);
379   rc = sqlite3BtreeEof(pCur);
380   sqlite3BtreeLeave(pCur->pBtree);
381   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
382   Tcl_AppendResult(interp, zBuf, 0);
383   return SQLITE_OK;
384 }
385 
386 /*
387 ** Usage:   btree_payload_size ID
388 **
389 ** Return the number of bytes of payload
390 */
391 static int btree_payload_size(
392   void *NotUsed,
393   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
394   int argc,              /* Number of arguments */
395   const char **argv      /* Text of each argument */
396 ){
397   BtCursor *pCur;
398   int n2;
399   u64 n1;
400   char zBuf[50];
401 
402   if( argc!=2 ){
403     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
404        " ID\"", 0);
405     return TCL_ERROR;
406   }
407   pCur = sqlite3TestTextToPtr(argv[1]);
408   sqlite3BtreeEnter(pCur->pBtree);
409 
410   /* The cursor may be in "require-seek" state. If this is the case, the
411   ** call to BtreeDataSize() will fix it. */
412   sqlite3BtreeDataSize(pCur, (u32*)&n2);
413   if( pCur->apPage[pCur->iPage]->intKey ){
414     n1 = 0;
415   }else{
416     sqlite3BtreeKeySize(pCur, (i64*)&n1);
417   }
418   sqlite3BtreeLeave(pCur->pBtree);
419   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
420   Tcl_AppendResult(interp, zBuf, 0);
421   return SQLITE_OK;
422 }
423 
424 /*
425 ** usage:   varint_test  START  MULTIPLIER  COUNT  INCREMENT
426 **
427 ** This command tests the putVarint() and getVarint()
428 ** routines, both for accuracy and for speed.
429 **
430 ** An integer is written using putVarint() and read back with
431 ** getVarint() and varified to be unchanged.  This repeats COUNT
432 ** times.  The first integer is START*MULTIPLIER.  Each iteration
433 ** increases the integer by INCREMENT.
434 **
435 ** This command returns nothing if it works.  It returns an error message
436 ** if something goes wrong.
437 */
438 static int btree_varint_test(
439   void *NotUsed,
440   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
441   int argc,              /* Number of arguments */
442   const char **argv      /* Text of each argument */
443 ){
444   u32 start, mult, count, incr;
445   u64 in, out;
446   int n1, n2, i, j;
447   unsigned char zBuf[100];
448   if( argc!=5 ){
449     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
450        " START MULTIPLIER COUNT INCREMENT\"", 0);
451     return TCL_ERROR;
452   }
453   if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
454   if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
455   if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
456   if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
457   in = start;
458   in *= mult;
459   for(i=0; i<count; i++){
460     char zErr[200];
461     n1 = putVarint(zBuf, in);
462     if( n1>9 || n1<1 ){
463       sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1);
464       Tcl_AppendResult(interp, zErr, 0);
465       return TCL_ERROR;
466     }
467     n2 = getVarint(zBuf, &out);
468     if( n1!=n2 ){
469       sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2);
470       Tcl_AppendResult(interp, zErr, 0);
471       return TCL_ERROR;
472     }
473     if( in!=out ){
474       sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out);
475       Tcl_AppendResult(interp, zErr, 0);
476       return TCL_ERROR;
477     }
478     if( (in & 0xffffffff)==in ){
479       u32 out32;
480       n2 = getVarint32(zBuf, out32);
481       out = out32;
482       if( n1!=n2 ){
483         sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d",
484                   n1, n2);
485         Tcl_AppendResult(interp, zErr, 0);
486         return TCL_ERROR;
487       }
488       if( in!=out ){
489         sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
490             in, out);
491         Tcl_AppendResult(interp, zErr, 0);
492         return TCL_ERROR;
493       }
494     }
495 
496     /* In order to get realistic timings, run getVarint 19 more times.
497     ** This is because getVarint is called about 20 times more often
498     ** than putVarint.
499     */
500     for(j=0; j<19; j++){
501       getVarint(zBuf, &out);
502     }
503     in += incr;
504   }
505   return TCL_OK;
506 }
507 
508 /*
509 ** usage:   btree_from_db  DB-HANDLE
510 **
511 ** This command returns the btree handle for the main database associated
512 ** with the database-handle passed as the argument. Example usage:
513 **
514 ** sqlite3 db test.db
515 ** set bt [btree_from_db db]
516 */
517 static int btree_from_db(
518   void *NotUsed,
519   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
520   int argc,              /* Number of arguments */
521   const char **argv      /* Text of each argument */
522 ){
523   char zBuf[100];
524   Tcl_CmdInfo info;
525   sqlite3 *db;
526   Btree *pBt;
527   int iDb = 0;
528 
529   if( argc!=2 && argc!=3 ){
530     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
531        " DB-HANDLE ?N?\"", 0);
532     return TCL_ERROR;
533   }
534 
535   if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
536     Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
537     return TCL_ERROR;
538   }
539   if( argc==3 ){
540     iDb = atoi(argv[2]);
541   }
542 
543   db = *((sqlite3 **)info.objClientData);
544   assert( db );
545 
546   pBt = db->aDb[iDb].pBt;
547   sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
548   Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
549   return TCL_OK;
550 }
551 
552 /*
553 ** Usage:   btree_ismemdb ID
554 **
555 ** Return true if the B-Tree is in-memory.
556 */
557 static int btree_ismemdb(
558   void *NotUsed,
559   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
560   int argc,              /* Number of arguments */
561   const char **argv      /* Text of each argument */
562 ){
563   Btree *pBt;
564   int res;
565 
566   if( argc!=2 ){
567     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
568        " ID\"", 0);
569     return TCL_ERROR;
570   }
571   pBt = sqlite3TestTextToPtr(argv[1]);
572   sqlite3_mutex_enter(pBt->db->mutex);
573   sqlite3BtreeEnter(pBt);
574   res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
575   sqlite3BtreeLeave(pBt);
576   sqlite3_mutex_leave(pBt->db->mutex);
577   Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
578   return SQLITE_OK;
579 }
580 
581 /*
582 ** usage:   btree_set_cache_size ID NCACHE
583 **
584 ** Set the size of the cache used by btree $ID.
585 */
586 static int btree_set_cache_size(
587   void *NotUsed,
588   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
589   int argc,              /* Number of arguments */
590   const char **argv      /* Text of each argument */
591 ){
592   int nCache;
593   Btree *pBt;
594 
595   if( argc!=3 ){
596     Tcl_AppendResult(
597         interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0);
598     return TCL_ERROR;
599   }
600   pBt = sqlite3TestTextToPtr(argv[1]);
601   if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
602 
603   sqlite3_mutex_enter(pBt->db->mutex);
604   sqlite3BtreeEnter(pBt);
605   sqlite3BtreeSetCacheSize(pBt, nCache);
606   sqlite3BtreeLeave(pBt);
607   sqlite3_mutex_leave(pBt->db->mutex);
608   return TCL_OK;
609 }
610 
611 
612 
613 /*
614 ** Register commands with the TCL interpreter.
615 */
616 int Sqlitetest3_Init(Tcl_Interp *interp){
617   static struct {
618      char *zName;
619      Tcl_CmdProc *xProc;
620   } aCmd[] = {
621      { "btree_open",               (Tcl_CmdProc*)btree_open               },
622      { "btree_close",              (Tcl_CmdProc*)btree_close              },
623      { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
624      { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
625      { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
626      { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
627      { "btree_next",               (Tcl_CmdProc*)btree_next               },
628      { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
629      { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
630      { "btree_first",              (Tcl_CmdProc*)btree_first              },
631      { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
632      { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
633      { "btree_ismemdb",            (Tcl_CmdProc*)btree_ismemdb            },
634      { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     }
635   };
636   int i;
637 
638   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
639     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
640   }
641 
642   return TCL_OK;
643 }
644