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