]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental change: On systems where it is not possible to unlink a file while one...
authordan <dan@noemail.net>
Wed, 16 Jun 2010 19:04:23 +0000 (19:04 +0000)
committerdan <dan@noemail.net>
Wed, 16 Jun 2010 19:04:23 +0000 (19:04 +0000)
FossilOrigin-Name: bede8c8a148fb9be5ffbf38df7fa733e35cc68c3

manifest
manifest.uuid
src/pager.c
src/sqlite.h.in
src/test_vfs.c
test/journal2.test [new file with mode: 0644]

index bab89a2a9923f0ea6f507eef7abaa541a465589f..8919437c24a64d00cc77afa7f4da0fe40bcadd2d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sextra\stest\scases\sto\spager1.test.
-D 2010-06-16T12:30:11
+C Experimental\schange:\sOn\ssystems\swhere\sit\sis\snot\spossible\sto\sunlink\sa\sfile\swhile\sone\sor\smore\sprocesses\shas\sit\sopen\s(i.e.\snot\sunix),\savoid\sclosing\sthe\sjournal\sfile\seach\stime\sthe\sdatabase\sis\sunlocked\sand\sreopening\sit\sat\sthe\sstart\sof\seach\stransaction.
+D 2010-06-16T19:04:23
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
 F src/os_os2.c 665876d5eec7585226b0a1cf5e18098de2b2da19
 F src/os_unix.c ae173c9f6afaa58b2833a1c95c6cd32021755c42
 F src/os_win.c dfde7d33c446e89dd9a277c036f2c4cc564b3138
-F src/pager.c 2964185d4356d0dc159b8340e52d2538d32394e5
+F src/pager.c 133cc49da000b2685a104ed9812084c61bd7e40f
 F src/pager.h ca1f23c0cf137ac26f8908df2427c8b308361efd
 F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -170,7 +170,7 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
 F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b
 F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714
-F src/sqlite.h.in 46c01e55cea31b91565ae41276c6310ee4032be8
+F src/sqlite.h.in 706e41c4526ed2674fa042ab3b7ba473b20cb141
 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
 F src/sqliteInt.h 242987ebd2366ea36650a09cdab04a9163c62109
 F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
@@ -209,7 +209,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 001c34e08748a4a02cd1c2d5531c160a007a84d8
+F src/test_vfs.c 687ba8db7830909955896488a66d3c6b655827f0
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb
 F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d
@@ -466,6 +466,7 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
 F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
 F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
 F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19
+F test/journal2.test 0f867f26c7f30bda2b69dc0e6451caa8fed18ef1
 F test/jrnlmode.test 76f94d61528c5ff32102a12f8cf34f4cc36f7849
 F test/jrnlmode2.test fe79ea1f0375c926b8de0362ddf94f34a64135fd
 F test/jrnlmode3.test cfcdb12b90e640a23b92785a002d96c0624c8710
@@ -822,7 +823,10 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 6c5c04eea1f0e8d61883ee8675c249fbf895dc01
-R d490808417f08b44a0b162eb48b4aec9
+P ad3209572d0e6afe5c8b52313e334509661045e2
+R 55fd31c025a3067f63c5d2afd30242b8
+T *branch * experimental
+T *sym-experimental *
+T -sym-trunk *
 U dan
