/ Check-in [a5ecffcf]
Login

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

Overview
Comment:Avoid writing the 8-byte journal-header magic until the journal-header is synced. In persistent journal-mode, this prevents any old content that follows an unsynced journal-header from being interpreted as part of the rollback journal. (CVS 6817)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:a5ecffcf025da2fcb241e83c7bebc1095a3b51d6
User & Date: danielk1977 2009-06-26 07:12:07
Context
2009-06-26
09:01
Update test_journal.c to account for (6817). Changes to test code only. (CVS 6818) check-in: 542ee8cc user: danielk1977 tags: trunk
07:12
Avoid writing the 8-byte journal-header magic until the journal-header is synced. In persistent journal-mode, this prevents any old content that follows an unsynced journal-header from being interpreted as part of the rollback journal. (CVS 6817) check-in: a5ecffcf user: danielk1977 tags: trunk
2009-06-25
16:11
Fix a problem with a return code being ignored in insertCell(). (CVS 6816) check-in: bb5f1c01 user: danielk1977 tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

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.601 2009/06/22 05:43:24 danielk1977 Exp $
           21  +** @(#) $Id: pager.c,v 1.602 2009/06/26 07:12:07 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   
    26     26   /*
    27     27   ** Macros for troubleshooting.  Normally turned off
    28     28   */
................................................................................
   753    753     for(ii=0; ii<pPager->nSavepoint; ii++){
   754    754       if( pPager->aSavepoint[ii].iHdrOffset==0 ){
   755    755         pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff;
   756    756       }
   757    757     }
   758    758   
   759    759     pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager);
   760         -  memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
   761    760   
   762    761     /* 
   763    762     ** Write the nRec Field - the number of page records that follow this
   764    763     ** journal header. Normally, zero is written to this value at this time.
   765    764     ** After the records are added to the journal (and the journal synced, 
   766    765     ** if in full-sync mode), the zero is overwritten with the true number
   767    766     ** of records (see syncJournal()).
................................................................................
   779    778     **   * When the SQLITE_IOCAP_SAFE_APPEND flag is set. This guarantees
   780    779     **     that garbage data is never appended to the journal file.
   781    780     */
   782    781     assert( isOpen(pPager->fd) || pPager->noSync );
   783    782     if( (pPager->noSync) || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
   784    783      || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND) 
   785    784     ){
          785  +    memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
   786    786       put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
   787    787     }else{
          788  +    zHeader[0] = '\0';
   788    789       put32bits(&zHeader[sizeof(aJournalMagic)], 0);
   789    790     }
   790    791   
   791    792     /* The random check-hash initialiser */ 
   792    793     sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
   793    794     put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
   794    795     /* The initial database size */
