aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2010-04-24 18:44:05 +0000
committerdan <dan@noemail.net>2010-04-24 18:44:05 +0000
commit74d6cd887f8cd63b7b3f9e92b372aaad8df17c69 (patch)
treef567bd556a299d3f8245f6f283ffc4b25eaa11f4 /src
parent9de7943783c0100d606fd082effcbb34044fe6b5 (diff)
downloadsqlite-74d6cd887f8cd63b7b3f9e92b372aaad8df17c69.tar.gz
sqlite-74d6cd887f8cd63b7b3f9e92b372aaad8df17c69.zip
Fix bugs in WAL mode rollback.
FossilOrigin-Name: 31215969f59be536fe87431bb9fbfa7d13027e35
Diffstat (limited to 'src')
-rw-r--r--src/log.c27
-rw-r--r--src/log.h3
-rw-r--r--src/pager.c64
3 files changed, 77 insertions, 17 deletions
diff --git a/src/log.c b/src/log.c
index 3837d7e17..d9637de02 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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.
*/
diff --git a/src/log.h b/src/log.h
index 02201c837..c0699ba6f 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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;
}