/ Check-in [bdf5cb8d]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add tests to simulate power-failure on devices that support IOCAP_SEQUENTIAL or IOCAP_SAFE_APPEND. (CVS 4284)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bdf5cb8d25d93d48220ce46acad2ccf967a87843
User & Date: danielk1977 2007-08-24 08:15:54
Context
2007-08-24
11:43
Remove unnecessary sqlite3MallocDisallow() that was preventing win32 from running. (CVS 4285) check-in: eb6c98fc user: drh tags: trunk
08:15
Add tests to simulate power-failure on devices that support IOCAP_SEQUENTIAL or IOCAP_SAFE_APPEND. (CVS 4284) check-in: bdf5cb8d user: danielk1977 tags: trunk
04:15
Bug fix in the memory leak trace output. (CVS 4283) check-in: a1b495c2 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/journal.c.

     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   **
    13         -** @(#) $Id: journal.c,v 1.2 2007/08/23 08:06:45 danielk1977 Exp $
           13  +** @(#) $Id: journal.c,v 1.3 2007/08/24 08:15:54 danielk1977 Exp $
    14     14   */
    15     15   
    16     16   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
    17     17   
    18     18   /*
    19     19   ** This file implements a special kind of sqlite3_file object used
    20     20   ** by SQLite to create journal files if the atomic-write optimization
................................................................................
   215    215     p->pMethod = &JournalFileMethods;
   216    216     p->nBuf = nBuf;
   217    217     p->flags = flags;
   218    218     p->zJournal = zName;
   219    219     p->pVfs = pVfs;
   220    220     return SQLITE_OK;
   221    221   }
          222  +
          223  +/*
          224  +** If the argument p points to a JournalFile structure, and the underlying
          225  +** file has not yet been created, create it now.
          226  +*/
          227  +int sqlite3JournalCreate(sqlite3_file *p){
          228  +  if( p->pMethods!=&JournalFileMethods ){
          229  +    return SQLITE_OK;
          230  +  }
          231  +  return createFile((JournalFile *)p);
          232  +}
   222    233   
   223    234   /* 
   224    235   ** Return the number of bytes required to store a JournalFile that uses vfs
   225    236   ** pVfs to create the underlying on-disk files.
   226    237   */
   227    238   int sqlite3JournalSize(sqlite3_vfs *pVfs){
   228    239     return (pVfs->szOsFile+sizeof(JournalFile));
   229    240   }
   230    241   #endif
   231    242   

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.372 2007/08/24 03:51:34 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.373 2007/08/24 08:15:54 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include <assert.h>
    26     26   #include <string.h>
    27     27   
    28     28   /*
................................................................................
  4188   4188     pagerEnter(pPager);
  4189   4189   
  4190   4190     /* If this is an in-memory db, or no pages have been written to, or this
  4191   4191     ** function has already been called, it is a no-op.
  4192   4192     */
  4193   4193     if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
  4194   4194       PgHdr *pPg;
  4195         -    assert( pPager->journalOpen );
  4196   4195   
  4197   4196   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
  4198   4197       /* The atomic-write optimization can be used if all of the
  4199   4198       ** following are true:
  4200   4199       **
  4201   4200       **    + The file-system supports the atomic-write property for
  4202   4201       **      blocks of size page-size, and
  4203   4202       **    + This commit is not part of a multi-file transaction, and
  4204   4203       **    + Exactly one page has been modified and store in the journal file.
  4205   4204       **
  4206   4205       ** If the optimization can be used, then the journal file will never
  4207   4206       ** be created for this transaction.
  4208   4207       */
  4209         -    if( !zMaster && pPager->journalOff==jrnlBufferSize(pPager) && nTrunc==0
  4210         -      && (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
  4211         -    ){
         4208  +    int useAtomicWrite = (
         4209  +        !zMaster && 
         4210  +        pPager->journalOff==jrnlBufferSize(pPager) && 
         4211  +        nTrunc==0 && 
         4212  +        (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
         4213  +    );
         4214  +    if( useAtomicWrite ){
  4212   4215         /* Update the nRec field in the journal file. */
  4213   4216         int offset = pPager->journalHdr + sizeof(aJournalMagic);
  4214   4217         assert(pPager->nRec==1);
  4215   4218         rc = write32bits(pPager->jfd, offset, pPager->nRec);
  4216   4219   
  4217   4220         /* Update the db file change counter. The following call will modify
  4218   4221         ** the in-memory representation of page 1 to include the updated
  4219   4222         ** change counter and then write page 1 directly to the database
  4220   4223         ** file. Because of the atomic-write property of the host file-system, 
  4221   4224         ** this is safe.
  4222   4225         */
  4223   4226         rc = pager_incr_changecounter(pPager, 1);
  4224         -    }else 
         4227  +    }else{
         4228  +      rc = sqlite3JournalCreate(pPager->jfd);
         4229  +      if( rc!=SQLITE_OK ) goto sync_exit;
         4230  +    }
         4231  +
         4232  +    if( !useAtomicWrite )
  4225   4233   #endif
  4226   4234   
  4227   4235       /* If a master journal file name has already been written to the
  4228   4236       ** journal file, then no sync is required. This happens when it is
  4229   4237       ** written, then the process fails to upgrade from a RESERVED to an
  4230   4238       ** EXCLUSIVE lock. The next time the process tries to commit the
  4231   4239       ** transaction the m-j name will have already been written.
  4232   4240       */
  4233   4241       if( !pPager->setMaster ){
         4242  +      assert( pPager->journalOpen );
  4234   4243         rc = pager_incr_changecounter(pPager, 0);
  4235   4244         if( rc!=SQLITE_OK ) goto sync_exit;
  4236   4245   #ifndef SQLITE_OMIT_AUTOVACUUM
  4237   4246         if( nTrunc!=0 ){
  4238   4247           /* If this transaction has made the database smaller, then all pages
  4239   4248           ** being discarded by the truncation must be written to the journal
  4240   4249           ** file.

Changes to src/sqliteInt.h.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.598 2007/08/23 02:47:53 drh Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.599 2007/08/24 08:15:54 danielk1977 Exp $
    15     15   */
    16     16   #ifndef _SQLITEINT_H_
    17     17   #define _SQLITEINT_H_
    18     18   #include "sqliteLimit.h"
    19     19   
    20     20   
    21     21   #if defined(SQLITE_TCL) || defined(TCLSH)
................................................................................
  1863   1863   int sqlite3Reprepare(Vdbe*);
  1864   1864   void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
  1865   1865   CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
  1866   1866   
  1867   1867   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
  1868   1868     int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
  1869   1869     int sqlite3JournalSize(sqlite3_vfs *);
         1870  +  int sqlite3JournalCreate(sqlite3_file *);
  1870   1871   #else
  1871   1872     #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
  1872   1873   #endif
  1873   1874   
  1874   1875   #if SQLITE_MAX_EXPR_DEPTH>0
  1875   1876     void sqlite3ExprSetHeight(Expr *);
  1876   1877     int sqlite3SelectExprHeight(Select *);

Changes to src/test6.c.

    10     10   **
    11     11   ******************************************************************************
    12     12   **
    13     13   ** This file contains code that modified the OS layer in order to simulate
    14     14   ** the effect on the database file of an OS crash or power failure.  This
    15     15   ** is used to test the ability of SQLite to recover from those situations.
    16     16   */
    17         -#if SQLITE_TEST          /* This file is used for the testing only */
           17  +#if SQLITE_TEST          /* This file is used for testing only */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   
    21     21   #ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */
    22     22   
    23     23   /* #define TRACE_CRASHTEST */
    24     24   
................................................................................
   158    158   /*
   159    159   ** Flush the write-list as if xSync() had been called on file handle
   160    160   ** pFile. If isCrash is true, simulate a crash.
   161    161   */
   162    162   static int writeListSync(CrashFile *pFile, int isCrash){
   163    163     int rc = SQLITE_OK;
   164    164     int iDc = g.iDeviceCharacteristics;
   165         -  i64 iSize;
   166    165   
   167    166     WriteBuffer *pWrite;
   168    167     WriteBuffer **ppPtr;
   169    168   
   170         -  /* Set pFinal to point to the last element of the write-list that
   171         -  ** is associated with file handle pFile.
          169  +  /* If this is not a crash simulation, set pFinal to point to the 
          170  +  ** last element of the write-list that is associated with file handle
          171  +  ** pFile.
          172  +  **
          173  +  ** If this is a crash simulation, set pFinal to an arbitrarily selected
          174  +  ** element of the write-list.
   172    175     */
   173    176     WriteBuffer *pFinal = 0;
   174    177     if( !isCrash ){
   175    178       for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
   176    179         if( pWrite->pFile==pFile ){
   177    180           pFinal = pWrite;
   178    181         }
   179    182       }
          183  +  }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
          184  +    int nWrite = 0;
          185  +    int iFinal;
          186  +    for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
          187  +    sqlite3Randomness(sizeof(int), &iFinal);
          188  +    iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
          189  +    for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
          190  +    pFinal = pWrite;
   180    191     }
   181    192   
   182    193   #ifdef TRACE_CRASHTEST
   183    194     printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
   184    195   #endif
   185    196   
   186         -  sqlite3OsFileSize((sqlite3_file *)pFile, &iSize);
   187         -
   188    197     ppPtr = &g.pWriteList;
   189    198     for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
   190    199       sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
   191    200   
   192    201       /* (eAction==1)      -> write block out normally,
   193    202       ** (eAction==2)      -> do nothing,
   194    203       ** (eAction==3)      -> trash sectors.
................................................................................
   199    208         if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
   200    209           eAction = 1;
   201    210         }
   202    211       }else{
   203    212         char random;
   204    213         sqlite3Randomness(1, &random);
   205    214   
   206         -      if( iDc&SQLITE_IOCAP_ATOMIC || pWrite->zBuf==0 ){
          215  +      /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag 
          216  +      ** is set or this is an OsTruncate(), not an Oswrite().
          217  +      */
          218  +      if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
   207    219           random &= 0x01;
   208    220         }
          221  +
          222  +      /* If IOCAP_SEQUENTIAL is set and this is not the final entry
          223  +      ** in the truncated write-list, always select option 1 (write
          224  +      ** out correctly).
          225  +      */
          226  +      if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
          227  +        random = 0;
          228  +      }
          229  +
          230  +      /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
          231  +      ** an append (first byte of the written region is 1 byte past the
          232  +      ** current EOF), always select option 1 (write out correctly).
          233  +      */
          234  +      if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
          235  +        i64 iSize;
          236  +        sqlite3OsFileSize(pRealFile, &iSize);
          237  +        if( iSize==pWrite->iOffset ){
          238  +          random = 0;
          239  +        }
          240  +      }
   209    241   
   210    242         if( (random&0x06)==0x06 ){
   211    243           eAction = 3;
   212    244         }else{
   213    245           eAction = ((random&0x01)?2:1);
   214    246         }
   215    247       }
................................................................................
   222    254             );
   223    255           }else{
   224    256             rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
   225    257           }
   226    258           *ppPtr = pWrite->pNext;
   227    259   #ifdef TRACE_CRASHTEST
   228    260           if( isCrash ){
   229         -          printf("Writing %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);
          261  +          printf("Writing %d bytes @ %d (%s)\n", 
          262  +            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
          263  +          );
   230    264           }
   231    265   #endif
   232    266           sqlite3_free(pWrite);
   233    267           break;
   234    268         }
   235    269         case 2: {               /* Do nothing */
   236    270           ppPtr = &pWrite->pNext;
   237    271   #ifdef TRACE_CRASHTEST
   238    272           if( isCrash ){
   239         -          printf("Omiting %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);
          273  +          printf("Omiting %d bytes @ %d (%s)\n", 
          274  +            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
          275  +          );
   240    276           }
   241    277   #endif
   242    278           break;
   243    279         }
   244    280         case 3: {               /* Trash sectors */
   245    281           u8 *zGarbage;
   246    282           int iFirst = (pWrite->iOffset/g.iSectorSize);
   247    283           int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;
   248    284   
   249    285           assert(pWrite->zBuf);
   250    286   
   251    287   #ifdef TRACE_CRASHTEST
   252         -        printf("Trashing %d sectors @ sector %d\n", 1+iLast-iFirst, iFirst);
          288  +        printf("Trashing %d sectors @ sector %d (%s)\n", 
          289  +            1+iLast-iFirst, iFirst, pWrite->pFile->zName
          290  +        );
   253    291   #endif
   254    292   
   255    293           zGarbage = sqlite3_malloc(g.iSectorSize);
   256    294           if( zGarbage ){
   257    295             sqlite3_int64 i;
   258    296             for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
   259    297               sqlite3Randomness(g.iSectorSize, zGarbage); 
................................................................................
   485    523   ** The caller will have allocated pVfs->szOsFile bytes of space
   486    524   ** at pFile. This file uses this space for the CrashFile structure
   487    525   ** and allocates space for the "real" file structure using 
   488    526   ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
   489    527   ** equal or greater than sizeof(CrashFile).
   490    528   */
   491    529   static int cfOpen(
   492         -  void *pAppData,
          530  +  sqlite3_vfs *pCfVfs,
   493    531     const char *zName,
   494    532     sqlite3_file *pFile,
   495    533     int flags,
   496    534     int *pOutFlags
   497    535   ){
   498         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
          536  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
   499    537     int rc;
   500    538     CrashFile *pWrapper = (CrashFile *)pFile;
   501    539     sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
   502    540   
   503    541     memset(pWrapper, 0, sizeof(CrashFile));
   504    542     rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);
   505    543   
................................................................................
   523    561     }
   524    562     if( rc!=SQLITE_OK && pWrapper->pMethod ){
   525    563       sqlite3OsClose(pFile);
   526    564     }
   527    565     return rc;
   528    566   }
   529    567   
   530         -static int cfDelete(void *pAppData, const char *zPath, int dirSync){
   531         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   532         -  return pVfs->xDelete(pVfs->pAppData, zPath, dirSync);
          568  +static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
          569  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          570  +  return pVfs->xDelete(pVfs, zPath, dirSync);
          571  +}
          572  +static int cfAccess(sqlite3_vfs *pCfVfs, const char *zPath, int flags){
          573  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          574  +  return pVfs->xAccess(pVfs, zPath, flags);
          575  +}
          576  +static int cfGetTempName(sqlite3_vfs *pCfVfs, char *zBufOut){
          577  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          578  +  return pVfs->xGetTempName(pVfs, zBufOut);
          579  +}
          580  +static int cfFullPathname(sqlite3_vfs *pCfVfs, const char *zPath, char *zPathOut){
          581  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          582  +  return pVfs->xFullPathname(pVfs, zPath, zPathOut);
          583  +}
          584  +static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
          585  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          586  +  return pVfs->xDlOpen(pVfs, zPath);
   533    587   }
   534         -static int cfAccess(void *pAppData, const char *zPath, int flags){
   535         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   536         -  return pVfs->xAccess(pVfs->pAppData, zPath, flags);
          588  +static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
          589  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          590  +  return pVfs->xRandomness(pVfs, nByte, zBufOut);
   537    591   }
   538         -static int cfGetTempName(void *pAppData, char *zBufOut){
   539         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   540         -  return pVfs->xGetTempName(pVfs->pAppData, zBufOut);
   541         -}
   542         -static int cfFullPathname(void *pAppData, const char *zPath, char *zPathOut){
   543         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   544         -  return pVfs->xFullPathname(pVfs->pAppData, zPath, zPathOut);
          592  +static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
          593  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          594  +  return pVfs->xSleep(pVfs, nMicro);
   545    595   }
   546         -static void *cfDlOpen(void *pAppData, const char *zPath){
   547         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   548         -  return pVfs->xDlOpen(pVfs->pAppData, zPath);
   549         -}
   550         -static int cfRandomness(void *pAppData, int nByte, char *zBufOut){
   551         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   552         -  return pVfs->xRandomness(pVfs->pAppData, nByte, zBufOut);
   553         -}
   554         -static int cfSleep(void *pAppData, int nMicro){
   555         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   556         -  return pVfs->xSleep(pVfs->pAppData, nMicro);
   557         -}
   558         -static int cfCurrentTime(void *pAppData, double *pTimeOut){
   559         -  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
   560         -  return pVfs->xCurrentTime(pVfs->pAppData, pTimeOut);
          596  +static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
          597  +  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
          598  +  return pVfs->xCurrentTime(pVfs, pTimeOut);
   561    599   }
   562    600   
   563    601   static int processDevSymArgs(
   564    602     Tcl_Interp *interp,
   565    603     int objc,
   566    604     Tcl_Obj *CONST objv[],
   567    605     int *piDeviceChar,

Changes to test/crash3.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: crash3.test,v 1.1 2007/08/23 11:07:10 danielk1977 Exp $
           12  +# This file contains tests that verify that SQLite can correctly rollback
           13  +# databases after crashes when using the special IO modes triggered 
           14  +# by device IOCAP flags.
           15  +#
           16  +# $Id: crash3.test,v 1.2 2007/08/24 08:15:54 danielk1977 Exp $
    13     17   
    14     18   set testdir [file dirname $argv0]
    15     19   source $testdir/tester.tcl
    16     20   
    17     21   ifcapable !crashtest {
    18     22     finish_test
    19     23     return
................................................................................
    30     34         }
    31     35         set res
    32     36       } {{$res1} or {$res2}}
    33     37     }]
    34     38     uplevel $script
    35     39   }
    36     40   
           41  +# This block tests crash-recovery when the IOCAP_ATOMIC flags is set.
           42  +#
    37     43   # Each iteration of the following loop sets up the database to contain
    38     44   # the following schema and data:
    39     45   #
    40     46   #    CREATE TABLE abc(a, b, c);
    41     47   #    INSERT INTO abc VALUES(1, 2, 3);
    42     48   #
    43     49   # Then execute the SQL statement, scheduling a crash for part-way through
