mirror of
https://gitlab.com/cznic/sqlite.git
synced 2024-11-24 02:26:14 +00:00
127 lines
3.2 KiB
Plaintext
127 lines
3.2 KiB
Plaintext
# 2023 January 31
|
|
#
|
|
# 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.
|
|
#
|
|
#***********************************************************************
|
|
#
|
|
|
|
set testdir [file dirname $argv0]
|
|
source $testdir/tester.tcl
|
|
set testprefix pendingrace
|
|
|
|
# This test file tests that a race condition surrounding hot-journal
|
|
# rollback that once existed has been resolved. The problem was that
|
|
# if, when attempting to upgrade from a SHARED to EXCLUSIVE lock in
|
|
# order to roll back a hot journal, a connection failed to take the
|
|
# lock, the file-descriptor was left holding a PENDING lock for
|
|
# a very short amount of time. In a multi-threaded deployment, this
|
|
# could allow a second connection to read the database without rolling
|
|
# back the hot journal.
|
|
#
|
|
|
|
testvfs tvfs
|
|
db close
|
|
sqlite3 db test.db -vfs tvfs
|
|
|
|
# Create a 20 page database using connection [db]. Connection [db] uses
|
|
# Tcl VFS wrapper "tvfs", but it is configured to do straight pass-through
|
|
# for now.
|
|
#
|
|
do_execsql_test 1.0 {
|
|
PRAGMA cache_size = 5;
|
|
CREATE TABLE t1(a, b);
|
|
CREATE INDEX i1 ON t1(a, b);
|
|
WITH s(i) AS (
|
|
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10
|
|
)
|
|
INSERT INTO t1 SELECT hex(randomblob(100)), hex(randomblob(100)) FROM s;
|
|
} {}
|
|
do_test 1.1a {
|
|
set nPg [db one { PRAGMA page_count }]
|
|
expr ($nPg==20 || $nPg==21)
|
|
} 1
|
|
|
|
# Simulate a crash in another process. This leaves the db with a hot-journal.
|
|
# Without the journal the db is corrupt.
|
|
#
|
|
sqlite3 db2 test.db
|
|
do_execsql_test -db db2 1.1 {
|
|
PRAGMA cache_size = 5;
|
|
BEGIN;
|
|
UPDATE t1 SET b=hex(randomblob(100));
|
|
}
|
|
db_save
|
|
db2 close
|
|
proc my_db_restore {} {
|
|
forcecopy sv_test.db-journal test.db-journal
|
|
|
|
set fd1 [open sv_test.db r]
|
|
fconfigure $fd1 -encoding binary -translation binary
|
|
set data [read $fd1]
|
|
close $fd1
|
|
|
|
set fd1 [open test.db w]
|
|
fconfigure $fd1 -encoding binary -translation binary
|
|
puts -nonewline $fd1 $data
|
|
close $fd1
|
|
}
|
|
my_db_restore
|
|
do_test 1.2 {
|
|
file exists test.db-journal
|
|
} {1}
|
|
|
|
# Set up connection [db2] to use Tcl VFS wrapper [tvfs2]. Which is configured
|
|
# so that the first call to xUnlock() fails. And then all VFS calls thereafter
|
|
# fail as well.
|
|
#
|
|
testvfs tvfs2
|
|
tvfs2 filter xUnlock
|
|
tvfs2 script xUnlock
|
|
set ::seen_unlock 0
|
|
proc xUnlock {args} {
|
|
if {$::seen_unlock==0} {
|
|
set ::seen_unlock 1
|
|
tvfs2 ioerr 1 1
|
|
tvfs2 filter {xLock xUnlock}
|
|
}
|
|
return ""
|
|
}
|
|
sqlite3 db2 test.db -vfs tvfs2
|
|
|
|
# Configure [tvfs] (used by [db]) so that within the first call to xAccess,
|
|
# [db2] attempts to read the db. This causes [db2] to fail to upgrade to
|
|
# EXCLUSIVE, leaving it with a PENDING lock. Which it holds on to,
|
|
# as the xUnlock() and all subsequent VFS calls fail.
|
|
#
|
|
tvfs filter xAccess
|
|
tvfs script xAccess
|
|
set ::seen_access 0
|
|
proc xAccess {args} {
|
|
if {$::seen_access==0} {
|
|
set ::seen_access 1
|
|
catch { db2 eval { SELECT count(*)+0 FROM t1 } }
|
|
breakpoint
|
|
}
|
|
return ""
|
|
}
|
|
|
|
# Run an integrity check using [db].
|
|
do_catchsql_test 1.3 {
|
|
PRAGMA integrity_check
|
|
} {1 {database is locked}}
|
|
|
|
db close
|
|
db2 close
|
|
tvfs delete
|
|
tvfs2 delete
|
|
|
|
finish_test
|
|
|
|
|
|
|