/ Check-in [cb9302cc]
Login

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

Overview
Comment:Defer opening and writing statement journals until the size reaches a threshold (currently 64KiB).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: cb9302cca423de41305719a49208daa392ec09da
User & Date: drh 2016-03-04 14:43:44
Context
2016-03-04
21:18
Fix an assert() in sqlite3VarintLen(), even though it is impossible to hit in SQLite due to the way sqlite3VarintLen() is used. check-in: 251424c5 user: drh tags: trunk
18:45
Merge changes from trunk. check-in: 5294c977 user: drh tags: analyze-worst-case
16:42
Merge recent enhancements from trunk. Default page size is 4096. Writes to statement journals are avoided. check-in: 456df336 user: drh tags: sessions
14:57
Merge recent enhancements from trunk, and especially the changes that reduce the heap-memory footprint of schemas, and defer opening and writing to statement journals. check-in: 2f0c195c user: drh tags: apple-osx
14:43
Defer opening and writing statement journals until the size reaches a threshold (currently 64KiB). check-in: cb9302cc user: drh tags: trunk
14:23
Update test cases to taken deferred statement-journal opening into account. Closed-Leaf check-in: 5b2fe521 user: drh tags: memjournal-exp
04:01
Change the default cache_size to -2000 (which means 2000*1024 bytes independent of page_size). check-in: 2682e8e4 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/journal.c.

    20     20   ** be used to service read() and write() requests. The actual file
    21     21   ** on disk is not created or populated until either:
    22     22   **
    23     23   **   1) The in-memory representation grows too large for the allocated 
    24     24   **      buffer, or
    25     25   **   2) The sqlite3JournalCreate() function is called.
    26     26   */
           27  +#if 0 
    27     28   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
    28     29   #include "sqliteInt.h"
    29     30   
    30     31   
    31     32   /*
    32     33   ** A JournalFile object is a subclass of sqlite3_file used by
    33     34   ** as an open file handle for journal files.
................................................................................
   249    250   /* 
   250    251   ** Return the number of bytes required to store a JournalFile that uses vfs
   251    252   ** pVfs to create the underlying on-disk files.
   252    253   */
   253    254   int sqlite3JournalSize(sqlite3_vfs *pVfs){
   254    255     return (pVfs->szOsFile+sizeof(JournalFile));
   255    256   }
          257  +#endif
   256    258   #endif

