Fix a problem whereby following an IO error in CommitPhaseTwo() of a multi-file transaction the b-tree layer could be left in TRANS_WRITE state, causing problems later on.

FossilOrigin-Name: dbe569a099c2855480e35c0cc4d9332821ad80da
This commit is contained in:
dan 2011-03-29 15:40:55 +00:00
parent 0fd7d86081
commit 60939d0ade
12 changed files with 165 additions and 27 deletions

0
install-sh Normal file → Executable file
View File

View File

@ -1,5 +1,5 @@
C Fix\sa\sproblem\sin\sthe\sunix\sVFS\simplementation\sof\sxNextSystemCall().\sAlso\ssome\stypos\sthat\sprevent\scompilation\swhen\sHAVE_POSIX_FALLOCATE\sis\sdefined.
D 2011-03-29T10:04:23
C Fix\sa\sproblem\swhereby\sfollowing\san\sIO\serror\sin\sCommitPhaseTwo()\sof\sa\smulti-file\stransaction\sthe\sb-tree\slayer\scould\sbe\sleft\sin\sTRANS_WRITE\sstate,\scausing\sproblems\slater\son.
D 2011-03-29T15:40:55.407
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -99,7 +99,7 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk 7e4d4d0433c9cbfd906c6451a7cc50310a8f4555
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
@ -118,11 +118,11 @@ F src/alter.c 6a0c176e64a34929a4436048066a84ef4f1445b3
F src/analyze.c a038162344265ac21dfb24b3fcc06c666ebb9c07
F src/attach.c 438ea6f6b5d5961c1f49b737f2ce0f14ce7c6877
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 6728d6d48d55b449af76a3e51c0808849cb32a2e
F src/backup.c 537f89c7ef5021cb580f31f782e556ffffcb2ed1
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c 43302cc4f3de6479b90fa6bb271b65d86333d00e
F src/btree.h e2f2cd9933bf30724f53ffa12c4c5a3a864bbd6e
F src/btree.c 2b9c81ff64da339a67dda4f94c0d763627be0b67
F src/btree.h 8d36f774ec4b1d0027b8966f8c03d9a72a518c14
F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4
F src/build.c 6c490fe14dedb094a202f559e3b29a276abffcf8
F src/callback.c 5069f224882cbdccd559f591271d28d7f37745bc
@ -220,7 +220,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c b2b3aef993253395da25d564a920014cc3673f09
F src/test_syscall.c bbdc88d0a5e42d0c35eaff8ae7ec86e8867f5543
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0
F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86
@ -236,7 +236,7 @@ F src/vdbe.c e3f37ca0afdd72e883475e2a32a06167df2810d0
F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2
F src/vdbeInt.h e1c6254641168507d25b46affb6dfb53c782f553
F src/vdbeapi.c a09ad9164cafc505250d5dd6b69660c960f1308c
F src/vdbeaux.c cfd3f3ac674691ba1166ceb9a2698b0d00b2ef91
F src/vdbeaux.c 9211dfa7d79d94d4e50714bfd0497ff3588a739d
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5
@ -459,7 +459,7 @@ F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246
F test/fts3fault2.test f275554f4a4fc7abf71e2975a9d6f4693f390526
F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
@ -604,7 +604,7 @@ F test/permutations.test 5b2a4cb756ffb2407cb4743163668d1d769febb6
F test/pragma.test fdfc09067ea104a0c247a1a79d8093b56656f850
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 x
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota.test ddafe133653093eb9a99ccd6264884ae43f9c9b8
@ -672,8 +672,8 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
F test/syscall.test 2f3e4e2cf91c3b870c4eac37f62c61208a199403
F test/sysfault.test 63144f0000167f12676adccee2a0db45eb5c2314
F test/syscall.test 5ae4b3d4f2aca2ef3c3a777f619e0c6b0cf592aa
F test/sysfault.test 359ea90a58788c867ac0f9cb52431f56ed975672
F test/table.test 04ba066432430657712d167ebf28080fe878d305
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
@ -919,7 +919,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 7270f80ac5dd17b979f1f790b2dfcf811866c1dc
R 667989871349daf241bf6e2f9ef88c3d
P bc6cce81565b17f886478bd51500bba2ed11ec1d
R 4ba5c752b69008d11d64e0bac5b06138
U dan
Z e094d9a9a392624fe79d654e33f9ed32
Z d0374b5c64afa57baa2ab5bac0bd8f73

View File

@ -1 +1 @@
bc6cce81565b17f886478bd51500bba2ed11ec1d
dbe569a099c2855480e35c0cc4d9332821ad80da

View File

@ -488,7 +488,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest))
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
){
rc = SQLITE_DONE;
}
@ -502,7 +502,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( bCloseTrans ){
TESTONLY( int rc2 );
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
assert( rc2==SQLITE_OK );
}

View File

