xref: /sqlite-3.40.0/src/test3.c (revision 5130c31b)
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