Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix bugs in WAL mode rollback. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | wal |
Files: | files | file ages | folders |
SHA1: |
31215969f59be536fe87431bb9fbfa7d |
User & Date: | dan 2010-04-24 18:44:05.000 |
Context
2010-04-24
| ||
19:07 | Add comment explaining checksum mechanism. (check-in: 3e9ef5153e user: dan tags: wal) | |
18:44 | Fix bugs in WAL mode rollback. (check-in: 31215969f5 user: dan tags: wal) | |
14:33 | Merge with [0291ed974d]. Merge with [0291ed974d]. Merge with [0291ed974d]. (check-in: a352f6285e user: dan tags: wal) | |
Changes
Changes to src/log.c.
︙ | ︙ | |||
1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 | }else if( pLog->isWriteLocked ){ logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_UNLOCK); memcpy(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)); pLog->isWriteLocked = 0; } return SQLITE_OK; } /* ** Return true if data has been written but not committed to the log file. */ int sqlite3LogDirty(Log *pLog){ assert( pLog->isWriteLocked ); return( pLog->hdr.iLastPg!=((LogSummaryHdr*)pLog->pSummary->aData)->iLastPg ); | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 | }else if( pLog->isWriteLocked ){ logLockRegion(pLog, LOG_REGION_C|LOG_REGION_D, LOG_UNLOCK); memcpy(&pLog->hdr, pLog->pSummary->aData, sizeof(pLog->hdr)); pLog->isWriteLocked = 0; } return SQLITE_OK; } /* ** The log handle passed to this function must be holding the write-lock. ** ** If any data has been written (but not committed) to the log file, this ** function moves the write-pointer back to the start of the transaction. ** ** Additionally, the callback function is invoked for each frame written ** to the log since the start of the transaction. If the callback returns ** other than SQLITE_OK, it is not invoked again and the error code is ** returned to the caller. ** ** Otherwise, if the callback function does not return an error, this ** function returns SQLITE_OK. */ int sqlite3LogUndo(Log *pLog, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; Pgno iMax = pLog->hdr.iLastPg; Pgno iFrame; assert( pLog->isWriteLocked ); logSummaryReadHdr(pLog, 0); for(iFrame=pLog->hdr.iLastPg+1; iFrame<=iMax && rc==SQLITE_OK; iFrame++){ rc = xUndo(pUndoCtx, pLog->pSummary->aData[logSummaryEntry(iFrame)]); } return rc; } /* ** Return true if data has been written but not committed to the log file. */ int sqlite3LogDirty(Log *pLog){ assert( pLog->isWriteLocked ); return( pLog->hdr.iLastPg!=((LogSummaryHdr*)pLog->pSummary->aData)->iLastPg ); |
︙ | ︙ |
Changes to src/log.h.
︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /* Read a page from the log, if it is present. */ int sqlite3LogRead(Log *pLog, Pgno pgno, int *pInLog, u8 *pOut); void sqlite3LogDbsize(Log *pLog, Pgno *pPgno); /* Obtain or release the WRITER lock. */ int sqlite3LogWriteLock(Log *pLog, int op); /* Return true if data has been written but not committed to the log file. */ int sqlite3LogDirty(Log *pLog); /* Write a frame or frames to the log. */ int sqlite3LogFrames(Log *pLog, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ | > > > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | /* Read a page from the log, if it is present. */ int sqlite3LogRead(Log *pLog, Pgno pgno, int *pInLog, u8 *pOut); void sqlite3LogDbsize(Log *pLog, Pgno *pPgno); /* Obtain or release the WRITER lock. */ int sqlite3LogWriteLock(Log *pLog, int op); /* Undo any frames written (but not committed) to the log */ int sqlite3LogUndo(Log *pLog, int (*xUndo)(void *, Pgno), void *pUndoCtx); /* Return true if data has been written but not committed to the log file. */ int sqlite3LogDirty(Log *pLog); /* Write a frame or frames to the log. */ int sqlite3LogFrames(Log *pLog, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
2231 2232 2233 2234 2235 2236 2237 | PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pPg))); return rc; } /* | | | > > | | > | > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < | < < | | < < | 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 | PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pPg))); return rc; } /* ** This function is invoked once for each page that has already been ** written into the log file when a WAL transaction is rolled back. ** Parameter iPg is the page number of said page. The pCtx argument ** is actually a pointer to the Pager structure. ** ** If page iPg is present in the cache, and has no outstanding references, ** it is discarded. Otherwise, if there are one or more outstanding ** references, the page content is reloaded from the database. If the ** attempt to reload content from the database is required and fails, ** return an SQLite error code. Otherwise, SQLITE_OK. */ static int pagerUndoCallback(void *pCtx, Pgno iPg){ int rc = SQLITE_OK; Pager *pPager = (Pager *)pCtx; PgHdr *pPg; pPg = sqlite3PagerLookup(pPager, iPg); if( pPg ){ if( sqlite3PcachePageRefcount(pPg)==1 ){ sqlite3PcacheDrop(pPg); }else{ rc = readDbPage(pPg); if( rc==SQLITE_OK ){ pPager->xReiniter(pPg); } sqlite3PagerUnref(pPg); } } return rc; } /* ** This function is called to rollback a transaction on a WAL database. */ static int pagerRollbackLog(Pager *pPager){ int rc; /* Return Code */ PgHdr *pList; /* List of dirty pages to revert */ /* Normally, if a transaction is rolled back, any backup processes are ** updated as data is copied out of the rollback journal and into the ** database. This is not generally possible with a WAL database, as ** rollback involves simply truncating the log file. Therefore, if one ** or more frames have already been written to the log (and therefore ** also copied into the backup databases) as part of this transaction, ** the backups must be restarted. */ if( sqlite3LogDirty(pPager->pLog) ){ sqlite3BackupRestart(pPager->pBackup); } /* For all pages in the cache that are currently dirty or have already ** been written (but not committed) to the log file, do one of the ** following: ** ** + Discard the cached page (if refcount==0), or ** + Reload page content from the database (if refcount>0). */ pPager->dbSize = pPager->dbOrigSize; rc = sqlite3LogUndo(pPager->pLog, pagerUndoCallback, (void *)pPager); pList = sqlite3PcacheDirtyList(pPager->pPCache); while( pList && rc==SQLITE_OK ){ PgHdr *pNext = pList->pDirty; rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } return rc; } /* ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback ** the entire master journal file. The case pSavepoint==NULL occurs when ** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction |
︙ | ︙ |
Changes to test/wal.test.
︙ | ︙ | |||
135 136 137 138 139 140 141 142 143 144 145 146 147 148 | } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-3.3 { execsql { ROLLBACK } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} db2 close do_test wal-4.1 { execsql { DELETE FROM t1; BEGIN; INSERT INTO t1 VALUES('a', 'b'); SAVEPOINT sp; INSERT INTO t1 VALUES('c', 'd'); | > > > > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-3.3 { execsql { ROLLBACK } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} db2 close #------------------------------------------------------------------------- # The following tests, wal-4.*, test that savepoints work with WAL # databases. # do_test wal-4.1 { execsql { DELETE FROM t1; BEGIN; INSERT INTO t1 VALUES('a', 'b'); SAVEPOINT sp; INSERT INTO t1 VALUES('c', 'd'); |
︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 | do_test wal-4.3 { execsql { COMMIT; SELECT * FROM t1; } } {a b} do_test wal-5.1 { execsql { CREATE TEMP TABLE t2(a, b); INSERT INTO t2 VALUES(1, 2); } } {} do_test wal-5.2 { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | do_test wal-4.3 { execsql { COMMIT; SELECT * FROM t1; } } {a b} do_test wal-4.4 { db close sqlite3 db test.db db func blob blob list [execsql { SELECT * FROM t1 }] [file size test.db-wal] } {{a b} 0} do_test wal-4.5 { execsql { PRAGMA cache_size = 10 } execsql { CREATE TABLE t2(a, b); INSERT INTO t2 VALUES(blob(400), blob(400)); SAVEPOINT tr; INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 2 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 4 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 8 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 16 */ INSERT INTO t2 SELECT blob(400), blob(400) FROM t2; /* 32 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 4 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 8 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 16 */ INSERT INTO t1 SELECT blob(400), blob(400) FROM t1; /* 32 */ SELECT count(*) FROM t2; } } {32} do_test wal-4.6 { execsql { ROLLBACK TO tr } } {} do_test wal-4.7 { set logsize [file size test.db-wal] execsql { INSERT INTO t1 VALUES('x', 'y'); RELEASE tr; } expr { $logsize == [file size test.db-wal] } } {1} do_test wal-4.8 { execsql { SELECT count(*) FROM t2 } } {1} do_test wal-4.9 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db execsql { SELECT count(*) FROM t2 ; SELECT count(*) FROM t1 } db2 } {1 2} do_test wal-4.10 { execsql { PRAGMA integrity_check } db2 } {ok} db2 close reopen_db do_test wal-5.1 { execsql { CREATE TEMP TABLE t2(a, b); INSERT INTO t2 VALUES(1, 2); } } {} do_test wal-5.2 { |
︙ | ︙ |