4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
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.
16 #include "sqliteInt.h"
23 ** Interpret an SQLite error number
25 static char *errorName(int 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;
49 ** A bogus sqlite3 connection structure for use in the btree
53 static int nRefSqlite3
= 0;
56 ** Usage: btree_open FILENAME NCACHE
58 ** Open a new database
60 static int btree_open(
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 */
72 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
73 " FILENAME NCACHE FLAGS\"", 0);
76 if( Tcl_GetInt(interp
, argv
[2], &nCache
) ) return TCL_ERROR
;
79 sDb
.pVfs
= sqlite3_vfs_find(0);
80 sDb
.mutex
= sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE
);
81 sqlite3_mutex_enter(sDb
.mutex
);
83 n
= (int)strlen(argv
[1]);
84 zFilename
= sqlite3_malloc( n
+2 );
85 if( zFilename
==0 ) return TCL_ERROR
;
86 memcpy(zFilename
, argv
[1], n
+1);
88 rc
= sqlite3BtreeOpen(sDb
.pVfs
, zFilename
, &sDb
, &pBt
, 0,
89 SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
| SQLITE_OPEN_MAIN_DB
);
90 sqlite3_free(zFilename
);
92 Tcl_AppendResult(interp
, errorName(rc
), 0);
95 sqlite3BtreeSetCacheSize(pBt
, nCache
);
96 sqlite3_snprintf(sizeof(zBuf
), zBuf
,"%p", pBt
);
97 Tcl_AppendResult(interp
, zBuf
, 0);
102 ** Usage: btree_close ID
104 ** Close the given database.
106 static int btree_close(
108 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
109 int argc
, /* Number of arguments */
110 const char **argv
/* Text of each argument */
115 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
119 pBt
= sqlite3TestTextToPtr(argv
[1]);
120 rc
= sqlite3BtreeClose(pBt
);
122 Tcl_AppendResult(interp
, errorName(rc
), 0);
126 if( nRefSqlite3
==0 ){
127 sqlite3_mutex_leave(sDb
.mutex
);
128 sqlite3_mutex_free(sDb
.mutex
);
137 ** Usage: btree_begin_transaction ID
139 ** Start a new transaction
141 static int btree_begin_transaction(
143 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
144 int argc
, /* Number of arguments */
145 const char **argv
/* Text of each argument */
150 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
154 pBt
= sqlite3TestTextToPtr(argv
[1]);
155 sqlite3BtreeEnter(pBt
);
156 rc
= sqlite3BtreeBeginTrans(pBt
, 1);
157 sqlite3BtreeLeave(pBt
);
159 Tcl_AppendResult(interp
, errorName(rc
), 0);
166 ** Usage: btree_pager_stats ID
168 ** Returns pager statistics
170 static int btree_pager_stats(
172 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
173 int argc
, /* Number of arguments */
174 const char **argv
/* Text of each argument */
181 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
185 pBt
= sqlite3TestTextToPtr(argv
[1]);
187 /* Normally in this file, with a b-tree handle opened using the
188 ** [btree_open] command it is safe to call sqlite3BtreeEnter() directly.
189 ** But this function is sometimes called with a btree handle obtained
190 ** from an open SQLite connection (using [btree_from_db]). In this case
191 ** we need to obtain the mutex for the controlling SQLite handle before
192 ** it is safe to call sqlite3BtreeEnter().
194 sqlite3_mutex_enter(pBt
->db
->mutex
);
196 sqlite3BtreeEnter(pBt
);
197 a
= sqlite3PagerStats(sqlite3BtreePager(pBt
));
199 static char *zName
[] = {
200 "ref", "page", "max", "size", "state", "err",
201 "hit", "miss", "ovfl", "read", "write"
204 Tcl_AppendElement(interp
, zName
[i
]);
205 sqlite3_snprintf(sizeof(zBuf
), zBuf
,"%d",a
[i
]);
206 Tcl_AppendElement(interp
, zBuf
);
208 sqlite3BtreeLeave(pBt
);
210 /* Release the mutex on the SQLite handle that controls this b-tree */
211 sqlite3_mutex_leave(pBt
->db
->mutex
);
216 ** Usage: btree_cursor ID TABLENUM WRITEABLE
218 ** Create a new cursor. Return the ID for the cursor.
220 static int btree_cursor(
222 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
223 int argc
, /* Number of arguments */
224 const char **argv
/* Text of each argument */
234 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
235 " ID TABLENUM WRITEABLE\"", 0);
238 pBt
= sqlite3TestTextToPtr(argv
[1]);
239 if( Tcl_GetInt(interp
, argv
[2], &iTable
) ) return TCL_ERROR
;
240 if( Tcl_GetBoolean(interp
, argv
[3], &wrFlag
) ) return TCL_ERROR
;
241 pCur
= (BtCursor
*)ckalloc(sqlite3BtreeCursorSize());
242 memset(pCur
, 0, sqlite3BtreeCursorSize());
243 sqlite3BtreeEnter(pBt
);
244 #ifndef SQLITE_OMIT_SHARED_CACHE
245 rc
= sqlite3BtreeLockTable(pBt
, iTable
, wrFlag
);
248 rc
= sqlite3BtreeCursor(pBt
, iTable
, wrFlag
, 0, pCur
);
250 sqlite3BtreeLeave(pBt
);
252 ckfree((char *)pCur
);
253 Tcl_AppendResult(interp
, errorName(rc
), 0);
256 sqlite3_snprintf(sizeof(zBuf
), zBuf
,"%p", pCur
);
257 Tcl_AppendResult(interp
, zBuf
, 0);
262 ** Usage: btree_close_cursor ID
264 ** Close a cursor opened using btree_cursor.
266 static int btree_close_cursor(
268 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
269 int argc
, /* Number of arguments */
270 const char **argv
/* Text of each argument */
277 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
281 pCur
= sqlite3TestTextToPtr(argv
[1]);
283 sqlite3BtreeEnter(pBt
);
284 rc
= sqlite3BtreeCloseCursor(pCur
);
285 sqlite3BtreeLeave(pBt
);
286 ckfree((char *)pCur
);
288 Tcl_AppendResult(interp
, errorName(rc
), 0);
295 ** Usage: btree_next ID
297 ** Move the cursor to the next entry in the table. Return 0 on success
298 ** or 1 if the cursor was already on the last entry in the table or if
299 ** the table is empty.
301 static int btree_next(
303 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
304 int argc
, /* Number of arguments */
305 const char **argv
/* Text of each argument */
313 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
317 pCur
= sqlite3TestTextToPtr(argv
[1]);
318 sqlite3BtreeEnter(pCur
->pBtree
);
319 rc
= sqlite3BtreeNext(pCur
, &res
);
320 sqlite3BtreeLeave(pCur
->pBtree
);
322 Tcl_AppendResult(interp
, errorName(rc
), 0);
325 sqlite3_snprintf(sizeof(zBuf
),zBuf
,"%d",res
);
326 Tcl_AppendResult(interp
, zBuf
, 0);
331 ** Usage: btree_first ID
333 ** Move the cursor to the first entry in the table. Return 0 if the
334 ** cursor was left point to something and 1 if the table is empty.
336 static int btree_first(
338 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
339 int argc
, /* Number of arguments */
340 const char **argv
/* Text of each argument */
348 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
352 pCur
= sqlite3TestTextToPtr(argv
[1]);
353 sqlite3BtreeEnter(pCur
->pBtree
);
354 rc
= sqlite3BtreeFirst(pCur
, &res
);
355 sqlite3BtreeLeave(pCur
->pBtree
);
357 Tcl_AppendResult(interp
, errorName(rc
), 0);
360 sqlite3_snprintf(sizeof(zBuf
),zBuf
,"%d",res
);
361 Tcl_AppendResult(interp
, zBuf
, 0);
366 ** Usage: btree_eof ID
368 ** Return TRUE if the given cursor is not pointing at a valid entry.
369 ** Return FALSE if the cursor does point to a valid entry.
371 static int btree_eof(
373 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
374 int argc
, /* Number of arguments */
375 const char **argv
/* Text of each argument */
382 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
386 pCur
= sqlite3TestTextToPtr(argv
[1]);
387 sqlite3BtreeEnter(pCur
->pBtree
);
388 rc
= sqlite3BtreeEof(pCur
);
389 sqlite3BtreeLeave(pCur
->pBtree
);
390 sqlite3_snprintf(sizeof(zBuf
),zBuf
, "%d", rc
);
391 Tcl_AppendResult(interp
, zBuf
, 0);
396 ** Usage: btree_payload_size ID
398 ** Return the number of bytes of payload
400 static int btree_payload_size(
402 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
403 int argc
, /* Number of arguments */
404 const char **argv
/* Text of each argument */
412 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
416 pCur
= sqlite3TestTextToPtr(argv
[1]);
417 sqlite3BtreeEnter(pCur
->pBtree
);
419 /* The cursor may be in "require-seek" state. If this is the case, the
420 ** call to BtreeDataSize() will fix it. */
421 sqlite3BtreeDataSize(pCur
, (u32
*)&n2
);
422 if( pCur
->apPage
[pCur
->iPage
]->intKey
){
425 sqlite3BtreeKeySize(pCur
, (i64
*)&n1
);
427 sqlite3BtreeLeave(pCur
->pBtree
);
428 sqlite3_snprintf(sizeof(zBuf
),zBuf
, "%d", (int)(n1
+n2
));
429 Tcl_AppendResult(interp
, zBuf
, 0);
434 ** usage: varint_test START MULTIPLIER COUNT INCREMENT
436 ** This command tests the putVarint() and getVarint()
437 ** routines, both for accuracy and for speed.
439 ** An integer is written using putVarint() and read back with
440 ** getVarint() and varified to be unchanged. This repeats COUNT
441 ** times. The first integer is START*MULTIPLIER. Each iteration
442 ** increases the integer by INCREMENT.
444 ** This command returns nothing if it works. It returns an error message
445 ** if something goes wrong.
447 static int btree_varint_test(
449 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
450 int argc
, /* Number of arguments */
451 const char **argv
/* Text of each argument */
453 u32 start
, mult
, count
, incr
;
456 unsigned char zBuf
[100];
458 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
459 " START MULTIPLIER COUNT INCREMENT\"", 0);
462 if( Tcl_GetInt(interp
, argv
[1], (int*)&start
) ) return TCL_ERROR
;
463 if( Tcl_GetInt(interp
, argv
[2], (int*)&mult
) ) return TCL_ERROR
;
464 if( Tcl_GetInt(interp
, argv
[3], (int*)&count
) ) return TCL_ERROR
;
465 if( Tcl_GetInt(interp
, argv
[4], (int*)&incr
) ) return TCL_ERROR
;
468 for(i
=0; i
<(int)count
; i
++){
470 n1
= putVarint(zBuf
, in
);
472 sprintf(zErr
, "putVarint returned %d - should be between 1 and 9", n1
);
473 Tcl_AppendResult(interp
, zErr
, 0);
476 n2
= getVarint(zBuf
, &out
);
478 sprintf(zErr
, "putVarint returned %d and getVarint returned %d", n1
, n2
);
479 Tcl_AppendResult(interp
, zErr
, 0);
483 sprintf(zErr
, "Wrote 0x%016llx and got back 0x%016llx", in
, out
);
484 Tcl_AppendResult(interp
, zErr
, 0);
487 if( (in
& 0xffffffff)==in
){
489 n2
= getVarint32(zBuf
, out32
);
492 sprintf(zErr
, "putVarint returned %d and GetVarint32 returned %d",
494 Tcl_AppendResult(interp
, zErr
, 0);
498 sprintf(zErr
, "Wrote 0x%016llx and got back 0x%016llx from GetVarint32",
500 Tcl_AppendResult(interp
, zErr
, 0);
505 /* In order to get realistic timings, run getVarint 19 more times.
506 ** This is because getVarint is called about 20 times more often
510 getVarint(zBuf
, &out
);
518 ** usage: btree_from_db DB-HANDLE
520 ** This command returns the btree handle for the main database associated
521 ** with the database-handle passed as the argument. Example usage:
523 ** sqlite3 db test.db
524 ** set bt [btree_from_db db]
526 static int btree_from_db(
528 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
529 int argc
, /* Number of arguments */
530 const char **argv
/* Text of each argument */
538 if( argc
!=2 && argc
!=3 ){
539 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
540 " DB-HANDLE ?N?\"", 0);
544 if( 1!=Tcl_GetCommandInfo(interp
, argv
[1], &info
) ){
545 Tcl_AppendResult(interp
, "No such db-handle: \"", argv
[1], "\"", 0);
552 db
= *((sqlite3
**)info
.objClientData
);
555 pBt
= db
->aDb
[iDb
].pBt
;
556 sqlite3_snprintf(sizeof(zBuf
), zBuf
, "%p", pBt
);
557 Tcl_SetResult(interp
, zBuf
, TCL_VOLATILE
);
562 ** Usage: btree_ismemdb ID
564 ** Return true if the B-Tree is in-memory.
566 static int btree_ismemdb(
568 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
569 int argc
, /* Number of arguments */
570 const char **argv
/* Text of each argument */
576 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
580 pBt
= sqlite3TestTextToPtr(argv
[1]);
581 sqlite3_mutex_enter(pBt
->db
->mutex
);
582 sqlite3BtreeEnter(pBt
);
583 res
= sqlite3PagerIsMemdb(sqlite3BtreePager(pBt
));
584 sqlite3BtreeLeave(pBt
);
585 sqlite3_mutex_leave(pBt
->db
->mutex
);
586 Tcl_SetObjResult(interp
, Tcl_NewBooleanObj(res
));
591 ** usage: btree_set_cache_size ID NCACHE
593 ** Set the size of the cache used by btree $ID.
595 static int btree_set_cache_size(
597 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
598 int argc
, /* Number of arguments */
599 const char **argv
/* Text of each argument */
606 interp
, "wrong # args: should be \"", argv
[0], " BT NCACHE\"", 0);
609 pBt
= sqlite3TestTextToPtr(argv
[1]);
610 if( Tcl_GetInt(interp
, argv
[2], &nCache
) ) return TCL_ERROR
;
612 sqlite3_mutex_enter(pBt
->db
->mutex
);
613 sqlite3BtreeEnter(pBt
);
614 sqlite3BtreeSetCacheSize(pBt
, nCache
);
615 sqlite3BtreeLeave(pBt
);
616 sqlite3_mutex_leave(pBt
->db
->mutex
);
623 ** Register commands with the TCL interpreter.
625 int Sqlitetest3_Init(Tcl_Interp
*interp
){
630 { "btree_open", (Tcl_CmdProc
*)btree_open
},
631 { "btree_close", (Tcl_CmdProc
*)btree_close
},
632 { "btree_begin_transaction", (Tcl_CmdProc
*)btree_begin_transaction
},
633 { "btree_pager_stats", (Tcl_CmdProc
*)btree_pager_stats
},
634 { "btree_cursor", (Tcl_CmdProc
*)btree_cursor
},
635 { "btree_close_cursor", (Tcl_CmdProc
*)btree_close_cursor
},
636 { "btree_next", (Tcl_CmdProc
*)btree_next
},
637 { "btree_eof", (Tcl_CmdProc
*)btree_eof
},
638 { "btree_payload_size", (Tcl_CmdProc
*)btree_payload_size
},
639 { "btree_first", (Tcl_CmdProc
*)btree_first
},
640 { "btree_varint_test", (Tcl_CmdProc
*)btree_varint_test
},
641 { "btree_from_db", (Tcl_CmdProc
*)btree_from_db
},
642 { "btree_ismemdb", (Tcl_CmdProc
*)btree_ismemdb
},
643 { "btree_set_cache_size", (Tcl_CmdProc
*)btree_set_cache_size
}
647 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
648 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);