-Z 861e5ff117287c588eecf635e107e097
+Z 33e1ea27519902b3169c3c25730a3e25
index 4d0fbc3ef3b075a759e15f2627a56be3a03c8f54..08f76d7826202913a32db01671d1bb0cb6398703 100644 (file)
@@ -1 +1 @@
-ad3209572d0e6afe5c8b52313e334509661045e2
\ No newline at end of file
+bede8c8a148fb9be5ffbf38df7fa733e35cc68c3
\ No newline at end of file
index 2b102fbea9a35d4c1f5e62e8ed8a51a11898ed6c..46eb7bc636f6ba4fc91bb0426c0c666c07f8cced 100644 (file)
@@ -336,6 +336,7 @@ struct Pager {
   u8 tempFile;                /* zFilename is a temporary file */
   u8 readOnly;                /* True for a read-only database */
   u8 memDb;                   /* True to inhibit all file I/O */
+  u8 safeJrnlHandle;          /* True if jrnl may be held open with no lock */
 
   /* The following block contains those class members that are dynamically
   ** modified during normal operations. The other variables in this structure
@@ -1224,7 +1225,13 @@ static void pager_unlock(Pager *pPager){
     ** Otherwise, another connection with journal_mode=delete might
     ** delete the file out from under us.
     */
-    sqlite3OsClose(pPager->jfd);
+    if( pPager->safeJrnlHandle==0 
+     || (pPager->journalMode!=PAGER_JOURNALMODE_TRUNCATE 
+      && pPager->journalMode!=PAGER_JOURNALMODE_PERSIST)
+    ){
+      sqlite3OsClose(pPager->jfd);
+    }
+
     sqlite3BitvecDestroy(pPager->pInJournal);
     pPager->pInJournal = 0;
     releaseAllSavepoints(pPager);
@@ -3089,6 +3096,7 @@ int sqlite3PagerClose(Pager *pPager){
   sqlite3BeginBenignMalloc();
   pPager->errCode = 0;
   pPager->exclusiveMode = 0;
+  pPager->safeJrnlHandle = 0;
 #ifndef SQLITE_OMIT_WAL
   sqlite3WalClose(pPager->pWal,
     (pPager->noSync ? 0 : pPager->sync_flags), 
@@ -3908,17 +3916,22 @@ int sqlite3PagerOpen(
 */
 static int hasHotJournal(Pager *pPager, int *pExists){
   sqlite3_vfs * const pVfs = pPager->pVfs;
-  int rc;                       /* Return code */
-  int exists;                   /* True if a journal file is present */
+  int rc = SQLITE_OK;           /* Return code */
+  int exists = 1;               /* True if a journal file is present */
+  int jrnlOpen = !!isOpen(pPager->jfd);
 
   assert( pPager!=0 );
   assert( pPager->useJournal );
   assert( isOpen(pPager->fd) );
-  assert( !isOpen(pPager->jfd) );
   assert( pPager->state <= PAGER_SHARED );
+  assert( jrnlOpen==0
+       || sqlite3OsDeviceCharacteristics(pPager->jfd)&SQLITE_IOCAP_SAFE_DELETE
+  );
 
   *pExists = 0;
-  rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+  if( !jrnlOpen ){
+    rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+  }
   if( rc==SQLITE_OK && exists ){
     int locked;                 /* True if some process holds a RESERVED lock */
 
@@ -3956,15 +3969,19 @@ static int hasHotJournal(Pager *pPager, int *pExists){
           ** If there is, then we consider this journal to be hot. If not, 
           ** it can be ignored.
           */
-          int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
-          rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
+          if( !jrnlOpen ){
+            int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
+            rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
+          }
           if( rc==SQLITE_OK ){
             u8 first = 0;
             rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
             if( rc==SQLITE_IOERR_SHORT_READ ){
               rc = SQLITE_OK;
             }
-            sqlite3OsClose(pPager->jfd);
+            if( !jrnlOpen ){
+              sqlite3OsClose(pPager->jfd);
+            }
             *pExists = (first!=0);
           }else if( rc==SQLITE_CANTOPEN ){
             /* If we cannot open the rollback journal file in order to see if
@@ -4490,6 +4507,10 @@ static int pager_open_journal(Pager *pPager){
       );
 #else
       rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
+      if( rc==SQLITE_OK ){
+        int iDc = sqlite3OsDeviceCharacteristics(pPager->jfd);
+        pPager->safeJrnlHandle = (iDc&SQLITE_IOCAP_SAFE_DELETE)!=0;
+      }
 #endif
     }
     assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
index 28778f7fc0041d7dee6dcce8c262b3917eec7d05..093d5b3e7168251dea861509d1919ef41aad489e 100644 (file)
@@ -508,6 +508,7 @@ int sqlite3_exec(
 #define SQLITE_IOCAP_ATOMIC64K       0x00000100
 #define SQLITE_IOCAP_SAFE_APPEND     0x00000200
 #define SQLITE_IOCAP_SEQUENTIAL      0x00000400
+#define SQLITE_IOCAP_SAFE_DELETE     0x00000800
 
 /*
 ** CAPI3REF: File Locking Levels
index e94bef7d2949d482595de1b50a8cf01e2ccf8f65..8a0095d87d5c8b63a3a2f7b0f9e6b69e2723f56a 100644 (file)
@@ -58,6 +58,9 @@ struct Testvfs {
   int iIoerrCnt;
   int ioerr;
   int nIoerrFail;
+
+  int iDevchar;
+  int iSectorsize;
 };
 
 /*
@@ -77,7 +80,10 @@ struct Testvfs {
 #define TESTVFS_OPEN_MASK       0x00000100
 #define TESTVFS_SYNC_MASK       0x00000200
 #define TESTVFS_DELETE_MASK     0x00000400
-#define TESTVFS_ALL_MASK        0x000007FF
+#define TESTVFS_CLOSE_MASK      0x00000800
+#define TESTVFS_WRITE_MASK      0x00001000
+#define TESTVFS_TRUNCATE_MASK   0x00002000
+#define TESTVFS_ALL_MASK        0x00003FFF
 
 
 #define TESTVFS_MAX_PAGES 256
@@ -245,15 +251,23 @@ static void tvfsExecTcl(
 ** Close an tvfs-file.
 */
 static int tvfsClose(sqlite3_file *pFile){
-  TestvfsFile *p = (TestvfsFile *)pFile;
-  if( p->pShmId ){
-    Tcl_DecrRefCount(p->pShmId);
-    p->pShmId = 0;
+  TestvfsFile *pFd = (TestvfsFile *)pFile;
+  Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+  if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
+    tvfsExecTcl(p, "xClose", 
+        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+    );
+  }
+
+  if( pFd->pShmId ){
+    Tcl_DecrRefCount(pFd->pShmId);
+    pFd->pShmId = 0;
   }
   if( pFile->pMethods ){
     ckfree((char *)pFile->pMethods);
   }
-  return sqlite3OsClose(p->pReal);
+  return sqlite3OsClose(pFd->pReal);
 }
 
 /*
@@ -278,16 +292,42 @@ static int tvfsWrite(
   int iAmt, 
   sqlite_int64 iOfst
 ){
-  TestvfsFile *p = (TestvfsFile *)pFile;
-  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+  int rc = SQLITE_OK;
+  TestvfsFile *pFd = (TestvfsFile *)pFile;
+  Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+  if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
+    tvfsExecTcl(p, "xWrite", 
+        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+    );
+    tvfsResultCode(p, &rc);
+  }
+  
+  if( rc==SQLITE_OK ){
+    rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
+  }
+  return rc;
 }
 
 /*
 ** Truncate an tvfs-file.
 */
 static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
-  TestvfsFile *p = (TestvfsFile *)pFile;
-  return sqlite3OsTruncate(p->pReal, size);
+  int rc = SQLITE_OK;
+  TestvfsFile *pFd = (TestvfsFile *)pFile;
+  Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+  if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
+    tvfsExecTcl(p, "xTruncate", 
+        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+    );
+    tvfsResultCode(p, &rc);
+  }
+  
+  if( rc==SQLITE_OK ){
+    rc = sqlite3OsTruncate(pFd->pReal, size);
+  }
+  return rc;
 }
 
 /*
@@ -376,16 +416,24 @@ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
 ** Return the sector-size in bytes for an tvfs-file.
 */
 static int tvfsSectorSize(sqlite3_file *pFile){
-  TestvfsFile *p = (TestvfsFile *)pFile;
-  return sqlite3OsSectorSize(p->pReal);
+  TestvfsFile *pFd = (TestvfsFile *)pFile;
+  Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+  if( p->iSectorsize>=0 ){
+    return p->iSectorsize;
+  }
+  return sqlite3OsSectorSize(pFd->pReal);
 }
 
 /*
 ** Return the device characteristic flags supported by an tvfs-file.
 */
 static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
-  TestvfsFile *p = (TestvfsFile *)pFile;
-  return sqlite3OsDeviceCharacteristics(p->pReal);
+  TestvfsFile *pFd = (TestvfsFile *)pFile;
+  Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+  if( p->iDevchar>=0 ){
+    return p->iDevchar;
+  }
+  return sqlite3OsDeviceCharacteristics(pFd->pReal);
 }
 
 /*
@@ -782,25 +830,37 @@ static int testvfs_obj_cmd(
 ){
   Testvfs *p = (Testvfs *)cd;
 
-  static const char *CMD_strs[] = { 
-    "shm",   "delete",   "filter",   "ioerr",   "script", 0 
-  };
   enum DB_enum { 
-    CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT
+    CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, 
+    CMD_DEVCHAR, CMD_SECTORSIZE
+  };
+  struct TestvfsSubcmd {
+    char *zName;
+    enum DB_enum eCmd;
+  } aSubcmd[] = {
+    { "shm",        CMD_SHM        },
+    { "delete",     CMD_DELETE     },
+    { "filter",     CMD_FILTER     },
+    { "ioerr",      CMD_IOERR      },
+    { "script",     CMD_SCRIPT     },
+    { "devchar",    CMD_DEVCHAR    },
+    { "sectorsize", CMD_SECTORSIZE },
+    { 0, 0 }
   };
-
   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) ){
+  if( Tcl_GetIndexFromObjStruct(
+        interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) 
+  ){
     return TCL_ERROR;
   }
   Tcl_ResetResult(interp);
 
-  switch( (enum DB_enum)i ){
+  switch( aSubcmd[i].eCmd ){
     case CMD_SHM: {
       Tcl_Obj *pObj;
       int i;
@@ -857,7 +917,10 @@ static int testvfs_obj_cmd(
         { "xShmMap",     TESTVFS_SHMMAP_MASK },
         { "xSync",       TESTVFS_SYNC_MASK },
         { "xDelete",     TESTVFS_DELETE_MASK },
+        { "xWrite",      TESTVFS_WRITE_MASK },
+        { "xTruncate",   TESTVFS_TRUNCATE_MASK },
         { "xOpen",       TESTVFS_OPEN_MASK },
+        { "xClose",      TESTVFS_CLOSE_MASK },
       };
       Tcl_Obj **apElem = 0;
       int nElem = 0;
@@ -948,6 +1011,89 @@ static int testvfs_obj_cmd(
       Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
       break;
     }
+
+    case CMD_DEVCHAR: {
+      struct DeviceFlag {
+        char *zName;
+        int iValue;
+      } aFlag[] = {
+        { "default",     -1 },
+        { "atomic",      SQLITE_IOCAP_ATOMIC      },
+        { "atomic512",   SQLITE_IOCAP_ATOMIC512   },
+        { "atomic1k",    SQLITE_IOCAP_ATOMIC1K    },
+        { "atomic2k",    SQLITE_IOCAP_ATOMIC2K    },
+        { "atomic4k",    SQLITE_IOCAP_ATOMIC4K    },
+        { "atomic8k",    SQLITE_IOCAP_ATOMIC8K    },
+        { "atomic16k",   SQLITE_IOCAP_ATOMIC16K   },
+        { "atomic32k",   SQLITE_IOCAP_ATOMIC32K   },
+        { "atomic64k",   SQLITE_IOCAP_ATOMIC64K   },
+        { "sequential",  SQLITE_IOCAP_SEQUENTIAL  },
+        { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
+        { "safe_delete", SQLITE_IOCAP_SAFE_DELETE },
+        { 0, 0 }
+      };
+      Tcl_Obj *pRet;
+      int iFlag;
+
+      if( objc>3 ){
+        Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
+        return TCL_ERROR;
+      }
+      if( objc==3 ){
+        int j;
+        int iNew = 0;
+        Tcl_Obj **flags = 0;
+        int nFlags = 0;
+
+        if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
+          return TCL_ERROR;
+        }
+
+        for(j=0; j<nFlags; j++){
+          int idx = 0;
+          if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag, 
+                sizeof(aFlag[0]), "flag", 0, &idx) 
+          ){
+            return TCL_ERROR;
+          }
+          if( aFlag[idx].iValue<0 && nFlags>1 ){
+            Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
+            return TCL_ERROR;
+          }
+          iNew |= aFlag[idx].iValue;
+        }
+
+        p->iDevchar = iNew;
+      }
+
+      pRet = Tcl_NewObj();
+      for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
+        if( p->iDevchar & aFlag[iFlag].iValue ){
+          Tcl_ListObjAppendElement(
+              interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
+          );
+        }
+      }
+      Tcl_SetObjResult(interp, pRet);
+
+      break;
+    }
+
+    case CMD_SECTORSIZE: {
+      if( objc>3 ){
+        Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
+        return TCL_ERROR;
+      }
+      if( objc==3 ){
+        int iNew = 0;
+        if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
+          return TCL_ERROR;
+        }
+        p->iSectorsize = iNew;
+      }
+      Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
+      break;
+    }
   }
 
   return TCL_OK;
@@ -1067,6 +1213,8 @@ static int testvfs_cmd(
   nByte = sizeof(Testvfs) + strlen(zVfs)+1;
   p = (Testvfs *)ckalloc(nByte);
   memset(p, 0, nByte);
+  p->iDevchar = -1;
+  p->iSectorsize = -1;
 
   /* 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
diff --git a/test/journal2.test b/test/journal2.test
new file mode 100644 (file)
index 0000000..8104299
--- /dev/null
@@ -0,0 +1,188 @@
+# 2010 June 16
+#
+# 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+db close
+
+set a_string_counter 1
+proc a_string {n} {
+  global a_string_counter
+  incr a_string_counter
+  string range [string repeat "${a_string_counter}." $n] 1 $n
+}
+
+# Create a [testvfs] and install it as the default VFS. Set the device
+# characteristics flags to "SAFE_DELETE".
+#
+testvfs tvfs -default 1
+tvfs devchar safe_delete
+
+# Set up a hook so that each time a journal file is opened, closed or
+# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
+# segment of the journal file-name (i.e. "test.db-journal") are appended to
+# global list variable $::oplog.
+#
+tvfs filter {xOpen xClose xDelete}
+tvfs script journal_op_catcher
+
+proc journal_op_catcher {method filename args} {
+
+  # If global variable ::tvfs_error_on_write is defined, then return an
+  # IO error to every attempt to modify the file-system. Otherwise, return
+  # SQLITE_OK.
+  #
+  if {[info exists ::tvfs_error_on_write]} {
+    if {$method == "xDelete" || $method == "xWrite" || $method == "xTruncate"} {
+      return SQLITE_IOERR 
+    }
+    return SQLITE_OK
+  }
+
+  if {[string match *journal* $filename]==0} return
+
+  set f [file tail $filename]
+  lappend ::oplog $method $f
+
+  if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 }
+  switch -- $method {
+    xOpen {
+      incr ::open_journals($f) +1
+    }
+    xClose {
+      incr ::open_journals($f) -1
+    }
+    xDelete {
+      if {$::open_journals($f)>0} { return SQLITE_IOERR }
+    }
+  }
+
+  return
+}
+
+
+do_test journal2-1.1 {
+  set ::oplog [list]
+  sqlite3 db test.db
+  execsql { CREATE TABLE t1(a, b) }
+  set ::oplog
+} {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal}
+do_test journal2-1.2 {
+  set ::oplog [list]
+  execsql { 
+    PRAGMA journal_mode = truncate;
+    INSERT INTO t1 VALUES(1, 2);
+  }
+  set ::oplog
+} {xOpen test.db-journal}
+do_test journal2-1.3 {
+  set ::oplog [list]
+  execsql { INSERT INTO t1 VALUES(3, 4) }
+  set ::oplog
+} {}
+do_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4}
+
+# Add a second connection. This connection attempts to commit data in
+# journal_mode=DELETE mode. When it tries to delete the journal file,
+# the VFS layer returns an IO error.
+#
+do_test journal2-1.5 {
+  set ::oplog [list]
+  sqlite3 db2 test.db
+  execsql  { PRAGMA journal_mode = delete } db2
+  catchsql { INSERT INTO t1 VALUES(5, 6)  } db2
+} {1 {disk I/O error}}
+do_test journal2-1.6 { file exists test.db-journal } 1
+do_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4}
+do_test journal2-1.8 {
+  execsql { PRAGMA journal_mode = truncate } db2
+  execsql { INSERT INTO t1 VALUES(5, 6)  } db2
+} {}
+do_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6}
+
+# Grow the database until it is reasonably large. Then, from a 
+# journal_mode=DELETE connection, attempt to commit a large transaction (one
+# that involves upgrading to an exclusive lock and writing the database
+# before the transaction is committed).
+#
+do_test journal2-1.10 {
+  db2 close
+  db func a_string a_string
+  execsql {
+    CREATE TABLE t2(a UNIQUE, b UNIQUE);
+    INSERT INTO t2 VALUES(a_string(200), a_string(300));
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  2
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  4
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  --  8
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 16
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 32
+    INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2;  -- 64
+  }
+  file size test.db-journal
+} {0}
+do_test journal2-1.11 {
+  set sz [expr [file size test.db] / 1024]
+  expr {$sz>120 && $sz<200}
+} 1
+
+do_test journal2-1.12 {
+  sqlite3 db2 test.db
+  execsql {
+    PRAGMA cache_size = 10;
+    BEGIN;
+      INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2;  -- 128
+  } db2
+} {}
+do_test journal2-1.13 {
+  tvfs filter {xOpen xClose xDelete xWrite xTruncate}
+  set ::tvfs_error_on_write 1
+  catchsql { COMMIT } db2
+} {1 {disk I/O error}}
+db2 close
+unset ::tvfs_error_on_write
+file copy -force test.db testX.db
+
+do_test journal2-1.14 { file exists test.db-journal } 1
+do_test journal2-1.15 {
+  execsql {
+    SELECT count(*) FROM t2;
+    PRAGMA integrity_check;
+  }
+} {64 ok}
+
+# This block checks that in the test case above, connection [db2] really
+# did begin writing to the database file before it hit IO errors. If
+# this is true, then the copy of the database file made before [db]
+# rolled back the hot journal should fail the integrity-check.
+#
+do_test journal2-1.16 {
+  set sz [expr [file size testX.db] / 1024]
+  expr {$sz>240 && $sz<400}
+} 1
+do_test journal2-1.17 {
+  expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"}
+} {1}
+do_test journal2-1.20 {
+  sqlite3 db2 testX.db
+  expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"}
+} {0}
+do_test journal2-1.21 {
+  db2 close
+} {}
+
+db close
+tvfs delete
+finish_test
+