]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Test the handling of errors returned by the xShmXXX() APIs.
authordan <dan@noemail.net>
Wed, 5 May 2010 19:04:59 +0000 (19:04 +0000)
committerdan <dan@noemail.net>
Wed, 5 May 2010 19:04:59 +0000 (19:04 +0000)
FossilOrigin-Name: 72663123d6be2b194cad7a6057d0f20dd0d9fe05

main.mk
manifest
manifest.uuid
src/tclsqlite.c
src/test_vfs.c [new file with mode: 0644]
src/wal.c
test/walfault.test

diff --git a/main.mk b/main.mk
index 353a3cef6bf2d90bfc9c3dfd555757bc511c1177..438ac5ff2fb9e01d4fda42102434d7c7e8d0a16c 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -249,6 +249,7 @@ TESTSRC = \
   $(TOP)/src/test_server.c \
   $(TOP)/src/test_tclvar.c \
   $(TOP)/src/test_thread.c \
+  $(TOP)/src/test_vfs.c \
   $(TOP)/src/test_wsd.c
 
 #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
index 1a2262bc8c6173e9e1d61c0acc6ae74d2d8972e8..5931045e7559004d9e4e84c22d39b1b2ffa0c462 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,5 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Do\snot\scompare\spage\ssizes\son\ssource\sand\sdestination\sof\sbackup\suntil\ntransactions\sare\sstarted\sand\sthe\spage\ssizes\sare\slocked.\s\sThis\sis\sa\nfix\sto\scheck-in\s[7bd44794c4].
-D 2010-05-05T18:46:45
+C Test\sthe\shandling\sof\serrors\sreturned\sby\sthe\sxShmXXX()\sAPIs.
+D 2010-05-05T19:04:59
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in d83a0ffef3dcbfb08b410a6c6dd6c009ec9167fb
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -92,7 +89,7 @@ F ext/rtree/tkt3363.test 2bf324f7908084a5f463de3109db9c6e607feb1b
 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
-F main.mk b681194d6bf557b80ca363306e9c172f81f15b1d
+F main.mk b39182f15fd980b86aceb7e4b9a1ba88abd75e2d
 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
 F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
 F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -175,7 +172,7 @@ F src/sqliteInt.h 9819b45610abeca390176243a9a31758c1f0ac7a
 F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
 F src/status.c 4df6fe7dce2d256130b905847c6c60055882bdbe
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
-F src/tclsqlite.c 4de81521174fedacd8393ea7b70b730ce17f8eae
+F src/tclsqlite.c a6d69438c21e89c26dc791bfa4c5ba6da9dbb515
 F src/test1.c ff95ca772d1df51618f9f1ef7ea432cdf851f97b
 F src/test2.c 31f1b9d076b4774a22d2605d0af1f34e14a9a7bd
 F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94
@@ -208,6 +205,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
 F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
 F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
 F src/test_thread.c aa9919c885a1fe53eafc73492f0898ee6c0a0726
+F src/test_vfs.c 4e84d17c6f64913684cd9c92c41337c55b3f3dc9
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb
 F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d
@@ -224,7 +222,7 @@ F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e
 F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
 F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
-F src/wal.c faafbea1d530e0ad60a2b77a9c32627077527d37
+F src/wal.c 85311299e9032957284b4c5b0f801fc4cb9416d6
 F src/wal.h b4c42014b5fa3b4e6244ac8c65de7ff67adeb27c
 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
 F src/where.c 75fee9e255b62f773fcadd1d1f25b6f63ac7a356
@@ -764,7 +762,7 @@ F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
 F test/wal.test f0b331017a12a31dd4bbb20aee9c179fbfdd5921
 F test/walbak.test a0e45187c7d8928df035dfea29b99b016b21ca3c
 F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f
-F test/walfault.test 2504c5c50d8f9a9e48969de381eb98e2a8b89195
+F test/walfault.test 9146e22807d6c75885614f623f5c8b1272c8488e
 F test/walhook.test 5f18e0fc8787f1f8889d7a9971af18f334f83786
 F test/walmode.test bac6f06544a8554588a1543def996bbe2fc41792
 F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