@ -3160,10 +3160,21 @@ static void btreeEndTransaction(Btree *p){
** the rollback journal (which causes the transaction to commit) and
** drop locks.
**
** Normally, if an error occurs while the pager layer is attempting to
** finalize the underlying journal file, this function returns an error and
** the upper layer will attempt a rollback. However, if the second argument
** is non-zero then this b-tree transaction is part of a multi-file
** transaction. In this case, the transaction has already been committed
** (by deleting a master journal file) and the caller will ignore this
** functions return code. So, even if an error occurs in the pager layer,
** reset the b-tree objects internal state to indicate that the write
** transaction has been closed. This is quite safe, as the pager will have
** transitioned to the error state.
**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
int sqlite3BtreeCommitPhaseTwo(Btree *p){
int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
sqlite3BtreeEnter(p);
@ -3178,7 +3189,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
if( rc!=SQLITE_OK ){
if( rc!=SQLITE_OK && bCleanup==0 ){
sqlite3BtreeLeave(p);
return rc;
}
@ -3198,7 +3209,7 @@ int sqlite3BtreeCommit(Btree *p){
sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeCommitPhaseTwo(p);
rc = sqlite3BtreeCommitPhaseTwo(p, 0);
}
sqlite3BtreeLeave(p);
return rc;

View File

@ -87,7 +87,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*);
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*,int);

View File

@ -120,7 +120,7 @@ struct TestSyscallArray {
/* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 },
/* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 },
/* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 },
/* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, 0, 0 },
/* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 },
/* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, 0, 0 },
/* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 },
/* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 },
@ -152,7 +152,6 @@ struct TestSyscallArray {
#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig)
#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
/*
** This function is called exactly once from within each invocation of a
** system call wrapper in this file. It returns 1 if the function should
@ -264,7 +263,7 @@ static int ts_fstat(int fd, struct stat *p){
** A wrapper around ftruncate().
*/
static int ts_ftruncate(int fd, off_t n){
if( tsIsFail() ){
if( tsIsFailErrno("ftruncate") ){
return -1;
}
return orig_ftruncate(fd, n);
@ -533,6 +532,8 @@ static int test_syscall_errno(
int i;
} aErrno[] = {
{ "EACCES", EACCES },
{ "EINTR", EINTR },
{ "EIO", EIO },
{ 0, 0 }
};

View File

@ -1703,7 +1703,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
rc = sqlite3BtreeCommitPhaseTwo(pBt);
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
if( rc==SQLITE_OK ){
@ -1835,7 +1835,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
sqlite3BtreeCommitPhaseTwo(pBt);
sqlite3BtreeCommitPhaseTwo(pBt, 1);
}
}
sqlite3EndBenignMalloc();

View File

@ -14,6 +14,9 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix fts3fault2
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
ifcapable !fts3 { finish_test ; return }
do_test 1.0 {
execsql {
CREATE VIRTUAL TABLE t1 USING fts4(x);

0
test/progress.test Executable file → Normal file
View File

View File

@ -59,4 +59,53 @@ set syscall_list [list \
if {[test_syscall exists fallocate]} {lappend syscall_list fallocate}
do_test 3.1 { test_syscall list } $syscall_list
#-------------------------------------------------------------------------
# This test verifies that if a call to open() fails and errno is set to
# EINTR, the call is retried. If it succeeds, execution continues as if
# nothing happened.
#
test_syscall reset
forcedelete test.db2
do_execsql_test 4.1 {
CREATE TABLE t1(x, y);
INSERT INTO t1 VALUES(1, 2);
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.t2(x, y);
INSERT INTO t2 VALUES(3, 4);
}
db_save_and_close
test_syscall install open
foreach jrnl [list wal delete] {
for {set i 1} {$i < 20} {incr i} {
db_restore_and_reopen
test_syscall fault $i 0
test_syscall errno open EINTR
do_test 4.2.$jrnl.$i {
sqlite3 db test.db
execsql { ATTACH 'test.db2' AS aux }
execsql "PRAGMA main.journal_mode = $jrnl"
execsql "PRAGMA aux.journal_mode = $jrnl"
execsql {
BEGIN;
INSERT INTO t1 VALUES(5, 6);
INSERT INTO t2 VALUES(7, 8);
COMMIT;
}
db close
sqlite3 db test.db
execsql { ATTACH 'test.db2' AS aux }
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
}
} {1 2 5 6 3 4 7 8}
}
}
finish_test

View File

@ -66,5 +66,79 @@ do_faultsim_test 1 -faults vfsfault-* -prep {
{1 {attempt to write a readonly database}}
}
#-------------------------------------------------------------------------
# Check that a single EINTR error does not affect processing.
#
proc vfsfault_install {} {
test_syscall reset
test_syscall install {open ftruncate close}
}
forcedelete test.db test.db2
sqlite3 db test.db
do_test 2.setup {
execsql {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
INSERT INTO t1 VALUES('abc', 'def', 'ghi');
ATTACH 'test.db2' AS 'aux';
CREATE TABLE aux.t2(x);
INSERT INTO t2 VALUES(1);
}
faultsim_save_and_close
} {}
do_faultsim_test 2.1 -faults vfsfault-transient -prep {
catch { db close }
faultsim_restore
} -body {
test_syscall errno open EINTR
test_syscall errno ftruncate EINTR
test_syscall errno close EINTR
sqlite3 db test.db
set res [db eval {
ATTACH 'test.db2' AS 'aux';
SELECT * FROM t1;
PRAGMA journal_mode = truncate;
BEGIN;
INSERT INTO t1 VALUES('jkl', 'mno', 'pqr');
UPDATE t2 SET x = 2;
COMMIT;
SELECT * FROM t1;
SELECT * FROM t2;
}]
db close
set res
} -test {
faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}}
}
do_faultsim_test 2.2 -faults vfsfault-* -prep {
catch { db close }
faultsim_restore
} -body {
sqlite3 db test.db
set res [db eval {
ATTACH 'test.db2' AS 'aux';
SELECT * FROM t1;
PRAGMA journal_mode = truncate;
BEGIN;
INSERT INTO t1 VALUES('jkl', 'mno', 'pqr');
UPDATE t2 SET x = 2;
COMMIT;
SELECT * FROM t1;
SELECT * FROM t2;
}]
db close
set res
} -test {
faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} \
{1 {unable to open database file}} \
{1 {unable to open database: test.db2}} \
{1 {attempt to write a readonly database}} \
{1 {disk I/O error}}
}
finish_test