Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | If, after obtaining a SHARED lock, there exists a *-wal file in the file-system, use WAL mode. This is necessary to recover from a crash that damages the first page of the database file. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
33cabf271b8f4dda508a610bf5996427 |
User & Date: | dan 2010-04-21 11:43:38.000 |
Context
2010-04-21
| ||
18:37 | Tests for (and changes to) the code to switch between WAL and rollback modes. (check-in: 9f4f933f2c user: dan tags: wal) | |
11:43 | If, after obtaining a SHARED lock, there exists a *-wal file in the file-system, use WAL mode. This is necessary to recover from a crash that damages the first page of the database file. (check-in: 33cabf271b user: dan tags: wal) | |
06:19 | Minor changes to test cases to account for the fact that databases with read/write versions of 2 are now understood. (check-in: 278ed41e1d user: dan tags: wal) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
2264 2265 2266 2267 2268 2269 2270 | if( page1[18]>2 ){ pBt->readOnly = 1; } if( page1[19]>2 ){ goto page1_init_failed; } | | > > > > > > > | 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 | if( page1[18]>2 ){ pBt->readOnly = 1; } if( page1[19]>2 ){ goto page1_init_failed; } /* If the write version is set to 2, this database should be accessed ** in WAL mode. If the log is not already open, open it now. Then ** return SQLITE_OK and return without populating BtShared.pPage1. ** The caller detects this and calls this function again. This is ** required as the version of page 1 currently in the page1 buffer ** may not be the latest version - there may be a newer one in the log ** file. */ if( page1[19]==2 ){ int isOpen = 0; rc = sqlite3PagerOpenLog(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ releasePage(pPage1); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 497 498 499 500 | /* The changeCountDone flag is always set for temp-files */ assert( pPager->tempFile==0 || pPager->changeCountDone ); return 1; } #endif /* ** Return true if it is necessary to write page *pPg into the sub-journal. ** A page needs to be written into the sub-journal if there exists one ** or more open savepoints for which: ** ** * The page-number is less than or equal to PagerSavepoint.nOrig, and | > > > > > > > > > > | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | /* The changeCountDone flag is always set for temp-files */ assert( pPager->tempFile==0 || pPager->changeCountDone ); return 1; } #endif #ifndef NDEBUG static void assert_file_lock(Pager *pPager, int eLock){ int locktype; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype); assert( locktype==eLock ); } #else # define assert_file_lock(x,y) #endif /* ** Return true if it is necessary to write page *pPg into the sub-journal. ** A page needs to be written into the sub-journal if there exists one ** or more open savepoints for which: ** ** * The page-number is less than or equal to PagerSavepoint.nOrig, and |
︙ | ︙ | |||
3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 | } } } return rc; } /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 | } } } return rc; } /* ** Check if the *-wal file that corresponds to the database opened by pPager ** exists. Assuming no error occurs, set *pExists to 1 if the file exists, ** or 0 otherwise and return SQLITE_OK. If an IO or OOM error occurs, return ** an SQLite error code. ** ** The caller must hold a SHARED lock on the database file to call this ** function. */ static int pagerHasWAL(Pager *pPager, int *pExists){ int rc; /* Return code */ /* Check that a SHARED lock is held on the database file. Because an ** EXCLUSIVE lock on the db file is required to delete a WAL, this ** ensures there is no race condition between the xAccess() below and ** an xDelete() being executed by some other connection. */ assert_file_lock(pPager, SQLITE_LOCK_SHARED); if( !pPager->tempFile ){ char *zLog = sqlite3_mprintf("%s-wal", pPager->zFilename); if( !zLog ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3OsAccess(pPager->pVfs, zLog, SQLITE_ACCESS_EXISTS, pExists); sqlite3_free(zLog); } }else{ rc = SQLITE_OK; *pExists = 0; } return rc; } static int pagerOpenSnapshot(Pager *pPager){ int rc; int changed; assert( pagerUseLog(pPager) ); rc = sqlite3LogOpenSnapshot(pPager->pLog, &changed); if( rc==SQLITE_OK ){ int dummy; if( changed ){ pager_reset(pPager); assert( pPager->errCode || pPager->dbSizeValid==0 ); } rc = sqlite3PagerPagecount(pPager, &dummy); } pPager->state = PAGER_SHARED; return rc; } /* ** This function is called to obtain a shared lock on the database file. ** It is illegal to call sqlite3PagerAcquire() until after this function ** has been successfully called. If a shared-lock is already held when ** this function is called, it is a no-op. ** |
︙ | ︙ | |||
3777 3778 3779 3780 3781 3782 3783 | isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } if( pagerUseLog(pPager) ){ | < < < | < < < < < < < < < < > | 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 | isErrorReset = 1; } pPager->errCode = SQLITE_OK; pager_reset(pPager); } if( pagerUseLog(pPager) ){ rc = pagerOpenSnapshot(pPager); }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; int isWal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); if( pPager->noReadlock ){ assert( pPager->readOnly ); pPager->state = PAGER_SHARED; }else{ rc = pager_wait_on_lock(pPager, SHARED_LOCK); |
︙ | ︙ | |||
3948 3949 3950 3951 3952 3953 3954 | if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); | > > > > > > > > > > | | 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 | if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); } } assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED ); rc = pagerHasWAL(pPager, &isWal); if( rc!=SQLITE_OK ){ goto failed; } if( isWal ){ pager_reset(pPager); rc = sqlite3PagerOpenLog(pPager, 0); if( rc==SQLITE_OK ){ rc = pagerOpenSnapshot(pPager); } }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){ pPager->journalMode = PAGER_JOURNALMODE_DELETE; } } failed: if( rc!=SQLITE_OK ){ /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */ |
︙ | ︙ | |||
5671 5672 5673 5674 5675 5676 5677 | ** ** The caller must be holding a SHARED lock on the database file to call ** this function. */ int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen){ int rc = SQLITE_OK; /* Return code */ | < < < | < < < | 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 | ** ** The caller must be holding a SHARED lock on the database file to call ** this function. */ int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen){ int rc = SQLITE_OK; /* Return code */ assert_file_lock(pPager, SQLITE_LOCK_SHARED); if( !pPager->pLog ){ /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog); |
︙ | ︙ | |||
5721 5722 5723 5724 5725 5726 5727 | rc = sqlite3LogClose(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), (u8*)pPager->pTmpSpace ); pPager->pLog = 0; } | < < < < < | < < < | 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 | rc = sqlite3LogClose(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), (u8*)pPager->pTmpSpace ); pPager->pLog = 0; } assert_file_lock(pPager, SQLITE_LOCK_EXCLUSIVE); } return rc; } #endif /* SQLITE_OMIT_DISKIO */ |
Changes to test/wal.test.
︙ | ︙ | |||
681 682 683 684 685 686 687 | file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 1} db2 close | < < | 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 1} db2 close do_test wal-12.5 { execsql { PRAGMA checkpoint; UPDATE t2 SET y = 2 WHERE x = 'B'; PRAGMA checkpoint; UPDATE t1 SET y = 1 WHERE x = 'A'; PRAGMA checkpoint; UPDATE t1 SET y = 0 WHERE x = 'A'; SELECT * FROM t2; } } {B 2} do_test wal-12.4 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 2} |
︙ | ︙ |
Changes to test/walcrash.test.
︙ | ︙ | |||
28 29 30 31 32 33 34 | set testdir [file dirname $argv0] source $testdir/tester.tcl db close set seed 0 set REPEATS 100 | < < < < < | < < | > > > | < | > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | set testdir [file dirname $argv0] source $testdir/tester.tcl db close set seed 0 set REPEATS 100 # walcrash-1.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal do_test walcrash-1.$i.1 { crashsql -delay 4 -file test.db-wal -seed [incr seed] { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(2, 3); INSERT INTO t1 VALUES(3, 6); } } {1 {child process exited abnormally}} do_test walcrash-1.$i.2 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-1.$i.3 db close do_test walcrash-1.$i.4 { crashsql -delay 2 -file test.db-wal -seed [incr seed] { INSERT INTO t1 VALUES(4, (SELECT sum(a) FROM t1) + 4); INSERT INTO t1 VALUES(5, (SELECT sum(a) FROM t1) + 5); } } {1 {child process exited abnormally}} do_test walcrash-1.$i.5 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-1.$i.6 do_test walcrash-1.$i.5 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-2.* # for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal do_test walcrash-2.$i.1 { crashsql -delay 4 -file test.db-wal -seed [incr seed] { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 9); } } {1 {child process exited abnormally}} do_test walcrash-2.$i.2 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-2.$i.3 db close do_test walcrash-2.$i.4 { crashsql -delay 2 -file test.db-wal -seed [incr seed] { INSERT INTO t1 VALUES(6, (SELECT sum(a) FROM t1) + 6); INSERT INTO t1 VALUES(7, (SELECT sum(a) FROM t1) + 7); } } {1 {child process exited abnormally}} do_test walcrash-2.$i.5 { sqlite3 db test.db execsql { SELECT sum(a)==max(b) FROM t1 } } {1} integrity_check walcrash-2.$i.6 do_test walcrash-2.$i.6 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-3.* # # for {set i 1} {$i < $REPEATS} {incr i} { # file delete -force test.db test.db-wal |
︙ | ︙ | |||
153 154 155 156 157 158 159 | CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } } {1 {child process exited abnormally}} do_test walcrash-4.$i.2 { | | > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); } } {1 {child process exited abnormally}} do_test walcrash-4.$i.2 { sqlite3 db test.db execsql { SELECT * FROM t1 WHERE a = 1; } } {1 2} do_test walcrash-4.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-4.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-5.* # for {set i 1} {$i < $REPEATS} {incr i} { |
︙ | ︙ | |||
195 196 197 198 199 200 201 | INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-5.$i.2 { | | > | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-5.$i.2 { sqlite3 db test.db execsql { SELECT count(*)==33 OR count(*)==34 FROM t1 WHERE x != 1 } } {1} do_test walcrash-5.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-5.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } # walcrash-6.* # for {set i 1} {$i < $REPEATS} {incr i} { |
︙ | ︙ | |||
235 236 237 238 239 240 241 | INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-6.$i.2 { | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); INSERT INTO t1 VALUES(randomblob(900)); } } {1 {child process exited abnormally}} do_test walcrash-6.$i.2 { sqlite3 db test.db execsql { SELECT count(*)==34 OR count(*)==35 FROM t1 WHERE x != 1 } } {1} do_test walcrash-6.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-6.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } for {set i 1} {$i < $REPEATS} {incr i} { file delete -force test.db test.db-wal do_test walcrash-7.$i.1 { crashsql -delay 3 -file test.db -seed [incr seed] -blocksize 512 { PRAGMA journal_mode = wal; BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA checkpoint; CREATE INDEX i1 ON t1(a); PRAGMA checkpoint; } } {1 {child process exited abnormally}} do_test walcrash-7.$i.2 { sqlite3 db test.db execsql { SELECT b FROM t1 WHERE a = 1 } } {2} do_test walcrash-7.$i.3 { execsql { PRAGMA main.integrity_check } } {ok} do_test walcrash-7.$i.4 { execsql { PRAGMA main.journal_mode } } {wal} db close } finish_test |