/ Check-in [37b26d12]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add some fault-injection tests to improve coverage.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: 37b26d125f4b1d8e75bb38800fefd145611f94aa
User & Date: dan 2010-06-14 07:53:26
Context
2010-06-14
10:30
Add the xShmPage method to the "crash" vfs in test6.c. check-in: 1008f536 user: dan tags: experimental
07:53
Add some fault-injection tests to improve coverage. check-in: 37b26d12 user: dan tags: experimental
2010-06-12
12:02
Fix some problems with handling IO errors on the experimental branch. check-in: eade8bc2 user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_vfs.c.

655
656
657
658
659
660
661

662
663
664
665
666
667
668

669
670
671
672
673
674
675
...
824
825
826
827
828
829
830


831


832
833
834

835
836
837
838
839
840
841
842
843
...
866
867
868
869
870
871
872

873
874
875
876
877
878
879
...
907
908
909
910
911
912
913

914
915
916
917
918
919
920
....
1079
1080
1081
1082
1083
1084
1085







1086
1087
1088
1089
1090
1091
1092
....
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
){
  int rc = SQLITE_OK;
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);

  if( p->pScript && p->mask&TESTVFS_SHMPAGE_MASK ){
    Tcl_Obj *pArg = Tcl_NewObj();

    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
    tvfsExecTcl(p, "xShmPage", 
        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
    );
    tvfsResultCode(p, &rc);

  }
  if( rc==SQLITE_OK && p->mask&TESTVFS_SHMPAGE_MASK && tvfsInjectIoerr(p) ){
    rc = SQLITE_IOERR;
  }

  if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
    tvfsAllocPage(pFd->pShm, iPage, pgsz);
