1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Fix a change-counter bug similar to #3584. This one is much more obscure though, requiring a transient IO or malloc error to occur while running in exclusive mode. (CVS 6189)

FossilOrigin-Name: 9f07d2d9226b73c4dc311fd142e0aaba4ffcb078
This commit is contained in:
danielk1977
2009-01-16 16:40:14 +00:00
parent 45d6882fd9
commit db3403973c
4 changed files with 87 additions and 10 deletions

View File

@ -16,7 +16,7 @@
# to see what happens in the library if a malloc were to really fail
# due to an out-of-memory situation.
#
# $Id: malloc.test,v 1.73 2009/01/10 16:15:09 danielk1977 Exp $
# $Id: malloc.test,v 1.74 2009/01/16 16:40:14 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -721,6 +721,72 @@ do_malloc_test 31 -sqlprep {
INSERT INTO t1 VALUES(1, 2);
}
# When written, this test provoked an obscure change-counter bug.
#
# If, when running in exclusive mode, a malloc() failure occurs
# after the database file change-counter has been written but
# before the transaction has been committed, then the transaction
# is automatically rolled back. However, internally the
# Pager.changeCounterDone flag was being left set. This means
# that if the same connection attempts another transaction following
# the malloc failure and rollback, the change counter will not
# be updated. This could corrupt another processes cache.
#
do_malloc_test 32 -tclprep {
# Build a small database containing an indexed table.
#
db eval {
BEGIN;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
COMMIT;
PRAGMA locking_mode = exclusive;
}
# Open a second database connection. Load the table (but not index)
# into the second connections pager cache.
#
sqlite3 db2 test.db
db2 eval { SELECT b FROM t1 }
} -tclbody {
# Running in exclusive mode, perform a database transaction that
# modifies both the database table and index. For iterations where
# the malloc failure occurs after updating the change counter but
# before committing the transaction, this should result in the
# transaction being rolled back but the changeCounterDone flag
# left set.
#
db eval { UPDATE t1 SET a = a + 3 }
} -cleanup {
# Perform another transaction using the first connection. Unlock
# the database after doing so. If this is one of the right iterations,
# then this should result in the database contents being updated but
# the change-counter left as it is.
#
db eval {
PRAGMA locking_mode = normal;
UPDATE t1 SET a = a + 3;
}
# Now do an integrity check with the second connection. The second
# connection still has the database table in its cache. If this is
# one of the magic iterations and the change counter was not modified,
# then it won't realize that the cached data is out of date. Since
# the cached data won't match the up to date index data read from
# the database file, the integrity check should fail.
#
set zRepeat "transient"
if {$::iRepeat} {set zRepeat "persistent"}
do_test malloc-32.$zRepeat.${::n}.integrity {
execsql {PRAGMA integrity_check} db2
} {ok}
db2 close
}
# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
catch {db close}