xref: /sqlite-3.40.0/src/test2.c (revision 4775ecd0)
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 pager.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 ** $Id: test2.c,v 1.74 2009/07/24 19:01:20 drh Exp $
17 */
18 #include "sqliteInt.h"
19 #include "tcl.h"
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 
24 /*
25 ** Interpret an SQLite error number
26 */
27 static char *errorName(int rc){
28   char *zName;
29   switch( rc ){
30     case SQLITE_OK:         zName = "SQLITE_OK";          break;
31     case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break;
32     case SQLITE_PERM:       zName = "SQLITE_PERM";        break;
33     case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
34     case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
35     case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
36     case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
37     case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
38     case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
39     case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
40     case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
41     case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
42     case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
43     case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
44     case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break;
45     case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break;
46     case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break;
47     case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break;
48     case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break;
49     default:                zName = "SQLITE_Unknown";     break;
50   }
51   return zName;
52 }
53 
54 /*
55 ** Page size and reserved size used for testing.
56 */
57 static int test_pagesize = 1024;
58 
59 /*
60 ** Dummy page reinitializer
61 */
62 static void pager_test_reiniter(DbPage *pNotUsed){
63   return;
64 }
65 
66 /*
67 ** Usage:   pager_open FILENAME N-PAGE
68 **
69 ** Open a new pager
70 */
71 static int pager_open(
72   void *NotUsed,
73   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
74   int argc,              /* Number of arguments */
75   const char **argv      /* Text of each argument */
76 ){
77   u16 pageSize;
78   Pager *pPager;
79   int nPage;
80   int rc;
81   char zBuf[100];
82   if( argc!=3 ){
83     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
84        " FILENAME N-PAGE\"", 0);
85     return TCL_ERROR;
86   }
87   if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
88   rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0,
89       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB,
90       pager_test_reiniter);
91   if( rc!=SQLITE_OK ){
92     Tcl_AppendResult(interp, errorName(rc), 0);
93     return TCL_ERROR;
94   }
95   sqlite3PagerSetCachesize(pPager, nPage);
96   pageSize = test_pagesize;
97   sqlite3PagerSetPagesize(pPager, &pageSize, -1);
98   sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPager);
99   Tcl_AppendResult(interp, zBuf, 0);
100   return TCL_OK;
101 }
102 
103 /*
104 ** Usage:   pager_close ID
105 **
106 ** Close the given pager.
107 */
108 static int pager_close(
109   void *NotUsed,
110   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
111   int argc,              /* Number of arguments */
112   const char **argv      /* Text of each argument */
113 ){
114   Pager *pPager;
115   int rc;
116   if( argc!=2 ){
117     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
118        " ID\"", 0);
119     return TCL_ERROR;
120   }
121   pPager = sqlite3TestTextToPtr(argv[1]);
122   rc = sqlite3PagerClose(pPager);
123   if( rc!=SQLITE_OK ){
124     Tcl_AppendResult(interp, errorName(rc), 0);
125     return TCL_ERROR;
126   }
127   return TCL_OK;
128 }
129 
130 /*
131 ** Usage:   pager_rollback ID
132 **
133 ** Rollback changes
134 */
135 static int pager_rollback(
136   void *NotUsed,
137   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
138   int argc,              /* Number of arguments */
139   const char **argv      /* Text of each argument */
140 ){
141   Pager *pPager;
142   int rc;
143   if( argc!=2 ){
144     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
145        " ID\"", 0);
146     return TCL_ERROR;
147   }
148   pPager = sqlite3TestTextToPtr(argv[1]);
149   rc = sqlite3PagerRollback(pPager);
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:   pager_commit ID
159 **
160 ** Commit all changes
161 */
162 static int pager_commit(
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   Pager *pPager;
169   int rc;
170   if( argc!=2 ){
171     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
172        " ID\"", 0);
173     return TCL_ERROR;
174   }
175   pPager = sqlite3TestTextToPtr(argv[1]);
176   rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);
177   if( rc!=SQLITE_OK ){
178     Tcl_AppendResult(interp, errorName(rc), 0);
179     return TCL_ERROR;
180   }
181   rc = sqlite3PagerCommitPhaseTwo(pPager);
182   if( rc!=SQLITE_OK ){
183     Tcl_AppendResult(interp, errorName(rc), 0);
184     return TCL_ERROR;
185   }
186   return TCL_OK;
187 }
188 
189 /*
190 ** Usage:   pager_stmt_begin ID
191 **
192 ** Start a new checkpoint.
193 */
194 static int pager_stmt_begin(
195   void *NotUsed,
196   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
197   int argc,              /* Number of arguments */
198   const char **argv      /* Text of each argument */
199 ){
200   Pager *pPager;
201   int rc;
202   if( argc!=2 ){
203     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
204        " ID\"", 0);
205     return TCL_ERROR;
206   }
207   pPager = sqlite3TestTextToPtr(argv[1]);
208   rc = sqlite3PagerOpenSavepoint(pPager, 1);
209   if( rc!=SQLITE_OK ){
210     Tcl_AppendResult(interp, errorName(rc), 0);
211     return TCL_ERROR;
212   }
213   return TCL_OK;
214 }
215 
216 /*
217 ** Usage:   pager_stmt_rollback ID
218 **
219 ** Rollback changes to a checkpoint
220 */
221 static int pager_stmt_rollback(
222   void *NotUsed,
223   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
224   int argc,              /* Number of arguments */
225   const char **argv      /* Text of each argument */
226 ){
227   Pager *pPager;
228   int rc;
229   if( argc!=2 ){
230     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
231        " ID\"", 0);
232     return TCL_ERROR;
233   }
234   pPager = sqlite3TestTextToPtr(argv[1]);
235   rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0);
236   sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
237   if( rc!=SQLITE_OK ){
238     Tcl_AppendResult(interp, errorName(rc), 0);
239     return TCL_ERROR;
240   }
241   return TCL_OK;
242 }
243 
244 /*
245 ** Usage:   pager_stmt_commit ID
246 **
247 ** Commit changes to a checkpoint
248 */
249 static int pager_stmt_commit(
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   Pager *pPager;
256   int rc;
257   if( argc!=2 ){
258     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
259        " ID\"", 0);
260     return TCL_ERROR;
261   }
262   pPager = sqlite3TestTextToPtr(argv[1]);
263   rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
264   if( rc!=SQLITE_OK ){
265     Tcl_AppendResult(interp, errorName(rc), 0);
266     return TCL_ERROR;
267   }
268   return TCL_OK;
269 }
270 
271 /*
272 ** Usage:   pager_stats ID
273 **
274 ** Return pager statistics.
275 */
276 static int pager_stats(
277   void *NotUsed,
278   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
279   int argc,              /* Number of arguments */
280   const char **argv      /* Text of each argument */
281 ){
282   Pager *pPager;
283   int i, *a;
284   if( argc!=2 ){
285     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
286        " ID\"", 0);
287     return TCL_ERROR;
288   }
289   pPager = sqlite3TestTextToPtr(argv[1]);
290   a = sqlite3PagerStats(pPager);
291   for(i=0; i<9; i++){
292     static char *zName[] = {
293       "ref", "page", "max", "size", "state", "err",
294       "hit", "miss", "ovfl",
295     };
296     char zBuf[100];
297     Tcl_AppendElement(interp, zName[i]);
298     sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",a[i]);
299     Tcl_AppendElement(interp, zBuf);
300   }
301   return TCL_OK;
302 }
303 
304 /*
305 ** Usage:   pager_pagecount ID
306 **
307 ** Return the size of the database file.
308 */
309 static int pager_pagecount(
310   void *NotUsed,
311   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
312   int argc,              /* Number of arguments */
313   const char **argv      /* Text of each argument */
314 ){
315   Pager *pPager;
316   char zBuf[100];
317   int nPage;
318   if( argc!=2 ){
319     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
320        " ID\"", 0);
321     return TCL_ERROR;
322   }
323   pPager = sqlite3TestTextToPtr(argv[1]);
324   sqlite3PagerPagecount(pPager, &nPage);
325   sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", nPage);
326   Tcl_AppendResult(interp, zBuf, 0);
327   return TCL_OK;
328 }
329 
330 /*
331 ** Usage:   page_get ID PGNO
332 **
333 ** Return a pointer to a page from the database.
334 */
335 static int page_get(
336   void *NotUsed,
337   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
338   int argc,              /* Number of arguments */
339   const char **argv      /* Text of each argument */
340 ){
341   Pager *pPager;
342   char zBuf[100];
343   DbPage *pPage;
344   int pgno;
345   int rc;
346   if( argc!=3 ){
347     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
348        " ID PGNO\"", 0);
349     return TCL_ERROR;
350   }
351   pPager = sqlite3TestTextToPtr(argv[1]);
352   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
353   rc = sqlite3PagerSharedLock(pPager);
354   if( rc==SQLITE_OK ){
355     rc = sqlite3PagerGet(pPager, pgno, &pPage);
356   }
357   if( rc!=SQLITE_OK ){
358     Tcl_AppendResult(interp, errorName(rc), 0);
359     return TCL_ERROR;
360   }
361   sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
362   Tcl_AppendResult(interp, zBuf, 0);
363   return TCL_OK;
364 }
365 
366 /*
367 ** Usage:   page_lookup ID PGNO
368 **
369 ** Return a pointer to a page if the page is already in cache.
370 ** If not in cache, return an empty string.
371 */
372 static int page_lookup(
373   void *NotUsed,
374   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
375   int argc,              /* Number of arguments */
376   const char **argv      /* Text of each argument */
377 ){
378   Pager *pPager;
379   char zBuf[100];
380   DbPage *pPage;
381   int pgno;
382   if( argc!=3 ){
383     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
384        " ID PGNO\"", 0);
385     return TCL_ERROR;
386   }
387   pPager = sqlite3TestTextToPtr(argv[1]);
388   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
389   pPage = sqlite3PagerLookup(pPager, pgno);
390   if( pPage ){
391     sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage);
392     Tcl_AppendResult(interp, zBuf, 0);
393   }
394   return TCL_OK;
395 }
396 
397 /*
398 ** Usage:   pager_truncate ID PGNO
399 */
400 static int pager_truncate(
401   void *NotUsed,
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 */
405 ){
406   Pager *pPager;
407   int pgno;
408   if( argc!=3 ){
409     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
410        " ID PGNO\"", 0);
411     return TCL_ERROR;
412   }
413   pPager = sqlite3TestTextToPtr(argv[1]);
414   if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
415   sqlite3PagerTruncateImage(pPager, pgno);
416   return TCL_OK;
417 }
418 
419 
420 /*
421 ** Usage:   page_unref PAGE
422 **
423 ** Drop a pointer to a page.
424 */
425 static int page_unref(
426   void *NotUsed,
427   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
428   int argc,              /* Number of arguments */
429   const char **argv      /* Text of each argument */
430 ){
431   DbPage *pPage;
432   if( argc!=2 ){
433     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
434        " PAGE\"", 0);
435     return TCL_ERROR;
436   }
437   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
438   sqlite3PagerUnref(pPage);
439   return TCL_OK;
440 }
441 
442 /*
443 ** Usage:   page_read PAGE
444 **
445 ** Return the content of a page
446 */
447 static int page_read(
448   void *NotUsed,
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 */
452 ){
453   char zBuf[100];
454   DbPage *pPage;
455   if( argc!=2 ){
456     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
457        " PAGE\"", 0);
458     return TCL_ERROR;
459   }
460   pPage = sqlite3TestTextToPtr(argv[1]);
461   memcpy(zBuf, sqlite3PagerGetData(pPage), sizeof(zBuf));
462   Tcl_AppendResult(interp, zBuf, 0);
463   return TCL_OK;
464 }
465 
466 /*
467 ** Usage:   page_number PAGE
468 **
469 ** Return the page number for a page.
470 */
471 static int page_number(
472   void *NotUsed,
473   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
474   int argc,              /* Number of arguments */
475   const char **argv      /* Text of each argument */
476 ){
477   char zBuf[100];
478   DbPage *pPage;
479   if( argc!=2 ){
480     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
481        " PAGE\"", 0);
482     return TCL_ERROR;
483   }
484   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
485   sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", sqlite3PagerPagenumber(pPage));
486   Tcl_AppendResult(interp, zBuf, 0);
487   return TCL_OK;
488 }
489 
490 /*
491 ** Usage:   page_write PAGE DATA
492 **
493 ** Write something into a page.
494 */
495 static int page_write(
496   void *NotUsed,
497   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
498   int argc,              /* Number of arguments */
499   const char **argv      /* Text of each argument */
500 ){
501   DbPage *pPage;
502   char *pData;
503   int rc;
504   if( argc!=3 ){
505     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
506        " PAGE DATA\"", 0);
507     return TCL_ERROR;
508   }
509   pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]);
510   rc = sqlite3PagerWrite(pPage);
511   if( rc!=SQLITE_OK ){
512     Tcl_AppendResult(interp, errorName(rc), 0);
513     return TCL_ERROR;
514   }
515   pData = sqlite3PagerGetData(pPage);
516   strncpy(pData, argv[2], test_pagesize-1);
517   pData[test_pagesize-1] = 0;
518   return TCL_OK;
519 }
520 
521 #ifndef SQLITE_OMIT_DISKIO
522 /*
523 ** Usage:   fake_big_file  N  FILENAME
524 **
525 ** Write a few bytes at the N megabyte point of FILENAME.  This will
526 ** create a large file.  If the file was a valid SQLite database, then
527 ** the next time the database is opened, SQLite will begin allocating
528 ** new pages after N.  If N is 2096 or bigger, this will test the
529 ** ability of SQLite to write to large files.
530 */
531 static int fake_big_file(
532   void *NotUsed,
533   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
534   int argc,              /* Number of arguments */
535   const char **argv      /* Text of each argument */
536 ){
537   sqlite3_vfs *pVfs;
538   sqlite3_file *fd = 0;
539   int rc;
540   int n;
541   i64 offset;
542   if( argc!=3 ){
543     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
544        " N-MEGABYTES FILE\"", 0);
545     return TCL_ERROR;
546   }
547   if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
548 
549   pVfs = sqlite3_vfs_find(0);
550   rc = sqlite3OsOpenMalloc(pVfs, argv[2], &fd,
551       (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
552   );
553   if( rc ){
554     Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0);
555     return TCL_ERROR;
556   }
557   offset = n;
558   offset *= 1024*1024;
559   rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset);
560   sqlite3OsCloseFree(fd);
561   if( rc ){
562     Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0);
563     return TCL_ERROR;
564   }
565   return TCL_OK;
566 }
567 #endif
568 
569 
570 /*
571 ** test_control_pending_byte  PENDING_BYTE
572 **
573 ** Set the PENDING_BYTE using the sqlite3_test_control() interface.
574 */
575 static int testPendingByte(
576   void *NotUsed,
577   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
578   int argc,              /* Number of arguments */
579   const char **argv      /* Text of each argument */
580 ){
581   int pbyte;
582   int rc;
583   if( argc!=2 ){
584     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
585                      " PENDING-BYTE\"", (void*)0);
586   }
587   if( Tcl_GetInt(interp, argv[1], &pbyte) ) return TCL_ERROR;
588   rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte);
589   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
590   return TCL_OK;
591 }
592 
593 /*
594 ** sqlite3BitvecBuiltinTest SIZE PROGRAM
595 **
596 ** Invoke the SQLITE_TESTCTRL_BITVEC_TEST operator on test_control.
597 ** See comments on sqlite3BitvecBuiltinTest() for additional information.
598 */
599 static int testBitvecBuiltinTest(
600   void *NotUsed,
601   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
602   int argc,              /* Number of arguments */
603   const char **argv      /* Text of each argument */
604 ){
605   int sz, rc;
606   int nProg = 0;
607   int aProg[100];
608   const char *z;
609   if( argc!=3 ){
610     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
611                      " SIZE PROGRAM\"", (void*)0);
612   }
613   if( Tcl_GetInt(interp, argv[1], &sz) ) return TCL_ERROR;
614   z = argv[2];
615   while( nProg<99 && *z ){
616     while( *z && !sqlite3Isdigit(*z) ){ z++; }
617     if( *z==0 ) break;
618     aProg[nProg++] = atoi(z);
619     while( sqlite3Isdigit(*z) ){ z++; }
620   }
621   aProg[nProg] = 0;
622   rc = sqlite3_test_control(SQLITE_TESTCTRL_BITVEC_TEST, sz, aProg);
623   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
624   return TCL_OK;
625 }
626 
627 /*
628 ** Register commands with the TCL interpreter.
629 */
630 int Sqlitetest2_Init(Tcl_Interp *interp){
631   extern int sqlite3_io_error_persist;
632   extern int sqlite3_io_error_pending;
633   extern int sqlite3_io_error_hit;
634   extern int sqlite3_io_error_hardhit;
635   extern int sqlite3_diskfull_pending;
636   extern int sqlite3_diskfull;
637   static struct {
638     char *zName;
639     Tcl_CmdProc *xProc;
640   } aCmd[] = {
641     { "pager_open",              (Tcl_CmdProc*)pager_open          },
642     { "pager_close",             (Tcl_CmdProc*)pager_close         },
643     { "pager_commit",            (Tcl_CmdProc*)pager_commit        },
644     { "pager_rollback",          (Tcl_CmdProc*)pager_rollback      },
645     { "pager_stmt_begin",        (Tcl_CmdProc*)pager_stmt_begin    },
646     { "pager_stmt_commit",       (Tcl_CmdProc*)pager_stmt_commit   },
647     { "pager_stmt_rollback",     (Tcl_CmdProc*)pager_stmt_rollback },
648     { "pager_stats",             (Tcl_CmdProc*)pager_stats         },
649     { "pager_pagecount",         (Tcl_CmdProc*)pager_pagecount     },
650     { "page_get",                (Tcl_CmdProc*)page_get            },
651     { "page_lookup",             (Tcl_CmdProc*)page_lookup         },
652     { "page_unref",              (Tcl_CmdProc*)page_unref          },
653     { "page_read",               (Tcl_CmdProc*)page_read           },
654     { "page_write",              (Tcl_CmdProc*)page_write          },
655     { "page_number",             (Tcl_CmdProc*)page_number         },
656     { "pager_truncate",          (Tcl_CmdProc*)pager_truncate      },
657 #ifndef SQLITE_OMIT_DISKIO
658     { "fake_big_file",           (Tcl_CmdProc*)fake_big_file       },
659 #endif
660     { "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest     },
661     { "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
662   };
663   int i;
664   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
665     Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
666   }
667   Tcl_LinkVar(interp, "sqlite_io_error_pending",
668      (char*)&sqlite3_io_error_pending, TCL_LINK_INT);
669   Tcl_LinkVar(interp, "sqlite_io_error_persist",
670      (char*)&sqlite3_io_error_persist, TCL_LINK_INT);
671   Tcl_LinkVar(interp, "sqlite_io_error_hit",
672      (char*)&sqlite3_io_error_hit, TCL_LINK_INT);
673   Tcl_LinkVar(interp, "sqlite_io_error_hardhit",
674      (char*)&sqlite3_io_error_hardhit, TCL_LINK_INT);
675   Tcl_LinkVar(interp, "sqlite_diskfull_pending",
676      (char*)&sqlite3_diskfull_pending, TCL_LINK_INT);
677   Tcl_LinkVar(interp, "sqlite_diskfull",
678      (char*)&sqlite3_diskfull, TCL_LINK_INT);
679   Tcl_LinkVar(interp, "sqlite_pending_byte",
680      (char*)&sqlite3PendingByte, TCL_LINK_INT | TCL_LINK_READ_ONLY);
681   return TCL_OK;
682 }
683