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