1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Fix a database corruption problem that can occur in auto-vacuum mode when

a malloc() failure causes a statement rollback, additional statements
are run in the same transaction, then the total transaction rolls back. (CVS 4079)

FossilOrigin-Name: c9dcf2b926c99ff9cee68589f364461ab2a1d11f
This commit is contained in:
drh
2007-06-16 04:42:12 +00:00
parent 477731b52d
commit 5e3853112a
3 changed files with 36 additions and 29 deletions

View File

@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.344 2007/06/16 03:06:28 drh Exp $
** @(#) $Id: pager.c,v 1.345 2007/06/16 04:42:12 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
@@ -1106,13 +1106,14 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
** This occurs when a page is changed prior to the start of a statement
** then changed again within the statement. When rolling back such a
** statement we must not write to the original database unless we know
** for certain that original page contents are in the main rollback
** journal. Otherwise, if a full ROLLBACK occurs after the statement
** rollback the full ROLLBACK will not restore the page to its original
** content. Two conditions must be met before writing to the database
** files. (1) the database must be locked. (2) we know that the original
** page content is in the main journal either because the page is not in
** cache or else it is marked as needSync==0.
** for certain that original page contents are synced into the main rollback
** journal. Otherwise, a power loss might leave modified data in the
** database file without an entry in the rollback journal that can
** restore the database to its original form. Two conditions must be
** met before writing to the database files. (1) the database must be
** locked. (2) we know that the original page content is fully synced
** in the main journal either because the page is not in cache or else
** the page is marked as needSync==0.
*/
pPg = pager_lookup(pPager, pgno);
PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
@@ -4243,15 +4244,15 @@ void sqlite3PagerSetCodec(
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Move the page identified by pData to location pgno in the file.
** Move the page pPg to location pgno in the file.
**
** There must be no references to the current page pgno. If current page
** pgno is not already in the rollback journal, it is not written there by
** by this routine. The same applies to the page pData refers to on entry to
** this routine.
** There must be no references to the page previously located at
** pgno (which we call pPgOld) though that page is allowed to be
** in cache. If the page previous located at pgno is not already
** in the rollback journal, it is not put there by by this routine.
**
** References to the page refered to by pData remain valid. Updating any
** meta-data associated with page pData (i.e. data stored in the nExtra bytes
** References to the page pPg remain valid. Updating any
** meta-data associated with pPg (i.e. data stored in the nExtra bytes
** allocated along with the page) is the responsibility of the caller.
**
** A transaction must be active when this routine is called. It used to be
@@ -4260,7 +4261,7 @@ void sqlite3PagerSetCodec(
** transaction is active).
*/
int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
PgHdr *pPgOld;
PgHdr *pPgOld; /* The page being overwritten. */
int h;
Pgno needSyncPgno = 0;
@@ -4286,17 +4287,23 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
** page pgno before the 'move' operation, it needs to be retained
** for the page moved there.
*/
pPg->needSync = 0;
pPgOld = pager_lookup(pPager, pgno);
if( pPgOld ){
assert( pPgOld->nRef==0 );
unlinkHashChain(pPager, pPgOld);
makeClean(pPgOld);
if( pPgOld->needSync ){
assert( pPgOld->inJournal );
pPg->inJournal = 1;
pPg->needSync = 1;
assert( pPager->needSync );
}
pPg->needSync = pPgOld->needSync;
}else{
pPg->needSync = 0;
}
if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
}else if( (int)pgno>=pPager->origDbSize ){
pPg->inJournal = 1;
}else{
pPg->inJournal = 0;
assert( pPg->needSync==0 );
}
/* Change the page number for pPg and insert it into the new hash-chain. */