................................................................................
      int i;
      TestvfsBuffer *pBuffer;
      char *zName;
      if( objc!=3 && objc!=4 ){
        Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
        return TCL_ERROR;
      }


      zName = Tcl_GetString(objv[2]);


      for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
        if( 0==strcmp(pBuffer->zFile, zName) ) break;
      }

      if( !pBuffer ){
        Tcl_AppendResult(interp, "no such file: ", zName, 0);
        return TCL_ERROR;
      }
      if( objc==4 ){
        int n;
        u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
        assert( pBuffer->pgsz==0 || pBuffer->pgsz==32768 );
        for(i=0; i*32768<n; i++){
................................................................................
        { "xShmOpen",    TESTVFS_SHMOPEN_MASK },
        { "xShmSize",    TESTVFS_SHMSIZE_MASK },
        { "xShmGet",     TESTVFS_SHMGET_MASK },
        { "xShmRelease", TESTVFS_SHMRELEASE_MASK },
        { "xShmLock",    TESTVFS_SHMLOCK_MASK },
        { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
        { "xShmClose",   TESTVFS_SHMCLOSE_MASK },

        { "xSync",       TESTVFS_SYNC_MASK },
        { "xOpen",       TESTVFS_OPEN_MASK },
      };
      Tcl_Obj **apElem = 0;
      int nElem = 0;
      int i;
      int mask = 0;
................................................................................
      if( objc==3 ){
        int nByte;
        if( p->pScript ){
          Tcl_DecrRefCount(p->pScript);
          ckfree((char *)p->apScript);
          p->apScript = 0;
          p->nScript = 0;

        }
        Tcl_GetStringFromObj(objv[2], &nByte);
        if( nByte>0 ){
          p->pScript = Tcl_DuplicateObj(objv[2]);
          Tcl_IncrRefCount(p->pScript);
        }
      }else if( objc!=2 ){
................................................................................
  }

  zVfs = Tcl_GetString(objv[1]);
  nByte = sizeof(Testvfs) + strlen(zVfs)+1;
  p = (Testvfs *)ckalloc(nByte);
  memset(p, 0, nByte);








  p->pParent = sqlite3_vfs_find(0);
  p->interp = interp;

  p->zName = (char *)&p[1];
  memcpy(p->zName, zVfs, strlen(zVfs)+1);

  pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
................................................................................
  pVfs->zName = p->zName;
  pVfs->mxPathname = p->pParent->mxPathname;
  pVfs->szOsFile += p->pParent->szOsFile;
  p->pVfs = pVfs;
  p->isNoshm = isNoshm;
  p->mask = TESTVFS_ALL_MASK;

  Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
  sqlite3_vfs_register(pVfs, isDefault);

  return TCL_OK;

 bad_args:
  Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?");
  return TCL_ERROR;







>







>







 







>
>
|
>
>



>

|







 







>







 







>







 







>
>
>
>
>
>
>







 







<







655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
...
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
...
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
...
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
....
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
....
1111
1112
1113
1114
1115
1116
1117

1118
1119
1120
1121
1122
1123
1124
){
  int rc = SQLITE_OK;
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);

  if( p->pScript && p->mask&TESTVFS_SHMPAGE_MASK ){
    Tcl_Obj *pArg = Tcl_NewObj();
    Tcl_IncrRefCount(pArg);
    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
    Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
    tvfsExecTcl(p, "xShmPage", 
        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
    );
    tvfsResultCode(p, &rc);
    Tcl_DecrRefCount(pArg);
  }
  if( rc==SQLITE_OK && p->mask&TESTVFS_SHMPAGE_MASK && tvfsInjectIoerr(p) ){
    rc = SQLITE_IOERR;
  }

  if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
    tvfsAllocPage(pFd->pShm, iPage, pgsz);
................................................................................
      int i;
      TestvfsBuffer *pBuffer;
      char *zName;
      if( objc!=3 && objc!=4 ){
        Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
        return TCL_ERROR;
      }
      zName = ckalloc(p->pParent->mxPathname);
      p->pParent->xFullPathname(
          p->pParent, Tcl_GetString(objv[2]), 
          p->pParent->mxPathname, zName
      );
      for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
        if( 0==strcmp(pBuffer->zFile, zName) ) break;
      }
      ckfree(zName);
      if( !pBuffer ){
        Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0);
        return TCL_ERROR;
      }
      if( objc==4 ){
        int n;
        u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
        assert( pBuffer->pgsz==0 || pBuffer->pgsz==32768 );
        for(i=0; i*32768<n; i++){
................................................................................
        { "xShmOpen",    TESTVFS_SHMOPEN_MASK },
        { "xShmSize",    TESTVFS_SHMSIZE_MASK },
        { "xShmGet",     TESTVFS_SHMGET_MASK },
        { "xShmRelease", TESTVFS_SHMRELEASE_MASK },
        { "xShmLock",    TESTVFS_SHMLOCK_MASK },
        { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
        { "xShmClose",   TESTVFS_SHMCLOSE_MASK },
        { "xShmPage",    TESTVFS_SHMPAGE_MASK },
        { "xSync",       TESTVFS_SYNC_MASK },
        { "xOpen",       TESTVFS_OPEN_MASK },
      };
      Tcl_Obj **apElem = 0;
      int nElem = 0;
      int i;
      int mask = 0;
................................................................................
      if( objc==3 ){
        int nByte;
        if( p->pScript ){
          Tcl_DecrRefCount(p->pScript);
          ckfree((char *)p->apScript);
          p->apScript = 0;
          p->nScript = 0;
          p->pScript = 0;
        }
        Tcl_GetStringFromObj(objv[2], &nByte);
        if( nByte>0 ){
          p->pScript = Tcl_DuplicateObj(objv[2]);
          Tcl_IncrRefCount(p->pScript);
        }
      }else if( objc!=2 ){
................................................................................
  }

  zVfs = Tcl_GetString(objv[1]);
  nByte = sizeof(Testvfs) + strlen(zVfs)+1;
  p = (Testvfs *)ckalloc(nByte);
  memset(p, 0, nByte);

  /* Create the new object command before querying SQLite for a default VFS
  ** to use for 'real' IO operations. This is because creating the new VFS
  ** may delete an existing [testvfs] VFS of the same name. If such a VFS
  ** is currently the default, the new [testvfs] may end up calling the 
  ** methods of a deleted object.
  */
  Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
  p->pParent = sqlite3_vfs_find(0);
  p->interp = interp;

  p->zName = (char *)&p[1];
  memcpy(p->zName, zVfs, strlen(zVfs)+1);

  pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
................................................................................
  pVfs->zName = p->zName;
  pVfs->mxPathname = p->pParent->mxPathname;
  pVfs->szOsFile += p->pParent->szOsFile;
  p->pVfs = pVfs;
  p->isNoshm = isNoshm;
  p->mask = TESTVFS_ALL_MASK;


  sqlite3_vfs_register(pVfs, isDefault);

  return TCL_OK;

 bad_args:
  Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?");
  return TCL_ERROR;

Changes to src/wal.c.

1276
1277
1278
1279
1280
1281
1282







1283
1284
1285
1286
1287
1288
1289
....
1333
1334
1335
1336
1337
1338
1339

1340
1341
1342
1343
1344
1345
1346
....
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
....
2072
2073
2074
2075
2076
2077
2078
2079
2080



2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102

2103
2104
2105
2106
2107
2108
2109
....
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
    int i;
    for(i=1; i<*pnList; i++){
      assert( aContent[aList[i]] > aContent[aList[i-1]] );
    }
  }
#endif
}








