xref: /sqlite-3.40.0/src/test3.c (revision 5d00d0a8)
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 ** $Id: test3.c,v 1.111 2009/07/09 05:07:38 danielk1977 Exp $
17 */
18 #include "sqliteInt.h"
19 #include "btreeInt.h"
20 #include "tcl.h"
21 #include <stdlib.h>
22 #include <string.h>
23 
24 /*
25 ** Interpret an SQLite error number
26 */
27 static char *errorName(int rc){
28   char *zName;
29   switch( rc ){
30     case SQLITE_OK:         zName = "SQLITE_OK";          break;
31     case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break;
32     case SQLITE_PERM:       zName = "SQLITE_PERM";        break;
33     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
34     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
35     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
36     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
37     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
38     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
39     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
40     case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
41     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
42     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
43     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
44     case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break;
45     default:                zName = "SQLITE_Unknown";     break;
46   }
47   return zName;
48 }
49 
50 /*
51 ** A bogus sqlite3 connection structure for use in the btree
52 ** tests.
53 */
54 static sqlite3 sDb;
55 static int nRefSqlite3 = 0;
56 
57 /*
58 ** Usage:   btree_open FILENAME NCACHE FLAGS
59 **
60 ** Open a new database
61 */
62 static int btree_open(
63   void *NotUsed,
64   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
65   int argc,              /* Number of arguments */
66   const char **argv      /* Text of each argument */
67 ){
68   Btree *pBt;
69   int rc, nCache, flags;
70   char zBuf[100];
71   if( argc!=4 ){
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   if( Tcl_GetInt(interp, argv[3], &flags) ) return TCL_ERROR;
78   nRefSqlite3++;
79   if( nRefSqlite3==1 ){
80     sDb.pVfs = sqlite3_vfs_find(0);
81     sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
82     sqlite3_mutex_enter(sDb.mutex);
83   }
84   rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, flags,
85      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
86   if( rc!=SQLITE_OK ){
87     Tcl_AppendResult(interp, errorName(rc), 0);
88     return TCL_ERROR;
89   }
90   sqlite3BtreeSetCacheSize(pBt, nCache);
91   sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
92   Tcl_AppendResult(interp, zBuf, 0);
93   return TCL_OK;
94 }
95 
96 /*
97 ** Usage:   btree_close ID
98 **
99 ** Close the given database.
100 */
101 static int btree_close(
102   void *NotUsed,
103   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
104   int argc,              /* Number of arguments */
105   const char **argv      /* Text of each argument */
106 ){
107   Btree *pBt;
108   int rc;
109   if( argc!=2 ){
110     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
111        " ID\"", 0);
112     return TCL_ERROR;
113   }
114   pBt = sqlite3TestTextToPtr(argv[1]);
115   rc = sqlite3BtreeClose(pBt);
116   if( rc!=SQLITE_OK ){
117     Tcl_AppendResult(interp, errorName(rc), 0);
118     return TCL_ERROR;
119   }
120   nRefSqlite3--;
121   if( nRefSqlite3==0 ){
122     sqlite3_mutex_leave(sDb.mutex);
123     sqlite3_mutex_free(sDb.mutex);
124     sDb.mutex = 0;
125     sDb.pVfs = 0;
126   }
127   return TCL_OK;
128 }
129 
130 
131 /*
132 ** Usage:   btree_begin_transaction ID
133 **
134 ** Start a new transaction
135 */
136 static int btree_begin_transaction(
137   void *NotUsed,
138   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
139   int argc,              /* Number of arguments */
140   const char **argv      /* Text of each argument */
141 ){
142   Btree *pBt;
143   int rc;
144   if( argc!=2 ){
145     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
146        " ID\"", 0);
147     return TCL_ERROR;
148   }
149   pBt = sqlite3TestTextToPtr(argv[1]);
150   sqlite3BtreeEnter(pBt);
151   rc = sqlite3BtreeBeginTrans(pBt, 1);
152   sqlite3BtreeLeave(pBt);
153   if( rc!=SQLITE_OK ){
154     Tcl_AppendResult(interp, errorName(rc), 0);
155     return TCL_ERROR;
156   }
157   return TCL_OK;
158 }
159 
160 /*
161 ** Usage:   btree_pager_stats ID
162 **
163 ** Returns pager statistics
164 */
165 static int btree_pager_stats(
166   void *NotUsed,
167   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
168   int argc,              /* Number of arguments */
169   const char **argv      /* Text of each argument */
170 ){
171   Btree *pBt;
172   int i;
173   int *a;
174 
175   if( argc!=2 ){
176     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
177        " ID\"", 0);
178     return TCL_ERROR;
179   }
180   pBt = sqlite3TestTextToPtr(argv[1]);
181 
182   /* Normally in this file, with a b-tree handle opened using the
183   ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
184   ** But this function is sometimes called with a btree handle obtained
185   ** from an open SQLite connection (using [btree_from_db]). In this case
186   ** we need to obtain the mutex for the controlling SQLite handle before
187   ** it is safe to call sqlite3BtreeEnter().
188   */
189   sqlite3_mutex_enter(pBt->db->mutex);
190 
191   sqlite3BtreeEnter(pBt);
192   a = sqlite3PagerStats(sqlite3BtreePager(pBt));
193   for(i=0; i<11; i++){
194     static char *zName[] = {
195       "ref", "page", "max", "size", "state", "err",
196       "hit", "miss", "ovfl", "read", "write"
197     };
198     char zBuf[100];
199     Tcl_AppendElement(interp, zName[i]);
200     sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);
201     Tcl_AppendElement(interp, zBuf);
202   }
203   sqlite3BtreeLeave(pBt);
204 
205   /* Release the mutex on the SQLite handle that controls this b-tree */
206   sqlite3_mutex_leave(pBt->db->mutex);
207   return TCL_OK;
208 }
209 
210 /*
211 ** Usage:   btree_cursor ID TABLENUM WRITEABLE
212 **
213 ** Create a new cursor.  Return the ID for the cursor.
214 */
215 static int btree_cursor(
216   void *NotUsed,
217   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
218   int argc,              /* Number of arguments */
219   const char **argv      /* Text of each argument */
220 ){
221   Btree *pBt;
222   int iTable;
223   BtCursor *pCur;
224   int rc;
225   int wrFlag;
226   char zBuf[30];
227 
228   if( argc!=4 ){
229     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
230        " ID TABLENUM WRITEABLE\"", 0);
231     return TCL_ERROR;
232   }
233   pBt = sqlite3TestTextToPtr(argv[1]);
234   if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
235   if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
236   pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
237   memset(pCur, 0, sqlite3BtreeCursorSize());
238   sqlite3BtreeEnter(pBt);
239   rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
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