xref: /sqlite-3.40.0/src/test3.c (revision 50f79f56)
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   int n;
70   char *zFilename;
71   if( argc!=3 ){
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   nRefSqlite3++;
78   if( nRefSqlite3==1 ){
79     sDb.pVfs = sqlite3_vfs_find(0);
80     sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
81     sqlite3_mutex_enter(sDb.mutex);
82   }
83   n = (int)strlen(argv[1]);
84   zFilename = sqlite3_malloc( n+2 );
85   if( zFilename==0 ) return TCL_ERROR;
86   memcpy(zFilename, argv[1], n+1);
87   zFilename[n+1] = 0;
88   rc = sqlite3BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0,
89      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
90   sqlite3_free(zFilename);
91   if( rc!=SQLITE_OK ){
92     Tcl_AppendResult(interp, errorName(rc), 0);
93     return TCL_ERROR;
94   }
95   sqlite3BtreeSetCacheSize(pBt, nCache);
96   sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
97   Tcl_AppendResult(interp, zBuf, 0);
98   return TCL_OK;
99 }
100 
101 /*
102 ** Usage:   btree_close ID
103 **
104 ** Close the given database.
105 */
106 static int btree_close(
107   void *NotUsed,
108   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
109   int argc,              /* Number of arguments */
110   const char **argv      /* Text of each argument */
111 ){
112   Btree *pBt;
113   int rc;
114   if( argc!=2 ){
115     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
116        " ID\"", 0);
117     return TCL_ERROR;
118   }
119   pBt = sqlite3TestTextToPtr(argv[1]);
120   rc = sqlite3BtreeClose(pBt);
121   if( rc!=SQLITE_OK ){
122     Tcl_AppendResult(interp, errorName(rc), 0);
123     return TCL_ERROR;
124   }
125   nRefSqlite3--;
126   if( nRefSqlite3==0 ){
127     sqlite3_mutex_leave(sDb.mutex);
128     sqlite3_mutex_free(sDb.mutex);
129     sDb.mutex = 0;
130     sDb.pVfs = 0;
131   }
132   return TCL_OK;
133 }
134 
135 
136 /*
137 ** Usage:   btree_begin_transaction ID
138 **
139 ** Start a new transaction
140 */
141 static int btree_begin_transaction(
142   void *NotUsed,
143   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
144   int argc,              /* Number of arguments */
145   const char **argv      /* Text of each argument */
146 ){
147   Btree *pBt;
148   int rc;
149   if( argc!=2 ){
150     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
151        " ID\"", 0);
152     return TCL_ERROR;
153   }
154   pBt = sqlite3TestTextToPtr(argv[1]);
155   sqlite3BtreeEnter(pBt);
156   rc = sqlite3BtreeBeginTrans(pBt, 1);
157   sqlite3BtreeLeave(pBt);
158   if( rc!=SQLITE_OK ){
159     Tcl_AppendResult(interp, errorName(rc), 0);
160     return TCL_ERROR;
161   }
162   return TCL_OK;
163 }
164 
165 /*
166 ** Usage:   btree_pager_stats ID
167 **
168 ** Returns pager statistics
169 */
170 static int btree_pager_stats(
171   void *NotUsed,
172   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
173   int argc,              /* Number of arguments */
174   const char **argv      /* Text of each argument */
175 ){
176   Btree *pBt;
177   int i;
178   int *a;
179 
180   if( argc!=2 ){
181     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
182        " ID\"", 0);
183     return TCL_ERROR;
184   }
185   pBt = sqlite3TestTextToPtr(argv[1]);
186 
187   /* Normally in this file, with a b-tree handle opened using the
188   ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
189   ** But this function is sometimes called with a btree handle obtained
190   ** from an open SQLite connection (using [btree_from_db]). In this case
191   ** we need to obtain the mutex for the controlling SQLite handle before
192   ** it is safe to call sqlite3BtreeEnter().
193   */
194   sqlite3_mutex_enter(pBt->db->mutex);
195 
196   sqlite3BtreeEnter(pBt);
197   a = sqlite3PagerStats(sqlite3BtreePager(pBt));
198   for(i=0; i<11; i++){
199     static char *zName[] = {
200       "ref", "page", "max", "size", "state", "err",
201       "hit", "miss", "ovfl", "read", "write"
202     };
203     char zBuf[100];
204     Tcl_AppendElement(interp, zName[i]);
205     sqlite3_snprintf(sizeof(zBuf), zBuf,"%d",a[i]);
206     Tcl_AppendElement(interp, zBuf);
207   }
208   sqlite3BtreeLeave(pBt);
209 
210   /* Release the mutex on the SQLite handle that controls this b-tree */
211   sqlite3_mutex_leave(pBt->db->mutex);
212   return TCL_OK;
213 }
214 
215 /*
216 ** Usage:   btree_cursor ID TABLENUM WRITEABLE
217 **
218 ** Create a new cursor.  Return the ID for the cursor.
219 */
220 static int btree_cursor(
221   void *NotUsed,
222   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
223   int argc,              /* Number of arguments */
224   const char **argv      /* Text of each argument */
225 ){
226   Btree *pBt;
227   int iTable;
228   BtCursor *pCur;
229   int rc = SQLITE_OK;
230   int wrFlag;
231   char zBuf[30];
232 
233   if( argc!=4 ){
234     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
235        " ID TABLENUM WRITEABLE\"", 0);
236     return TCL_ERROR;
237   }
238   pBt = sqlite3TestTextToPtr(argv[1]);
239   if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR;
240   if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR;
241   pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
242   memset(pCur, 0, sqlite3BtreeCursorSize());
243   sqlite3BtreeEnter(pBt);
244 #ifndef SQLITE_OMIT_SHARED_CACHE
245   rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
246 #endif
247   if( rc==SQLITE_OK ){
248     rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
249   }
250   sqlite3BtreeLeave(pBt);
251   if( rc ){
252     ckfree((char *)pCur);
253     Tcl_AppendResult(interp, errorName(rc), 0);
254     return TCL_ERROR;
255   }
256   sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
257   Tcl_AppendResult(interp, zBuf, 0);
258   return SQLITE_OK;
259 }
260 
261 /*
262 ** Usage:   btree_close_cursor ID
263 **
264 ** Close a cursor opened using btree_cursor.
265 */
266 static int btree_close_cursor(
267   void *NotUsed,
268   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
269   int argc,              /* Number of arguments */
270   const char **argv      /* Text of each argument */
271 ){
272   BtCursor *pCur;
273   Btree *pBt;
274   int rc;
275 
276   if( argc!=2 ){
277     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
278        " ID\"", 0);
279     return TCL_ERROR;
280   }
281   pCur = sqlite3TestTextToPtr(argv[1]);
282   pBt = pCur->pBtree;
283   sqlite3BtreeEnter(pBt);
284   rc = sqlite3BtreeCloseCursor(pCur);
285   sqlite3BtreeLeave(pBt);
286   ckfree((char *)pCur);
287   if( rc ){
288     Tcl_AppendResult(interp, errorName(rc), 0);
289     return TCL_ERROR;
290   }
291   return SQLITE_OK;
292 }
293 
294 /*
295 ** Usage:   btree_next ID
296 **
297 ** Move the cursor to the next entry in the table.  Return 0 on success
298 ** or 1 if the cursor was already on the last entry in the table or if
299 ** the table is empty.
300 */
301 static int btree_next(
302   void *NotUsed,
303   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
304   int argc,              /* Number of arguments */
305   const char **argv      /* Text of each argument */
306 ){
307   BtCursor *pCur;
308   int rc;
309   int res = 0;
310   char zBuf[100];
311 
312   if( argc!=2 ){
313     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
314        " ID\"", 0);
315     return TCL_ERROR;
316   }
317   pCur = sqlite3TestTextToPtr(argv[1]);
318   sqlite3BtreeEnter(pCur->pBtree);
319   rc = sqlite3BtreeNext(pCur, &res);
320   sqlite3BtreeLeave(pCur->pBtree);
321   if( rc ){
322     Tcl_AppendResult(interp, errorName(rc), 0);
323     return TCL_ERROR;
324   }
325   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
326   Tcl_AppendResult(interp, zBuf, 0);
327   return SQLITE_OK;
328 }
329 
330 /*
331 ** Usage:   btree_first ID
332 **
333 ** Move the cursor to the first entry in the table.  Return 0 if the
334 ** cursor was left point to something and 1 if the table is empty.
335 */
336 static int btree_first(
337   void *NotUsed,
338   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
339   int argc,              /* Number of arguments */
340   const char **argv      /* Text of each argument */
341 ){
342   BtCursor *pCur;
343   int rc;
344   int res = 0;
345   char zBuf[100];
346 
347   if( argc!=2 ){
348     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
349        " ID\"", 0);
350     return TCL_ERROR;
351   }
352   pCur = sqlite3TestTextToPtr(argv[1]);
353   sqlite3BtreeEnter(pCur->pBtree);
354   rc = sqlite3BtreeFirst(pCur, &res);
355   sqlite3BtreeLeave(pCur->pBtree);
356   if( rc ){
357     Tcl_AppendResult(interp, errorName(rc), 0);
358     return TCL_ERROR;
359   }
360   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
361   Tcl_AppendResult(interp, zBuf, 0);
362   return SQLITE_OK;
363 }
364 
365 /*
366 ** Usage:   btree_eof ID
367 **
368 ** Return TRUE if the given cursor is not pointing at a valid entry.
369 ** Return FALSE if the cursor does point to a valid entry.
370 */
371 static int btree_eof(
372   void *NotUsed,
373   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
374   int argc,              /* Number of arguments */
375   const char **argv      /* Text of each argument */
376 ){
377   BtCursor *pCur;
378   int rc;
379   char zBuf[50];
380 
381   if( argc!=2 ){
382     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
383        " ID\"", 0);
384     return TCL_ERROR;
385   }
386   pCur = sqlite3TestTextToPtr(argv[1]);
387   sqlite3BtreeEnter(pCur->pBtree);
388   rc = sqlite3BtreeEof(pCur);
389   sqlite3BtreeLeave(pCur->pBtree);
390   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
391   Tcl_AppendResult(interp, zBuf, 0);
392   return SQLITE_OK;
393 }
394 
395 /*
396 ** Usage:   btree_payload_size ID
397 **
398 ** Return the number of bytes of payload
399 */
400 static int btree_payload_size(
401   void *NotUsed,
402   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
403   int argc,              /* Number of arguments */
404   const char **argv      /* Text of each argument */
405 ){
406   BtCursor *pCur;
407   int n2;
408   u64 n1;
409   char zBuf[50];
410 
411   if( argc!=2 ){
412     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
413        " ID\"", 0);
414     return TCL_ERROR;
415   }
416   pCur = sqlite3TestTextToPtr(argv[1]);
417   sqlite3BtreeEnter(pCur->pBtree);
418 
419   /* The cursor may be in "require-seek" state. If this is the case, the
420   ** call to BtreeDataSize() will fix it. */
421   sqlite3BtreeDataSize(pCur, (u32*)&n2);
422   if( pCur->apPage[pCur->iPage]->intKey ){
423     n1 = 0;
424   }else{
425     sqlite3BtreeKeySize(pCur, (i64*)&n1);
426   }
427   sqlite3BtreeLeave(pCur->pBtree);
428   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
429   Tcl_AppendResult(interp, zBuf, 0);
430   return SQLITE_OK;
431 }
432 
433 /*
434 ** usage:   varint_test  START  MULTIPLIER  COUNT  INCREMENT
435 **
436 ** This command tests the putVarint() and getVarint()
437 ** routines, both for accuracy and for speed.
438 **
439 ** An integer is written using putVarint() and read back with
440 ** getVarint() and varified to be unchanged.  This repeats COUNT
441 ** times.  The first integer is START*MULTIPLIER.  Each iteration
442 ** increases the integer by INCREMENT.
443 **
444 ** This command returns nothing if it works.  It returns an error message
445 ** if something goes wrong.
446 */
447 static int btree_varint_test(
448   void *NotUsed,
449   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
450   int argc,              /* Number of arguments */
451   const char **argv      /* Text of each argument */
452 ){
453   u32 start, mult, count, incr;
454   u64 in, out;
455   int n1, n2, i, j;
456   unsigned char zBuf[100];
457   if( argc!=5 ){
458     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
459        " START MULTIPLIER COUNT INCREMENT\"", 0);
460     return TCL_ERROR;
461   }
462   if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
463   if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
464   if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
465   if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
466   in = start;
467   in *= mult;
468   for(i=0; i<(int)count; i++){
469     char zErr[200];
470     n1 = putVarint(zBuf, in);
471     if( n1>9 || n1<1 ){
472       sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1);
473       Tcl_AppendResult(interp, zErr, 0);
474       return TCL_ERROR;
475     }
476     n2 = getVarint(zBuf, &out);
477     if( n1!=n2 ){
478       sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2);
479       Tcl_AppendResult(interp, zErr, 0);
480       return TCL_ERROR;
481     }
482     if( in!=out ){
483       sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out);
484       Tcl_AppendResult(interp, zErr, 0);
485       return TCL_ERROR;
486     }
487     if( (in & 0xffffffff)==in ){
488       u32 out32;
489       n2 = getVarint32(zBuf, out32);
490       out = out32;
491       if( n1!=n2 ){
492         sprintf(zErr, "putVarint returned %d and GetVarint32 returned %d",
493                   n1, n2);
494         Tcl_AppendResult(interp, zErr, 0);
495         return TCL_ERROR;
496       }
497       if( in!=out ){
498         sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
499             in, out);
500         Tcl_AppendResult(interp, zErr, 0);
501         return TCL_ERROR;
502       }
503     }
504 
505     /* In order to get realistic timings, run getVarint 19 more times.
506     ** This is because getVarint is called about 20 times more often
507     ** than putVarint.
508     */
509     for(j=0; j<19; j++){
510       getVarint(zBuf, &out);
511     }
512     in += incr;
513   }
514   return TCL_OK;
515 }
516 
517 /*
518 ** usage:   btree_from_db  DB-HANDLE
519 **
520 ** This command returns the btree handle for the main database associated
521 ** with the database-handle passed as the argument. Example usage:
522 **
523 ** sqlite3 db test.db
524 ** set bt [btree_from_db db]
525 */
526 static int btree_from_db(
527   void *NotUsed,
528   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
529   int argc,              /* Number of arguments */
530   const char **argv      /* Text of each argument */
531 ){
532   char zBuf[100];
533   Tcl_CmdInfo info;
534   sqlite3 *db;
535   Btree *pBt;
536   int iDb = 0;
537 
538   if( argc!=2 && argc!=3 ){
539     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
540        " DB-HANDLE ?N?\"", 0);
541     return TCL_ERROR;
542   }
543 
544   if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
545     Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
546     return TCL_ERROR;
547   }
548   if( argc==3 ){
549     iDb = atoi(argv[2]);
550   }
551 
552   db = *((sqlite3 **)info.objClientData);
553   assert( db );
554 
555   pBt = db->aDb[iDb].pBt;
556   sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
557   Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
558   return TCL_OK;
559 }
560 
561 /*
562 ** Usage:   btree_ismemdb ID
563 **
564 ** Return true if the B-Tree is in-memory.
565 */
566 static int btree_ismemdb(
567   void *NotUsed,
568   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
569   int argc,              /* Number of arguments */
570   const char **argv      /* Text of each argument */
571 ){
572   Btree *pBt;
573   int res;
574 
575   if( argc!=2 ){
576     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
577        " ID\"", 0);
578     return TCL_ERROR;
579   }
580   pBt = sqlite3TestTextToPtr(argv[1]);
581   sqlite3_mutex_enter(pBt->db->mutex);
582   sqlite3BtreeEnter(pBt);
583   res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
584   sqlite3BtreeLeave(pBt);
585   sqlite3_mutex_leave(pBt->db->mutex);
586   Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
587   return SQLITE_OK;
588 }
589 
590 /*
591 ** usage:   btree_set_cache_size ID NCACHE
592 **
593 ** Set the size of the cache used by btree $ID.
594 */
595 static int btree_set_cache_size(
596   void *NotUsed,
597   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
598   int argc,              /* Number of arguments */
599   const char **argv      /* Text of each argument */
600 ){
601   int nCache;
602   Btree *pBt;
603 
604   if( argc!=3 ){
605     Tcl_AppendResult(
606         interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0);
607     return TCL_ERROR;
608   }
609   pBt = sqlite3TestTextToPtr(argv[1]);
610   if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
611 
612   sqlite3_mutex_enter(pBt->db->mutex);
613   sqlite3BtreeEnter(pBt);
614   sqlite3BtreeSetCacheSize(pBt, nCache);
615   sqlite3BtreeLeave(pBt);
616   sqlite3_mutex_leave(pBt->db->mutex);
617   return TCL_OK;
618 }
619 
620 
621 
622 /*
623 ** Register commands with the TCL interpreter.
624 */
625 int Sqlitetest3_Init(Tcl_Interp *interp){
626   static struct {
627      char *zName;
628      Tcl_CmdProc *xProc;
629   } aCmd[] = {
630      { "btree_open",               (Tcl_CmdProc*)btree_open               },
631      { "btree_close",              (Tcl_CmdProc*)btree_close              },
632      { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
633      { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
634      { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
635      { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
636      { "btree_next",               (Tcl_CmdProc*)btree_next               },
637      { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
638      { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
639      { "btree_first",              (Tcl_CmdProc*)btree_first              },
640      { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
641      { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
642      { "btree_ismemdb",            (Tcl_CmdProc*)btree_ismemdb            },
643      { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     }
644   };
645   int i;
646 
647   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
648     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
649   }
650 
651   return TCL_OK;
652 }
653