Home | History | Annotate | Download | only in src
      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 */
     25 static 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 */
     55 static int test_pagesize = 1024;
     56 
     57 /*
     58 ** Dummy page reinitializer
     59 */
     60 static 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 */
     69 static 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 */
    106 static 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 */
    133 static 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 */
    160 static 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 */
    192 static 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 */
    219 static 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 */
    247 static 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 */
    274 static 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 */
    307 static 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 */
    333 static 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 */
    370 static 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 */
    398 static 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 */
    423 static 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 */
    445 static 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 */
    469 static 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 */
    493 static 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 */
    529 static 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 */
    573 static 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 */
    598 static 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 */
    629 int 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