Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Avoid leaking page references after an IO error is encountered. (CVS 5082) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
198c395b01140ef48b6913c00188ba71 |
User & Date: | danielk1977 2008-05-05 15:26:51.000 |
Context
2008-05-05
| ||
16:23 | If an IO error occurs while locking the database and checking the cache validity, unlock the database before returning. Ticket #3030. (CVS 5083) (check-in: 4ad1809192 user: danielk1977 tags: trunk) | |
15:26 | Avoid leaking page references after an IO error is encountered. (CVS 5082) (check-in: 198c395b01 user: danielk1977 tags: trunk) | |
13:23 | Fix a couple of minor problems with transactions in virtual tables. (CVS 5081) (check-in: 2275fc6ee0 user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2004 April 6 ** ** 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. ** ************************************************************************* ** $Id: btree.c,v 1.455 2008/05/05 15:26:51 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ #include "btreeInt.h" |
︙ | ︙ | |||
6831 6832 6833 6834 6835 6836 6837 | ** ** iSkip is the page number of the locking page (PENDING_BYTE_PAGE) ** in database *pTo (before the copy). This page is never written ** into the journal file. Unless i==iSkip or the page was not ** present in pTo before the copy operation, journal page i from pTo. */ if( i!=iSkip && i<=nToPage ){ | | | < < | < < | | 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 | ** ** iSkip is the page number of the locking page (PENDING_BYTE_PAGE) ** in database *pTo (before the copy). This page is never written ** into the journal file. Unless i==iSkip or the page was not ** present in pTo before the copy operation, journal page i from pTo. */ if( i!=iSkip && i<=nToPage ){ DbPage *pDbPage = 0; rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pDbPage); } if( rc==SQLITE_OK && i>nFromPage ){ /* Yeah. It seems wierd to call DontWrite() right after Write(). But ** that is because the names of those procedures do not exactly ** represent what they do. Write() really means "put this page in the ** rollback journal and mark it as dirty so that it will be written ** to the database file later." DontWrite() undoes the second part of ** that and prevents the page from being written to the database. The ** page is still on the rollback journal, though. And that is the |
︙ | ︙ |
Changes to test/ioerr.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # This file implements regression tests for SQLite library. The # focus of this file is testing for correct handling of I/O errors # such as writes failing because the disk is full. # # The tests in this file use special facilities that are only # available in the SQLite test fixture. # | | < | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # This file implements regression tests for SQLite library. The # focus of this file is testing for correct handling of I/O errors # such as writes failing because the disk is full. # # The tests in this file use special facilities that are only # available in the SQLite test fixture. # # $Id: ioerr.test,v 1.35 2008/05/05 15:26:51 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error # on the 8th IO operation in the SQL script below doesn't report an error. # # This is because the 8th IO call attempts to read page 2 of the database # file when the file on disk is only 1 page. The pager layer detects that # this has happened and suppresses the error returned by the OS layer. # do_ioerr_test ioerr-1 -erc 1 -ckrefcount 1 -sqlprep { SELECT * FROM sqlite_master; } -sqlbody { CREATE TABLE t1(a,b,c); SELECT * FROM sqlite_master; BEGIN TRANSACTION; INSERT INTO t1 VALUES(1,2,3); INSERT INTO t1 VALUES(4,5,6); |
︙ | ︙ | |||
52 53 54 55 56 57 58 | # the file-header of the temporary database used by VACUUM. Since the # database doesn't exist at that point, the IO error is not detected. # # Additionally, if auto-vacuum is enabled, the 12th IO error is not # detected. Same reason as the 8th in the test case above. # ifcapable vacuum { | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | # the file-header of the temporary database used by VACUUM. Since the # database doesn't exist at that point, the IO error is not detected. # # Additionally, if auto-vacuum is enabled, the 12th IO error is not # detected. Same reason as the 8th in the test case above. # ifcapable vacuum { do_ioerr_test ioerr-2 -cksum true -ckrefcount true -sqlprep { BEGIN; CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50)); INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1; INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1; INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1; INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1; |
︙ | ︙ | |||
74 75 76 77 78 79 80 | DROP TABLE t2; } -sqlbody { VACUUM; } -exclude [list \ 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]] } | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | DROP TABLE t2; } -sqlbody { VACUUM; } -exclude [list \ 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]] } do_ioerr_test ioerr-3 -ckrefcount true -tclprep { execsql { PRAGMA cache_size = 10; BEGIN; CREATE TABLE abc(a); INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow } for {set i 0} {$i<150} {incr i} { |
︙ | ︙ | |||
98 99 100 101 102 103 104 | UPDATE abc SET a = randstr(90,90); COMMIT; CREATE TABLE abc3(a); } # Test IO errors that can occur retrieving a record header that flows over # onto an overflow page. | | | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | UPDATE abc SET a = randstr(90,90); COMMIT; CREATE TABLE abc3(a); } # Test IO errors that can occur retrieving a record header that flows over # onto an overflow page. do_ioerr_test ioerr-4 -ckrefcount true -tclprep { set sql "CREATE TABLE abc(a1" for {set i 2} {$i<1300} {incr i} { append sql ", a$i" } append sql ");" execsql $sql execsql {INSERT INTO abc (a1) VALUES(NULL)} } -sqlbody { SELECT * FROM abc; } # Test IO errors that may occur during a multi-file commit. # # Tests 8 and 17 are excluded when auto-vacuum is enabled for the same # reason as in test cases ioerr-1.XXX ifcapable attach { set ex "" if {[string match [execsql {pragma auto_vacuum}] 1]} { set ex [list 4 17] } do_ioerr_test ioerr-5 -ckrefcount true -sqlprep { ATTACH 'test2.db' AS test2; } -sqlbody { BEGIN; CREATE TABLE t1(a,b,c); CREATE TABLE test2.t2(a,b,c); COMMIT; } -exclude $ex } # Test IO errors when replaying two hot journals from a 2-file # transaction. This test only runs on UNIX. ifcapable crashtest&&attach { if {![catch {sqlite3 -has_codec} r] && !$r} { do_ioerr_test ioerr-6 -ckrefcount true -tclprep { execsql { ATTACH 'test2.db' as aux; CREATE TABLE tx(a, b); CREATE TABLE aux.ty(a, b); } set rc [crashsql -delay 2 -file test2.db-journal { ATTACH 'test2.db' as aux; |
︙ | ︙ | |||
189 190 191 192 193 194 195 | } -exclude 1 } # For test coverage: Cause an I/O failure while trying to read a # short field (one that fits into a Mem buffer without mallocing # for space). # | | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | } -exclude 1 } # For test coverage: Cause an I/O failure while trying to read a # short field (one that fits into a Mem buffer without mallocing # for space). # do_ioerr_test ioerr-8 -ckrefcount true -tclprep { execsql { CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2); } db close sqlite3 db test.db } -sqlbody { SELECT c FROM t1; } # For test coverage: Cause an IO error whilst reading the master-journal # name from a journal file. if {$tcl_platform(platform)=="unix"} { do_ioerr_test ioerr-9 -ckrefcount true -tclprep { execsql { CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2); BEGIN; INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2); } copy_file test.db-journal test2.db-journal |
︙ | ︙ | |||
228 229 230 231 232 233 234 | } -sqlbody { SELECT a FROM t1; } } # For test coverage: Cause an IO error during statement playback (i.e. # a constraint). | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | } -sqlbody { SELECT a FROM t1; } } # For test coverage: Cause an IO error during statement playback (i.e. # a constraint). do_ioerr_test ioerr-10 -ckrefcount true -tclprep { execsql { BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); } for {set i 0} {$i < 500} {incr i} { execsql {INSERT INTO t1 VALUES(:i, 'hello world');} } |
︙ | ︙ | |||
256 257 258 259 260 261 262 | if {$msg != "column a is not unique"} { error $msg } } # Assertion fault bug reported by alex dimitrov. # | | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | if {$msg != "column a is not unique"} { error $msg } } # Assertion fault bug reported by alex dimitrov. # do_ioerr_test ioerr-11 -ckrefcount true -erc 1 -sqlprep { CREATE TABLE A(Id INTEGER, Name TEXT); INSERT INTO A(Id, Name) VALUES(1, 'Name'); } -sqlbody { UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1; } # Test that an io error encountered in a sync() caused by a call to # sqlite3_release_memory() is handled Ok. Only try this if # memory-management is enabled. # ifcapable memorymanage { do_ioerr_test memmanage-ioerr1 -ckrefcount true -sqlprep { BEGIN; CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES(randstr(50,50), randstr(100,100), randstr(10,10)); INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1; INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1; INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1; INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1; |
︙ | ︙ |
Changes to test/tester.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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 some common TCL routines used for regression # testing the SQLite library # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 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 some common TCL routines used for regression # testing the SQLite library # # $Id: tester.tcl,v 1.117 2008/05/05 15:26:51 danielk1977 Exp $ # # What for user input before continuing. This gives an opportunity # to connect profiling tools to the process. # for {set i 0} {$i<[llength $argv]} {incr i} { if {[regexp {^-+pause$} [lindex $argv $i] all value]} { |
︙ | ︙ | |||
571 572 573 574 575 576 577 578 579 580 581 582 583 584 | proc do_ioerr_test {testname args} { set ::ioerropts(-start) 1 set ::ioerropts(-cksum) 0 set ::ioerropts(-erc) 0 set ::ioerropts(-count) 100000000 set ::ioerropts(-persist) 1 array set ::ioerropts $args set ::go 1 #reset_prng_state save_prng_state for {set n $::ioerropts(-start)} {$::go} {incr n} { set ::TN $n | > | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | proc do_ioerr_test {testname args} { set ::ioerropts(-start) 1 set ::ioerropts(-cksum) 0 set ::ioerropts(-erc) 0 set ::ioerropts(-count) 100000000 set ::ioerropts(-persist) 1 set ::ioerropts(-ckrefcount) 0 array set ::ioerropts $args set ::go 1 #reset_prng_state save_prng_state for {set n $::ioerropts(-start)} {$::go} {incr n} { set ::TN $n |
︙ | ︙ | |||
678 679 680 681 682 683 684 685 686 687 688 | # One of two things must have happened. either # 1. We never hit the IO error and the SQL returned OK # 2. An IO error was hit and the SQL failed # expr { ($s && !$r && !$q) || (!$s && $r && $q) } } {1} # If an IO error occured, then the checksum of the database should # be the same as before the script that caused the IO error was run. if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} { | > > > > > > > > > > > > > | | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | # One of two things must have happened. either # 1. We never hit the IO error and the SQL returned OK # 2. An IO error was hit and the SQL failed # expr { ($s && !$r && !$q) || (!$s && $r && $q) } } {1} if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} { # Check that no page references were leaked. There should be # a single reference if there is still an active transaction, # or zero otherwise. do_test $testname.$n.4 { set bt [btree_from_db db] db_enter db array set stats [btree_pager_stats $bt] db_leave db set stats(ref) } [expr {[sqlite3_get_autocommit db]?0:1}] } # If an IO error occured, then the checksum of the database should # be the same as before the script that caused the IO error was run. if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} { do_test $testname.$n.5 { catch {db close} set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] cksum } $checksum } set ::sqlite_io_error_hardhit 0 |
︙ | ︙ |