Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.157 2004/08/18 16:05:19 drh Exp $ +** @(#) $Id: pager.c,v 1.158 2004/08/18 19:09:44 drh Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" #include "pager.h" #include @@ -1014,10 +1014,18 @@ pPg->needSync = 0; pPg->dirty = 0; } return rc; } + +/* +** Truncate the main file of the given pager to the number of pages +** indicated. +*/ +static int pager_truncate(Pager *pPager, int nPage){ + return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)nPage); +} /* ** Playback the journal and thus restore the database file to ** the state it was in before we started making changes. ** @@ -1133,11 +1141,11 @@ /* If this is the first header read from the journal, truncate the ** database file back to it's original size. */ if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){ assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg ); - rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)mxPg); + rc = pager_truncate(pPager, mxPg); if( rc!=SQLITE_OK ){ goto end_playback; } pPager->dbSize = mxPg; } @@ -1233,11 +1241,11 @@ } /* Truncate the database back to its original size. */ - rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)pPager->stmtSize); + rc = pager_truncate(pPager, pPager->stmtSize); pPager->dbSize = pPager->stmtSize; /* Figure out how many records are in the statement journal. */ assert( pPager->stmtInUse && pPager->journalOpen ); @@ -1671,11 +1679,11 @@ } rc = syncJournal(pPager); if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)nPage); + rc = pager_truncate(pPager, nPage); if( rc==SQLITE_OK ){ pPager->dbSize = nPage; } return rc; } @@ -2858,15 +2866,17 @@ pager_playback(pPager); } return pager_errcode(pPager); } if( pPager->state==PAGER_RESERVED ){ - int rc2; + int rc2, rc3; rc = pager_reload_cache(pPager); - rc2 = pager_unwritelock(pPager); + rc2 = pager_truncate(pPager, pPager->origDbSize); + rc3 = pager_unwritelock(pPager); if( rc==SQLITE_OK ){ rc = rc2; + if( rc3 ) rc = rc3; } }else{ rc = pager_playback(pPager); } if( rc!=SQLITE_OK ){ ADDED test/pager3.test Index: test/pager3.test ================================================================== --- /dev/null +++ test/pager3.test @@ -0,0 +1,68 @@ +# 2001 September 15 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is page cache subsystem. +# +# $Id: pager3.test,v 1.1 2004/08/18 19:09:44 drh Exp $ + + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# This test makes sure the database file is truncated back to the correct +# length on a rollback. +# +# After some preliminary setup, a transaction is start at NOTE (1). +# The create table on the following line allocates an additional page +# at the end of the database file. But that page is not written because +# the database still has a RESERVED lock, not an EXCLUSIVE lock. The +# new page is held in memory and the size of the file is unchanged. +# The insert at NOTE (2) begins adding additional pages. Then it hits +# a constraint error and aborts. The abort causes sqlite3OsTruncate() +# to be called to restore the file to the same length as it was after +# the create table. But the create table results had not yet been +# written so the file is actually lengthened by this truncate. Finally, +# the rollback at NOTE (3) is called to undo all the changes since the +# begin. This rollback should truncate the database again. +# +# This test was added because the second truncate at NOTE (3) was not +# occurring on early versions of SQLite 3.0. +# +do_test pager3-1.1 { + execsql { + create table t1(a unique, b); + insert into t1 values(1, 'abcdefghijklmnopqrstuvwxyz'); + insert into t1 values(2, 'abcdefghijklmnopqrstuvwxyz'); + update t1 set b=b||a||b; + update t1 set b=b||a||b; + update t1 set b=b||a||b; + update t1 set b=b||a||b; + update t1 set b=b||a||b; + update t1 set b=b||a||b; + create temp table t2 as select * from t1; + begin; ------- NOTE (1) + create table t3(x); + } + catchsql { + insert into t1 select 4-a, b from t2; ----- NOTE (2) + } + execsql { + rollback; ------- NOTE (3) + } + db close + sqlite3 db test.db + execsql { + pragma integrity_check; + } +} ok + + +finish_test