Merge sqlite-release(3.43.1) into prerelease-integration
[sqlcipher.git] / test / pendingrace.test
blobf0e1a18ffa44cd47528bb864ac236ed279aad861
1 # 2023 January 31
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
6 #    May you do good and not evil.
7 #    May you find forgiveness for yourself and forgive others.
8 #    May you share freely, never taking more than you give.
10 #***********************************************************************
13 set testdir [file dirname $argv0]
14 source $testdir/tester.tcl
15 set testprefix pendingrace
17 # This test file tests that a race condition surrounding hot-journal
18 # rollback that once existed has been resolved. The problem was that
19 # if, when attempting to upgrade from a SHARED to EXCLUSIVE lock in
20 # order to roll back a hot journal, a connection failed to take the
21 # lock, the file-descriptor was left holding a PENDING lock for 
22 # a very short amount of time. In a multi-threaded deployment, this
23 # could allow a second connection to read the database without rolling
24 # back the hot journal.
27 testvfs tvfs 
28 db close
29 sqlite3 db test.db -vfs tvfs
31 # Create a 20 page database using connection [db]. Connection [db] uses
32 # Tcl VFS wrapper "tvfs", but it is configured to do straight pass-through
33 # for now.
35 do_execsql_test 1.0 {
36   PRAGMA cache_size = 5;
37   CREATE TABLE t1(a, b);
38   CREATE INDEX i1 ON t1(a, b);
39   WITH s(i) AS (
40     SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10
41   )
42   INSERT INTO t1 SELECT hex(randomblob(100)), hex(randomblob(100)) FROM s;
43 } {}
44 do_test 1.1a {
45   set nPg [db one { PRAGMA page_count }]
46   expr ($nPg==20 || $nPg==21)
47 } 1
49 # Simulate a crash in another process. This leaves the db with a hot-journal.
50 # Without the journal the db is corrupt.
52 sqlite3 db2 test.db
53 do_execsql_test -db db2 1.1 {
54   PRAGMA cache_size = 5;
55   BEGIN;
56     UPDATE t1 SET b=hex(randomblob(100));
58 db_save
59 db2 close
60 proc my_db_restore {} {
61   forcecopy sv_test.db-journal test.db-journal
63   set fd1 [open sv_test.db r]
64   fconfigure $fd1 -encoding binary -translation binary
65   set data [read $fd1]
66   close $fd1
68   set fd1 [open test.db w]
69   fconfigure $fd1 -encoding binary -translation binary
70   puts -nonewline $fd1 $data
71   close $fd1
73 my_db_restore
74 do_test 1.2 {
75   file exists test.db-journal
76 } {1}
78 # Set up connection [db2] to use Tcl VFS wrapper [tvfs2]. Which is configured
79 # so that the first call to xUnlock() fails. And then all VFS calls thereafter
80 # fail as well.
82 testvfs tvfs2
83 tvfs2 filter xUnlock
84 tvfs2 script xUnlock
85 set ::seen_unlock 0
86 proc xUnlock {args} {
87   if {$::seen_unlock==0} {
88     set ::seen_unlock 1
89     tvfs2 ioerr 1 1
90     tvfs2 filter {xLock xUnlock}
91   }
92   return ""
94 sqlite3 db2 test.db -vfs tvfs2
96 # Configure [tvfs] (used by [db]) so that within the first call to xAccess,
97 # [db2] attempts to read the db. This causes [db2] to fail to upgrade to
98 # EXCLUSIVE, leaving it with a PENDING lock. Which it holds on to, 
99 # as the xUnlock() and all subsequent VFS calls fail.
101 tvfs filter xAccess
102 tvfs script xAccess
103 set ::seen_access 0
104 proc xAccess {args} {
105   if {$::seen_access==0} {
106     set ::seen_access 1
107     catch { db2 eval { SELECT count(*)+0 FROM t1 } }
108     breakpoint
109   }
110   return ""
113 # Run an integrity check using [db].
114 do_catchsql_test 1.3 {
115   PRAGMA integrity_check
116 } {1 {database is locked}}
118 db close
119 db2 close
120 tvfs delete
121 tvfs2 delete
123 finish_test