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*/
25static 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*/
52static sqlite3 sDb;
53static int nRefSqlite3 = 0;
54
55/*
56** Usage:   btree_open FILENAME NCACHE
57**
58** Open a new database
59*/
60static 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*/
98static 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*/
133static 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*/
162static 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*/
212static 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*/
258static 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*/
293static 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*/
328static 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*/
363static 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*/
392static 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*/
439static 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*/
518static 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*/
558static 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*/
587static 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*/
617int 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