Modify the journal format to be more robust against garbage that might appear
in the file after a power failure. The changes are mostly working but more testing is still required. This check-in is to checkpoint the changes so far. (CVS 861) FossilOrigin-Name: 8ec5632536eea31197a3b1fd6abc57881a0cf1d7
This commit is contained in:
parent
324ccefeb5
commit
968af52acf
22
manifest
22
manifest
@ -1,9 +1,9 @@
|
||||
C Make\sthe\sshell\srun\smuch\sfaster\sfor\sinputs\swhere\sa\ssingle\sSQL\sstatement\sspans\nthousands\sof\slines\sby\savoiding\sthe\scall\sto\ssqlite_complete()\sunless\sthe\ninput\sends\sin\sa\ssemicolon.\s(CVS\s860)
|
||||
D 2003-02-05T14:06:20
|
||||
C Modify\sthe\sjournal\sformat\sto\sbe\smore\srobust\sagainst\sgarbage\sthat\smight\sappear\nin\sthe\sfile\safter\sa\spower\sfailure.\s\sThe\schanges\sare\smostly\sworking\sbut\smore\ntesting\sis\sstill\srequired.\s\sThis\scheck-in\sis\sto\scheckpoint\sthe\schanges\sso\sfar.\s(CVS\s861)
|
||||
D 2003-02-11T14:55:41
|
||||
F Makefile.in 6606854b1512f185b8e8c779b8d7fc2750463d64
|
||||
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||
F VERSION e19dab5300287e54674b5d57f6343dfe540d7f2c
|
||||
F VERSION e5b03976c56deafa24511d6ef17d64a28679e9bd
|
||||
F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
|
||||
F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
|
||||
F config.sub f14b07d544ca26b5d698259045136b783e18fc7f
|
||||
@ -33,8 +33,8 @@ F src/main.c 764a72e6a4f021ae1d3db7e82dab625075f4fedb
|
||||
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
||||
F src/os.c ed27e178e0c4b71f2807da81b8851f0fadc50778
|
||||
F src/os.h afa3e096213bad86845f8bdca81a9e917505e401
|
||||
F src/pager.c 7ca152bb9fcab56e2f6df62e2ebd20f538214fad
|
||||
F src/pager.h 540833e8cb826b80ce2e39aa917deee5e12db626
|
||||
F src/pager.c 4adf3cc6d031504e2941791f5838138d4fad2803
|
||||
F src/pager.h ce264d558c8ec289f5a9c50ca4ad499e3522a67e
|
||||
F src/parse.y cdaed5009423d851708848bd279147c268e6022e
|
||||
F src/printf.c f8fd911a8738f9b2eb07aca2870473d34707055d
|
||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||
@ -42,11 +42,11 @@ F src/select.c d12d4c12d6536deccdede90b482d24f0590f5dc8
|
||||
F src/shell.c 0d260a007e0668fc7dda2b0c89bd597ef2966ec6
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 6f648803f2ffb9beb35cb1cfa42b323d55519171
|
||||
F src/sqliteInt.h f22092ed33fea784f58bcd57b90c0babd16a0e29
|
||||
F src/sqliteInt.h 8beea34db78e1452569e22b020934002da5debee
|
||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||
F src/tclsqlite.c 8167d40fd34036701e07492d07a6f9e5c4015241
|
||||
F src/test1.c eb05abd3ec6822f800476c04aed4db112690b144
|
||||
F src/test2.c 03f05e984c8e2f2badc44644d42baf72b249096b
|
||||
F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
|
||||
F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728
|
||||
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
|
||||
F src/tokenize.c bc40937d6666f188037aa3e54f0a2661a6fef6d1
|
||||
@ -108,7 +108,7 @@ F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
|
||||
F test/tclsqlite.test f650195b8124aca24bee175393a1ed2e5a544a38
|
||||
F test/temptable.test 03b7bdb7d6ce2c658ad20c94b037652c6cad34e0
|
||||
F test/tester.tcl 6f603d90881bd835ea27c568a7fecaa57dce91cc
|
||||
F test/trans.test 10b53c77e2cc4ad9529c15fdcb390b8d5722ea65
|
||||
F test/trans.test 0d3584f8d50646ff22776ac0a28521a410a673d6
|
||||
F test/trigger1.test ec1da76e1a9f618deb96e505f459dcf8a23f2247
|
||||
F test/trigger2.test 592daa956dc62b19930fe673405e389a80c5764e
|
||||
F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
|
||||
@ -155,7 +155,7 @@ F www/speed.tcl 4d463e2aea41f688ed320a937f93ff885be918c3
|
||||
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
|
||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||
P b68792315883eed8523f5e11856ec8378dc972c1
|
||||
R 58fd040d5f3c326dafe0f8ea3c342f54
|
||||
P e21afb82b53eade9ee267a97c58db0606f0c0a41
|
||||
R 4995a0d231005397acc1e0444b8f7298
|
||||
U drh
|
||||
Z b59e898cbd5005ef4b11e662eace5dcc
|
||||
Z 334d201676289fffaf14b926d96d12b8
|
||||
|
@ -1 +1 @@
|
||||
e21afb82b53eade9ee267a97c58db0606f0c0a41
|
||||
8ec5632536eea31197a3b1fd6abc57881a0cf1d7
|
285
src/pager.c
285
src/pager.c
@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.72 2003/01/29 22:58:26 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.73 2003/02/11 14:55:41 drh Exp $
|
||||
*/
|
||||
#include "os.h" /* Must be first to enable large file support */
|
||||
#include "sqliteInt.h"
|
||||
@ -101,7 +101,7 @@ struct PgHdr {
|
||||
u8 alwaysRollback; /* Disable dont_rollback() for this page */
|
||||
PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */
|
||||
/* SQLITE_PAGE_SIZE bytes of page data follow this header */
|
||||
/* Pager.nExtra bytes of local data follow the page data */
|
||||
/* Pager.nExtra bytes of local data follow the page data and checksum */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -138,6 +138,8 @@ struct Pager {
|
||||
#ifndef NDEBUG
|
||||
off_t syncJSize; /* Size of journal at last fsync() call */
|
||||
#endif
|
||||
int nRec; /* Number of pages written to the journal */
|
||||
u32 cksumInit; /* Quasi-random value added to every checksum */
|
||||
int ckptNRec; /* Number of records in the checkpoint journal */
|
||||
int nExtra; /* Add this many bytes to each in-memory page */
|
||||
void (*xDestructor)(void*); /* Call this routine when freeing pages */
|
||||
@ -152,6 +154,7 @@ struct Pager {
|
||||
u8 ckptInUse; /* True we are in a checkpoint */
|
||||
u8 ckptAutoopen; /* Open ckpt journal when main journal is opened*/
|
||||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
|
||||
u8 errMask; /* One of several kinds of errors */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
@ -159,7 +162,6 @@ struct Pager {
|
||||
u8 needSync; /* True if an fsync() is needed on the journal */
|
||||
u8 dirtyFile; /* True if database file has changed in any way */
|
||||
u8 alwaysRollback; /* Disable dont_rollback() for all pages */
|
||||
u8 journalFormat; /* Version number of the journal file */
|
||||
u8 *aInJournal; /* One bit for each page in the database file */
|
||||
u8 *aInCkpt; /* One bit for each page in the database */
|
||||
PgHdr *pFirst, *pLast; /* List of free pages */
|
||||
@ -181,6 +183,10 @@ struct Pager {
|
||||
/*
|
||||
** The journal file contains page records in the following
|
||||
** format.
|
||||
**
|
||||
** Actually, this structure is the complete page record for pager
|
||||
** formats less than 3. Beginning with format 3, this record is surrounded
|
||||
** by two checksums.
|
||||
*/
|
||||
typedef struct PageRecord PageRecord;
|
||||
struct PageRecord {
|
||||
@ -192,32 +198,69 @@ struct PageRecord {
|
||||
** Journal files begin with the following magic string. The data
|
||||
** was obtained from /dev/random. It is used only as a sanity check.
|
||||
**
|
||||
** There are two journal formats. The older journal format writes
|
||||
** 32-bit integers in the byte-order of the host machine. The new
|
||||
** format writes integers as big-endian. All new journals use the
|
||||
** There are three journal formats (so far). The 1st journal format writes
|
||||
** 32-bit integers in the byte-order of the host machine. New
|
||||
** formats writes integers as big-endian. All new journals use the
|
||||
** new format, but we have to be able to read an older journal in order
|
||||
** to roll it back.
|
||||
** to rollback journals created by older versions of the library.
|
||||
**
|
||||
** The 3rd journal format (added for 2.8.0) adds additional sanity
|
||||
** checking information to the journal. If the power fails while the
|
||||
** journal is being written, semi-random garbage data might appear in
|
||||
** the journal file after power is restored. If an attempt is then made
|
||||
** to roll the journal back, the database could be corrupted. The additional
|
||||
** sanity checking data is an attempt to discover the garbage in the
|
||||
** journal and ignore it.
|
||||
**
|
||||
** The sanity checking information for the 3rd journal format consists
|
||||
** of a 32-bit checksum on each page of data. The checksum covers both
|
||||
** the page number and the SQLITE_PAGE_SIZE bytes of data for the page.
|
||||
** This cksum is initialized to a 32-bit random value that appears in the
|
||||
** journal file right after the header. The random initializer is important,
|
||||
** because garbage data that appears at the end of a journal is likely
|
||||
** data that was once in other files that have now been deleted. If the
|
||||
** garbage data came from an obsolete journal file, the checksums might
|
||||
** be correct. But by initializing the checksum to random value which
|
||||
** is different for every journal, we minimize that risk.
|
||||
*/
|
||||
static const unsigned char aOldJournalMagic[] = {
|
||||
static const unsigned char aJournalMagic1[] = {
|
||||
0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4,
|
||||
};
|
||||
static const unsigned char aJournalMagic[] = {
|
||||
static const unsigned char aJournalMagic2[] = {
|
||||
0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd5,
|
||||
};
|
||||
#define SQLITE_NEW_JOURNAL_FORMAT 1
|
||||
#define SQLITE_OLD_JOURNAL_FORMAT 0
|
||||
static const unsigned char aJournalMagic3[] = {
|
||||
0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd6,
|
||||
};
|
||||
#define JOURNAL_FORMAT_1 1
|
||||
#define JOURNAL_FORMAT_2 2
|
||||
#define JOURNAL_FORMAT_3 3
|
||||
|
||||
/*
|
||||
** The following integer, if set, causes journals to be written in the
|
||||
** old format. This is used for testing purposes only - to make sure
|
||||
** the code is able to rollback an old journal.
|
||||
** The following integer determines what format to use when creating
|
||||
** new primary journal files. By default we always use format 3.
|
||||
** When testing, we can set this value to older journal formats in order to
|
||||
** make sure that newer versions of the library are able to rollback older
|
||||
** journal files.
|
||||
**
|
||||
** Note that checkpoint journals always use format 2 and omit the header.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
int pager_old_format = 0;
|
||||
int journal_format = 3;
|
||||
#else
|
||||
# define pager_old_format 0
|
||||
# define journal_format 3
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The size of the header and of each page in the journal varies according
|
||||
** to which journal format is being used. The following macros figure out
|
||||
** the sizes based on format numbers.
|
||||
*/
|
||||
#define JOURNAL_HDR_SZ(X) \
|
||||
(sizeof(aJournalMagic1) + sizeof(Pgno) + ((X)>=3)*2*sizeof(u32))
|
||||
#define JOURNAL_PG_SZ(X) \
|
||||
(SQLITE_PAGE_SIZE + sizeof(Pgno) + ((X)>=3)*sizeof(u32))
|
||||
|
||||
/*
|
||||
** Enable reference count tracking here:
|
||||
*/
|
||||
@ -240,11 +283,11 @@ int pager_old_format = 0;
|
||||
/*
|
||||
** Read a 32-bit integer from the given file descriptor
|
||||
*/
|
||||
static int read32bits(Pager *pPager, OsFile *fd, u32 *pRes){
|
||||
static int read32bits(int format, OsFile *fd, u32 *pRes){
|
||||
u32 res;
|
||||
int rc;
|
||||
rc = sqliteOsRead(fd, &res, sizeof(res));
|
||||
if( rc==SQLITE_OK && pPager->journalFormat==SQLITE_NEW_JOURNAL_FORMAT ){
|
||||
if( rc==SQLITE_OK && format>JOURNAL_FORMAT_1 ){
|
||||
unsigned char ac[4];
|
||||
memcpy(ac, &res, 4);
|
||||
res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3];
|
||||
@ -259,7 +302,7 @@ static int read32bits(Pager *pPager, OsFile *fd, u32 *pRes){
|
||||
*/
|
||||
static int write32bits(OsFile *fd, u32 val){
|
||||
unsigned char ac[4];
|
||||
if( pager_old_format ){
|
||||
if( journal_format<=1 ){
|
||||
return sqliteOsWrite(fd, &val, 4);
|
||||
}
|
||||
ac[0] = (val>>24) & 0xff;
|
||||
@ -273,11 +316,10 @@ static int write32bits(OsFile *fd, u32 val){
|
||||
** Write a 32-bit integer into a page header right before the
|
||||
** page data. This will overwrite the PgHdr.pDirty pointer.
|
||||
*/
|
||||
static void storePageNumber(PgHdr *p){
|
||||
u32 val = p->pgno;
|
||||
static void store32bits(u32 val, PgHdr *p, int offset){
|
||||
unsigned char *ac;
|
||||
ac = &((char*)PGHDR_TO_DATA(p))[-4];
|
||||
if( pager_old_format ){
|
||||
ac = &((char*)PGHDR_TO_DATA(p))[offset];
|
||||
if( journal_format<=1 ){
|
||||
memcpy(ac, &val, 4);
|
||||
}else{
|
||||
ac[0] = (val>>24) & 0xff;
|
||||
@ -423,22 +465,54 @@ static int pager_unwritelock(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute and return a checksum for the page of data.
|
||||
*/
|
||||
static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
|
||||
u32 cksum = pPager->cksumInit + pgno;
|
||||
/* const u8 *a = (const u8*)aData;
|
||||
int i;
|
||||
for(i=0; i<SQLITE_PAGE_SIZE; i++){ cksum += a[i]; } */
|
||||
/* fprintf(stderr,"CKSUM for %p(%08x) page %d: %08x\n", pPager, pPager->cksumInit, pgno, cksum); */
|
||||
return cksum;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a single page from the journal file opened on file descriptor
|
||||
** jfd. Playback this one page.
|
||||
**
|
||||
** There are three different journal formats. The format parameter determines
|
||||
** which format is used by the journal that is played back.
|
||||
*/
|
||||
static int pager_playback_one_page(Pager *pPager, OsFile *jfd){
|
||||
static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
||||
int rc;
|
||||
PgHdr *pPg; /* An existing page in the cache */
|
||||
PageRecord pgRec;
|
||||
u32 cksum;
|
||||
|
||||
rc = read32bits(pPager, jfd, &pgRec.pgno);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
rc = read32bits(format, jfd, &pgRec.pgno);
|
||||
if( rc!=SQLITE_OK ) return SQLITE_DONE;
|
||||
rc = sqliteOsRead(jfd, &pgRec.aData, sizeof(pgRec.aData));
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( rc!=SQLITE_OK ) return SQLITE_DONE;
|
||||
|
||||
/* Sanity checking on the page */
|
||||
if( pgRec.pgno>pPager->dbSize || pgRec.pgno==0 ) return SQLITE_CORRUPT;
|
||||
/* Sanity checking on the page. This is more important that I originally
|
||||
** thought. If a power failure occurs while the journal is being written,
|
||||
** it could cause invalid data to be written into the journal. We need to
|
||||
** detect this invalid data (with high probability) and ignore it.
|
||||
*/
|
||||
if( pgRec.pgno==0 ){
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
if( pgRec.pgno>pPager->dbSize ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
rc = read32bits(format, jfd, &cksum);
|
||||
if( rc ) return SQLITE_DONE;
|
||||
if( pager_cksum(pPager, pgRec.pgno, pgRec.aData)!=cksum ){
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Playback the page. Update the in-memory copy of the page
|
||||
** at the same time, if there is one.
|
||||
@ -490,10 +564,12 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd){
|
||||
** works, then this routine returns SQLITE_OK.
|
||||
*/
|
||||
static int pager_playback(Pager *pPager){
|
||||
off_t nRec; /* Number of Records */
|
||||
off_t szJ; /* Size of the journal file in bytes */
|
||||
int nRec; /* Number of Records in the journal */
|
||||
int i; /* Loop counter */
|
||||
Pgno mxPg = 0; /* Size of the original file in pages */
|
||||
unsigned char aMagic[sizeof(aJournalMagic)];
|
||||
int format; /* Format of the journal file. */
|
||||
unsigned char aMagic[sizeof(aJournalMagic1)];
|
||||
int rc;
|
||||
|
||||
/* Figure out how many records are in the journal. Abort early if
|
||||
@ -501,14 +577,13 @@ static int pager_playback(Pager *pPager){
|
||||
*/
|
||||
assert( pPager->journalOpen );
|
||||
sqliteOsSeek(&pPager->jfd, 0);
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &nRec);
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &szJ);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
if( nRec < sizeof(aMagic)+sizeof(Pgno) ){
|
||||
if( szJ < sizeof(aMagic)+sizeof(Pgno) ){
|
||||
goto end_playback;
|
||||
}
|
||||
nRec = (nRec - (sizeof(aMagic)+sizeof(Pgno))) / sizeof(PageRecord);
|
||||
|
||||
/* Read the beginning of the journal and truncate the
|
||||
** database file back to its original size.
|
||||
@ -518,15 +593,28 @@ static int pager_playback(Pager *pPager){
|
||||
rc = SQLITE_PROTOCOL;
|
||||
goto end_playback;
|
||||
}
|
||||
if( memcmp(aMagic, aOldJournalMagic, sizeof(aMagic))==0 ){
|
||||
pPager->journalFormat = SQLITE_OLD_JOURNAL_FORMAT;
|
||||
}else if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))==0 ){
|
||||
pPager->journalFormat = SQLITE_NEW_JOURNAL_FORMAT;
|
||||
if( memcmp(aMagic, aJournalMagic3, sizeof(aMagic))==0 ){
|
||||
format = JOURNAL_FORMAT_3;
|
||||
}else if( memcmp(aMagic, aJournalMagic2, sizeof(aMagic))==0 ){
|
||||
format = JOURNAL_FORMAT_2;
|
||||
}else if( memcmp(aMagic, aJournalMagic1, sizeof(aMagic))==0 ){
|
||||
format = JOURNAL_FORMAT_1;
|
||||
}else{
|
||||
rc = SQLITE_PROTOCOL;
|
||||
goto end_playback;
|
||||
}
|
||||
rc = read32bits(pPager, &pPager->jfd, &mxPg);
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
rc = read32bits(format, &pPager->jfd, &nRec);
|
||||
if( rc ) goto end_playback;
|
||||
rc = read32bits(format, &pPager->jfd, &pPager->cksumInit);
|
||||
if( rc ) goto end_playback;
|
||||
if( nRec==0xffffffff ){
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(3))/JOURNAL_PG_SZ(3);
|
||||
}
|
||||
}else{
|
||||
nRec = (szJ - (sizeof(aMagic)+sizeof(Pgno))) / sizeof(PageRecord);
|
||||
}
|
||||
rc = read32bits(format, &pPager->jfd, &mxPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
@ -538,9 +626,15 @@ static int pager_playback(Pager *pPager){
|
||||
|
||||
/* Copy original pages out of the journal and back into the database file.
|
||||
*/
|
||||
for(i=nRec-1; i>=0; i--){
|
||||
rc = pager_playback_one_page(pPager, &pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
for(i=0; i<nRec; i++){
|
||||
rc = pager_playback_one_page(pPager, &pPager->jfd, format);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_DONE ){
|
||||
fprintf(stderr,"Playback complete after %d of %d records\n", i, nRec);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -598,7 +692,8 @@ end_playback:
|
||||
** at offset pPager->ckptJSize.
|
||||
*/
|
||||
static int pager_ckpt_playback(Pager *pPager){
|
||||
off_t nRec; /* Number of Records */
|
||||
off_t szJ; /* Size of the full journal */
|
||||
int nRec; /* Number of Records */
|
||||
int i; /* Loop counter */
|
||||
int rc;
|
||||
|
||||
@ -614,15 +709,13 @@ static int pager_ckpt_playback(Pager *pPager){
|
||||
nRec = pPager->ckptNRec;
|
||||
|
||||
/* Copy original pages out of the checkpoint journal and back into the
|
||||
** database file.
|
||||
** database file. Note that the checkpoint journal always uses format
|
||||
** 2 instead of format 3 since it does not need to be concerned with
|
||||
** power failures corrupting the journal and can thus omit the checksums.
|
||||
*/
|
||||
if( pager_old_format ){
|
||||
pPager->journalFormat = SQLITE_OLD_JOURNAL_FORMAT;
|
||||
}else{
|
||||
pPager->journalFormat = SQLITE_NEW_JOURNAL_FORMAT;
|
||||
}
|
||||
for(i=nRec-1; i>=0; i--){
|
||||
rc = pager_playback_one_page(pPager, &pPager->cpfd);
|
||||
rc = pager_playback_one_page(pPager, &pPager->cpfd, 2);
|
||||
assert( rc!=SQLITE_DONE );
|
||||
if( rc!=SQLITE_OK ) goto end_ckpt_playback;
|
||||
}
|
||||
|
||||
@ -633,16 +726,18 @@ static int pager_ckpt_playback(Pager *pPager){
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_ckpt_playback;
|
||||
}
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &nRec);
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &szJ);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_ckpt_playback;
|
||||
}
|
||||
nRec = (nRec - pPager->ckptJSize)/sizeof(PageRecord);
|
||||
nRec = (szJ - pPager->ckptJSize)/JOURNAL_PG_SZ(journal_format);
|
||||
for(i=nRec-1; i>=0; i--){
|
||||
rc = pager_playback_one_page(pPager, &pPager->jfd);
|
||||
if( rc!=SQLITE_OK ) goto end_ckpt_playback;
|
||||
rc = pager_playback_one_page(pPager, &pPager->jfd, journal_format);
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( rc!=SQLITE_DONE );
|
||||
goto end_ckpt_playback;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end_ckpt_playback:
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -931,15 +1026,32 @@ static int syncAllPages(Pager *pPager){
|
||||
*/
|
||||
if( pPager->needSync ){
|
||||
if( !pPager->tempFile ){
|
||||
off_t szJ;
|
||||
assert( pPager->journalOpen );
|
||||
assert( !pPager->noSync );
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
off_t hdrSz, pgSz;
|
||||
hdrSz = JOURNAL_HDR_SZ(journal_format);
|
||||
pgSz = JOURNAL_PG_SZ(journal_format);
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &pPager->syncJSize);
|
||||
if( rc!=0 ) return rc;
|
||||
assert( pPager->nRec*pgSz+hdrSz==pPager->syncJSize );
|
||||
}
|
||||
#endif
|
||||
if( pPager->fullSync ){
|
||||
TRACE1("SYNC\n");
|
||||
rc = sqliteOsSync(&pPager->jfd);
|
||||
if( rc!=0 ) return rc;
|
||||
#ifndef NDEBUG
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &pPager->syncJSize);
|
||||
}
|
||||
sqliteOsSeek(&pPager->jfd, sizeof(aJournalMagic1));
|
||||
write32bits(&pPager->jfd, pPager->nRec);
|
||||
szJ = JOURNAL_HDR_SZ(journal_format) +
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format);
|
||||
sqliteOsSeek(&pPager->jfd, szJ);
|
||||
TRACE1("SYNC\n");
|
||||
rc = sqliteOsSync(&pPager->jfd);
|
||||
if( rc!=0 ) return rc;
|
||||
#endif
|
||||
pPager->journalStarted = 1;
|
||||
}
|
||||
pPager->needSync = 0;
|
||||
@ -1107,7 +1219,8 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
pPager->nMiss++;
|
||||
if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){
|
||||
/* Create a new page */
|
||||
pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE + pPager->nExtra );
|
||||
pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE
|
||||
+ sizeof(u32) + pPager->nExtra );
|
||||
if( pPg==0 ){
|
||||
*ppPage = 0;
|
||||
pager_unwritelock(pPager);
|
||||
@ -1370,13 +1483,23 @@ static int pager_open_journal(Pager *pPager){
|
||||
pPager->journalStarted = 0;
|
||||
pPager->needSync = 0;
|
||||
pPager->alwaysRollback = 0;
|
||||
pPager->nRec = 0;
|
||||
sqlitepager_pagecount(pPager);
|
||||
pPager->origDbSize = pPager->dbSize;
|
||||
if( pager_old_format ){
|
||||
rc = sqliteOsWrite(&pPager->jfd, aOldJournalMagic,
|
||||
sizeof(aOldJournalMagic));
|
||||
if( journal_format==JOURNAL_FORMAT_3 ){
|
||||
rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = write32bits(&pPager->jfd, pPager->tempFile ? 0xffffffff : 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->cksumInit = (u32)sqliteRandomInteger();
|
||||
rc = write32bits(&pPager->jfd, pPager->cksumInit);
|
||||
}
|
||||
}else if( journal_format==JOURNAL_FORMAT_2 ){
|
||||
rc = sqliteOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2));
|
||||
}else{
|
||||
rc = sqliteOsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
|
||||
assert( journal_format==JOURNAL_FORMAT_1 );
|
||||
rc = sqliteOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1));
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = write32bits(&pPager->jfd, pPager->dbSize);
|
||||
@ -1504,8 +1627,22 @@ int sqlitepager_write(void *pData){
|
||||
*/
|
||||
if( !pPg->inJournal && pPager->useJournal ){
|
||||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||
storePageNumber(pPg);
|
||||
rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4);
|
||||
int szPg;
|
||||
u32 saved;
|
||||
if( journal_format>=JOURNAL_FORMAT_3 ){
|
||||
u32 cksum = pager_cksum(pPager, pPg->pgno, pData);
|
||||
saved = *(u32*)PGHDR_TO_EXTRA(pPg);
|
||||
store32bits(cksum, pPg, SQLITE_PAGE_SIZE);
|
||||
szPg = SQLITE_PAGE_SIZE+8;
|
||||
}else{
|
||||
szPg = SQLITE_PAGE_SIZE+4;
|
||||
}
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
|
||||
if( journal_format>=JOURNAL_FORMAT_3 ){
|
||||
*(u32*)PGHDR_TO_EXTRA(pPg) = saved;
|
||||
}
|
||||
pPager->nRec++;
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlitepager_rollback(pPager);
|
||||
pPager->errMask |= PAGER_ERR_FULL;
|
||||
@ -1530,11 +1667,13 @@ int sqlitepager_write(void *pData){
|
||||
}
|
||||
|
||||
/* If the checkpoint journal is open and the page is not in it,
|
||||
** then write the current page to the checkpoint journal.
|
||||
** then write the current page to the checkpoint journal. Note that
|
||||
** the checkpoint journal always uses the simplier format 2 that lacks
|
||||
** checksums. The header is also omitted from the checkpoint journal.
|
||||
*/
|
||||
if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
|
||||
assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
|
||||
storePageNumber(pPg);
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlitepager_rollback(pPager);
|
||||
@ -1729,13 +1868,15 @@ int sqlitepager_rollback(Pager *pPager){
|
||||
** loss.
|
||||
*/
|
||||
if( !pPager->noSync ){
|
||||
int m = JOURNAL_HDR_SZ(journal_format);
|
||||
assert( !pPager->tempFile );
|
||||
if( pPager->syncJSize<sizeof(aJournalMagic)+sizeof(Pgno) ){
|
||||
pPager->syncJSize = sizeof(aJournalMagic)+sizeof(Pgno);
|
||||
if( pPager->syncJSize<m ){
|
||||
pPager->syncJSize = m;
|
||||
}
|
||||
TRACE2("TRUNCATE JOURNAL %lld\n", pPager->syncJSize);
|
||||
rc = sqliteOsTruncate(&pPager->jfd, pPager->syncJSize);
|
||||
if( rc ) return rc;
|
||||
pPager->nRec = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1803,8 +1944,14 @@ int sqlitepager_ckpt_begin(Pager *pPager){
|
||||
sqliteOsReadLock(&pPager->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &pPager->ckptJSize);
|
||||
if( rc ) goto ckpt_begin_failed;
|
||||
assert( pPager->ckptJSize ==
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format)+JOURNAL_HDR_SZ(journal_format) );
|
||||
#endif
|
||||
pPager->ckptJSize = pPager->nRec*JOURNAL_PG_SZ(journal_format)
|
||||
+ JOURNAL_HDR_SZ(journal_format);
|
||||
pPager->ckptSize = pPager->dbSize;
|
||||
if( !pPager->ckptOpen ){
|
||||
rc = sqlitepager_opentemp(zTemp, &pPager->cpfd);
|
||||
|
@ -13,7 +13,7 @@
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.18 2002/12/02 04:25:21 drh Exp $
|
||||
** @(#) $Id: pager.h,v 1.19 2003/02/11 14:55:41 drh Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -74,5 +74,5 @@ int *sqlitepager_stats(Pager*);
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlitepager_refdump(Pager*);
|
||||
int pager_refinfo_enable;
|
||||
int pager_old_format;
|
||||
int journal_format;
|
||||
#endif
|
||||
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.159 2003/01/29 18:46:53 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.160 2003/02/11 14:55:41 drh Exp $
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "sqlite.h"
|
||||
@ -417,7 +417,7 @@ struct FKey {
|
||||
** referenced table row is propagated into the row that holds the
|
||||
** foreign key.
|
||||
**
|
||||
** The following there symbolic values are used to record which type
|
||||
** The following symbolic values are used to record which type
|
||||
** of action to take.
|
||||
*/
|
||||
#define OE_None 0 /* There is no constraint to check */
|
||||
|
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test2.c,v 1.14 2002/12/17 14:19:49 drh Exp $
|
||||
** $Id: test2.c,v 1.15 2003/02/11 14:55:41 drh Exp $
|
||||
*/
|
||||
#include "os.h"
|
||||
#include "sqliteInt.h"
|
||||
@ -553,8 +553,8 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
|
||||
Tcl_LinkVar(interp, "sqlite_io_error_pending",
|
||||
(char*)&sqlite_io_error_pending, TCL_LINK_INT);
|
||||
#ifdef SQLITE_TEST
|
||||
Tcl_LinkVar(interp, "pager_old_format",
|
||||
(char*)&pager_old_format, TCL_LINK_INT);
|
||||
Tcl_LinkVar(interp, "journal_format",
|
||||
(char*)&journal_format, TCL_LINK_INT);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is database locks.
|
||||
#
|
||||
# $Id: trans.test,v 1.16 2002/08/18 20:28:07 drh Exp $
|
||||
# $Id: trans.test,v 1.17 2003/02/11 14:55:42 drh Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@ -857,7 +857,7 @@ if {[info exists ISQUICK]} {
|
||||
for {set i 2} {$i<=$limit} {incr i} {
|
||||
set ::sig [signature]
|
||||
set cnt [lindex $::sig 0]
|
||||
set ::pager_old_format [expr {($i%4)==0}]
|
||||
set ::journal_format [expr {($i%3)+1}]
|
||||
do_test trans-9.$i.1-$cnt {
|
||||
execsql {
|
||||
BEGIN;
|
||||
|
Loading…
Reference in New Issue
Block a user