@@ -812,14 +810,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 9de05bfb09e29bafdf5782263330fe8eefcfaba3
-R 37171b2dee1e2cfb0d4c65563316cb42
-U drh
-Z 23a5102ed1b42825806ab1e4a794b6d7
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.6 (GNU/Linux)
-
-iD8DBQFL4b0ZoxKgR168RlERAiSAAJ417bV1PGOMpJIz3ysmcloS8N8BuwCcDdW2
-cZ5DIznx/YBIFs/Nrb9NWhQ=
-=nHgc
------END PGP SIGNATURE-----
+P ec7157788b16936b4b6e4642107b3c86aa44df24
+R 6831c56fbef38774f5073956c09582ec
+U dan
+Z 80e37064d0fb8ee254d6e645af9fdd96
index dc94d496b5fe525e63ff0ec4606d024afb0d3920..083d34427fe7ecb9287cf51d2eca5cb19bb31794 100644 (file)
@@ -1 +1 @@
-ec7157788b16936b4b6e4642107b3c86aa44df24
\ No newline at end of file
+72663123d6be2b194cad7a6057d0f20dd0d9fe05
\ No newline at end of file
index 4ef51d8566c3446a310ec672517f779405f19785..d9d6a7d8ae5df0acb2e0686a03e6f6fcfabc4c19 100644 (file)
@@ -3530,6 +3530,7 @@ int TCLSH_MAIN(int argc, char **argv){
     extern int SqlitetestOsinst_Init(Tcl_Interp*);
     extern int Sqlitetestbackup_Init(Tcl_Interp*);
     extern int Sqlitetestintarray_Init(Tcl_Interp*);
+    extern int Sqlitetestvfs_Init(Tcl_Interp *);
 
     Sqliteconfig_Init(interp);
     Sqlitetest1_Init(interp);
@@ -3556,6 +3557,7 @@ int TCLSH_MAIN(int argc, char **argv){
     SqlitetestOsinst_Init(interp);
     Sqlitetestbackup_Init(interp);
     Sqlitetestintarray_Init(interp);
+    Sqlitetestvfs_Init(interp);
 
 #ifdef SQLITE_SSE
     Sqlitetestsse_Init(interp);
diff --git a/src/test_vfs.c b/src/test_vfs.c
new file mode 100644 (file)
index 0000000..1212a51
--- /dev/null
@@ -0,0 +1,774 @@
+/*
+** 2010 May 05
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+#if SQLITE_TEST          /* This file is used for testing only */
+
+#include "sqlite3.h"
+#include "sqliteInt.h"
+
+typedef struct tvfs_file tvfs_file;
+struct tvfs_file {
+  sqlite3_file base;
+  sqlite3_file *pReal;
+};
+
+typedef struct Testvfs Testvfs;
+typedef struct TestvfsShm TestvfsShm;
+typedef struct TestvfsBuffer TestvfsBuffer;
+
+/*
+** An instance of this structure is allocated for each VFS created. The
+** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
+** is set to point to it.
+*/
+struct Testvfs {
+  char *zName;                    /* Name of this VFS */
+  sqlite3_vfs *pParent;           /* The VFS to use for file IO */
+  sqlite3_vfs *pVfs;              /* The testvfs registered with SQLite */
+  Tcl_Interp *interp;             /* Interpreter to run script in */
+  int nScript;                    /* Number of elements in array apScript */
+  Tcl_Obj **apScript;             /* Script to execute */
+  TestvfsBuffer *pBuffer;         /* List of shared buffers */
+};
+
+/*
+** A shared-memory buffer.
+*/
+struct TestvfsBuffer {
+  char *zFile;                    /* Associated file name */
+  int n;                          /* Size of allocated buffer in bytes */
+  u8 *a;                          /* Buffer allocated using ckalloc() */
+  int nRef;                       /* Number of references to this object */
+  TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
+};
+
+/*
+** A shared-memory handle returned by tvfsShmOpen().
+*/
+struct TestvfsShm {
+  Tcl_Obj *id;                    /* Name of this handle */
+  TestvfsBuffer *pBuffer;         /* Underlying buffer */
+};
+
+
+#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
+
+
+/*
+** Method declarations for tvfs_file.
+*/
+static int tvfsClose(sqlite3_file*);
+static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
+static int tvfsSync(sqlite3_file*, int flags);
+static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int tvfsLock(sqlite3_file*, int);
+static int tvfsUnlock(sqlite3_file*, int);
+static int tvfsCheckReservedLock(sqlite3_file*, int *);
+static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
+static int tvfsSectorSize(sqlite3_file*);
+static int tvfsDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for tvfs_vfs.
+*/
+static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
+static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
+static void tvfsDlClose(sqlite3_vfs*, void*);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int tvfsSleep(sqlite3_vfs*, int microseconds);
+static int tvfsCurrentTime(sqlite3_vfs*, double*);
+
+static int tvfsShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **);
+static int tvfsShmSize(sqlite3_vfs*, sqlite3_shm *, int , int *);
+static int tvfsShmGet(sqlite3_vfs*, sqlite3_shm *, int , int *, void **);
+static int tvfsShmRelease(sqlite3_vfs*, sqlite3_shm *);
+static int tvfsShmLock(sqlite3_vfs*, sqlite3_shm *, int , int *);
+static int tvfsShmClose(sqlite3_vfs*, sqlite3_shm *, int);
+
+static sqlite3_io_methods tvfs_io_methods = {
+  1,                            /* iVersion */
+  tvfsClose,                      /* xClose */
+  tvfsRead,                       /* xRead */
+  tvfsWrite,                      /* xWrite */
+  tvfsTruncate,                   /* xTruncate */
+  tvfsSync,                       /* xSync */
+  tvfsFileSize,                   /* xFileSize */
+  tvfsLock,                       /* xLock */
+  tvfsUnlock,                     /* xUnlock */
+  tvfsCheckReservedLock,          /* xCheckReservedLock */
+  tvfsFileControl,                /* xFileControl */
+  tvfsSectorSize,                 /* xSectorSize */
+  tvfsDeviceCharacteristics       /* xDeviceCharacteristics */
+};
+
+/*
+** Close an tvfs-file.
+*/
+static int tvfsClose(sqlite3_file *pFile){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsClose(p->pReal);
+}
+
+/*
+** Read data from an tvfs-file.
+*/
+static int tvfsRead(
+  sqlite3_file *pFile, 
+  void *zBuf, 
+  int iAmt, 
+  sqlite_int64 iOfst
+){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+}
+
+/*
+** Write data to an tvfs-file.
+*/
+static int tvfsWrite(
+  sqlite3_file *pFile, 
+  const void *zBuf, 
+  int iAmt, 
+  sqlite_int64 iOfst
+){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+}
+
+/*
+** Truncate an tvfs-file.
+*/
+static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsTruncate(p->pReal, size);
+}
+
+/*
+** Sync an tvfs-file.
+*/
+static int tvfsSync(sqlite3_file *pFile, int flags){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsSync(p->pReal, flags);
+}
+
+/*
+** Return the current file-size of an tvfs-file.
+*/
+static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsFileSize(p->pReal, pSize);
+}
+
+/*
+** Lock an tvfs-file.
+*/
+static int tvfsLock(sqlite3_file *pFile, int eLock){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsLock(p->pReal, eLock);
+}
+
+/*
+** Unlock an tvfs-file.
+*/
+static int tvfsUnlock(sqlite3_file *pFile, int eLock){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsUnlock(p->pReal, eLock);
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an tvfs-file.
+*/
+static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsCheckReservedLock(p->pReal, pResOut);
+}
+
+/*
+** File control method. For custom operations on an tvfs-file.
+*/
+static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsFileControl(p->pReal, op, pArg);
+}
+
+/*
+** Return the sector-size in bytes for an tvfs-file.
+*/
+static int tvfsSectorSize(sqlite3_file *pFile){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsSectorSize(p->pReal);
+}
+
+/*
+** Return the device characteristic flags supported by an tvfs-file.
+*/
+static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
+  tvfs_file *p = (tvfs_file *)pFile;
+  return sqlite3OsDeviceCharacteristics(p->pReal);
+}
+
+/*
+** Open an tvfs file handle.
+*/
+static int tvfsOpen(
+  sqlite3_vfs *pVfs,
+  const char *zName,
+  sqlite3_file *pFile,
+  int flags,
+  int *pOutFlags
+){
+  int rc;
+  tvfs_file *p = (tvfs_file *)pFile;
+  p->pReal = (sqlite3_file *)&p[1];
+  rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, p->pReal, flags, pOutFlags);
+  if( p->pReal->pMethods ){
+    pFile->pMethods = &tvfs_io_methods;
+  }
+  return rc;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+  return sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int tvfsAccess(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int flags, 
+  int *pResOut
+){
+  return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
+*/
+static int tvfsFullPathname(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int nOut, 
+  char *zOut
+){
+  return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
+}
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+  return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated 
+** with dynamic libraries.
+*/
+static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+  sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+  return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+  sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of 
+** random data.
+*/
+static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+  return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds 
+** actually slept.
+*/
+static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
+  return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+  return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
+}
+
+static void tvfsGrowBuffer(TestvfsShm *pShm, int reqSize, int *pNewSize){
+  TestvfsBuffer *pBuffer = pShm->pBuffer;
+  if( reqSize>pBuffer->n ){
+    pBuffer->a = (u8 *)ckrealloc((char *)pBuffer->a, reqSize);
+    pBuffer->n = reqSize;
+  }
+  *pNewSize = pBuffer->n;
+}
+
+static void tvfsExecTcl(
+  Testvfs *p, 
+  const char *zMethod,
+  Tcl_Obj *arg1,
+  Tcl_Obj *arg2,
+  Tcl_Obj *arg3
+){
+  int rc;                         /* Return code from Tcl_EvalObj() */
+  int nArg;                       /* Elements in eval'd list */
+
+  p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
+  p->apScript[p->nScript+1] = arg1;
+  p->apScript[p->nScript+2] = arg2;
+  p->apScript[p->nScript+3] = arg3;
+
+  for(nArg=p->nScript; p->apScript[nArg]; nArg++){
+    Tcl_IncrRefCount(p->apScript[nArg]);
+  }
+
+  rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL);
+  if( rc!=TCL_OK ){
+    Tcl_BackgroundError(p->interp);
+    Tcl_ResetResult(p->interp);
+  }
+
+  for(nArg=p->nScript; p->apScript[nArg]; nArg++){
+    Tcl_DecrRefCount(p->apScript[nArg]);
+    p->apScript[nArg] = 0;
+  }
+}
+
+static int tvfsResultCode(Testvfs *p, int *pRc){
+  struct errcode {
+    int eCode;
+    const char *zCode;
+  } aCode[] = {
+    { SQLITE_OK,     "SQLITE_OK"     },
+    { SQLITE_ERROR,  "SQLITE_ERROR"  },
+    { SQLITE_IOERR,  "SQLITE_IOERR"  },
+    { SQLITE_LOCKED, "SQLITE_LOCKED" },
+  };
+
+  const char *z;
+  int i;
+
+  z = Tcl_GetStringResult(p->interp);
+  for(i=0; i<ArraySize(aCode); i++){
+    if( 0==strcmp(z, aCode[i].zCode) ){
+      *pRc = aCode[i].eCode;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int tvfsShmOpen(
+  sqlite3_vfs *pVfs, 
+  const char *zName, 
+  sqlite3_shm **pp
+){
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  int rc = SQLITE_OK;             /* Return code */
+  Tcl_Obj *pId = 0;               /* Id for this connection */
+  TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
+  TestvfsShm *pShm;               /* New shm handle */
+
+  /* Evaluate the Tcl script: 
+  **
+  **   SCRIPT xShmOpen FILENAME
+  **
+  ** If the script returns an SQLite error code other than SQLITE_OK, an
+  ** error is returned to the caller. If it returns SQLITE_OK, the new
+  ** connection is named "anon". Otherwise, the value returned by the
+  ** script is used as the connection name.
+  */
+  tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(zName, -1), 0, 0);
+  if( tvfsResultCode(p, &rc) ){
+    if( rc!=SQLITE_OK ) return rc;
+    pId = Tcl_NewStringObj("anon", -1);
+  }else{
+    pId = Tcl_GetObjResult(p->interp);
+  }
+  Tcl_IncrRefCount(pId);
+
+  /* Allocate the TestvfsShm handle. */
+  pShm = (TestvfsShm *)ckalloc(sizeof(TestvfsShm));
+  memset(pShm, 0, sizeof(TestvfsShm));
+  pShm->id = pId;
+
+  /* Search for a TestvfsBuffer. Create a new one if required. */
+  for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
+    if( 0==strcmp(zName, pBuffer->zFile) ) break;
+  }
+  if( !pBuffer ){
+    int nByte = sizeof(TestvfsBuffer) + strlen(zName) + 1;
+    pBuffer = (TestvfsBuffer *)ckalloc(nByte);
+    memset(pBuffer, 0, nByte);
+    pBuffer->zFile = (char *)&pBuffer[1];
+    strcpy(pBuffer->zFile, zName);
+    pBuffer->pNext = p->pBuffer;
+    p->pBuffer = pBuffer;
+  }
+
+  /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
+  pBuffer->nRef++;
+  pShm->pBuffer = pBuffer;
+  *pp = (sqlite3_shm *)pShm;
+  return SQLITE_OK;
+}
+
+static int tvfsShmSize(
+  sqlite3_vfs *pVfs,
+  sqlite3_shm *pShmHandle,
+  int reqSize,
+  int *pNewSize
+){
+  int rc = SQLITE_OK;
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  TestvfsShm *pShm = (TestvfsShm *)pShmHandle;
+
+  tvfsGrowBuffer(pShm, reqSize, pNewSize);
+  tvfsExecTcl(p, "xShmSize", 
+      Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0
+  );
+  tvfsResultCode(p, &rc);
+  return rc;
+}
+
+static int tvfsShmGet(
+  sqlite3_vfs *pVfs,
+  sqlite3_shm *pShmHandle, 
+  int reqMapSize, 
+  int *pMapSize, 
+  void **pp
+){
+  int rc = SQLITE_OK;
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  TestvfsShm *pShm = (TestvfsShm *)pShmHandle;
+
+  tvfsGrowBuffer(pShm, reqMapSize, pMapSize);
+  tvfsExecTcl(p, "xShmGet", 
+      Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0
+  );
+  tvfsResultCode(p, &rc);
+  *pp = pShm->pBuffer->a;
+  return rc;
+}
+
+static int tvfsShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *pShmHandle){
+  int rc = SQLITE_OK;
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  TestvfsShm *pShm = (TestvfsShm *)pShmHandle;
+
+  tvfsExecTcl(p, "xShmRelease", 
+      Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0
+  );
+  tvfsResultCode(p, &rc);
+
+  return rc;
+}
+
+static int tvfsShmLock(
+  sqlite3_vfs *pVfs,
+  sqlite3_shm *pShmHandle,
+  int desiredLock,
+  int *gotLock
+){
+  int rc = SQLITE_OK;
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  TestvfsShm *pShm = (TestvfsShm *)pShmHandle;
+  char *zLock = "";
+
+  switch( desiredLock ){
+    case SQLITE_SHM_READ:         zLock = "READ";       break;
+    case SQLITE_SHM_WRITE:        zLock = "WRITE";      break;
+    case SQLITE_SHM_CHECKPOINT:   zLock = "CHECKPOINT"; break;
+    case SQLITE_SHM_RECOVER:      zLock = "RECOVER";    break;
+    case SQLITE_SHM_PENDING:      zLock = "PENDING";    break;
+    case SQLITE_SHM_UNLOCK:       zLock = "UNLOCK";     break;
+  }
+  tvfsExecTcl(p, "xShmLock", 
+      Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id,
+      Tcl_NewStringObj(zLock, -1)
+  );
+  tvfsResultCode(p, &rc);
+  if( rc==SQLITE_OK ){
+    *gotLock = desiredLock;
+  }
+
+  return rc;
+}
+
+static int tvfsShmClose(
+  sqlite3_vfs *pVfs, 
+  sqlite3_shm *pShmHandle, 
+  int deleteFlag
+){
+  int rc = SQLITE_OK;
+  Testvfs *p = (Testvfs *)(pVfs->pAppData);
+  TestvfsShm *pShm = (TestvfsShm *)pShmHandle;
+  TestvfsBuffer *pBuffer = pShm->pBuffer;
+
+  assert( (deleteFlag!=0)==(pBuffer->nRef==1) );
+
+  tvfsExecTcl(p, "xShmClose", 
+      Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0
+  );
+  tvfsResultCode(p, &rc);
+
+  pBuffer->nRef--;
+  if( pBuffer->nRef==0 ){
+    TestvfsBuffer **pp;
+    for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
+    *pp = (*pp)->pNext;
+    ckfree((char *)pBuffer->a);
+    ckfree((char *)pBuffer);
+  }
+  Tcl_DecrRefCount(pShm->id);
+  ckfree((char *)pShm);
+
+  return rc;
+}
+
+static int testvfs_obj_cmd(
+  ClientData cd,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  Testvfs *p = (Testvfs *)cd;
+
+  static const char *CMD_strs[] = { "shm", "delete", 0 };
+  enum DB_enum { CMD_SHM, CMD_DELETE };
+  int i;
+  
+  if( objc<2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
+    return TCL_ERROR;
+  }
+  if( Tcl_GetIndexFromObj(interp, objv[1], CMD_strs, "subcommand", 0, &i) ){
+    return TCL_ERROR;
+  }
+  Tcl_ResetResult(interp);
+
+  switch( (enum DB_enum)i ){
+    case CMD_SHM: {
+      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);
+        pBuffer->a = (u8 *)ckrealloc((char *)pBuffer->a, n);
+        pBuffer->n = n;
+        memcpy(pBuffer->a, a, n);
+      }
+      Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pBuffer->a, pBuffer->n));
+      break;
+    }
+    case CMD_DELETE: {
+      Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
+      break;
+    }
+  }
+
+  return TCL_OK;
+}
+
+static void testvfs_obj_del(ClientData cd){
+  int i;
+  Testvfs *p = (Testvfs *)cd;
+  for(i=0; i<p->nScript; i++){
+    Tcl_DecrRefCount(p->apScript[i]);
+  }
+  sqlite3_vfs_unregister(p->pVfs);
+  ckfree((char *)p->pVfs);
+  ckfree((char *)p);
+}
+
+#define TESTVFS_MAX_ARGS 12
+
+/*
+** Usage:  testvfs VFSNAME SCRIPT
+**
+** This command creates two things when it is invoked: an SQLite VFS, and
+** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
+** installed as the default VFS.
+**
+** The VFS passes all file I/O calls through to the underlying VFS.
+**
+** Whenever one of the xShmSize, xShmGet or xShmRelease methods of the VFS
+** are invoked, the SCRIPT is executed as follows:
+**
+**   SCRIPT xShmSize    FILENAME ID
+**   SCRIPT xShmGet     FILENAME ID
+**   SCRIPT xShmRelease FILENAME ID
+**
+** The value returned by the invocation of SCRIPT above is interpreted as
+** an SQLite error code and returned to SQLite. Either a symbolic 
+** "SQLITE_OK" or numeric "0" value may be returned.
+**
+** The contents of the shared-memory buffer associated with a given file
+** may be read and set using the following command:
+**
+**   VFSNAME shm FILENAME ?NEWVALUE?
+**
+** When the xShmLock method is invoked by SQLite, the following script is
+** run:
+**
+**   SCRIPT xShmLock    FILENAME ID LOCK
+**
+** where LOCK is one of "UNLOCK", "READ", "READ_FULL", "WRITE", "PENDING",
+** "CHECKPOINT" or "RECOVER". The script should return an SQLite error
+** code.
+*/
+static int testvfs_cmd(
+  ClientData cd,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+
+  static sqlite3_vfs tvfs_vfs = {
+    2,                            /* iVersion */
+    sizeof(tvfs_file),            /* szOsFile */
+    0,                            /* mxPathname */
+    0,                            /* pNext */
+    0,                            /* zName */
+    0,                            /* pAppData */
+    tvfsOpen,                     /* xOpen */
+    tvfsDelete,                   /* xDelete */
+    tvfsAccess,                   /* xAccess */
+    tvfsFullPathname,             /* xFullPathname */
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+    tvfsDlOpen,                   /* xDlOpen */
+    tvfsDlError,                  /* xDlError */
+    tvfsDlSym,                    /* xDlSym */
+    tvfsDlClose,                  /* xDlClose */
+#else
+    0,                            /* xDlOpen */
+    0,                            /* xDlError */
+    0,                            /* xDlSym */
+    0,                            /* xDlClose */
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+    tvfsRandomness,               /* xRandomness */
+    tvfsSleep,                    /* xSleep */
+    tvfsCurrentTime,              /* xCurrentTime */
+    0,                            /* xGetLastError */
+    tvfsShmOpen,
+    tvfsShmSize,
+    tvfsShmGet,
+    tvfsShmRelease,
+    tvfsShmLock,
+    tvfsShmClose,
+    0,
+    0,
+  };
+
+  Testvfs *p;                     /* New object */
+  sqlite3_vfs *pVfs;              /* New VFS */
+  char *zVfs;
+  Tcl_Obj *pScript;
+  int nScript;                    /* Number of elements in list pScript */
+  Tcl_Obj **apScript;             /* Array of pScript elements */
+  int nByte;                      /* Bytes of space to allocate at p */
+  int i;                          /* Counter variable */
+
+  if( objc!=3 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME SCRIPT");
+    return TCL_ERROR;
+  }
+  zVfs = Tcl_GetString(objv[1]);
+  pScript = objv[2];
+
+  if( TCL_OK!=Tcl_ListObjGetElements(interp, pScript, &nScript, &apScript) ){
+    return TCL_ERROR;
+  }
+
+  nByte = sizeof(Testvfs)
+        + (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *) 
+        + strlen(zVfs)+1;
+  p = (Testvfs *)ckalloc(nByte);
+  memset(p, 0, nByte);
+
+  p->pParent = sqlite3_vfs_find(0);
+  p->interp = interp;
+  p->nScript = nScript;
+  p->apScript = (Tcl_Obj **)&p[1];
+  for(i=0; i<nScript; i++){
+    p->apScript[i] = apScript[i];
+    Tcl_IncrRefCount(p->apScript[i]);
+  }
+  p->zName = (char *)&p->apScript[nScript+TESTVFS_MAX_ARGS];
+  strcpy(p->zName, zVfs);
+
+  pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
+  memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
+  pVfs->pAppData = (void *)p;
+  pVfs->zName = p->zName;
+  pVfs->mxPathname = p->pParent->mxPathname;
+  pVfs->szOsFile += p->pParent->szOsFile;
+
+  Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
+  sqlite3_vfs_register(pVfs, 0);
+
+  return TCL_OK;
+}
+
+int Sqlitetestvfs_Init(Tcl_Interp *interp){
+  Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
+  return TCL_OK;
+}
+
+#endif
index 4baeead3192ae7e2fb4305a083f0832b2f311beb..5a6e41fd519cf42fec915fc31230a41113d195cc 100644 (file)
--- a/src/wal.c
+++ b/src/wal.c
@@ -433,11 +433,14 @@ static int walIndexRemap(Wal *pWal, int enlargeTo){
 ** here.
 */
 static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
+  int rc;
   u32 iSlot = walIndexEntry(iFrame);
   
-  walIndexMap(pWal, -1);
+  rc = walIndexMap(pWal, -1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
   while( ((iSlot+128)*sizeof(u32))>=pWal->szWIndex ){
-    int rc;
     int nByte = pWal->szWIndex + WALINDEX_MMAP_INCREMENT;
 
     /* Enlarge the storage, then remap it. */
@@ -539,7 +542,8 @@ static int walIndexRecover(Wal *pWal){
       if( rc!=SQLITE_OK ) break;
       isValid = walDecodeFrame(aCksum, &pgno, &nTruncate, nPgsz, aData, aFrame);
       if( !isValid ) break;
-      walIndexAppend(pWal, ++iFrame, pgno);
+      rc = walIndexAppend(pWal, ++iFrame, pgno);
+      if( rc!=SQLITE_OK ) break;
 
       /* If nTruncate is non-zero, this is a commit record. */
       if( nTruncate ){
@@ -851,7 +855,7 @@ int sqlite3WalClose(
 **
 ** If the checksum cannot be verified return SQLITE_ERROR.
 */
-int walIndexTryHdr(Wal *pWal, int *pChanged){
+int walIndexTryHdr(Wal *pWal, int *pisValid, int *pChanged){
   u32 aCksum[2] = {1, 1};
   u32 aHdr[WALINDEX_HDR_NFIELD+2];
 
@@ -874,8 +878,9 @@ int walIndexTryHdr(Wal *pWal, int *pChanged){
   if( aCksum[0]!=aHdr[WALINDEX_HDR_NFIELD]
    || aCksum[1]!=aHdr[WALINDEX_HDR_NFIELD+1]
   ){
-    return SQLITE_ERROR;
+    return SQLITE_OK;
   }
+  *pisValid = 1;
 
   if( memcmp(&pWal->hdr, aHdr, sizeof(WalIndexHdr)) ){
     if( pChanged ){
@@ -896,15 +901,20 @@ int walIndexTryHdr(Wal *pWal, int *pChanged){
 */
 static int walIndexReadHdr(Wal *pWal, int *pChanged){
   int rc;
+  int isValid = 0;
 
   assert( pWal->lockState>=SQLITE_SHM_READ );
-  walIndexMap(pWal, -1);
+  rc = walIndexMap(pWal, -1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
 
   /* First try to read the header without a lock. Verify the checksum
   ** before returning. This will almost always work.  
   */
-  if( SQLITE_OK==walIndexTryHdr(pWal, pChanged) ){
-    return SQLITE_OK;
+  rc = walIndexTryHdr(pWal, &isValid, pChanged);
+  if( isValid || rc!=SQLITE_OK ){
+    return rc;
   }
 
   /* If the first attempt to read the header failed, lock the wal-index
@@ -912,18 +922,22 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
   ** time as well, run log recovery.
   */
   if( SQLITE_OK==(rc = walSetLock(pWal, SQLITE_SHM_RECOVER)) ){
-    if( SQLITE_OK!=walIndexTryHdr(pWal, pChanged) ){
+    rc = walIndexTryHdr(pWal, &isValid, pChanged);
+    if( rc==SQLITE_OK && isValid==0 ){
       if( pChanged ){
         *pChanged = 1;
       }
       rc = walIndexRecover(pWal);
       if( rc==SQLITE_OK ){
-        rc = walIndexTryHdr(pWal, 0);
+        rc = walIndexTryHdr(pWal, &isValid, 0);
       }
     }
     walSetLock(pWal, SQLITE_SHM_READ);
   }
 
+  if( rc==SQLITE_OK && isValid==0 ){
+    rc = SQLITE_ERROR;
+  }
   return rc;
 }
 
@@ -981,12 +995,16 @@ int sqlite3WalRead(
   int nOut,
   u8 *pOut
 ){
+  int rc;                         /* Return code */
   u32 iRead = 0;
   u32 *aData; 
   int iFrame = (pWal->hdr.iLastPg & 0xFFFFFF00);
 
   assert( pWal->lockState==SQLITE_SHM_READ||pWal->lockState==SQLITE_SHM_WRITE );
-  walIndexMap(pWal, -1);
+  rc = walIndexMap(pWal, -1);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
 
   /* Do a linear search of the unindexed block of page-numbers (if any) 
   ** at the end of the wal-index. An alternative to this would be to
@@ -1105,7 +1123,7 @@ int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
   Pgno iMax = pWal->hdr.iLastPg;
   Pgno iFrame;
 
-  walIndexReadHdr(pWal, 0);
+  rc = walIndexReadHdr(pWal, 0);
   for(iFrame=pWal->hdr.iLastPg+1; iFrame<=iMax && rc==SQLITE_OK; iFrame++){
     assert( pWal->lockState==SQLITE_SHM_WRITE );
     rc = xUndo(pUndoCtx, pWal->pWiData[walIndexEntry(iFrame)]);
@@ -1237,9 +1255,6 @@ int sqlite3WalFrames(
     }
 
     rc = sqlite3OsSync(pWal->pFd, sync_flags);
-    if( rc!=SQLITE_OK ){
-      return rc;
-    }
   }
   assert( pWal->pWiData==0 );
 
@@ -1249,33 +1264,35 @@ int sqlite3WalFrames(
   ** be in use by existing readers is being overwritten.
   */
   iFrame = pWal->hdr.iLastPg;
-  for(p=pList; p; p=p->pDirty){
+  for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
     iFrame++;
-    walIndexAppend(pWal, iFrame, p->pgno);
+    rc = walIndexAppend(pWal, iFrame, p->pgno);
   }
-  while( nLast>0 ){
+  while( nLast>0 && rc==SQLITE_OK ){
     iFrame++;
     nLast--;
-    walIndexAppend(pWal, iFrame, pLast->pgno);
+    rc = walIndexAppend(pWal, iFrame, pLast->pgno);
   }
 
-  /* Update the private copy of the header. */
-  pWal->hdr.pgsz = nPgsz;
-  pWal->hdr.iLastPg = iFrame;
-  if( isCommit ){
-    pWal->hdr.iChange++;
-    pWal->hdr.nPage = nTruncate;
-  }
-  pWal->hdr.iCheck1 = aCksum[0];
-  pWal->hdr.iCheck2 = aCksum[1];
+  if( rc==SQLITE_OK ){
+    /* Update the private copy of the header. */
+    pWal->hdr.pgsz = nPgsz;
+    pWal->hdr.iLastPg = iFrame;
+    if( isCommit ){
+      pWal->hdr.iChange++;
+      pWal->hdr.nPage = nTruncate;
+    }
+    pWal->hdr.iCheck1 = aCksum[0];
+    pWal->hdr.iCheck2 = aCksum[1];
 
-  /* If this is a commit, update the wal-index header too. */
-  if( isCommit ){
-    walIndexWriteHdr(pWal, &pWal->hdr);
-    pWal->iCallback = iFrame;
+    /* If this is a commit, update the wal-index header too. */
+    if( isCommit ){
+      walIndexWriteHdr(pWal, &pWal->hdr);
+      pWal->iCallback = iFrame;
+    }
   }
-  walIndexUnmap(pWal);
 
+  walIndexUnmap(pWal);
   return rc;
 }
 
index f01d405197ef762262b873b4300f8fd0dec6b81e..159f71ab3233bb41e196f5df970cd2d1851d7ff2 100644 (file)
@@ -52,5 +52,75 @@ do_malloc_test walfault-oom-2 -tclprep {
   SELECT count(*) FROM x;
 }
 
+# A [testvfs] callback for the VFS created by [do_shmfault_test]. This
+# callback injects SQLITE_IOERR faults into the following methods:
+#
+#   xShmOpen
+#   xShmSize
+#   xShmGet
+# 
+# Faults are not injected into xShmRelease, xShmClose or xShmLock method 
+# calls. The global tcl variables used are:
+#
+#   $::shmfault_ioerr_countdown
+#   $::shmfault_ioerr_persist
+#
+proc shmfault_vfs_cb {method args} {
+
+  # If ::shmfault_ioerr_countdown is not set, always return SQLITE_OK.
+  #
+  if {[info exists ::shmfault_ioerr_countdown]==0} { return SQLITE_OK }
+
+  if {$method == "xShmOpen"
+   || $method == "xShmSize"
+   || $method == "xShmGet"
+  } {
+    incr ::shmfault_ioerr_countdown -1
+    if { ($::shmfault_ioerr_countdown==0)
+      || ($::shmfault_ioerr_countdown<=0 && $::shmfault_ioerr_persist)
+    } {
+      return SQLITE_IOERR
+    }
+  }
+  return SQLITE_OK
+}
+
+proc do_shmfault_test {name args} {
+  array set A $args
+
+  # Create a VFS to use:
+  testvfs shmfault shmfault_vfs_cb
+  
+  foreach mode {transient persistent} {
+    set ::shmfault_ioerr_persist [expr {$mode == "persistent"}]
+    for {set nDelay 1} {$nDelay < 10000} {incr nDelay} {
+      set ::shmfault_ioerr_countdown $nDelay
+  
+      file delete -force test.db test.db-wal test.db-journal
+      
+      set rc [catch {
+        sqlite3 db test.db -vfs shmfault
+        db eval $A(-sqlbody)
+      } msg]
+      set hit_error [expr {$::shmfault_ioerr_countdown<=0}]
+      unset ::shmfault_ioerr_countdown
+      catch { db close }
+      
+      do_test $name-$mode.$nDelay.1 [list set {} $hit_error] $rc
+  
+      if {$hit_error==0} break
+    }
+  }
+
+  shmfault delete
+}
+
+do_shmfault_test walfault-shm-1 -sqlbody {
+  PRAGMA journal_mode = WAL;
+  CREATE TABLE t1(a PRIMARY KEY, b);
+  INSERT INTO t1 VALUES('a', 'b');
+  PRAGMA wal_checkpoint;
+}
 
 finish_test
+