From: dan Date: Tue, 29 Mar 2011 15:40:55 +0000 (+0000) Subject: Fix a problem whereby following an IO error in CommitPhaseTwo() of a multi-file trans... X-Git-Tag: version-3.7.6~76 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=60939d0ade41bc220f1e40cd0e1bd6666536a206;p=thirdparty%2Fsqlite.git 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 --- diff --git a/install-sh b/install-sh old mode 100644 new mode 100755 diff --git a/manifest b/manifest index 5041a0d7f0..e1497c5c61 100644 --- a/manifest +++ b/manifest @@ -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 diff --git a/manifest.uuid b/manifest.uuid index 96763a4275..e5596a7f20 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bc6cce81565b17f886478bd51500bba2ed11ec1d \ No newline at end of file +dbe569a099c2855480e35c0cc4d9332821ad80da \ No newline at end of file diff --git a/src/backup.c b/src/backup.c index 5d8ea7f3fe..82be9635b1 100644 --- a/src/backup.c +++ b/src/backup.c @@ -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 ); } diff --git a/src/btree.c b/src/btree.c index 33d7460675..088c555fc6 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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; diff --git a/src/btree.h b/src/btree.h index 6886dd9444..468723b33e 100644 --- a/src/btree.h +++ b/src/btree.h @@ -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); diff --git a/src/test_syscall.c b/src/test_syscall.c index 88e1943a66..0ecf6c4001 100644 --- a/src/test_syscall.c +++ b/src/test_syscall.c @@ -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 } }; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 36e0d260b3..5887cab047 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1703,7 +1703,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; rc==SQLITE_OK && inDb; 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; inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - sqlite3BtreeCommitPhaseTwo(pBt); + sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); diff --git a/test/fts3fault2.test b/test/fts3fault2.test index 6d41aee1c6..fb877737f4 100644 --- a/test/fts3fault2.test +++ b/test/fts3fault2.test @@ -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); diff --git a/test/progress.test b/test/progress.test old mode 100755 new mode 100644 diff --git a/test/syscall.test b/test/syscall.test index a860e40ad3..836e00dd86 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -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 diff --git a/test/sysfault.test b/test/sysfault.test index 2d0d8b3828..2e958642df 100644 --- a/test/sysfault.test +++ b/test/sysfault.test @@ -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