Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Tests for (and changes to) the code to switch between WAL and rollback modes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
9f4f933f2c6596064fcfc7fb5add87e8 |
User & Date: | dan 2010-04-21 18:37:57.000 |
Context
2010-04-22
| ||
06:27 | Further tests and changes related to switching between WAL and rollback modes. (check-in: 1236318477 user: dan tags: wal) | |
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) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
2272 2273 2274 2275 2276 2277 2278 | ** 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. */ | | | 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 | ** 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 && pBt->doNotUseWAL==0 ){ int isOpen = 0; rc = sqlite3PagerOpenLog(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else if( isOpen==0 ){ releasePage(pPage1); return SQLITE_OK; |
︙ | ︙ | |||
7988 7989 7990 7991 7992 7993 7994 | ** "write version" (single byte at byte offset 19) fields in the database ** header to iVersion. */ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ BtShared *pBt = pBtree->pBt; int rc; /* Return code */ | | < > > > > | > > > > > > | | | | > > > > | 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 | ** "write version" (single byte at byte offset 19) fields in the database ** header to iVersion. */ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ BtShared *pBt = pBtree->pBt; int rc; /* Return code */ assert( pBtree->inTrans==TRANS_NONE ); assert( iVersion==1 || iVersion==2 ); /* If setting the version fields to 1, do not automatically open the ** WAL connection, even if the version fields are currently set to 2. */ pBt->doNotUseWAL = (iVersion==1); rc = sqlite3BtreeBeginTrans(pBtree, 0); if( rc==SQLITE_OK ){ u8 *aData = pBt->pPage1->aData; if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){ rc = sqlite3BtreeBeginTrans(pBtree, 1); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); if( rc==SQLITE_OK ){ aData[18] = (u8)iVersion; aData[19] = (u8)iVersion; } } } } pBt->doNotUseWAL = 0; return rc; } |
Changes to src/btreeInt.h.
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 | u16 pageSize; /* Total number of bytes on a page */ u16 usableSize; /* Number of usable bytes on each page */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE | > | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | u16 pageSize; /* Total number of bytes on a page */ u16 usableSize; /* Number of usable bytes on each page */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u8 inTransaction; /* Transaction state */ u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE |
︙ | ︙ |
Changes to src/log.c.
︙ | ︙ | |||
1231 1232 1233 1234 1235 1236 1237 | logSummaryUnmap(pSummary, 1); }else{ if( rc==SQLITE_BUSY ){ rc = SQLITE_OK; } logSummaryUnmap(pSummary, 0); } | < | 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 | logSummaryUnmap(pSummary, 1); }else{ if( rc==SQLITE_BUSY ){ rc = SQLITE_OK; } logSummaryUnmap(pSummary, 0); } sqlite3_mutex_free(pSummary->mutex); sqlite3_free(pSummary); }else{ sqlite3_mutex_leave(mutex); } |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
486 487 488 489 490 491 492 | /* The changeCountDone flag is always set for temp-files */ assert( pPager->tempFile==0 || pPager->changeCountDone ); return 1; } #endif | < < < < < < < < < < < | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | /* 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 |
︙ | ︙ | |||
3004 3005 3006 3007 3008 3009 3010 | ** Variable iNextHdrOffset is set to the offset at which this ** problematic header will occur, if it exists. aMagic is used ** as a temporary buffer to inspect the first couple of bytes of ** the potential journal header. */ i64 iNextHdrOffset; u8 aMagic[8]; | | | | | 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 | ** Variable iNextHdrOffset is set to the offset at which this ** problematic header will occur, if it exists. aMagic is used ** as a temporary buffer to inspect the first couple of bytes of ** the potential journal header. */ i64 iNextHdrOffset; u8 aMagic[8]; u8 zHeader[sizeof(aJournalMagic)+4]; memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec); iNextHdrOffset = journalHdrOffset(pPager); rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset); if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){ static const u8 zerobyte = 0; rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset); } |
︙ | ︙ | |||
3039 3040 3041 3042 3043 3044 3045 | IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); if( rc!=SQLITE_OK ) return rc; } IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); rc = sqlite3OsWrite( pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr | | | 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 | IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags); if( rc!=SQLITE_OK ) return rc; } IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); rc = sqlite3OsWrite( pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr ); if( rc!=SQLITE_OK ) return rc; } if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) |
︙ | ︙ | |||
3749 3750 3751 3752 3753 3754 3755 | 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. */ | | | 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 | 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( pPager->state>=PAGER_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); |
︙ | ︙ | |||
5732 5733 5734 5735 5736 5737 5738 | ** ** 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 */ | | | 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 | ** ** 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( pPager->state>=PAGER_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); |
︙ | ︙ | |||
5765 5766 5767 5768 5769 5770 5771 | ** If successful, the EXCLUSIVE lock is not released before returning. */ int sqlite3PagerCloseLog(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pLog ){ /* Try to obtain an EXCLUSIVE lock on the database file. */ | | < < | 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 | ** If successful, the EXCLUSIVE lock is not released before returning. */ int sqlite3PagerCloseLog(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pLog ){ /* Try to obtain an EXCLUSIVE lock on the database file. */ rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE); /* If the EXCLUSIVE lock was obtained, checkpoint and close the log. */ if( rc==SQLITE_OK ){ rc = sqlite3LogClose(pPager->pLog, pPager->fd, (pPager->noSync ? 0 : pPager->sync_flags), (u8*)pPager->pTmpSpace ); pPager->pLog = 0; } } return rc; } #endif /* SQLITE_OMIT_DISKIO */ |
Changes to src/vdbe.c.
︙ | ︙ | |||
5256 5257 5258 5259 5260 5261 5262 | sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ assert( sqlite3BtreeIsInTrans(pBt)==0 ); | < < < | | 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 | sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ assert( sqlite3BtreeIsInTrans(pBt)==0 ); rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); if( rc!=SQLITE_OK ) goto abort_due_to_error; } } } eNew = sqlite3PagerJournalMode(pPager, eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = (char *)sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); break; }; #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) |
︙ | ︙ |
Changes to test/wal.test.
︙ | ︙ | |||
621 622 623 624 625 626 627 | execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 32 SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] | | | | | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t1 SELECT blob(900) FROM t1; -- 32 SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 35 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; ROLLBACK; SELECT count(*) FROM t1; } } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 35 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); SELECT count(*) FROM t1; PRAGMA integrity_check; } } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] } [list 37 [log_file_size 35 1024]] #------------------------------------------------------------------------- # This block of tests, wal-12.*, tests the fix for a problem that # could occur if a log that is a prefix of an older log is written # into a reused log file. # |
︙ | ︙ |
Changes to test/walmode.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test walmode-1.1 { | > | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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 | # "PRAGMA journal_mode=WAL" mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test walmode-1.1 { set sqlite_sync_count 0 execsql { PRAGMA page_size = 1024 } execsql { PRAGMA journal_mode = wal } } {wal} do_test walmode-1.2 { file size test.db } {1024} do_test walmode-1.3 { set sqlite_sync_count } {4} do_test walmode-1.4 { file exists test.db-wal } {0} do_test walmode-1.5 { execsql { CREATE TABLE t1(a, b) } file size test.db } {1024} do_test walmode-1.6 { file exists test.db-wal } {1} do_test walmode-1.7 { db close file exists test.db-wal } {0} # There is now a database file with the read and write versions set to 2 # in the file system. This file should default to WAL mode. # do_test walmode-2.1 { sqlite3 db test.db file exists test.db-wal } {0} do_test walmode-2.2 { execsql { SELECT * FROM sqlite_master } file exists test.db-wal } {1} do_test walmode-2.3 { db close file exists test.db-wal } {0} # If the first statement executed is "PRAGMA journal_mode = wal", and # the file is already configured for WAL (read and write versions set # to 2), then there should be no need to write the database. The # statement should cause the client to connect to the log file. # set sqlite_sync_count 0 do_test walmode-3.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = wal } } {wal} do_test walmode-3.2 { list $sqlite_sync_count [file exists test.db-wal] [file size test.db-wal] } {0 1 0} do_test walmode-4.1 { execsql { INSERT INTO t1 VALUES(1, 2) } execsql { PRAGMA journal_mode = persist } } {persist} do_test walmode-4.2 { list [file exists test.db-journal] [file exists test.db-wal] } {1 0} do_test walmode-4.3 { execsql { SELECT * FROM t1 } } {1 2} do_test walmode-4.4 { db close sqlite3 db test.db execsql { SELECT * FROM t1 } } {1 2} do_test walmode-4.5 { list [file exists test.db-journal] [file exists test.db-wal] } {1 0} finish_test |