/*
** Map the wal-index into memory owned by this thread, if it is not
** mapped already.  Then construct a WalInterator object that can be
** used to loop over all pages in the WAL in ascending order.  
**
** On success, make *pp point to the newly allocated WalInterator object
................................................................................
    u32 iZero;
    int nEntry;
    volatile u32 *aPgno;
    int rc;

    rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero);
    if( rc!=SQLITE_OK ){

      return rc;
    }
    nEntry = ((i+1)==nSegment)?iLast-iZero:(u32 *)aHash-(u32 *)&aPgno[iZero+1];

    iZero++;
    aPgno += iZero;

................................................................................
  assert( aSpace==aTmp );

  /* Return the fully initialized WalIterator object */
  *pp = p;
  return SQLITE_OK ;
}

/* 
** Free an iterator allocated by walIteratorInit().
*/
static void walIteratorFree(WalIterator *p){
  sqlite3_free(p);
}

/*
** Copy as much content as we can from the WAL back into the database file
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
**
** The amount of information copies from WAL to database might be limited
** by active readers.  This routine will never overwrite a database page
** that a concurrent reader might be using.
................................................................................
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
  int rc = SQLITE_OK;
  if( pWal->writeLock ){
    int unused;
    Pgno iMax = pWal->hdr.mxFrame;
    Pgno iFrame;
  
    rc = walIndexReadHdr(pWal, &unused);
    if( rc==SQLITE_OK ){



      for(iFrame=pWal->hdr.mxFrame+1; 
          ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; 
          iFrame++
      ){
        /* This call cannot fail. Unless the page for which the page number
        ** is passed as the second argument is (a) in the cache and 
        ** (b) has an outstanding reference, then xUndo is either a no-op
        ** (if (a) is false) or simply expels the page from the cache (if (b)
        ** is false).
        **
        ** If the upper layer is doing a rollback, it is guaranteed that there
        ** are no outstanding references to any page other than page 1. And
        ** page 1 is never written to the log until the transaction is
        ** committed. As a result, the call to xUndo may not fail.
        */
        assert( pWal->writeLock );
        assert( walFramePgno(pWal, iFrame)!=1 );
        rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
      }
      walCleanupHash(pWal);
    }
  }

  return rc;
}

/* 
** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
** values. This function populates the array with values required to 
** "rollback" the write position of the WAL handle back to the current 
................................................................................
    aWalData[3] = pWal->nCkpt;
  }

  if( aWalData[0]<pWal->hdr.mxFrame ){
    pWal->hdr.mxFrame = aWalData[0];
    pWal->hdr.aFrameCksum[0] = aWalData[1];
    pWal->hdr.aFrameCksum[1] = aWalData[2];
    if( rc==SQLITE_OK ){
      walCleanupHash(pWal);
    }
  }

  return rc;
}

/*
** This function is called just before writing a set of frames to the log







>
>
>
>
>
>
>







 







>







 







<
<
<
<
<
<
<







 







|
|
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
>







 







<
|
<







1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
....
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
....
1365
1366
1367
1368
1369
1370
1371







1372
1373
1374
1375
1376
1377
1378
....
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105

2106
2107
2108
2109
2110
2111
2112
2113
....
2142
2143
2144
2145
2146
2147
2148

2149

2150
2151
2152
2153
2154
2155
2156
    int i;
    for(i=1; i<*pnList; i++){
      assert( aContent[aList[i]] > aContent[aList[i-1]] );
    }
  }
#endif
}

/* 
** Free an iterator allocated by walIteratorInit().
*/
static void walIteratorFree(WalIterator *p){
  sqlite3_free(p);
}

