From df2566a33d3808521f53302960709b421e23474a Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Wed, 7 May 2008 19:11:03 +0000 Subject: [PATCH] Fix some problems with multi-file transactions in persistent journal mode. (CVS 5102) FossilOrigin-Name: e98a7f87f91c62676f94ad5a0c4980ab929ca79d --- manifest | 18 ++++++++-------- manifest.uuid | 2 +- src/btree.c | 28 ++++++++++++------------ src/pager.c | 53 ++++++++++++++++++++++++++++++++++------------ test/jrnlmode.test | 45 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 107 insertions(+), 39 deletions(-) diff --git a/manifest b/manifest index 56338c1f1f..33ffeb7208 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\stest\scases\sfor\scorrupt\sSerialTypeLen\sheader\svalues,\sand\sadditional\scheck\sto\simprove\sdetection\sof\scorrupt\svalues.\s(CVS\s5101) -D 2008-05-07T18:59:29 +C Fix\ssome\sproblems\swith\smulti-file\stransactions\sin\spersistent\sjournal\smode.\s(CVS\s5102) +D 2008-05-07T19:11:03 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7 F Makefile.in 8b9b8263852f0217157f9042b8e3dae7427ec739 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -85,7 +85,7 @@ F src/attach.c 496cc628b2e8c4d8db99d7c136761fcbebd8420b F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627 F src/bitvec.c 8ec2212cfb702bc4f402c0b7ae7623d85320c714 F src/btmutex.c 483ced3c52205b04b97df69161fadbf87f4f1ea2 -F src/btree.c 556adb8f6905303d6ea00d35a0d1e1fe8a07efe2 +F src/btree.c 7401bcbdabd87360351953e5009530c47e4fb305 F src/btree.h 8826591bf54dd35fcf2e67473d5f1bae253861c7 F src/btreeInt.h dc04ee33d8eb84714b2acdf81336fbbf6e764530 F src/build.c a52d9d51341444a2131e3431608f245db80d9591 @@ -121,7 +121,7 @@ F src/os_common.h e8b748b2f2ecc8a498e50bfe5d8721f189c19d2a F src/os_os2.c 30c378b093d9c17387ebb0ebbf21b7d55a98202b F src/os_unix.c a810e2aefdaddacf479407f76f8f4ca381d231b2 F src/os_win.c 3a60bddd07ea6f8adb2314dd5996ac97b988f403 -F src/pager.c 547079d36fb3ca227e77e669268ea910c90774ef +F src/pager.c fc173b7ee0b9ee630688466adacd506225417eb7 F src/pager.h 4f051fd856de6fd3c19aef5f82eace54122b9173 F src/parse.y fc4bd35c6088901f7c8daead26c6fb11c87d22e7 F src/pragma.c 2e4bb2e76e48a32750529fdc4bfe86ac5f54e01b @@ -351,7 +351,7 @@ F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19 -F test/jrnlmode.test 89acaa81503e349a49da73570b1a104f8fd93de7 +F test/jrnlmode.test 5b650ba0630fc1089688e18bb7f0c9b8a33417ed F test/jrnlmode2.test e48ec49320a3f849a5036e3551bf2394112a4aae F test/jrnlmode3.test c77f9d4095945f234dddd60ca0f73c24802ed0c1 F test/jrnlmode4.test 8ee031603fef8ed5deba0de8b012a82be6d5a6a0 @@ -634,7 +634,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P ed728104c8e77a5526a2fcb62fea577940731d90 -R 662f2572cbb8e504e1cbbe6b8cab0ff5 -U shane -Z d87bdc7540c0da4b026b69711c54995d +P 530c6360610f737e85608b23ede2646d69d1bc9a +R b3765e5e9b24b682270f8e36c5449957 +U danielk1977 +Z 58d2f3d9b823776050be08ba53db12b8 diff --git a/manifest.uuid b/manifest.uuid index 7fbb5f90d2..f1bb64c6c7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -530c6360610f737e85608b23ede2646d69d1bc9a \ No newline at end of file +e98a7f87f91c62676f94ad5a0c4980ab929ca79d \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index f61be96266..d87eb040f2 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.456 2008/05/07 07:13:16 danielk1977 Exp $ +** $Id: btree.c,v 1.457 2008/05/07 19:11:03 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -6839,20 +6839,20 @@ static int btreeCopyFile(Btree *pTo, Btree *pFrom){ rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pDbPage); + if( rc==SQLITE_OK && i>nFromPage ){ + /* Yeah. It seems wierd to call DontWrite() right after Write(). But + ** that is because the names of those procedures do not exactly + ** represent what they do. Write() really means "put this page in the + ** rollback journal and mark it as dirty so that it will be written + ** to the database file later." DontWrite() undoes the second part of + ** that and prevents the page from being written to the database. The + ** page is still on the rollback journal, though. And that is the + ** whole point of this block: to put pages on the rollback journal. + */ + sqlite3PagerDontWrite(pDbPage); + } + sqlite3PagerUnref(pDbPage); } - if( rc==SQLITE_OK && i>nFromPage ){ - /* Yeah. It seems wierd to call DontWrite() right after Write(). But - ** that is because the names of those procedures do not exactly - ** represent what they do. Write() really means "put this page in the - ** rollback journal and mark it as dirty so that it will be written - ** to the database file later." DontWrite() undoes the second part of - ** that and prevents the page from being written to the database. The - ** page is still on the rollback journal, though. And that is the - ** whole point of this block: to put pages on the rollback journal. - */ - sqlite3PagerDontWrite(pDbPage); - } - sqlite3PagerUnref(pDbPage); } /* Overwrite the data in page i of the target database */ diff --git a/src/pager.c b/src/pager.c index 5a7c2667fb..e99e60a53b 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.442 2008/05/07 12:45:41 drh Exp $ +** @(#) $Id: pager.c,v 1.443 2008/05/07 19:11:03 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -968,14 +968,20 @@ static void seekJournalHdr(Pager *pPager){ ** effect of invalidating the journal file and committing the ** transaction. */ -static int zeroJournalHdr(Pager *pPager){ - int rc; +static int zeroJournalHdr(Pager *pPager, int doTruncate){ + int rc = SQLITE_OK; static const char zeroHdr[28]; - IOTRACE(("JZEROHDR %p\n", pPager)) - rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); - if( rc==SQLITE_OK ){ - rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY | pPager->sync_flags); + if( pPager->journalOff ){ + IOTRACE(("JZEROHDR %p\n", pPager)) + if( doTruncate ){ + rc = sqlite3OsTruncate(pPager->jfd, 0); + }else{ + rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags); + } } return rc; } @@ -1163,6 +1169,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ int len; int i; i64 jrnlOff; + i64 jrnlSize; u32 cksum = 0; char zBuf[sizeof(aJournalMagic)+2*4]; @@ -1196,7 +1203,25 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ put32bits(&zBuf[4], cksum); memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic)); rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff); + jrnlOff += 8+sizeof(aJournalMagic); pPager->needSync = !pPager->noSync; + + /* If the pager is in peristent-journal mode, then the physical + ** journal-file may extend past the end of the master-journal name + ** and 8 bytes of magic data just written to the file. This is + ** dangerous because the code to rollback a hot-journal file + ** will not be able to find the master-journal name to determine + ** whether or not the journal is hot. + ** + ** Easiest thing to do in this scenario is to truncate the journal + ** file to the required size. + */ + if( (rc==SQLITE_OK) + && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK + && jrnlSize>jrnlOff + ){ + rc = sqlite3OsTruncate(pPager->jfd, jrnlOff); + } return rc; } @@ -1358,7 +1383,7 @@ static void pagerUnlockAndRollback(Pager *p){ ** This might give a performance improvement on windows where opening ** a file is an expensive operation. */ -static int pager_end_transaction(Pager *pPager){ +static int pager_end_transaction(Pager *pPager, int hasMaster){ PgHdr *pPg; int rc = SQLITE_OK; int rc2 = SQLITE_OK; @@ -1374,7 +1399,7 @@ static int pager_end_transaction(Pager *pPager){ if( pPager->journalOpen ){ if( (pPager->exclusiveMode || pPager->journalMode==PAGER_JOURNALMODE_PERSIST) - && (rc = zeroJournalHdr(pPager))==SQLITE_OK ){ + && (rc = zeroJournalHdr(pPager, hasMaster))==SQLITE_OK ){ pPager->journalOff = 0; pPager->journalStarted = 0; }else{ @@ -1908,7 +1933,7 @@ end_playback: rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); } if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager); + rc = pager_end_transaction(pPager, zMaster[0]!='\0'); } if( rc==SQLITE_OK && zMaster[0] ){ /* If there was a master journal and this routine will return success, @@ -3960,7 +3985,7 @@ static int pager_open_journal(Pager *pPager){ rc = sqlite3PagerStmtBegin(pPager); } if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_NOMEM ){ - rc = pager_end_transaction(pPager); + rc = pager_end_transaction(pPager, 0); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } @@ -4759,7 +4784,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ return SQLITE_OK; } assert( pPager->state==PAGER_SYNCED || !pPager->dirtyCache ); - rc = pager_end_transaction(pPager); + rc = pager_end_transaction(pPager, pPager->setMaster); rc = pager_error(pPager, rc); pagerLeave(pPager); return rc; @@ -4818,7 +4843,7 @@ int sqlite3PagerRollback(Pager *pPager){ pagerEnter(pPager); if( !pPager->dirtyCache || !pPager->journalOpen ){ - rc = pager_end_transaction(pPager); + rc = pager_end_transaction(pPager, pPager->setMaster); pagerLeave(pPager); return rc; } @@ -4833,7 +4858,7 @@ int sqlite3PagerRollback(Pager *pPager){ if( pPager->state==PAGER_RESERVED ){ int rc2; rc = pager_playback(pPager, 0); - rc2 = pager_end_transaction(pPager); + rc2 = pager_end_transaction(pPager, pPager->setMaster); if( rc==SQLITE_OK ){ rc = rc2; } diff --git a/test/jrnlmode.test b/test/jrnlmode.test index 78fd8a5698..a5e60adabb 100644 --- a/test/jrnlmode.test +++ b/test/jrnlmode.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The focus # of these tests is the journal mode pragma. # -# $Id: jrnlmode.test,v 1.1 2008/04/19 20:34:19 drh Exp $ +# $Id: jrnlmode.test,v 1.2 2008/05/07 19:11:03 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -152,5 +152,48 @@ ifcapable attach { } {} } +ifcapable attach { + file delete -force test2.db + do_test jrnlmode-2.1 { + execsql { + ATTACH 'test2.db' AS aux; + PRAGMA main.journal_mode = persist; + PRAGMA aux.journal_mode = persist; + CREATE TABLE abc(a, b, c); + CREATE TABLE aux.def(d, e, f); + } + execsql { + BEGIN; + INSERT INTO abc VALUES(1, 2, 3); + INSERT INTO def VALUES(4, 5, 6); + COMMIT; + } + list [file exists test.db-journal] [file exists test2.db-journal] + } {1 1} + + do_test jrnlmode-2.2 { + file size test.db-journal + } {0} + + do_test jrnlmode-2.3 { + execsql { + SELECT * FROM abc; + } + } {1 2 3} + + do_test jrnlmode-2.4 { + file size test.db-journal + } {0} + + do_test jrnlmode-2.5 { + execsql { + SELECT * FROM def; + } + } {4 5 6} + +} + + + finish_test