................................................................................
    91     97       do_test2 crash3-1.$tn.3 {
    92     98         execsql { SELECT * FROM abc }
    93     99       } {1 2 3} $res2
    94    100   
    95    101       incr tn
    96    102     }
    97    103   }
          104  +
          105  +# This block tests both the IOCAP_SEQUENTIAL and IOCAP_SAFE_APPEND flags.
          106  +#
          107  +db close
          108  +file delete -force test.db test.db-journal
          109  +sqlite3 db test.db
          110  +do_test crash3-2.0 {
          111  +  execsql {
          112  +    BEGIN;
          113  +    CREATE TABLE abc(a PRIMARY KEY, b, c);
          114  +    CREATE TABLE def(d PRIMARY KEY, e, f);
          115  +    PRAGMA default_cache_size = 10;
          116  +    INSERT INTO abc VALUES(randstr(10,1000),randstr(10,1000),randstr(10,1000));
          117  +    INSERT INTO abc 
          118  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          119  +    INSERT INTO abc 
          120  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          121  +    INSERT INTO abc 
          122  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          123  +    INSERT INTO abc 
          124  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          125  +    INSERT INTO abc 
          126  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          127  +    INSERT INTO abc 
          128  +      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
          129  +    COMMIT;
          130  +  }
          131  +} {}
          132  +
          133  +set tn 1
          134  +foreach {::crashfile ::delay ::char} {
          135  +  test.db         1 sequential
          136  +  test.db         1 safe_append
          137  +  test.db-journal 1 sequential
          138  +  test.db-journal 1 safe_append
          139  +  test.db-journal 2 safe_append
          140  +  test.db-journal 2 sequential
          141  +  test.db-journal 3 sequential
          142  +  test.db-journal 3 safe_append
          143  +} {
          144  +  for {set ii 0} {$ii < 100} {incr ii} {
          145  +    set ::SQL [subst {
          146  +      SELECT randstr($ii,$ii+10);
          147  +      BEGIN;
          148  +      DELETE FROM abc WHERE random()%5;
          149  +      INSERT INTO abc 
          150  +        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
          151  +        FROM abc
          152  +        WHERE (random()%5)==0;
          153  +      DELETE FROM def WHERE random()%5;
          154  +      INSERT INTO def 
          155  +        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
          156  +        FROM def
          157  +        WHERE (random()%5)==0;
          158  +      COMMIT;
          159  +    }]
          160  +
          161  +    do_test crash3-2.$tn.$ii {
          162  +      crashsql -file $::crashfile -delay $::delay -char $::char $::SQL
          163  +      db close
          164  +      sqlite3 db test.db
          165  +      execsql {PRAGMA integrity_check}
          166  +    } {ok}
          167  +  }
          168  +  incr tn
          169  +}
          170  +
          171  +# The following block tests an interaction between IOCAP_ATOMIC and
          172  +# IOCAP_SEQUENTIAL. At one point, if both flags were set, small
          173  +# journal files that contained only a single page, but were required 
          174  +# for some other reason (i.e. nTrunk) were not being written to
          175  +# disk.
          176  +#
          177  +for {set ii 0} {$ii < 10} {incr ii} {
          178  +  db close
          179  +  file delete -force test.db test.db-journal
          180  +  crashsql -file test.db -char {sequential atomic} {
          181  +    CREATE TABLE abc(a, b, c);
          182  +  }
          183  +  sqlite3 db test.db
          184  +  do_test crash3-3.$ii {
          185  +    execsql {PRAGMA integrity_check}
          186  +  } {ok}
          187  +}
    98    188   
    99    189   finish_test
   100    190