/*
** Map the wal-index into memory owned by this thread, if it is not
** mapped already.  Then construct a WalInterator object that can be
** used to loop over all pages in the WAL in ascending order.  
**
** On success, make *pp point to the newly allocated WalInterator object
................................................................................
    u32 iZero;
    int nEntry;
    volatile u32 *aPgno;
    int rc;

    rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero);
    if( rc!=SQLITE_OK ){
      walIteratorFree(p);
      return rc;
    }
    nEntry = ((i+1)==nSegment)?iLast-iZero:(u32 *)aHash-(u32 *)&aPgno[iZero+1];

    iZero++;
    aPgno += iZero;

................................................................................
  assert( aSpace==aTmp );

  /* Return the fully initialized WalIterator object */
  *pp = p;
  return SQLITE_OK ;
}








/*
** Copy as much content as we can from the WAL back into the database file
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
**
** The amount of information copies from WAL to database might be limited
** by active readers.  This routine will never overwrite a database page
** that a concurrent reader might be using.
................................................................................
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
  int rc = SQLITE_OK;
  if( pWal->writeLock ){
    int unused;
    Pgno iMax = pWal->hdr.mxFrame;
    Pgno iFrame;
  
    /* Restore the clients cache of the wal-index header to the state it
    ** was in before the client began writing to the database. 
    */
    memcpy(&pWal->hdr, walIndexHdr(pWal), sizeof(WalIndexHdr));

    for(iFrame=pWal->hdr.mxFrame+1; 
        ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; 
        iFrame++
    ){
      /* This call cannot fail. Unless the page for which the page number
      ** is passed as the second argument is (a) in the cache and 
      ** (b) has an outstanding reference, then xUndo is either a no-op
      ** (if (a) is false) or simply expels the page from the cache (if (b)
      ** is false).
      **
      ** If the upper layer is doing a rollback, it is guaranteed that there
      ** are no outstanding references to any page other than page 1. And
      ** page 1 is never written to the log until the transaction is
      ** committed. As a result, the call to xUndo may not fail.
      */
      assert( pWal->writeLock );
      assert( walFramePgno(pWal, iFrame)!=1 );
      rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
    }
    walCleanupHash(pWal);
  }

  assert( rc==SQLITE_OK );
  return rc;
}

/* 
** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
** values. This function populates the array with values required to 
** "rollback" the write position of the WAL handle back to the current 
................................................................................
    aWalData[3] = pWal->nCkpt;
  }

  if( aWalData[0]<pWal->hdr.mxFrame ){
    pWal->hdr.mxFrame = aWalData[0];
    pWal->hdr.aFrameCksum[0] = aWalData[1];
    pWal->hdr.aFrameCksum[1] = aWalData[2];

    walCleanupHash(pWal);

  }

  return rc;
}

/*
** This function is called just before writing a set of frames to the log

Changes to test/walfault.test.

115
116
117
118
119
120
121

122
123
124
125
126
127
128
...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
...
358
359
360
361
362
363
364
365















































































366
367
  db eval {
    DELETE FROM abc;
    PRAGMA wal_checkpoint;
  }
} -test {
  faultsim_test_result {0 {}}
}


#--------------------------------------------------------------------------
#
faultsim_delete_and_reopen
faultsim_save_and_close
do_faultsim_test walfault-4 -prep {
  faultsim_restore_and_reopen
................................................................................
    PRAGMA journal_mode = WAL;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-5 -faults shmerr* -prep {
  faultsim_restore_and_reopen
  execsql { PRAGMA wal_autocheckpoint = 0 }
  shmfault filter xShmSize
} -body {
  execsql {
    CREATE TABLE t1(x);
    BEGIN;
      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
................................................................................
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
    COMMIT;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-6 -faults shmerr* -prep {
  faultsim_restore_and_reopen
  shmfault filter xShmSize
} -body {
  execsql { SELECT count(*) FROM t1 }
} -test {
  faultsim_test_result {0 16384}
  faultsim_integrity_check
  set n [db one {SELECT count(*) FROM t1}]
  if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" }
................................................................................
  if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
}

do_test walfault-10-pre1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    PRAGMA wal_checkpoint = 0;
    CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
    CREATE INDEX zzzz ON z(zzz);
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
................................................................................
  faultsim_test_result {0 {}}
  catch { db eval { ROLLBACK } }
  faultsim_integrity_check

  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
  if {$n != "64 51200"} { error "Incorrect data: $n" }
}
















































































finish_test








>







 







|







 







|







 







|







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  db eval {
    DELETE FROM abc;
    PRAGMA wal_checkpoint;
  }
} -test {
  faultsim_test_result {0 {}}
}


#--------------------------------------------------------------------------
#
faultsim_delete_and_reopen
faultsim_save_and_close
do_faultsim_test walfault-4 -prep {
  faultsim_restore_and_reopen
................................................................................
    PRAGMA journal_mode = WAL;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-5 -faults shmerr* -prep {
  faultsim_restore_and_reopen
  execsql { PRAGMA wal_autocheckpoint = 0 }
  shmfault filter xShmPage
} -body {
  execsql {
    CREATE TABLE t1(x);
    BEGIN;
      INSERT INTO t1 VALUES(randomblob(400));           /* 1 */
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 2 */
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 4 */
................................................................................
      INSERT INTO t1 SELECT randomblob(400) FROM t1;    /* 16384 */
    COMMIT;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-6 -faults shmerr* -prep {
  faultsim_restore_and_reopen
  shmfault filter xShmPage
} -body {
  execsql { SELECT count(*) FROM t1 }
} -test {
  faultsim_test_result {0 16384}
  faultsim_integrity_check
  set n [db one {SELECT count(*) FROM t1}]
  if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" }
................................................................................
  if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
}

