diff --git a/VERSION b/VERSION index 49cdd668e1..834f262953 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.6 +2.8.0 diff --git a/manifest b/manifest index b390c77da7..cb939577b2 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index 54315e6596..60f0f4c99e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e21afb82b53eade9ee267a97c58db0606f0c0a41 \ No newline at end of file +8ec5632536eea31197a3b1fd6abc57881a0cf1d7 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index 7b0b4bd937..a8aa1467a6 100644 --- a/src/pager.c +++ b/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; icksumInit, 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; ijfd, 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,17 +726,19 @@ 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 ){ pPager->errMask |= PAGER_ERR_CORRUPT; @@ -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; + } + 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; -#ifndef NDEBUG - rc = sqliteOsFileSize(&pPager->jfd, &pPager->syncJSize); - 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->nPagemxPage || 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->syncJSizesyncJSize = sizeof(aJournalMagic)+sizeof(Pgno); + if( pPager->syncJSizesyncJSize = 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); diff --git a/src/pager.h b/src/pager.h index e1a09adbf5..64692b6ecd 100644 --- a/src/pager.h +++ b/src/pager.h @@ -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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0920b47f3e..5d8ee0a185 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 */ diff --git a/src/test2.c b/src/test2.c index b5c6912b62..cd57d3e3bd 100644 --- a/src/test2.c +++ b/src/test2.c @@ -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; } diff --git a/test/trans.test b/test/trans.test index 225f5b77b7..fe5e43d1df 100644 --- a/test/trans.test +++ b/test/trans.test @@ -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;