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