do_test walfault-10-pre1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    PRAGMA wal_autocheckpoint = 0;
    CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
    CREATE INDEX zzzz ON z(zzz);
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
................................................................................
  faultsim_test_result {0 {}}
  catch { db eval { ROLLBACK } }
  faultsim_integrity_check

  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
  if {$n != "64 51200"} { error "Incorrect data: $n" }
}

#--------------------------------------------------------------------------
# Test fault injection while checkpointing a large WAL file, if the 
# checkpoint is the first operation run after opening the database.
# This means that some of the required wal-index pages are mapped as part of
# the checkpoint process, which means there are a few more opportunities
# for IO errors.
#
# To speed this up, IO errors are only simulated within xShmPage() calls.
#
do_test walfault-11-pre-1 {
  sqlite3 db test.db
  execsql {
    PRAGMA journal_mode = WAL;
    PRAGMA wal_autocheckpoint = 0;
    BEGIN;
      CREATE TABLE abc(a PRIMARY KEY);
      INSERT INTO abc VALUES(randomblob(1500));
      INSERT INTO abc VALUES(randomblob(1500));
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    4
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --    8
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   16
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   32
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --   64
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  128
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  256
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   --  512
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 1024
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 2048
      INSERT INTO abc SELECT randomblob(1500) FROM abc;   -- 4096
    COMMIT;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-11 -faults shmerr* -prep {
  catch { db2 close }
  faultsim_restore_and_reopen
  shmfault filter xShmPage
} -body {
  db eval { SELECT count(*) FROM abc }
  sqlite3 db2 test.db -vfs shmfault
  db2 eval { PRAGMA wal_checkpoint }
} -test {
  faultsim_test_result {0 {}}
}

#-------------------------------------------------------------------------
# Test the handling of the various IO/OOM/SHM errors that may occur during 
# a log recovery operation undertaken as part of a call to 
# sqlite3_wal_checkpoint().
# 
do_test walfault-12-pre-1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    PRAGMA wal_autocheckpoint = 0;
    BEGIN;
      CREATE TABLE abc(a PRIMARY KEY);
      INSERT INTO abc VALUES(randomblob(1500));
      INSERT INTO abc VALUES(randomblob(1500));
    COMMIT;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-12 -prep {
  if {[info commands shmfault] == ""} {
    testvfs shmfault -default true
  }
  faultsim_restore_and_reopen
  db eval { SELECT * FROM sqlite_master }
  shmfault shm test.db [string repeat "\000" 40]
} -body {
  set rc [sqlite3_wal_checkpoint db]
  if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] }
} -test {
  db close
  faultsim_test_result {0 {}}
}


finish_test