diff options
author | dan <dan@noemail.net> | 2010-04-24 18:44:05 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2010-04-24 18:44:05 +0000 |
commit | 74d6cd887f8cd63b7b3f9e92b372aaad8df17c69 (patch) | |
tree | f567bd556a299d3f8245f6f283ffc4b25eaa11f4 /src | |
parent | 9de7943783c0100d606fd082effcbb34044fe6b5 (diff) | |
download | sqlite-74d6cd887f8cd63b7b3f9e92b372aaad8df17c69.tar.gz sqlite-74d6cd887f8cd63b7b3f9e92b372aaad8df17c69.zip |
Fix bugs in WAL mode rollback.
FossilOrigin-Name: 31215969f59be536fe87431bb9fbfa7d13027e35
Diffstat (limited to 'src')
-rw-r--r-- | src/log.c | 27 | ||||
-rw-r--r-- | src/log.h | 3 | ||||
-rw-r--r-- | src/pager.c | 64 |
3 files changed, 77 insertions, 17 deletions
@@ -1611,6 +1611,33 @@ int sqlite3LogWriteLock(Log *pLog, int op){ 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. */ @@ -37,6 +37,9 @@ 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); diff --git a/src/pager.c b/src/pager.c index 2db8b9336..18b8960da 100644 --- a/src/pager.c +++ b/src/pager.c @@ -2235,16 +2235,44 @@ static int readDbPage(PgHdr *pPg){ } /* -** This function is called when a transaction on a WAL database is rolled -** back. For each dirty page in the cache, do one of the following: -** -** * If the page has no outstanding references, simply discard it. -** * Otherwise, if the page has one or more outstanding references, -** reload the original content from the database (or log file). +** 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 pagerRollbackLog(Pager *pPager){ +static int pagerUndoCallback(void *pCtx, Pgno iPg){ int rc = SQLITE_OK; - PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); + 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 @@ -2258,20 +2286,22 @@ static int pagerRollbackLog(Pager *pPager){ 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; - if( sqlite3PcachePageRefcount(pList)==0 ){ - sqlite3PagerLookup(pPager, pList->pgno); - sqlite3PcacheDrop(pList); - }else{ - rc = readDbPage(pList); - if( rc==SQLITE_OK ){ - pPager->xReiniter(pList); - } - } + rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } + return rc; } |