Changes to src/memjournal.c.

    17     17   #include "sqliteInt.h"
    18     18   
    19     19   /* Forward references to internal structures */
    20     20   typedef struct MemJournal MemJournal;
    21     21   typedef struct FilePoint FilePoint;
    22     22   typedef struct FileChunk FileChunk;
    23     23   
    24         -/* Space to hold the rollback journal is allocated in increments of
    25         -** this many bytes.
    26         -**
    27         -** The size chosen is a little less than a power of two.  That way,
    28         -** the FileChunk object will have a size that almost exactly fills
    29         -** a power-of-two allocation.  This minimizes wasted space in power-of-two
    30         -** memory allocators.
    31         -*/
    32         -#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
    33         -
    34     24   /*
    35     25   ** The rollback journal is composed of a linked list of these structures.
           26  +**
           27  +** The zChunk array is always at least 8 bytes in size - usually much more.
           28  +** Its actual size is stored in the MemJournal.nChunkSize variable.
    36     29   */
    37     30   struct FileChunk {
    38     31     FileChunk *pNext;               /* Next chunk in the journal */
    39         -  u8 zChunk[JOURNAL_CHUNKSIZE];   /* Content of this chunk */
           32  +  u8 zChunk[8];                   /* Content of this chunk */
    40     33   };
    41     34   
           35  +/*
           36  +** By default, allocate this many bytes of memory for each FileChunk object.
           37  +*/
           38  +#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
           39  +
           40  +/*
           41  +** For chunk size nChunkSize, return the number of bytes that should
           42  +** be allocated for each FileChunk structure.
           43  +*/
           44  +#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
           45  +
    42     46   /*
    43     47   ** An instance of this object serves as a cursor into the rollback journal.
    44     48   ** The cursor can be either for reading or writing.
    45     49   */
    46     50   struct FilePoint {
    47     51     sqlite3_int64 iOffset;          /* Offset from the beginning of the file */
    48     52     FileChunk *pChunk;              /* Specific chunk into which cursor points */
    49     53   };
    50     54   
    51     55   /*
    52         -** This subclass is a subclass of sqlite3_file.  Each open memory-journal
           56  +** This structure is a subclass of sqlite3_file. Each open memory-journal
    53     57   ** is an instance of this class.
    54     58   */
    55     59   struct MemJournal {
    56         -  sqlite3_io_methods *pMethod;    /* Parent class. MUST BE FIRST */
           60  +  const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
           61  +  int nChunkSize;                 /* In-memory chunk-size */
           62  +
           63  +  int nBuf;                       /* Bytes of data before flushing */
           64  +  int nSize;                      /* Bytes of data currently in memory */
    57     65     FileChunk *pFirst;              /* Head of in-memory chunk-list */
    58     66     FilePoint endpoint;             /* Pointer to the end of the file */
    59     67     FilePoint readpoint;            /* Pointer to the end of the last xRead() */
           68  +
           69  +  int flags;                      /* xOpen flags */
           70  +  sqlite3_vfs *pVfs;              /* The "real" underlying VFS */
           71  +  const char *zJournal;           /* Name of the journal file */
           72  +  sqlite3_file *pReal;            /* The "real" underlying file descriptor */
    60     73   };
    61     74   
    62     75   /*
    63     76   ** Read data from the in-memory journal file.  This is the implementation
    64     77   ** of the sqlite3_vfs.xRead method.
    65     78   */
    66     79   static int memjrnlRead(
    67     80     sqlite3_file *pJfd,    /* The journal file from which to read */
    68     81     void *zBuf,            /* Put the results here */
    69     82     int iAmt,              /* Number of bytes to read */
    70     83     sqlite_int64 iOfst     /* Begin reading at this offset */
    71     84   ){
    72     85     MemJournal *p = (MemJournal *)pJfd;
    73         -  u8 *zOut = zBuf;
    74         -  int nRead = iAmt;
    75         -  int iChunkOffset;
    76         -  FileChunk *pChunk;
           86  +  if( p->pReal ){
           87  +    return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
           88  +  }else if( (iAmt+iOfst)>p->endpoint.iOffset ){
           89  +    return SQLITE_IOERR_SHORT_READ;
           90  +  }else{
           91  +    u8 *zOut = zBuf;
           92  +    int nRead = iAmt;
           93  +    int iChunkOffset;
           94  +    FileChunk *pChunk;
    77     95   
    78         -  /* SQLite never tries to read past the end of a rollback journal file */
    79         -  assert( iOfst+iAmt<=p->endpoint.iOffset );
    80         -
    81         -  if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
    82         -    sqlite3_int64 iOff = 0;
    83         -    for(pChunk=p->pFirst; 
    84         -        ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
    85         -        pChunk=pChunk->pNext
    86         -    ){
    87         -      iOff += JOURNAL_CHUNKSIZE;
           96  +    if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
           97  +      sqlite3_int64 iOff = 0;
           98  +      for(pChunk=p->pFirst; 
           99  +          ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
          100  +          pChunk=pChunk->pNext
          101  +      ){
          102  +        iOff += p->nChunkSize;
          103  +      }
          104  +    }else{
          105  +      pChunk = p->readpoint.pChunk;
    88    106       }
    89         -  }else{
    90         -    pChunk = p->readpoint.pChunk;
          107  +
          108  +    iChunkOffset = (int)(iOfst%p->nChunkSize);
          109  +    do {
          110  +      int iSpace = p->nChunkSize - iChunkOffset;
          111  +      int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
          112  +      memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
          113  +      zOut += nCopy;
          114  +      nRead -= iSpace;
          115  +      iChunkOffset = 0;
          116  +    } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
          117  +    p->readpoint.iOffset = iOfst+iAmt;
          118  +    p->readpoint.pChunk = pChunk;
    91    119     }
    92    120   
    93         -  iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
    94         -  do {
    95         -    int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
    96         -    int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
    97         -    memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
    98         -    zOut += nCopy;
    99         -    nRead -= iSpace;
   100         -    iChunkOffset = 0;
   101         -  } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
   102         -  p->readpoint.iOffset = iOfst+iAmt;
   103         -  p->readpoint.pChunk = pChunk;
   104         -
   105    121     return SQLITE_OK;
   106    122   }
          123  +
          124  +/*
          125  +** Free the list of FileChunk structures headed at MemJournal.pFirst.
          126  +*/
          127  +static void memjrnlFreeChunks(MemJournal *p){
          128  +  FileChunk *pIter;
          129  +  FileChunk *pNext;
          130  +  for(pIter=p->pFirst; pIter; pIter=pNext){
          131  +    pNext = pIter->pNext;
          132  +    sqlite3_free(pIter);
          133  +  } 
          134  +  p->pFirst = 0;
          135  +}
          136  +
          137  +/*
          138  +** Flush the contents of memory to a real file on disk.
          139  +*/
          140  +static int createFile(MemJournal *p){
          141  +  int rc = SQLITE_OK;
          142  +  if( !p->pReal ){
          143  +    sqlite3_file *pReal = (sqlite3_file *)&p[1];
          144  +    rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
          145  +    if( rc==SQLITE_OK ){
          146  +      int nChunk = p->nChunkSize;
          147  +      i64 iOff = 0;
          148  +      FileChunk *pIter;
          149  +      p->pReal = pReal;
          150  +      for(pIter=p->pFirst; pIter && rc==SQLITE_OK; pIter=pIter->pNext){
          151  +        int nWrite = nChunk;
          152  +        if( pIter==p->endpoint.pChunk ){
          153  +          nWrite = p->endpoint.iOffset % p->nChunkSize;
          154  +          if( nWrite==0 ) nWrite = p->nChunkSize;
          155  +        }
          156  +        rc = sqlite3OsWrite(pReal, pIter->zChunk, nWrite, iOff);
          157  +        iOff += nWrite;
          158  +      }
          159  +      if( rc!=SQLITE_OK ){
          160  +        /* If an error occurred while writing to the file, close it before
          161  +        ** returning. This way, SQLite uses the in-memory journal data to 
          162  +        ** roll back changes made to the internal page-cache before this
          163  +        ** function was called.  */
          164  +        sqlite3OsClose(pReal);
          165  +        p->pReal = 0;
          166  +      }else{
          167  +        /* No error has occurred. Free the in-memory buffers. */
          168  +        memjrnlFreeChunks(p);
          169  +      }
          170  +    }
          171  +  }
          172  +  return rc;
          173  +}
          174  +
   107    175   
   108    176   /*
   109    177   ** Write data to the file.
   110    178   */
   111    179   static int memjrnlWrite(
   112    180     sqlite3_file *pJfd,    /* The journal file into which to write */
   113    181     const void *zBuf,      /* Take data to be written from here */
................................................................................
   114    182     int iAmt,              /* Number of bytes to write */
   115    183     sqlite_int64 iOfst     /* Begin writing at this offset into the file */
   116    184   ){
   117    185     MemJournal *p = (MemJournal *)pJfd;
   118    186     int nWrite = iAmt;
   119    187     u8 *zWrite = (u8 *)zBuf;
   120    188   
   121         -  /* An in-memory journal file should only ever be appended to. Random
   122         -  ** access writes are not required by sqlite.
   123         -  */
   124         -  assert( iOfst==p->endpoint.iOffset );
   125         -  UNUSED_PARAMETER(iOfst);
          189  +  /* If the file has already been created on disk. */
          190  +  if( p->pReal ){
          191  +    return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
          192  +  }
   126    193   
   127         -  while( nWrite>0 ){
   128         -    FileChunk *pChunk = p->endpoint.pChunk;
   129         -    int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
   130         -    int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
   131         -
   132         -    if( iChunkOffset==0 ){
   133         -      /* New chunk is required to extend the file. */
   134         -      FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
   135         -      if( !pNew ){
   136         -        return SQLITE_IOERR_NOMEM_BKPT;
   137         -      }
   138         -      pNew->pNext = 0;
   139         -      if( pChunk ){
   140         -        assert( p->pFirst );
   141         -        pChunk->pNext = pNew;
   142         -      }else{
   143         -        assert( !p->pFirst );
   144         -        p->pFirst = pNew;
   145         -      }
   146         -      p->endpoint.pChunk = pNew;
          194  +  /* If the file should be created now. */
          195  +  else if( p->nBuf>0 && (iAmt+iOfst)>p->nBuf ){
          196  +    int rc = createFile(p);
          197  +    if( rc==SQLITE_OK ){
          198  +      rc = memjrnlWrite(pJfd, zBuf, iAmt, iOfst);
   147    199       }
          200  +    return rc;
          201  +  }
   148    202   
   149         -    memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
   150         -    zWrite += iSpace;
   151         -    nWrite -= iSpace;
   152         -    p->endpoint.iOffset += iSpace;
          203  +  /* If the contents of this write should be stored in memory */
          204  +  else{
          205  +    /* An in-memory journal file should only ever be appended to. Random
          206  +    ** access writes are not required. The only exception to this is when
          207  +    ** the in-memory journal is being used by a connection using the
          208  +    ** atomic-write optimization. In this case the first 28 bytes of the
          209  +    ** journal file may be written as part of committing the transaction. */ 
          210  +    assert( iOfst==p->endpoint.iOffset || iOfst==0 );
          211  +    if( iOfst==0 && p->pFirst ){
          212  +      assert( p->nChunkSize>iAmt );
          213  +      memcpy(p->pFirst->zChunk, zBuf, iAmt);
          214  +    }else{
          215  +      while( nWrite>0 ){
          216  +        FileChunk *pChunk = p->endpoint.pChunk;
          217  +        int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
          218  +        int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
          219  +
          220  +        if( iChunkOffset==0 ){
          221  +          /* New chunk is required to extend the file. */
          222  +          FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
          223  +          if( !pNew ){
          224  +            return SQLITE_IOERR_NOMEM_BKPT;
          225  +          }
          226  +          pNew->pNext = 0;
          227  +          if( pChunk ){
          228  +            assert( p->pFirst );
          229  +            pChunk->pNext = pNew;
          230  +          }else{
          231  +            assert( !p->pFirst );
          232  +            p->pFirst = pNew;
          233  +          }
          234  +          p->endpoint.pChunk = pNew;
          235  +        }
          236  +
          237  +        memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
          238  +        zWrite += iSpace;
          239  +        nWrite -= iSpace;
          240  +        p->endpoint.iOffset += iSpace;
          241  +      }
          242  +      p->nSize = iAmt + iOfst;
          243  +    }
   153    244     }
   154    245   
   155    246     return SQLITE_OK;
   156    247   }
   157    248   
   158    249   /*
   159    250   ** Truncate the file.
          251  +**
          252  +** If the journal file is already on disk, truncate it there. Or, if it
          253  +** is still in main memory but is being truncated to zero bytes in size,
          254  +** ignore 
   160    255   */
   161    256   static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
   162    257     MemJournal *p = (MemJournal *)pJfd;
   163         -  FileChunk *pChunk;
   164         -  assert(size==0);
   165         -  UNUSED_PARAMETER(size);
   166         -  pChunk = p->pFirst;
   167         -  while( pChunk ){
   168         -    FileChunk *pTmp = pChunk;
   169         -    pChunk = pChunk->pNext;
   170         -    sqlite3_free(pTmp);
          258  +  if( p->pReal ){
          259  +    return sqlite3OsTruncate(p->pReal, size);
          260  +  }else if( size==0 ){
          261  +    memjrnlFreeChunks(p);
          262  +    p->nSize = 0;
          263  +    p->endpoint.pChunk = 0;
          264  +    p->endpoint.iOffset = 0;
          265  +    p->readpoint.pChunk = 0;
          266  +    p->readpoint.iOffset = 0;
   171    267     }
   172         -  sqlite3MemJournalOpen(pJfd);
   173    268     return SQLITE_OK;
   174    269   }
   175    270   
   176    271   /*
   177    272   ** Close the file.
   178    273   */
   179    274   static int memjrnlClose(sqlite3_file *pJfd){
   180         -  memjrnlTruncate(pJfd, 0);
          275  +  MemJournal *p = (MemJournal *)pJfd;
          276  +  memjrnlFreeChunks(p);
          277  +  if( p->pReal ) sqlite3OsClose(p->pReal);
   181    278     return SQLITE_OK;
   182    279   }
   183         -
   184    280   
   185    281   /*
   186    282   ** Sync the file.
   187    283   **
   188         -** Syncing an in-memory journal is a no-op.  And, in fact, this routine
   189         -** is never called in a working implementation.  This implementation
   190         -** exists purely as a contingency, in case some malfunction in some other
   191         -** part of SQLite causes Sync to be called by mistake.
          284  +** If the real file has been created, call its xSync method. Otherwise, 
          285  +** syncing an in-memory journal is a no-op. 
   192    286   */
   193         -static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
   194         -  UNUSED_PARAMETER2(NotUsed, NotUsed2);
          287  +static int memjrnlSync(sqlite3_file *pJfd, int flags){
          288  +  MemJournal *p = (MemJournal *)pJfd;
          289  +  if( p->pReal ){
          290  +    return sqlite3OsSync(p->pReal, flags);
          291  +  }
   195    292     return SQLITE_OK;
   196    293   }
   197    294   
   198    295   /*
   199    296   ** Query the size of the file in bytes.
   200    297   */
   201    298   static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
   202    299     MemJournal *p = (MemJournal *)pJfd;
          300  +  if( p->pReal ){
          301  +    return sqlite3OsFileSize(p->pReal, pSize);
          302  +  }
   203    303     *pSize = (sqlite_int64) p->endpoint.iOffset;
   204    304     return SQLITE_OK;
   205    305   }
   206    306   
   207    307   /*
   208    308   ** Table of methods for MemJournal sqlite3_file object.
   209    309   */
................................................................................
   226    326     0,                /* xShmBarrier */
   227    327     0,                /* xShmUnmap */
   228    328     0,                /* xFetch */
   229    329     0                 /* xUnfetch */
   230    330   };
   231    331   
   232    332   /* 
   233         -** Open a journal file.
          333  +** Open a journal file. 
          334  +**
          335  +** The behaviour of the journal file depends on the value of parameter 
          336  +** nBuf. If nBuf is 0, then the journal file is always create and 
          337  +** accessed using the underlying VFS. If nBuf is less than zero, then
          338  +** all content is always stored in main-memory. Finally, if nBuf is a
          339  +** positive value, then the journal file is initially created in-memory
          340  +** but may be flushed to disk later on. In this case the journal file is
          341  +** flushed to disk either when it grows larger than nBuf bytes in size,
          342  +** or when sqlite3JournalCreate() is called.
          343  +*/
          344  +int sqlite3JournalOpen(
          345  +  sqlite3_vfs *pVfs,         /* The VFS to use for actual file I/O */
          346  +  const char *zName,         /* Name of the journal file */
          347  +  sqlite3_file *pJfd,        /* Preallocated, blank file handle */
          348  +  int flags,                 /* Opening flags */
          349  +  int nBuf                   /* Bytes buffered before opening the file */
          350  +){
          351  +  MemJournal *p = (MemJournal*)pJfd;
          352  +
          353  +  /* Zero the file-handle object. If nBuf was passed zero, initialize
          354  +  ** it using the sqlite3OsOpen() function of the underlying VFS. In this
          355  +  ** case none of the code in this module is executed as a result of calls
          356  +  ** made on the journal file-handle.  */
          357  +  memset(p, 0, sizeof(MemJournal) + (pVfs ? pVfs->szOsFile : 0));
          358  +  if( nBuf==0 ){
          359  +    return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
          360  +  }
          361  +
          362  +  if( nBuf>0 ){
          363  +    p->nChunkSize = nBuf;
          364  +  }else{
          365  +    p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
          366  +    assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
          367  +  }
          368  +
          369  +  p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
          370  +  p->nBuf = nBuf;
          371  +  p->flags = flags;
          372  +  p->zJournal = zName;
          373  +  p->pVfs = pVfs;
          374  +  return SQLITE_OK;
          375  +}
          376  +
          377  +/*
          378  +** Open an in-memory journal file.
   234    379   */
   235    380   void sqlite3MemJournalOpen(sqlite3_file *pJfd){
   236         -  MemJournal *p = (MemJournal *)pJfd;
   237         -  assert( EIGHT_BYTE_ALIGNMENT(p) );
   238         -  memset(p, 0, sqlite3MemJournalSize());
   239         -  p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
          381  +  sqlite3JournalOpen(0, 0, pJfd, 0, -1);
          382  +}
          383  +
          384  +#ifdef SQLITE_ENABLE_ATOMIC_WRITE
          385  +/*
          386  +** If the argument p points to a MemJournal structure that is not an 
          387  +** in-memory-only journal file (i.e. is one that was opened with a +ve
          388  +** nBuf parameter), and the underlying file has not yet been created, 
          389  +** create it now.
          390  +*/
          391  +int sqlite3JournalCreate(sqlite3_file *p){
          392  +  int rc = SQLITE_OK;
          393  +  if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nBuf>0 ){
          394  +    rc = createFile((MemJournal*)p);
          395  +  }
          396  +  return rc;
   240    397   }
          398  +#endif
   241    399   
   242    400   /*
   243         -** Return true if the file-handle passed as an argument is 
   244         -** an in-memory journal 
          401  +** The file-handle passed as the only argument is open on a journal file.
          402  +** Return true if this "journal file" is currently stored in heap memory,
          403  +** or false otherwise.
   245    404   */
   246         -int sqlite3IsMemJournal(sqlite3_file *pJfd){
   247         -  return pJfd->pMethods==&MemJournalMethods;
          405  +int sqlite3JournalIsInMemory(sqlite3_file *p){
          406  +  return p->pMethods==&MemJournalMethods && ((MemJournal*)p)->pReal==0;
   248    407   }
   249    408   
   250    409   /* 
   251         -** Return the number of bytes required to store a MemJournal file descriptor.
          410  +** Return the number of bytes required to store a JournalFile that uses vfs
          411  +** pVfs to create the underlying on-disk files.
   252    412   */
   253         -int sqlite3MemJournalSize(void){
   254         -  return sizeof(MemJournal);
          413  +int sqlite3JournalSize(sqlite3_vfs *pVfs){
          414  +  return pVfs->szOsFile + sizeof(MemJournal);
   255    415   }

Changes to src/os.c.

    62     62   **     sqlite3OsAccess()
    63     63   **     sqlite3OsFullPathname()
    64     64   **
    65     65   */
    66     66   #if defined(SQLITE_TEST)
    67     67   int sqlite3_memdebug_vfs_oom_test = 1;
    68     68     #define DO_OS_MALLOC_TEST(x)                                       \
    69         -  if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) {  \
           69  +  if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
    70     70       void *pTstAlloc = sqlite3Malloc(10);                             \
    71     71       if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT;                  \
    72     72       sqlite3_free(pTstAlloc);                                         \
    73     73     }
    74     74   #else
    75     75     #define DO_OS_MALLOC_TEST(x)
    76     76   #endif

Changes to src/pager.c.

  1339   1339   **
  1340   1340   ** If an IO error occurs, abandon processing and return the IO error code.
  1341   1341   ** Otherwise, return SQLITE_OK.
  1342   1342   */
  1343   1343   static int zeroJournalHdr(Pager *pPager, int doTruncate){
  1344   1344     int rc = SQLITE_OK;                               /* Return code */
  1345   1345     assert( isOpen(pPager->jfd) );
         1346  +  assert( !sqlite3JournalIsInMemory(pPager->jfd) );
  1346   1347     if( pPager->journalOff ){
  1347   1348       const i64 iLimit = pPager->journalSizeLimit;    /* Local cache of jsl */
  1348   1349   
  1349   1350       IOTRACE(("JZEROHDR %p\n", pPager))
  1350   1351       if( doTruncate || iLimit==0 ){
  1351   1352         rc = sqlite3OsTruncate(pPager->jfd, 0);
  1352   1353       }else{
................................................................................
  1720   1721   ** if it is open and the pager is not in exclusive mode.
  1721   1722   */
  1722   1723   static void releaseAllSavepoints(Pager *pPager){
  1723   1724     int ii;               /* Iterator for looping through Pager.aSavepoint */
  1724   1725     for(ii=0; ii<pPager->nSavepoint; ii++){
  1725   1726       sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
  1726   1727     }
  1727         -  if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
         1728  +  if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){
  1728   1729       sqlite3OsClose(pPager->sjfd);
  1729   1730     }
  1730   1731     sqlite3_free(pPager->aSavepoint);
  1731   1732     pPager->aSavepoint = 0;
  1732   1733     pPager->nSavepoint = 0;
  1733   1734     pPager->nSubRec = 0;
  1734   1735   }
................................................................................
  1958   1959   
  1959   1960     releaseAllSavepoints(pPager);
  1960   1961     assert( isOpen(pPager->jfd) || pPager->pInJournal==0 );
  1961   1962     if( isOpen(pPager->jfd) ){
  1962   1963       assert( !pagerUseWal(pPager) );
  1963   1964   
  1964   1965       /* Finalize the journal file. */
  1965         -    if( sqlite3IsMemJournal(pPager->jfd) ){
  1966         -      assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
         1966  +    if( sqlite3JournalIsInMemory(pPager->jfd) ){
         1967  +      /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */
  1967   1968         sqlite3OsClose(pPager->jfd);
  1968   1969       }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
  1969   1970         if( pPager->journalOff==0 ){
  1970   1971           rc = SQLITE_OK;
  1971   1972         }else{
  1972   1973           rc = sqlite3OsTruncate(pPager->jfd, 0);
  1973   1974           if( rc==SQLITE_OK && pPager->fullSync ){
................................................................................
  1985   1986       ){
  1986   1987         rc = zeroJournalHdr(pPager, hasMaster);
  1987   1988         pPager->journalOff = 0;
  1988   1989       }else{
  1989   1990         /* This branch may be executed with Pager.journalMode==MEMORY if
  1990   1991         ** a hot-journal was just rolled back. In this case the journal
  1991   1992         ** file should be closed and deleted. If this connection writes to
  1992         -      ** the database file, it will do so using an in-memory journal. 
         1993  +      ** the database file, it will do so using an in-memory journal.
  1993   1994         */
  1994         -      int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd));
         1995  +      int bDelete = !pPager->tempFile;
         1996  +      assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
  1995   1997         assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE 
  1996   1998              || pPager->journalMode==PAGER_JOURNALMODE_MEMORY 
  1997   1999              || pPager->journalMode==PAGER_JOURNALMODE_WAL 
  1998   2000         );
  1999   2001         sqlite3OsClose(pPager->jfd);
  2000   2002         if( bDelete ){
  2001   2003           rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync);
................................................................................
  2725   2727     ** If a master journal file name is specified, but the file is not
  2726   2728     ** present on disk, then the journal is not hot and does not need to be
  2727   2729     ** played back.
  2728   2730     **
  2729   2731     ** TODO: Technically the following is an error because it assumes that
  2730   2732     ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
  2731   2733     ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
  2732         -  **  mxPathname is 512, which is the same as the minimum allowable value
         2734  +  ** mxPathname is 512, which is the same as the minimum allowable value
  2733   2735     ** for pageSize.
  2734   2736     */
  2735   2737     zMaster = pPager->pTmpSpace;
  2736   2738     rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  2737   2739     if( rc==SQLITE_OK && zMaster[0] ){
  2738   2740       rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
  2739   2741     }
................................................................................
  4349   4351   ** SQLITE_OK is returned if everything goes according to plan. An 
  4350   4352   ** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen() 
  4351   4353   ** fails.
  4352   4354   */
  4353   4355   static int openSubJournal(Pager *pPager){
  4354   4356     int rc = SQLITE_OK;
  4355   4357     if( !isOpen(pPager->sjfd) ){
         4358  +    const int flags =  SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE 
         4359  +      | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE 
         4360  +      | SQLITE_OPEN_DELETEONCLOSE;
         4361  +    int nBuf = 64*1024;
  4356   4362       if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
  4357         -      sqlite3MemJournalOpen(pPager->sjfd);
  4358         -    }else{
  4359         -      rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
         4363  +      nBuf = -1;
  4360   4364       }
         4365  +    rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nBuf);
  4361   4366     }
  4362   4367     return rc;
  4363   4368   }
  4364   4369   
  4365   4370   /*
  4366   4371   ** Append a record of the current state of page pPg to the sub-journal. 
  4367   4372   **
................................................................................
  4574   4579     int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
  4575   4580     int pcacheSize = sqlite3PcacheSize();       /* Bytes to allocate for PCache */
  4576   4581     u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE;  /* Default page size */
  4577   4582     const char *zUri = 0;    /* URI args to copy */
  4578   4583     int nUri = 0;            /* Number of bytes of URI args at *zUri */
  4579   4584   
  4580   4585     /* Figure out how much space is required for each journal file-handle
  4581         -  ** (there are two of them, the main journal and the sub-journal). This
  4582         -  ** is the maximum space required for an in-memory journal file handle 
  4583         -  ** and a regular journal file-handle. Note that a "regular journal-handle"
  4584         -  ** may be a wrapper capable of caching the first portion of the journal
  4585         -  ** file in memory to implement the atomic-write optimization (see 
  4586         -  ** source file journal.c).
  4587         -  */
  4588         -  if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
  4589         -    journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
  4590         -  }else{
  4591         -    journalFileSize = ROUND8(sqlite3MemJournalSize());
  4592         -  }
         4586  +  ** (there are two of them, the main journal and the sub-journal).  */
         4587  +  journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
  4593   4588   
  4594   4589     /* Set the output variable to NULL in case an error occurs. */
  4595   4590     *ppPager = 0;
  4596   4591   
  4597   4592   #ifndef SQLITE_OMIT_MEMORYDB
  4598   4593     if( flags & PAGER_MEMORY ){
  4599   4594       memDb = 1;
................................................................................
  6663   6658       pPager->nSavepoint = nNew;
  6664   6659   
  6665   6660       /* If this is a release of the outermost savepoint, truncate 
  6666   6661       ** the sub-journal to zero bytes in size. */
  6667   6662       if( op==SAVEPOINT_RELEASE ){
  6668   6663         if( nNew==0 && isOpen(pPager->sjfd) ){
  6669   6664           /* Only truncate if it is an in-memory sub-journal. */
  6670         -        if( sqlite3IsMemJournal(pPager->sjfd) ){
         6665  +        if( sqlite3JournalIsInMemory(pPager->sjfd) ){
  6671   6666             rc = sqlite3OsTruncate(pPager->sjfd, 0);
  6672   6667             assert( rc==SQLITE_OK );
  6673   6668           }
  6674   6669           pPager->nSubRec = 0;
  6675   6670         }
  6676   6671       }
  6677   6672       /* Else this is a rollback operation, playback the specified savepoint.

Changes to src/sqliteInt.h.

  3994   3994   ** Allowed flags for the 3rd parameter to sqlite3FindInIndex().
  3995   3995   */
  3996   3996   #define IN_INDEX_NOOP_OK     0x0001  /* OK to return IN_INDEX_NOOP */
  3997   3997   #define IN_INDEX_MEMBERSHIP  0x0002  /* IN operator used for membership test */
  3998   3998   #define IN_INDEX_LOOP        0x0004  /* IN operator used as a loop */
  3999   3999   int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
  4000   4000   
         4001  +int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
         4002  +int sqlite3JournalSize(sqlite3_vfs *);
  4001   4003   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
  4002         -  int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
  4003         -  int sqlite3JournalSize(sqlite3_vfs *);
  4004   4004     int sqlite3JournalCreate(sqlite3_file *);
  4005         -  int sqlite3JournalExists(sqlite3_file *p);
  4006         -#else
  4007         -  #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
  4008         -  #define sqlite3JournalExists(p) 1
  4009   4005   #endif
  4010   4006   
         4007  +int sqlite3JournalIsInMemory(sqlite3_file *p);
  4011   4008   void sqlite3MemJournalOpen(sqlite3_file *);
  4012         -int sqlite3MemJournalSize(void);
  4013         -int sqlite3IsMemJournal(sqlite3_file *);
  4014   4009   
  4015   4010   void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p);
  4016   4011   #if SQLITE_MAX_EXPR_DEPTH>0
  4017   4012     int sqlite3SelectExprHeight(Select *);
  4018   4013     int sqlite3ExprCheckHeight(Parse*, int);
  4019   4014   #else
  4020   4015     #define sqlite3SelectExprHeight(x) 0

Changes to test/conflict.test.

   281    281   #   cmd    An UPDATE command to execute against table t1
   282    282   #   t0     True if there is an error from $cmd
   283    283   #   t1     Content of "b" column of t1 assuming no error in $cmd
   284    284   #   t2     Content of "x" column of t3
   285    285   #   t3     Number of temporary files for tables
   286    286   #   t4     Number of temporary files for statement journals
   287    287   #
   288         -# Update: Since temporary table files are now opened lazily, and none
   289         -# of the following tests use large quantities of data, t3 is always 0.
          288  +# Update (2007-08-21): Since temporary table files are now opened lazily, 
          289  +# and none of the following tests use large quantities of data, t3 is always 0.
          290  +#
          291  +# Update (2016-03-04): Subjournals now also open lazily, so t4 is also always 0.
   290    292   #
   291    293   foreach {i conf1 cmd t0 t1 t2 t3 t4} {
   292         -  1 {}       UPDATE                  1 {6 7 8 9}  1 0 1
          294  +  1 {}       UPDATE                  1 {6 7 8 9}  1 0 0
   293    295     2 REPLACE  UPDATE                  0 {7 6 9}    1 0 0
   294    296     3 IGNORE   UPDATE                  0 {6 7 3 9}  1 0 0
   295    297     4 FAIL     UPDATE                  1 {6 7 3 4}  1 0 0
   296         -  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 1
          298  +  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 0
   297    299     6 ROLLBACK UPDATE                  1 {1 2 3 4}  0 0 0
   298    300     7 REPLACE  {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   299    301     8 IGNORE   {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   300    302     9 FAIL     {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   301    303    10 ABORT    {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   302    304    11 ROLLBACK {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   303    305    12 {}       {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   304    306    13 {}       {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   305    307    14 {}       {UPDATE OR FAIL}        1 {6 7 3 4}  1 0 0
   306         - 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 1
          308  + 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 0
   307    309    16 {}       {UPDATE OR ROLLBACK}    1 {1 2 3 4}  0 0 0
   308    310   } {
   309    311     if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
   310    312     if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
   311    313       set t3 0
   312    314     } else {
   313    315       set t3 [expr {$t3+$t4}]

Changes to test/conflict2.test.

   282    282   #   t1     Content of "b" column of t1 assuming no error in $cmd
   283    283   #   t2     Content of "x" column of t3
   284    284   #   t3     Number of temporary files for tables
   285    285   #   t4     Number of temporary files for statement journals
   286    286   #
   287    287   # Update: Since temporary table files are now opened lazily, and none
   288    288   # of the following tests use large quantities of data, t3 is always 0.
          289  +#
          290  +# Update (2016-03-04): Subjournals now only open when their size
          291  +# exceeds 64KB.
   289    292   #
   290    293   foreach {i conf1 cmd t0 t1 t2 t3 t4} {
   291         -  1 {}       UPDATE                  1 {6 7 8 9}  1 0 1
   292         -  2 REPLACE  UPDATE                  0 {7 6 9}    1 0 1
   293         -  3 IGNORE   UPDATE                  0 {6 7 3 9}  1 0 1
   294         -  4 FAIL     UPDATE                  1 {6 7 3 4}  1 0 1
   295         -  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 1
   296         -  6 ROLLBACK UPDATE                  1 {1 2 3 4}  0 0 1
          294  +  1 {}       UPDATE                  1 {6 7 8 9}  1 0 0
          295  +  2 REPLACE  UPDATE                  0 {7 6 9}    1 0 0
          296  +  3 IGNORE   UPDATE                  0 {6 7 3 9}  1 0 0
          297  +  4 FAIL     UPDATE                  1 {6 7 3 4}  1 0 0
          298  +  5 ABORT    UPDATE                  1 {1 2 3 4}  1 0 0
          299  +  6 ROLLBACK UPDATE                  1 {1 2 3 4}  0 0 0
   297    300     7 REPLACE  {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   298         -  8 IGNORE   {UPDATE OR REPLACE}     0 {7 6 9}    1 0 1
          301  +  8 IGNORE   {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   299    302     9 FAIL     {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   300         - 10 ABORT    {UPDATE OR REPLACE}     0 {7 6 9}    1 0 1
          303  + 10 ABORT    {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   301    304    11 ROLLBACK {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   302    305    12 {}       {UPDATE OR IGNORE}      0 {6 7 3 9}  1 0 0
   303         - 13 {}       {UPDATE OR REPLACE}     0 {7 6 9}    1 0 1
          306  + 13 {}       {UPDATE OR REPLACE}     0 {7 6 9}    1 0 0
   304    307    14 {}       {UPDATE OR FAIL}        1 {6 7 3 4}  1 0 0
   305         - 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 1
          308  + 15 {}       {UPDATE OR ABORT}       1 {1 2 3 4}  1 0 0
   306    309    16 {}       {UPDATE OR ROLLBACK}    1 {1 2 3 4}  0 0 0
   307    310   } {
   308    311   
   309    312     # When using in-memory journals, no temporary files are required for
   310    313     # statement journals.
   311    314     if {[permutation] == "inmemory_journal"} { set t4 0 }
   312    315   

Changes to test/exclusive.test.

   416    416       BEGIN;
   417    417       INSERT INTO abc VALUES(1, 2, 3);
   418    418       INSERT INTO abc SELECT a+1, b+1, c+1 FROM abc;
   419    419     }
   420    420   } {}
   421    421   do_test exclusive-5.1 {
   422    422     # Three files are open: The db, journal and statement-journal.
          423  +  # (2016-03-04) The statement-journal is now opened lazily
   423    424     set sqlite_open_file_count
   424    425     expr $sqlite_open_file_count-$extrafds
   425         -} [expr 3 - ($TEMP_STORE>=2)]
          426  +} [expr 2 - ($TEMP_STORE>=2)]
   426    427   do_test exclusive-5.2 {
   427    428     execsql {
   428    429       COMMIT;
   429    430     }
   430    431     # One file open: the db.
   431    432     set sqlite_open_file_count
   432    433     expr $sqlite_open_file_count-$extrafds
................................................................................
   442    443     expr $sqlite_open_file_count-$extrafds
   443    444   } {2}
   444    445   do_test exclusive-5.4 {
   445    446     execsql {
   446    447       INSERT INTO abc SELECT a+10, b+10, c+10 FROM abc;
   447    448     }
   448    449     # Three files are open: The db, journal and statement-journal.
          450  +  # 2016-03-04: The statement-journal open is deferred
   449    451     set sqlite_open_file_count
   450    452     expr $sqlite_open_file_count-$extrafds
   451         -} [expr 3 - ($TEMP_STORE>=2)]
          453  +} [expr 2 - ($TEMP_STORE>=2)]
   452    454   do_test exclusive-5.5 {
   453    455     execsql {
   454    456       COMMIT;
   455    457     }
   456    458     # Three files are still open: The db, journal and statement-journal.
          459  +  # 2016-03-04: The statement-journal open is deferred
   457    460     set sqlite_open_file_count
   458    461     expr $sqlite_open_file_count-$extrafds
   459         -} [expr 3 - ($TEMP_STORE>=2)]
          462  +} [expr 2 - ($TEMP_STORE>=2)]
   460    463   do_test exclusive-5.6 {
   461    464     execsql {
   462    465       PRAGMA locking_mode = normal;
   463    466       SELECT * FROM abc;
   464    467     }
   465    468   } {normal 1 2 3 2 3 4 5 6 7 11 12 13 12 13 14 15 16 17}
   466    469   do_test exclusive-5.7 {

Changes to test/stmt.test.

    42     42     set sqlite_open_file_count
    43     43   } {2}
    44     44   do_test stmt-1.4 {
    45     45     execsql {
    46     46       INSERT INTO t1 SELECT a+1, b+1 FROM t1;
    47     47     }
    48     48     set sqlite_open_file_count
    49         -} {3}
           49  +  # 2016-03-04: statement-journal open deferred
           50  +} {2}
    50     51   do_test stmt-1.5 {
    51     52     execsql COMMIT
    52     53     set sqlite_open_file_count
    53     54   } {1}
    54     55   do_test stmt-1.6.1 {
    55     56     execsql {
    56     57       BEGIN;
................................................................................
    57     58         INSERT INTO t1 SELECT a+2, b+2 FROM t1;
    58     59     }
    59     60     set sqlite_open_file_count
    60     61   } {2}
    61     62   do_test stmt-1.6.2 {
    62     63     execsql { INSERT INTO t1 SELECT a+4, b+4 FROM t1 }
    63     64     set sqlite_open_file_count
    64         -} {3}
           65  +  # 2016-03-04: statement-journal open deferred
           66  +} {2}
    65     67   do_test stmt-1.7 {
    66     68     execsql COMMIT
    67     69     set sqlite_open_file_count
    68     70   } {1}
    69     71   
    70     72   
    71     73   proc filecount {testname sql expected} {
................................................................................
    80     82   
    81     83   filecount stmt-2.1 { INSERT INTO t1 VALUES(9, 9)  } 2
    82     84   filecount stmt-2.2 { REPLACE INTO t1 VALUES(9, 9) } 2
    83     85   filecount stmt-2.3 { INSERT INTO t1 SELECT 9, 9   } 2
    84     86   filecount stmt-2.4 { 
    85     87       INSERT INTO t1 SELECT 9, 9;
    86     88       INSERT INTO t1 SELECT 10, 10;
    87         -} 3
           89  +} 2
    88     90   
    89     91   do_test stmt-2.5 {
    90     92     execsql { CREATE INDEX i1 ON t1(b) }
    91     93   } {}
    92     94   filecount stmt-2.6 { 
    93     95     REPLACE INTO t1 VALUES(5, 5);
    94     96     REPLACE INTO t1 VALUES(5, 5); 
    95         -} 3
           97  +} 2
    96     98   
    97     99   finish_test

Changes to test/tempdb.test.

    72     72         INSERT INTO t1 VALUES(1, 2, 3);
    73     73         INSERT INTO t1 VALUES(4, 5, 6);
    74     74         INSERT INTO t2 VALUES(7, 8, 9);
    75     75         INSERT INTO t2 SELECT * FROM t1;
    76     76     }
    77     77     catchsql { INSERT INTO t1 SELECT * FROM t2 }
    78     78     set sqlite_open_file_count
    79         -} [expr 1 + (0==$jrnl_in_memory) + (0==$subj_in_memory)]
           79  +} [expr 1 + (0==$jrnl_in_memory)]
    80     80   do_test tempdb-2.3 {
    81     81     execsql {
    82     82       PRAGMA temp_store = 'memory';
    83     83       ROLLBACK;
    84     84       BEGIN;
    85     85         INSERT INTO t1 VALUES(1, 2, 3);
    86     86         INSERT INTO t1 VALUES(4, 5, 6);