]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add tests to simulate power-failure on devices that support IOCAP_SEQUENTIAL or IOCAP...
authordanielk1977 <danielk1977@noemail.net>
Fri, 24 Aug 2007 08:15:53 +0000 (08:15 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 24 Aug 2007 08:15:53 +0000 (08:15 +0000)
FossilOrigin-Name: bdf5cb8d25d93d48220ce46acad2ccf967a87843

manifest
manifest.uuid
src/journal.c
src/pager.c
src/sqliteInt.h
src/test6.c
test/crash3.test

index 80eca102fab640b7aa92c307894ee13a9b19b967..9a3083125425bd29adc80a8539ed6447c9bc69c1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfix\sin\sthe\smemory\sleak\strace\soutput.\s(CVS\s4283)
-D 2007-08-24T04:15:00
+C Add\stests\sto\ssimulate\spower-failure\son\sdevices\sthat\ssupport\sIOCAP_SEQUENTIAL\sor\sIOCAP_SAFE_APPEND.\s(CVS\s4284)
+D 2007-08-24T08:15:54
 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe
 F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -94,7 +94,7 @@ F src/func.c aa8a3a8db571c46e5197664ddbc2784006ee5525
 F src/hash.c 2f322979071dd2bdba7503b5276d66f028744382
 F src/hash.h 3ad3da76bfb954978d227bf495568b0e6da2c19e
 F src/insert.c 633322aef1799f6604fa805e12488bc628570b0c
-F src/journal.c 03d6b5cc1afe7c5e3cd0af55415f5168eb094398
+F src/journal.c 573911e274738b3fda813d4f0a6e319f8a9ecd36
 F src/legacy.c 7e1b1c57694e49cbadf561e2a7d9cd984dc743b5
 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35
 F src/loadext.c 8b31e2e0e961918fa045515459aee1c122d8c266
@@ -115,7 +115,7 @@ F src/os_unix.c c45b20f868fab1178710ea2f1c8043ce706d3a99
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c 1cb94dd33d38e01de82d77bef107c7f3323463ec
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c 89dfc6a0bd72898d147264517a43bdf35348dd2c
+F src/pager.c a46c807f6c00d8172ea7f17b8bfd2c870d2e9d98
 F src/pager.h 53087c6fb9db01aed17c7fd044662a27507e89b8
 F src/parse.y 2d2ce439dc6184621fb0b86f4fc5aca7f391a590
 F src/pragma.c 9b989506a1b7c8aecd6befb8235e2f57a4aba7e5
@@ -127,7 +127,7 @@ F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb
 F src/sqlite.h.in 09a5256ee80dfc7cb4353739f78e4267be323574
 F src/sqlite3ext.h 9a26028378c288af500d8b94ed079666fed5806b
-F src/sqliteInt.h 951229c727f14f12f1c5555d2ed079bd2201415c
+F src/sqliteInt.h ea7a14a33c0a03479d2d98ce9ac83fe5e7f4bb55
 F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa
 F src/table.c c725e47f6f3092b9a7b569fc58e408e2173ee008
 F src/tclsqlite.c d76af53f45c9e9f7f7d39531fa4c7bee7d0adad6
@@ -136,7 +136,7 @@ F src/test2.c 4f742e99ed1bea5c14692f627bdb59a146f30504
 F src/test3.c a7d011c51d6b2e2a73c43983d5c2b731d69c74d7
 F src/test4.c c2c0f5dc907f1346f5d4b65eb5799f11eb9e4071
 F src/test5.c 3a6a5717a149d7ca2e6d14f5be72cf7555d54dc4
-F src/test6.c de2dbcd67401f00bfa0affc044ba671aa62384a5
+F src/test6.c 37caea087882ecce1cbc6c83f9f1d457bd42b731
 F src/test7.c a9d509d0e9ad214b4772696f49f6e61be26213d1
 F src/test8.c e6a543c8b248efe120ae33a6859fcd55dcf46a96
 F src/test9.c b46c8fe02ac7cca1a7316436d8d38d50c66f4b2f
@@ -227,7 +227,7 @@ F test/corrupt2.test 572f8df0303d0ce63ddad5c5c9101a83a345ae46
 F test/corrupt3.test 263e8bb04e2728df832fddf6973cf54c91db0c32
 F test/crash.test 24020168cc42977a4dd83ff78d2b5eb6577715db
 F test/crash2.test 423c6ec404d15b7d7d0e40aef0a26740cce6075f
-F test/crash3.test caa79052f29ee5cb17ecede2c386251eb932c05f
+F test/crash3.test 96eee2301b000264d355591fe650a9ad5ac01be9
 F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
 F test/createtab.test b562aba1a65be49935fc43a04e90766e39231804
 F test/date.test 4834d022b2fa5982cafed20938f7523a7475e4cd
@@ -561,7 +561,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 3a68fcddfa9184e4b310ce0a21312c54b9462ec8
-R 8577d385c536daf1df94ce55e3096cab
-U drh
-Z 81d0ac074a196345faf13b53e0789aed
+P a1b495c28a092c7eb79ffeeb6a217749acb4c12c
+R 9e378d30b0d2403f2a8c2ca36386519e
+U danielk1977
+Z cedf0a8ddde877cdc3adcde56bbd62a1
index 2342ce477f270ba728c520d93823473013cd4bdc..885530dd5a6745e168addcb1e31029073ebb76b1 100644 (file)
@@ -1 +1 @@
-a1b495c28a092c7eb79ffeeb6a217749acb4c12c
\ No newline at end of file
+bdf5cb8d25d93d48220ce46acad2ccf967a87843
\ No newline at end of file
index dc4c6ee55892076f9b22a73c825b0cfb7017def9..0006f56c449d18fb0ab97a0545a38b7d72a86f08 100644 (file)
@@ -10,7 +10,7 @@
 **
 *************************************************************************
 **
-** @(#) $Id: journal.c,v 1.2 2007/08/23 08:06:45 danielk1977 Exp $
+** @(#) $Id: journal.c,v 1.3 2007/08/24 08:15:54 danielk1977 Exp $
 */
 
 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
@@ -220,6 +220,17 @@ int sqlite3JournalOpen(
   return SQLITE_OK;
 }
 
+/*
+** If the argument p points to a JournalFile structure, and the underlying
+** file has not yet been created, create it now.
+*/
+int sqlite3JournalCreate(sqlite3_file *p){
+  if( p->pMethods!=&JournalFileMethods ){
+    return SQLITE_OK;
+  }
+  return createFile((JournalFile *)p);
+}
+
 /* 
 ** Return the number of bytes required to store a JournalFile that uses vfs
 ** pVfs to create the underlying on-disk files.
index 2c5c9cab6ca7f210824f722a7f1aeeed0b4505ce..09ad423f1a80a41f20153f99100ef6d595c15f0b 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.372 2007/08/24 03:51:34 drh Exp $
+** @(#) $Id: pager.c,v 1.373 2007/08/24 08:15:54 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -4192,7 +4192,6 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
   */
   if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
     PgHdr *pPg;
-    assert( pPager->journalOpen );
 
 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
     /* The atomic-write optimization can be used if all of the
@@ -4206,9 +4205,13 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
     ** If the optimization can be used, then the journal file will never
     ** be created for this transaction.
     */
-    if( !zMaster && pPager->journalOff==jrnlBufferSize(pPager) && nTrunc==0
-      && (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
-    ){
+    int useAtomicWrite = (
+        !zMaster && 
+        pPager->journalOff==jrnlBufferSize(pPager) && 
+        nTrunc==0 && 
+        (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
+    );
+    if( useAtomicWrite ){
       /* Update the nRec field in the journal file. */
       int offset = pPager->journalHdr + sizeof(aJournalMagic);
       assert(pPager->nRec==1);
@@ -4221,7 +4224,12 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
       ** this is safe.
       */
       rc = pager_incr_changecounter(pPager, 1);
-    }else 
+    }else{
+      rc = sqlite3JournalCreate(pPager->jfd);
+      if( rc!=SQLITE_OK ) goto sync_exit;
+    }
+
+    if( !useAtomicWrite )
 #endif
 
     /* If a master journal file name has already been written to the
@@ -4231,6 +4239,7 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
     ** transaction the m-j name will have already been written.
     */
     if( !pPager->setMaster ){
+      assert( pPager->journalOpen );
       rc = pager_incr_changecounter(pPager, 0);
       if( rc!=SQLITE_OK ) goto sync_exit;
 #ifndef SQLITE_OMIT_AUTOVACUUM
index 29da21f5aa7dea60fa20092ff4425f7a290afe89..171919940c6540cff27eddd471df7ddf846ea755 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.598 2007/08/23 02:47:53 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.599 2007/08/24 08:15:54 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1867,6 +1867,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
 #ifdef SQLITE_ENABLE_ATOMIC_WRITE
   int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
   int sqlite3JournalSize(sqlite3_vfs *);
+  int sqlite3JournalCreate(sqlite3_file *);
 #else
   #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
 #endif
index 90bdba1f9bc74abf600f8265c80c7dda5f644dcd..f32acab26725ab2a78cd33cd554f17b9a235f22d 100644 (file)
@@ -14,7 +14,7 @@
 ** the effect on the database file of an OS crash or power failure.  This
 ** is used to test the ability of SQLite to recover from those situations.
 */
-#if SQLITE_TEST          /* This file is used for the testing only */
+#if SQLITE_TEST          /* This file is used for testing only */
 #include "sqliteInt.h"
 #include "tcl.h"
 
@@ -162,13 +162,16 @@ static int sqlite3CrashTestEnable = 0;
 static int writeListSync(CrashFile *pFile, int isCrash){
   int rc = SQLITE_OK;
   int iDc = g.iDeviceCharacteristics;
-  i64 iSize;
 
   WriteBuffer *pWrite;
   WriteBuffer **ppPtr;
 
-  /* Set pFinal to point to the last element of the write-list that
-  ** is associated with file handle pFile.
+  /* If this is not a crash simulation, set pFinal to point to the 
+  ** last element of the write-list that is associated with file handle
+  ** pFile.
+  **
+  ** If this is a crash simulation, set pFinal to an arbitrarily selected
+  ** element of the write-list.
   */
   WriteBuffer *pFinal = 0;
   if( !isCrash ){
@@ -177,14 +180,20 @@ static int writeListSync(CrashFile *pFile, int isCrash){
         pFinal = pWrite;
       }
     }
+  }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
+    int nWrite = 0;
+    int iFinal;
+    for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
+    sqlite3Randomness(sizeof(int), &iFinal);
+    iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
+    for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
+    pFinal = pWrite;
   }
 
 #ifdef TRACE_CRASHTEST
   printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
 #endif
 
-  sqlite3OsFileSize((sqlite3_file *)pFile, &iSize);
-
   ppPtr = &g.pWriteList;
   for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
     sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
@@ -203,10 +212,33 @@ static int writeListSync(CrashFile *pFile, int isCrash){
       char random;
       sqlite3Randomness(1, &random);
 
-      if( iDc&SQLITE_IOCAP_ATOMIC || pWrite->zBuf==0 ){
+      /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag 
+      ** is set or this is an OsTruncate(), not an Oswrite().
+      */
+      if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
         random &= 0x01;
       }
 
+      /* If IOCAP_SEQUENTIAL is set and this is not the final entry
+      ** in the truncated write-list, always select option 1 (write
+      ** out correctly).
+      */
+      if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
+        random = 0;
+      }
+
+      /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
+      ** an append (first byte of the written region is 1 byte past the
+      ** current EOF), always select option 1 (write out correctly).
+      */
+      if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
+        i64 iSize;
+        sqlite3OsFileSize(pRealFile, &iSize);
+        if( iSize==pWrite->iOffset ){
+          random = 0;
+        }
+      }
+
       if( (random&0x06)==0x06 ){
         eAction = 3;
       }else{
@@ -226,7 +258,9 @@ static int writeListSync(CrashFile *pFile, int isCrash){
         *ppPtr = pWrite->pNext;
 #ifdef TRACE_CRASHTEST
         if( isCrash ){
-          printf("Writing %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);
+          printf("Writing %d bytes @ %d (%s)\n", 
+            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
+          );
         }
 #endif
         sqlite3_free(pWrite);
@@ -236,7 +270,9 @@ static int writeListSync(CrashFile *pFile, int isCrash){
         ppPtr = &pWrite->pNext;
 #ifdef TRACE_CRASHTEST
         if( isCrash ){
-          printf("Omiting %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);
+          printf("Omiting %d bytes @ %d (%s)\n", 
+            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
+          );
         }
 #endif
         break;
@@ -249,7 +285,9 @@ static int writeListSync(CrashFile *pFile, int isCrash){
         assert(pWrite->zBuf);
 
 #ifdef TRACE_CRASHTEST
-        printf("Trashing %d sectors @ sector %d\n", 1+iLast-iFirst, iFirst);
+        printf("Trashing %d sectors @ sector %d (%s)\n", 
+            1+iLast-iFirst, iFirst, pWrite->pFile->zName
+        );
 #endif
 
         zGarbage = sqlite3_malloc(g.iSectorSize);
@@ -489,13 +527,13 @@ struct crashAppData {
 ** equal or greater than sizeof(CrashFile).
 */
 static int cfOpen(
-  void *pAppData,
+  sqlite3_vfs *pCfVfs,
   const char *zName,
   sqlite3_file *pFile,
   int flags,
   int *pOutFlags
 ){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
   int rc;
   CrashFile *pWrapper = (CrashFile *)pFile;
   sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
@@ -527,37 +565,37 @@ static int cfOpen(
   return rc;
 }
 
-static int cfDelete(void *pAppData, const char *zPath, int dirSync){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xDelete(pVfs->pAppData, zPath, dirSync);
+static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xDelete(pVfs, zPath, dirSync);
 }
-static int cfAccess(void *pAppData, const char *zPath, int flags){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xAccess(pVfs->pAppData, zPath, flags);
+static int cfAccess(sqlite3_vfs *pCfVfs, const char *zPath, int flags){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xAccess(pVfs, zPath, flags);
 }
-static int cfGetTempName(void *pAppData, char *zBufOut){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xGetTempName(pVfs->pAppData, zBufOut);
+static int cfGetTempName(sqlite3_vfs *pCfVfs, char *zBufOut){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xGetTempName(pVfs, zBufOut);
 }
-static int cfFullPathname(void *pAppData, const char *zPath, char *zPathOut){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xFullPathname(pVfs->pAppData, zPath, zPathOut);
+static int cfFullPathname(sqlite3_vfs *pCfVfs, const char *zPath, char *zPathOut){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xFullPathname(pVfs, zPath, zPathOut);
 }
-static void *cfDlOpen(void *pAppData, const char *zPath){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xDlOpen(pVfs->pAppData, zPath);
+static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xDlOpen(pVfs, zPath);
 }
-static int cfRandomness(void *pAppData, int nByte, char *zBufOut){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xRandomness(pVfs->pAppData, nByte, zBufOut);
+static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xRandomness(pVfs, nByte, zBufOut);
 }
-static int cfSleep(void *pAppData, int nMicro){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xSleep(pVfs->pAppData, nMicro);
+static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xSleep(pVfs, nMicro);
 }
-static int cfCurrentTime(void *pAppData, double *pTimeOut){
-  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
-  return pVfs->xCurrentTime(pVfs->pAppData, pTimeOut);
+static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
+  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
+  return pVfs->xCurrentTime(pVfs, pTimeOut);
 }
 
 static int processDevSymArgs(
index 35e2e7b66af3640f2a05c7163d7a0ff44b968d23..4dabf5512694bdc4855cf1e4dfc1c148ce299feb 100644 (file)
@@ -9,7 +9,11 @@
 #
 #***********************************************************************
 #
-# $Id: crash3.test,v 1.1 2007/08/23 11:07:10 danielk1977 Exp $
+# This file contains tests that verify that SQLite can correctly rollback
+# databases after crashes when using the special IO modes triggered 
+# by device IOCAP flags.
+#
+# $Id: crash3.test,v 1.2 2007/08/24 08:15:54 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -34,6 +38,8 @@ proc do_test2 {name tcl res1 res2} {
   uplevel $script
 }
 
+# This block tests crash-recovery when the IOCAP_ATOMIC flags is set.
+#
 # Each iteration of the following loop sets up the database to contain
 # the following schema and data:
 #
@@ -96,5 +102,89 @@ foreach {sql res2} [list \
   }
 }
 
+# This block tests both the IOCAP_SEQUENTIAL and IOCAP_SAFE_APPEND flags.
+#
+db close
+file delete -force test.db test.db-journal
+sqlite3 db test.db
+do_test crash3-2.0 {
+  execsql {
+    BEGIN;
+    CREATE TABLE abc(a PRIMARY KEY, b, c);
+    CREATE TABLE def(d PRIMARY KEY, e, f);
+    PRAGMA default_cache_size = 10;
+    INSERT INTO abc VALUES(randstr(10,1000),randstr(10,1000),randstr(10,1000));
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    INSERT INTO abc 
+      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
+    COMMIT;
+  }
+} {}
+
+set tn 1
+foreach {::crashfile ::delay ::char} {
+  test.db         1 sequential
+  test.db         1 safe_append
+  test.db-journal 1 sequential
+  test.db-journal 1 safe_append
+  test.db-journal 2 safe_append
+  test.db-journal 2 sequential
+  test.db-journal 3 sequential
+  test.db-journal 3 safe_append
+} {
+  for {set ii 0} {$ii < 100} {incr ii} {
+    set ::SQL [subst {
+      SELECT randstr($ii,$ii+10);
+      BEGIN;
+      DELETE FROM abc WHERE random()%5;
+      INSERT INTO abc 
+        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
+        FROM abc
+        WHERE (random()%5)==0;
+      DELETE FROM def WHERE random()%5;
+      INSERT INTO def 
+        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
+        FROM def
+        WHERE (random()%5)==0;
+      COMMIT;
+    }]
+
+    do_test crash3-2.$tn.$ii {
+      crashsql -file $::crashfile -delay $::delay -char $::char $::SQL
+      db close
+      sqlite3 db test.db
+      execsql {PRAGMA integrity_check}
+    } {ok}
+  }
+  incr tn
+}
+
+# The following block tests an interaction between IOCAP_ATOMIC and
+# IOCAP_SEQUENTIAL. At one point, if both flags were set, small
+# journal files that contained only a single page, but were required 
+# for some other reason (i.e. nTrunk) were not being written to
+# disk.
+#
+for {set ii 0} {$ii < 10} {incr ii} {
+  db close
+  file delete -force test.db test.db-journal
+  crashsql -file test.db -char {sequential atomic} {
+    CREATE TABLE abc(a, b, c);
+  }
+  sqlite3 db test.db
+  do_test crash3-3.$ii {
+    execsql {PRAGMA integrity_check}
+  } {ok}
+}
+
 finish_test