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