** file simultaneously, or one process from reading the database while
** another is writing.
**
-** @(#) $Id: pager.c,v 1.93 2004/02/08 00:40:52 drh Exp $
+** @(#) $Id: pager.c,v 1.94 2004/02/08 06:05:46 drh Exp $
*/
#include "os.h" /* Must be first to enable large file support */
#include "sqliteInt.h"
int mxPage; /* Maximum number of pages to hold in cache */
int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */
u8 journalOpen; /* True if journal file descriptors is valid */
- u8 journalStarted; /* True if initial magic of journal is synced */
- u8 useJournal; /* Do not use a rollback journal on this file */
+ u8 journalStarted; /* True if header of journal is synced */
+ u8 useJournal; /* Use a rollback journal on this file */
u8 ckptOpen; /* True if the checkpoint journal is open */
u8 ckptInUse; /* True we are in a checkpoint */
u8 ckptAutoopen; /* Open ckpt journal when main journal is opened*/
#endif
/*
-** Read a 32-bit integer from the given file descriptor
+** Read a 32-bit integer from the given file descriptor. Store the integer
+** that is read in *pRes. Return SQLITE_OK if everything worked, or an
+** error code is something goes wrong.
+**
+** If the journal format is 2 or 3, read a big-endian integer. If the
+** journal format is 1, read an integer in the native byte-order of the
+** host machine.
*/
static int read32bits(int format, OsFile *fd, u32 *pRes){
u32 res;
}
/*
-** Write a 32-bit integer into the given file descriptor. Writing
-** is always done using the new journal format.
+** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
+** on success or an error code is something goes wrong.
+**
+** If the journal format is 2 or 3, write the integer as 4 big-endian
+** bytes. If the journal format is 1, write the integer in the native
+** byte order. In normal operation, only formats 2 and 3 are used.
+** Journal format 1 is only used for testing.
*/
static int write32bits(OsFile *fd, u32 val){
unsigned char ac[4];
/*
** Write a 32-bit integer into a page header right before the
** page data. This will overwrite the PgHdr.pDirty pointer.
+**
+** The integer is big-endian for formats 2 and 3 and native byte order
+** for journal format 1.
*/
static void store32bits(u32 val, PgHdr *p, int offset){
unsigned char *ac;
/*
** Compute and return a checksum for the page of data.
+**
+** This is not a real checksum. It is really just the sum of the
+** random initial value and the page number. We considered do a checksum
+** of the database, but that was found to be too slow.
*/
static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
u32 cksum = pPager->cksumInit + pgno;
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.
**
-** The journal file format is as follows: There is an initial
-** file-type string for sanity checking. Then there is a single
-** Pgno number which is the number of pages in the database before
-** changes were made. The database is truncated to this size.
-** Next come zero or more page records where each page record
-** consists of a Pgno and SQLITE_PAGE_SIZE bytes of data. See
-** the PageRecord structure for details.
+** The journal file format is as follows:
+**
+** * 8 byte prefix. One of the aJournalMagic123 vectors defined
+** above. The format of the journal file is determined by which
+** of the three prefix vectors is seen.
+** * 4 byte big-endian integer which is the number of valid page records
+** in the journal. If this value is 0xffffffff, then compute the
+** number of page records from the journal size. This field appears
+** in format 3 only.
+** * 4 byte big-endian integer which is the initial value for the
+** sanity checksum. This field appears in format 3 only.
+** * 4 byte integer which is the number of pages to truncate the
+** database to during a rollback.
+** * Zero or more pages instances, each as follows:
+** + 4 byte page number.
+** + SQLITE_PAGE_SIZE bytes of data.
+** + 4 byte checksum (format 3 only)
+**
+** When we speak of the journal header, we mean the first 4 bullets above.
+** Each entry in the journal is an instance of the 5th bullet. Note that
+** bullets 2 and 3 only appear in format-3 journals.
+**
+** Call the value from the second bullet "nRec". nRec is the number of
+** valid page entries in the journal. In most cases, you can compute the
+** value of nRec from the size of the journal file. But if a power
+** failure occurred while the journal was being written, it could be the
+** case that the size of the journal file had already been increased but
+** the extra entries had not yet made it safely to disk. In such a case,
+** the value of nRec computed from the file size would be too large. For
+** that reason, we always use the nRec value in the header.
+**
+** If the nRec value is 0xffffffff it means that nRec should be computed
+** from the file size. This value is used when the user selects the
+** no-sync option for the journal. A power failure could lead to corruption
+** in this case. But for things like temporary table (which will be
+** deleted when the power is restored) we don't care.
+**
+** Journal formats 1 and 2 do not have an nRec value in the header so we
+** have to compute nRec from the file size. This has risks (as described
+** above) which is why all persistent tables have been changed to use
+** format 3.
**
** If the file opened as the journal file is not a well-formed
-** journal file (as determined by looking at the magic number
-** at the beginning) then this routine returns SQLITE_PROTOCOL.
-** If any other errors occur during playback, the database will
-** likely be corrupted, so the PAGER_ERR_CORRUPT bit is set in
-** pPager->errMask and SQLITE_CORRUPT is returned. If it all
-** works, then this routine returns SQLITE_OK.
+** journal file then the database will likely already be
+** corrupted, so the PAGER_ERR_CORRUPT bit is set in pPager->errMask
+** and SQLITE_CORRUPT is returned. If it all works, then this routine
+** returns SQLITE_OK.
*/
static int pager_playback(Pager *pPager, int useJournalSize){
off_t szJ; /* Size of the journal file in bytes */
}
/* If the journal file is too small to contain a complete header,
- ** then ignore the journal completely.
+ ** it must mean that the process that created the journal was just
+ ** beginning to write the journal file when it died. In that case,
+ ** the database file should have still been completely unchanged.
+ ** Nothing needs to be rolled back. We can safely ignore this journal.
*/
if( szJ < sizeof(aMagic)+sizeof(Pgno) ){
goto end_playback;
** header. We already did this test once above, but at the prior
** test, we did not know the journal format and so we had to assume
** the smallest possible header. Now we know the header is bigger
- ** than that so we test again.
+ ** than the minimum so we test again.
*/
goto end_playback;
}
** when it is rolled back.
**
** FULL The journal is synced twice before writes begin on the
-** database (with some additional information being written
-** in between the two syncs. If we assume that writing a
+** database (with some additional information - the nRec field
+** of the journal header - being written in between the two
+** syncs). If we assume that writing a
** single disk sector is atomic, then this mode provides
** assurance that the journal will not be corrupted to the
** point of causing damage to the database during rollback.
/*
** Forward declaration
*/
-static int syncAllPages(Pager*);
+static int syncJournal(Pager*);
/*
** Truncate the file to the number of pages specified.
if( nPage>=(unsigned)pPager->dbSize ){
return SQLITE_OK;
}
- syncAllPages(pPager);
+ syncJournal(pPager);
rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
if( rc==SQLITE_OK ){
pPager->dbSize = nPage;
}
/*
-** Sync the journal and then write all free dirty pages to the database
-** file.
+** Sync the journal. In other words, make sure all the pages that have
+** been written to the journal have actually reached the surface of the
+** disk. It is not safe to modify the original database file until after
+** the journal has been synced. If the original database is modified before
+** the journal is synced and a power failure occurs, the unsynced journal
+** data would be lost and we would be unable to completely rollback the
+** database changes. Database corruption would occur.
+**
+** This routine also updates the nRec field in the header of the journal.
+** (See comments on the pager_playback() routine for additional information.)
+** If the sync mode is FULL, two syncs will occur. First the whole journal
+** is synced, then the nRec field is updated, then a second sync occurs.
**
-** Writing all free dirty pages to the database after the sync is a
-** non-obvious optimization. fsync() is an expensive operation so we
-** want to minimize the number ot times it is called. After an fsync() call,
-** we are free to write dirty pages back to the database. It is best
-** to go ahead and write as many dirty pages as possible to minimize
-** the risk of having to do another fsync() later on. Writing dirty
-** free pages in this way was observed to make database operations go
-** up to 10 times faster.
+** For temporary databases, we do not care if we are able to rollback
+** after a power failure, so sync occurs.
**
-** If we are writing to temporary database, there is no need to preserve
-** the integrity of the journal file, so we can save time and skip the
-** fsync().
+** This routine clears the needSync field of every page current held in
+** memory.
*/
-static int syncAllPages(Pager *pPager){
+static int syncJournal(Pager *pPager){
PgHdr *pPg;
int rc = SQLITE_OK;
assert( !pPager->noSync );
#ifndef NDEBUG
{
+ /* Make sure the pPager->nRec counter we are keeping agrees
+ ** with the nRec computed from the size of the journal file.
+ */
off_t hdrSz, pgSz, jSz;
hdrSz = JOURNAL_HDR_SZ(journal_format);
pgSz = JOURNAL_PG_SZ(journal_format);
}
#endif
if( journal_format>=3 ){
+ /* Write the nRec value into the journal file header */
off_t szJ;
if( pPager->fullSync ){
TRACE1("SYNC\n");
** it can't be helped.
*/
if( pPg==0 ){
- int rc = syncAllPages(pPager);
+ int rc = syncJournal(pPager);
if( rc!=0 ){
sqlitepager_rollback(pPager);
return SQLITE_IOERR;
return rc;
}
assert( pPager->journalOpen );
- rc = syncAllPages(pPager);
+ rc = syncJournal(pPager);
if( rc!=SQLITE_OK ){
goto commit_abort;
}