................................................................................
   848    849   **
   849    850   ** If the journal header file appears to be corrupted, SQLITE_DONE is
   850    851   ** returned and *pNRec and *PDbSize are undefined.  If JOURNAL_HDR_SZ bytes
   851    852   ** cannot be read from the journal file an error code is returned.
   852    853   */
   853    854   static int readJournalHdr(
   854    855     Pager *pPager,               /* Pager object */
          856  +  int isHot,
   855    857     i64 journalSize,             /* Size of the open journal file in bytes */
   856    858     u32 *pNRec,                  /* OUT: Value read from the nRec field */
   857    859     u32 *pDbSize                 /* OUT: Value of original database size field */
   858    860   ){
   859    861     int rc;                      /* Return code */
   860    862     unsigned char aMagic[8];     /* A buffer to hold the magic header */
   861    863     i64 iHdrOff;                 /* Offset of journal header being read */
................................................................................
   873    875     iHdrOff = pPager->journalOff;
   874    876   
   875    877     /* Read in the first 8 bytes of the journal header. If they do not match
   876    878     ** the  magic string found at the start of each journal header, return
   877    879     ** SQLITE_DONE. If an IO error occurs, return an error code. Otherwise,
   878    880     ** proceed.
   879    881     */
   880         -  rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff);
   881         -  if( rc ){
   882         -    return rc;
   883         -  }
   884         -  if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
   885         -    return SQLITE_DONE;
          882  +  if( isHot || iHdrOff!=pPager->journalHdr ){
          883  +    rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), iHdrOff);
          884  +    if( rc ){
          885  +      return rc;
          886  +    }
          887  +    if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
          888  +      return SQLITE_DONE;
          889  +    }
   886    890     }
   887    891   
   888    892     /* Read the first three 32-bit fields of the journal header: The nRec
   889    893     ** field, the checksum-initializer and the database size at the start
   890    894     ** of the transaction. Return an error code if anything goes wrong.
   891    895     */
   892    896     if( SQLITE_OK!=(rc = read32bits(pPager->jfd, iHdrOff+8, pNRec))
................................................................................
  1973   1977       int isUnsync = 0;
  1974   1978   
  1975   1979       /* Read the next journal header from the journal file.  If there are
  1976   1980       ** not enough bytes left in the journal file for a complete header, or
  1977   1981       ** it is corrupted, then a process must of failed while writing it.
  1978   1982       ** This indicates nothing more needs to be rolled back.
  1979   1983       */
  1980         -    rc = readJournalHdr(pPager, szJ, &nRec, &mxPg);
         1984  +    rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
  1981   1985       if( rc!=SQLITE_OK ){ 
  1982   1986         if( rc==SQLITE_DONE ){
  1983   1987           rc = SQLITE_OK;
  1984   1988         }
  1985   1989         goto end_playback;
  1986   1990       }
  1987   1991   
................................................................................
  2193   2197     ** of the main journal file.  Continue to skip out-of-range pages and
  2194   2198     ** continue adding pages rolled back to pDone.
  2195   2199     */
  2196   2200     while( rc==SQLITE_OK && pPager->journalOff<szJ ){
  2197   2201       u32 ii;            /* Loop counter */
  2198   2202       u32 nJRec = 0;     /* Number of Journal Records */
  2199   2203       u32 dummy;
  2200         -    rc = readJournalHdr(pPager, szJ, &nJRec, &dummy);
         2204  +    rc = readJournalHdr(pPager, 0, szJ, &nJRec, &dummy);
  2201   2205       assert( rc!=SQLITE_DONE );
  2202   2206   
  2203   2207       /*
  2204   2208       ** The "pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff"
  2205   2209       ** test is related to ticket #2565.  See the discussion in the
  2206   2210       ** pager_playback() function for additional information.
  2207   2211       */
................................................................................
  2760   2764         assert( isOpen(pPager->jfd) );
  2761   2765   
  2762   2766         if( 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
  2763   2767           /* Variable iNRecOffset is set to the offset in the journal file
  2764   2768           ** of the nRec field of the most recently written journal header.
  2765   2769           ** This field will be updated following the xSync() operation
  2766   2770           ** on the journal file. */
  2767         -        i64 iNRecOffset = pPager->journalHdr + sizeof(aJournalMagic);
         2771  +	u8 zHeader[sizeof(aJournalMagic)+4];
         2772  +	memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
         2773  +	put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec);
  2768   2774   
  2769   2775           /* This block deals with an obscure problem. If the last connection
  2770   2776           ** that wrote to this database was operating in persistent-journal
  2771   2777           ** mode, then the journal file may at this point actually be larger
  2772   2778           ** than Pager.journalOff bytes. If the next thing in the journal
  2773   2779           ** file happens to be a journal-header (written as part of the
  2774   2780           ** previous connections transaction), and a crash or power-failure 
................................................................................
  2813   2819           if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
  2814   2820             PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
  2815   2821             IOTRACE(("JSYNC %p\n", pPager))
  2816   2822             rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
  2817   2823             if( rc!=SQLITE_OK ) return rc;
  2818   2824           }
  2819   2825           IOTRACE(("JHDR %p %lld %d\n", pPager, iNRecOffset, 4));
  2820         -        rc = write32bits(pPager->jfd, iNRecOffset, pPager->nRec);
         2826  +        rc = sqlite3OsWrite(
         2827  +            pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr
         2828  +	);
  2821   2829           if( rc!=SQLITE_OK ) return rc;
  2822   2830         }
  2823   2831         if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
  2824   2832           PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
  2825   2833           IOTRACE(("JSYNC %p\n", pPager))
  2826   2834           rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| 
  2827   2835             (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)

Changes to test/rollback.test.

     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this file is verifying that a rollback in one statement
    13     13   # caused by an ON CONFLICT ROLLBACK clause aborts any other pending
    14     14   # statements.
    15     15   #
    16         -# $Id: rollback.test,v 1.10 2008/10/17 18:51:53 danielk1977 Exp $
           16  +# $Id: rollback.test,v 1.11 2009/06/26 07:12:07 danielk1977 Exp $
    17     17   
    18     18   set testdir [file dirname $argv0]
    19     19   source $testdir/tester.tcl
    20     20   
    21     21   set DB [sqlite3_connection_pointer db]
    22     22   
    23     23   do_test rollback-1.1 {
................................................................................
   110    110       "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
   111    111     ]
   112    112     set iOffset [expr (([file size testA.db-journal] + 511)/512)*512]
   113    113     set fd [open testA.db-journal a+]
   114    114     fconfigure $fd -encoding binary -translation binary
   115    115     seek $fd $iOffset
   116    116     puts -nonewline $fd $zAppend
          117  +
          118  +  # Also, fix the first journal-header in the journal-file. Because the
          119  +  # journal file has not yet been synced, the 8-byte magic string at the
          120  +  # start of the first journal-header has not been written by SQLite.
          121  +  # So write it now.
          122  +  seek $fd 0
          123  +  puts -nonewline $fd "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
   117    124     close $fd
   118    125   
   119    126     # Open a handle on testA.db and use it to query the database. At one
   120    127     # point the first query would attempt a hot rollback, attempt to open
   121    128     # the master-journal file and return SQLITE_CANTOPEN when it could not
   122    129     # be opened. This is incorrect, it should simply delete the journal
   123    130     # file and proceed with the query.

Changes to test/tkt3457.test.

     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   # This file implements regression tests for SQLite library.
    12     12   #
    13         -# $Id: tkt3457.test,v 1.2 2009/06/05 17:09:12 drh Exp $
           13  +# $Id: tkt3457.test,v 1.3 2009/06/26 07:12:07 danielk1977 Exp $
    14     14   
    15     15   set testdir [file dirname $argv0]
    16     16   source $testdir/tester.tcl
    17     17   
    18     18   if {$tcl_platform(platform) != "unix"} {
    19     19     finish_test
    20     20     return
................................................................................
    44     44       INSERT INTO t1 VALUES(1, 2, 3);
    45     45       BEGIN;
    46     46       INSERT INTO t1 VALUES(4, 5, 6);
    47     47     }
    48     48   
    49     49     file copy -force test.db bak.db
    50     50     file copy -force test.db-journal bak.db-journal
           51  +
           52  +  # Fix the first journal-header in the journal-file. Because the
           53  +  # journal file has not yet been synced, the 8-byte magic string at the
           54  +  # start of the first journal-header has not been written by SQLite.
           55  +  # So write it now.
           56  +  set fd [open bak.db-journal a+]
           57  +  fconfigure $fd -encoding binary -translation binary
           58  +  seek $fd 0
           59  +  puts -nonewline $fd "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
           60  +  close $fd
    51     61   
    52     62     execsql COMMIT
    53     63   } {}
    54     64   
    55     65   do_test tkt3457-1.2 {
    56     66     file copy -force bak.db-journal test.db-journal
    57     67     file attributes test.db-journal -permissions ---------