xref: /sqlite-3.40.0/src/test3.c (revision 08d41890)
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 *sqlite3TestErrorName(int rc);
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, sqlite3TestErrorName(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, sqlite3TestErrorName(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, sqlite3TestErrorName(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   pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
218   memset(pCur, 0, sqlite3BtreeCursorSize());
219   sqlite3BtreeEnter(pBt);
220 #ifndef SQLITE_OMIT_SHARED_CACHE
221   rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
222 #endif
223   if( rc==SQLITE_OK ){
224     rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
225   }
226   sqlite3BtreeLeave(pBt);
227   if( rc ){
228     ckfree((char *)pCur);
229     Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
230     return TCL_ERROR;
231   }
232   sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur);
233   Tcl_AppendResult(interp, zBuf, 0);
234   return SQLITE_OK;
235 }
236 
237 /*
238 ** Usage:   btree_close_cursor ID
239 **
240 ** Close a cursor opened using btree_cursor.
241 */
242 static int btree_close_cursor(
243   void *NotUsed,
244   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
245   int argc,              /* Number of arguments */
246   const char **argv      /* Text of each argument */
247 ){
248   BtCursor *pCur;
249   Btree *pBt;
250   int rc;
251 
252   if( argc!=2 ){
253     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
254        " ID\"", 0);
255     return TCL_ERROR;
256   }
257   pCur = sqlite3TestTextToPtr(argv[1]);
258   pBt = pCur->pBtree;
259   sqlite3BtreeEnter(pBt);
260   rc = sqlite3BtreeCloseCursor(pCur);
261   sqlite3BtreeLeave(pBt);
262   ckfree((char *)pCur);
263   if( rc ){
264     Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
265     return TCL_ERROR;
266   }
267   return SQLITE_OK;
268 }
269 
270 /*
271 ** Usage:   btree_next ID
272 **
273 ** Move the cursor to the next entry in the table.  Return 0 on success
274 ** or 1 if the cursor was already on the last entry in the table or if
275 ** the table is empty.
276 */
277 static int btree_next(
278   void *NotUsed,
279   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
280   int argc,              /* Number of arguments */
281   const char **argv      /* Text of each argument */
282 ){
283   BtCursor *pCur;
284   int rc;
285   int res = 0;
286   char zBuf[100];
287 
288   if( argc!=2 ){
289     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
290        " ID\"", 0);
291     return TCL_ERROR;
292   }
293   pCur = sqlite3TestTextToPtr(argv[1]);
294   sqlite3BtreeEnter(pCur->pBtree);
295   rc = sqlite3BtreeNext(pCur, &res);
296   sqlite3BtreeLeave(pCur->pBtree);
297   if( rc ){
298     Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
299     return TCL_ERROR;
300   }
301   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
302   Tcl_AppendResult(interp, zBuf, 0);
303   return SQLITE_OK;
304 }
305 
306 /*
307 ** Usage:   btree_first ID
308 **
309 ** Move the cursor to the first entry in the table.  Return 0 if the
310 ** cursor was left point to something and 1 if the table is empty.
311 */
312 static int btree_first(
313   void *NotUsed,
314   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
315   int argc,              /* Number of arguments */
316   const char **argv      /* Text of each argument */
317 ){
318   BtCursor *pCur;
319   int rc;
320   int res = 0;
321   char zBuf[100];
322 
323   if( argc!=2 ){
324     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
325        " ID\"", 0);
326     return TCL_ERROR;
327   }
328   pCur = sqlite3TestTextToPtr(argv[1]);
329   sqlite3BtreeEnter(pCur->pBtree);
330   rc = sqlite3BtreeFirst(pCur, &res);
331   sqlite3BtreeLeave(pCur->pBtree);
332   if( rc ){
333     Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0);
334     return TCL_ERROR;
335   }
336   sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res);
337   Tcl_AppendResult(interp, zBuf, 0);
338   return SQLITE_OK;
339 }
340 
341 /*
342 ** Usage:   btree_eof ID
343 **
344 ** Return TRUE if the given cursor is not pointing at a valid entry.
345 ** Return FALSE if the cursor does point to a valid entry.
346 */
347 static int btree_eof(
348   void *NotUsed,
349   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
350   int argc,              /* Number of arguments */
351   const char **argv      /* Text of each argument */
352 ){
353   BtCursor *pCur;
354   int rc;
355   char zBuf[50];
356 
357   if( argc!=2 ){
358     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
359        " ID\"", 0);
360     return TCL_ERROR;
361   }
362   pCur = sqlite3TestTextToPtr(argv[1]);
363   sqlite3BtreeEnter(pCur->pBtree);
364   rc = sqlite3BtreeEof(pCur);
365   sqlite3BtreeLeave(pCur->pBtree);
366   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", rc);
367   Tcl_AppendResult(interp, zBuf, 0);
368   return SQLITE_OK;
369 }
370 
371 /*
372 ** Usage:   btree_payload_size ID
373 **
374 ** Return the number of bytes of payload
375 */
376 static int btree_payload_size(
377   void *NotUsed,
378   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
379   int argc,              /* Number of arguments */
380   const char **argv      /* Text of each argument */
381 ){
382   BtCursor *pCur;
383   int n2;
384   u64 n1;
385   char zBuf[50];
386 
387   if( argc!=2 ){
388     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
389        " ID\"", 0);
390     return TCL_ERROR;
391   }
392   pCur = sqlite3TestTextToPtr(argv[1]);
393   sqlite3BtreeEnter(pCur->pBtree);
394 
395   /* The cursor may be in "require-seek" state. If this is the case, the
396   ** call to BtreeDataSize() will fix it. */
397   sqlite3BtreeDataSize(pCur, (u32*)&n2);
398   if( pCur->apPage[pCur->iPage]->intKey ){
399     n1 = 0;
400   }else{
401     sqlite3BtreeKeySize(pCur, (i64*)&n1);
402   }
403   sqlite3BtreeLeave(pCur->pBtree);
404   sqlite3_snprintf(sizeof(zBuf),zBuf, "%d", (int)(n1+n2));
405   Tcl_AppendResult(interp, zBuf, 0);
406   return SQLITE_OK;
407 }
408 
409 /*
410 ** usage:   varint_test  START  MULTIPLIER  COUNT  INCREMENT
411 **
412 ** This command tests the putVarint() and getVarint()
413 ** routines, both for accuracy and for speed.
414 **
415 ** An integer is written using putVarint() and read back with
416 ** getVarint() and varified to be unchanged.  This repeats COUNT
417 ** times.  The first integer is START*MULTIPLIER.  Each iteration
418 ** increases the integer by INCREMENT.
419 **
420 ** This command returns nothing if it works.  It returns an error message
421 ** if something goes wrong.
422 */
423 static int btree_varint_test(
424   void *NotUsed,
425   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
426   int argc,              /* Number of arguments */
427   const char **argv      /* Text of each argument */
428 ){
429   u32 start, mult, count, incr;
430   u64 in, out;
431   int n1, n2, i, j;
432   unsigned char zBuf[100];
433   if( argc!=5 ){
434     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
435        " START MULTIPLIER COUNT INCREMENT\"", 0);
436     return TCL_ERROR;
437   }
438   if( Tcl_GetInt(interp, argv[1], (int*)&start) ) return TCL_ERROR;
439   if( Tcl_GetInt(interp, argv[2], (int*)&mult) ) return TCL_ERROR;
440   if( Tcl_GetInt(interp, argv[3], (int*)&count) ) return TCL_ERROR;
441   if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
442   in = start;
443   in *= mult;
444   for(i=0; i<(int)count; i++){
445     char zErr[200];
446     n1 = putVarint(zBuf, in);
447     if( n1>9 || n1<1 ){
448       sprintf(zErr, "putVarint returned %d - should be between 1 and 9", n1);
449       Tcl_AppendResult(interp, zErr, 0);
450       return TCL_ERROR;
451     }
452     n2 = getVarint(zBuf, &out);
453     if( n1!=n2 ){
454       sprintf(zErr, "putVarint returned %d and getVarint returned %d", n1, n2);
455       Tcl_AppendResult(interp, zErr, 0);
456       return TCL_ERROR;
457     }
458     if( in!=out ){
459       sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx", in, out);
460       Tcl_AppendResult(interp, zErr, 0);
461       return TCL_ERROR;
462     }
463     if( (in & 0xffffffff)==in ){
464       u32 out32;
465       n2 = getVarint32(zBuf, out32);
466       out = out32;
467       if( n1!=n2 ){
468         sprintf(zErr, "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         sprintf(zErr, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
475             in, out);
476         Tcl_AppendResult(interp, zErr, 0);
477         return TCL_ERROR;
478       }
479     }
480 
481     /* In order to get realistic timings, run getVarint 19 more times.
482     ** This is because getVarint is called about 20 times more often
483     ** than putVarint.
484     */
485     for(j=0; j<19; j++){
486       getVarint(zBuf, &out);
487     }
488     in += incr;
489   }
490   return TCL_OK;
491 }
492 
493 /*
494 ** usage:   btree_from_db  DB-HANDLE
495 **
496 ** This command returns the btree handle for the main database associated
497 ** with the database-handle passed as the argument. Example usage:
498 **
499 ** sqlite3 db test.db
500 ** set bt [btree_from_db db]
501 */
502 static int btree_from_db(
503   void *NotUsed,
504   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
505   int argc,              /* Number of arguments */
506   const char **argv      /* Text of each argument */
507 ){
508   char zBuf[100];
509   Tcl_CmdInfo info;
510   sqlite3 *db;
511   Btree *pBt;
512   int iDb = 0;
513 
514   if( argc!=2 && argc!=3 ){
515     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
516        " DB-HANDLE ?N?\"", 0);
517     return TCL_ERROR;
518   }
519 
520   if( 1!=Tcl_GetCommandInfo(interp, argv[1], &info) ){
521     Tcl_AppendResult(interp, "No such db-handle: \"", argv[1], "\"", 0);
522     return TCL_ERROR;
523   }
524   if( argc==3 ){
525     iDb = atoi(argv[2]);
526   }
527 
528   db = *((sqlite3 **)info.objClientData);
529   assert( db );
530 
531   pBt = db->aDb[iDb].pBt;
532   sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", pBt);
533   Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
534   return TCL_OK;
535 }
536 
537 /*
538 ** Usage:   btree_ismemdb ID
539 **
540 ** Return true if the B-Tree is in-memory.
541 */
542 static int btree_ismemdb(
543   void *NotUsed,
544   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
545   int argc,              /* Number of arguments */
546   const char **argv      /* Text of each argument */
547 ){
548   Btree *pBt;
549   int res;
550 
551   if( argc!=2 ){
552     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
553        " ID\"", 0);
554     return TCL_ERROR;
555   }
556   pBt = sqlite3TestTextToPtr(argv[1]);
557   sqlite3_mutex_enter(pBt->db->mutex);
558   sqlite3BtreeEnter(pBt);
559   res = sqlite3PagerIsMemdb(sqlite3BtreePager(pBt));
560   sqlite3BtreeLeave(pBt);
561   sqlite3_mutex_leave(pBt->db->mutex);
562   Tcl_SetObjResult(interp, Tcl_NewBooleanObj(res));
563   return SQLITE_OK;
564 }
565 
566 /*
567 ** usage:   btree_set_cache_size ID NCACHE
568 **
569 ** Set the size of the cache used by btree $ID.
570 */
571 static int btree_set_cache_size(
572   void *NotUsed,
573   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
574   int argc,              /* Number of arguments */
575   const char **argv      /* Text of each argument */
576 ){
577   int nCache;
578   Btree *pBt;
579 
580   if( argc!=3 ){
581     Tcl_AppendResult(
582         interp, "wrong # args: should be \"", argv[0], " BT NCACHE\"", 0);
583     return TCL_ERROR;
584   }
585   pBt = sqlite3TestTextToPtr(argv[1]);
586   if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
587 
588   sqlite3_mutex_enter(pBt->db->mutex);
589   sqlite3BtreeEnter(pBt);
590   sqlite3BtreeSetCacheSize(pBt, nCache);
591   sqlite3BtreeLeave(pBt);
592   sqlite3_mutex_leave(pBt->db->mutex);
593   return TCL_OK;
594 }
595 
596 
597 
598 /*
599 ** Register commands with the TCL interpreter.
600 */
601 int Sqlitetest3_Init(Tcl_Interp *interp){
602   static struct {
603      char *zName;
604      Tcl_CmdProc *xProc;
605   } aCmd[] = {
606      { "btree_open",               (Tcl_CmdProc*)btree_open               },
607      { "btree_close",              (Tcl_CmdProc*)btree_close              },
608      { "btree_begin_transaction",  (Tcl_CmdProc*)btree_begin_transaction  },
609      { "btree_pager_stats",        (Tcl_CmdProc*)btree_pager_stats        },
610      { "btree_cursor",             (Tcl_CmdProc*)btree_cursor             },
611      { "btree_close_cursor",       (Tcl_CmdProc*)btree_close_cursor       },
612      { "btree_next",               (Tcl_CmdProc*)btree_next               },
613      { "btree_eof",                (Tcl_CmdProc*)btree_eof                },
614      { "btree_payload_size",       (Tcl_CmdProc*)btree_payload_size       },
615      { "btree_first",              (Tcl_CmdProc*)btree_first              },
616      { "btree_varint_test",        (Tcl_CmdProc*)btree_varint_test        },
617      { "btree_from_db",            (Tcl_CmdProc*)btree_from_db            },
618      { "btree_ismemdb",            (Tcl_CmdProc*)btree_ismemdb            },
619      { "btree_set_cache_size",     (Tcl_CmdProc*)btree_set_cache_size     }
620   };
621   int i;
622 
623   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
624     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
625   }
626 
627   return TCL_OK;
628 }
629