xref: /sqlite-3.40.0/test/superlock.test (revision 4a8a6467)
1e336b001Sdan# 2010 November 19
2e336b001Sdan#
3e336b001Sdan# The author disclaims copyright to this source code.  In place of
4e336b001Sdan# a legal notice, here is a blessing:
5e336b001Sdan#
6e336b001Sdan#    May you do good and not evil.
7e336b001Sdan#    May you find forgiveness for yourself and forgive others.
8e336b001Sdan#    May you share freely, never taking more than you give.
9e336b001Sdan#
10e336b001Sdan#***********************************************************************
11e336b001Sdan#
12e336b001Sdan
13e336b001Sdanset testdir [file dirname $argv0]
14e336b001Sdansource $testdir/tester.tcl
15e336b001Sdansource $testdir/lock_common.tcl
16e336b001Sdan
17e336b001Sdanset testprefix superlock
187bd6b49aSdrhdo_not_use_codec
19e336b001Sdan
20e284a0e9Sdan# Test organization:
21e284a0e9Sdan#
22e284a0e9Sdan#   1.*: Test superlock on a rollback database. Test that once the db is
23e284a0e9Sdan#        superlocked, it is not possible for a second client to read from
24e284a0e9Sdan#        it.
25e284a0e9Sdan#
26e284a0e9Sdan#   2.*: Test superlock on a WAL database with zero frames in the WAL file.
27e284a0e9Sdan#        Test that once the db is superlocked, it is not possible to read,
28e284a0e9Sdan#        write or checkpoint the db.
29e284a0e9Sdan#
30e284a0e9Sdan#   3.*: As 2.*, for WAL databases with one or more frames in the WAL.
31e284a0e9Sdan#
32e284a0e9Sdan#   4.*: As 2.*, for WAL databases with one or more checkpointed frames
33e284a0e9Sdan#        in the WAL.
34e284a0e9Sdan#
35e284a0e9Sdan#   5.*: Test that a call to sqlite3demo_superlock() uses the busy handler
36e284a0e9Sdan#        correctly to wait for existing clients to clear on a WAL database.
37e284a0e9Sdan#        And returns SQLITE_BUSY if no busy handler is defined or the busy
38e284a0e9Sdan#        handler returns 0 before said clients relinquish their locks.
39e284a0e9Sdan#
40e284a0e9Sdan#   6.*: Test that if a superlocked WAL database is overwritten, existing
41e284a0e9Sdan#        clients run the recovery to build the new wal-index after the
42e284a0e9Sdan#        superlock is released.
43e284a0e9Sdan#
44e284a0e9Sdan#
45e284a0e9Sdan
46e336b001Sdando_execsql_test 1.1 {
47e336b001Sdan  CREATE TABLE t1(a, b);
48e336b001Sdan  INSERT INTO t1 VALUES(1, 2);
49e336b001Sdan  PRAGMA journal_mode = DELETE;
50e336b001Sdan} {delete}
51e336b001Sdan
525209132aSdanifcapable !wal {
535209132aSdan  finish_test
545209132aSdan  return
555209132aSdan}
565209132aSdan
57e336b001Sdando_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
58e336b001Sdando_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
59e336b001Sdando_test 1.4 { unlock } {}
60e336b001Sdan
61e336b001Sdando_execsql_test 2.1 {
62e336b001Sdan  INSERT INTO t1 VALUES(3, 4);
63e336b001Sdan  PRAGMA journal_mode = WAL;
64e336b001Sdan} {wal}
65e336b001Sdan
66e336b001Sdando_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock}
67e336b001Sdando_catchsql_test 2.3 { SELECT * FROM t1 }           {1 {database is locked}}
68e336b001Sdando_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
69d42892ebSdando_catchsql_test 2.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
70e336b001Sdando_test 2.6 { unlock } {}
71e336b001Sdan
72e336b001Sdando_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) }
73e336b001Sdan
74e336b001Sdando_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock}
75e336b001Sdando_catchsql_test 3.3 { SELECT * FROM t1 }           {1 {database is locked}}
76e336b001Sdando_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
77d42892ebSdando_catchsql_test 3.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
78e336b001Sdando_test 3.6 { unlock } {}
79e336b001Sdan
800774bb59Sdan# At this point the WAL file consists of a single frame only - written
810774bb59Sdan# by test case 3.1. If the ZERO_DAMAGE flag were not set, it would consist
820774bb59Sdan# of two frames - the frame written by 3.1 and a padding frame.
830774bb59Sdando_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 1 1}
84e336b001Sdan
85e336b001Sdando_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
86e336b001Sdando_catchsql_test 4.3 { SELECT * FROM t1 }           {1 {database is locked}}
87e336b001Sdando_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
88d42892ebSdando_catchsql_test 4.5 { PRAGMA wal_checkpoint }      {0 {1 -1 -1}}
89e336b001Sdando_test 4.6 { unlock } {}
90e336b001Sdan
91e336b001Sdando_multiclient_test tn {
92e284a0e9Sdan
93e336b001Sdan  proc busyhandler {x} {
94e336b001Sdan    switch -- $x {
95e336b001Sdan      1 { sql1 "COMMIT" }
96e336b001Sdan      2 { sql2 "COMMIT" }
97e336b001Sdan      3 { sql3 "COMMIT" }
98e336b001Sdan    }
99e336b001Sdan    lappend ::busylist $x
100e336b001Sdan    return 1
101e336b001Sdan  }
102e336b001Sdan  set ::busylist [list]
103e336b001Sdan
104e336b001Sdan  do_test 5.$tn.1 {
105e336b001Sdan    sql1 {
106e336b001Sdan      CREATE TABLE t1(a, b);
107e336b001Sdan      PRAGMA journal_mode = WAL;
108e336b001Sdan      INSERT INTO t1 VALUES(1, 2);
109e336b001Sdan    }
110e336b001Sdan  } {wal}
111e336b001Sdan
112e336b001Sdan  do_test 5.$tn.2 {
113e336b001Sdan    sql1 { BEGIN ; SELECT * FROM t1 }
114e336b001Sdan    sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) }
115e336b001Sdan    sql3 { BEGIN ; SELECT * FROM t1 }
116e336b001Sdan  } {1 2}
117e336b001Sdan
118e336b001Sdan  do_test 5.$tn.3 {
119e336b001Sdan    set ::busylist [list]
120e336b001Sdan    sqlite3demo_superlock unlock test.db "" busyhandler
121e336b001Sdan    set ::busylist
122e336b001Sdan  } {0 1 2 3}
123e336b001Sdan
124e336b001Sdan  do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
125e336b001Sdan  do_test 5.$tn.5 {
126e336b001Sdan    csql3 { INSERT INTO t1 VALUES(5, 6) }
127e336b001Sdan  } {1 {database is locked}}
128d42892ebSdan  do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {0 {1 -1 -1}}
129e336b001Sdan
130e336b001Sdan  do_test 5.$tn.7 { unlock } {}
131e284a0e9Sdan
132e284a0e9Sdan
133e284a0e9Sdan  do_test 5.$tn.8 {
134e284a0e9Sdan    sql1 { BEGIN ; SELECT * FROM t1 }
135e284a0e9Sdan    sql2 { BEGIN ; INSERT INTO t1 VALUES(5, 6) }
136e284a0e9Sdan    sql3 { BEGIN ; SELECT * FROM t1 }
137e284a0e9Sdan  } {1 2 3 4}
138e284a0e9Sdan
139e284a0e9Sdan  do_test 5.$tn.9 {
140e284a0e9Sdan    list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
141e284a0e9Sdan  } {1 {database is locked}}
142e284a0e9Sdan  do_test 5.$tn.10 {
143e284a0e9Sdan    sql1 COMMIT
144e284a0e9Sdan    list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
145e284a0e9Sdan  } {1 {database is locked}}
146e284a0e9Sdan  do_test 5.$tn.11 {
147e284a0e9Sdan    sql2 COMMIT
148e284a0e9Sdan    list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
149e284a0e9Sdan  } {1 {database is locked}}
150e284a0e9Sdan  do_test 5.$tn.12 {
151e284a0e9Sdan    sql3 COMMIT
152e284a0e9Sdan    list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
153e284a0e9Sdan  } {0 unlock}
154e284a0e9Sdan  unlock
155c216eee7Sdan
156c216eee7Sdan
157c216eee7Sdan  do_test 5.$tn.13 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
158c216eee7Sdan  do_test 5.$tn.14 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
159c216eee7Sdan  do_test 5.$tn.15 { sqlite3demo_superlock unlock test.db } {unlock}
160c216eee7Sdan  do_test 5.$tn.16 { unlock } {}
161c216eee7Sdan  do_test 5.$tn.17 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
162c216eee7Sdan  do_test 5.$tn.18 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
163c216eee7Sdan  do_test 5.$tn.19 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
164e336b001Sdan}
165e336b001Sdan
166e284a0e9Sdanproc read_content {file} {
167e284a0e9Sdan  if {[file exists $file]==0} {return ""}
168e284a0e9Sdan  set fd [open $file]
169e284a0e9Sdan  fconfigure $fd -encoding binary -translation binary
170e284a0e9Sdan  set content [read $fd]
171e284a0e9Sdan  close $fd
172e284a0e9Sdan  return $content
173e284a0e9Sdan}
174e284a0e9Sdan
175e284a0e9Sdanproc write_content {file content} {
176e284a0e9Sdan  set fd [open $file w+]
177e284a0e9Sdan  fconfigure $fd -encoding binary -translation binary
178e284a0e9Sdan  puts -nonewline $fd $content
179e284a0e9Sdan  close $fd
180e284a0e9Sdan}
181e284a0e9Sdan
182e284a0e9Sdan# Both $file1 and $file2 are database files. This function takes a
183e284a0e9Sdan# superlock on each, then exchanges the content of the two files (i.e.
184e284a0e9Sdan# overwrites $file1 with the initial contents of $file2, and overwrites
185e284a0e9Sdan# $file2 with the initial contents of $file1). The contents of any WAL
186e284a0e9Sdan# file is also exchanged.
187e284a0e9Sdan#
188e284a0e9Sdanproc db_swap {file1 file2} {
189e284a0e9Sdan  sqlite3demo_superlock unlock1 $file1
190e284a0e9Sdan  sqlite3demo_superlock unlock2 $file2
191e284a0e9Sdan
192e284a0e9Sdan  set db1 [read_content $file1]
193e284a0e9Sdan  set db2 [read_content $file2]
194e284a0e9Sdan  write_content $file1 $db2
195e284a0e9Sdan  write_content $file2 $db1
196e284a0e9Sdan
197e284a0e9Sdan  set wal1 [read_content ${file1}-wal]
198e284a0e9Sdan  set wal2 [read_content ${file2}-wal]
199e284a0e9Sdan  write_content ${file1}-wal $wal2
200e284a0e9Sdan  write_content ${file2}-wal $wal1
201e284a0e9Sdan
202e284a0e9Sdan  unlock1
203e284a0e9Sdan  unlock2
204e284a0e9Sdan}
205e284a0e9Sdan
206e284a0e9Sdanforcedelete test.db
207e284a0e9Sdansqlite3 db  test.db
208e284a0e9Sdando_execsql_test 6.1 {
209e284a0e9Sdan  ATTACH 'test.db2' AS aux;
210e284a0e9Sdan  PRAGMA aux.journal_mode = wal;
211e284a0e9Sdan  CREATE TABLE aux.t2(x, y);
212e284a0e9Sdan  INSERT INTO aux.t2 VALUES('a', 'b');
213e284a0e9Sdan  PRAGMA schema_version = 450;
214e284a0e9Sdan  DETACH aux;
215e284a0e9Sdan
216e284a0e9Sdan  PRAGMA main.journal_mode = wal;
217e284a0e9Sdan  CREATE TABLE t1(a, b);
218e284a0e9Sdan  INSERT INTO t1 VALUES(1, 2);
219e284a0e9Sdan  INSERT INTO t1 VALUES(3, 4);
220e284a0e9Sdan  SELECT * FROM t1;
221e284a0e9Sdan} {wal wal 1 2 3 4}
222e284a0e9Sdan
223e284a0e9Sdan
224e284a0e9Sdandb_swap test.db2 test.db
225e284a0e9Sdando_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}}
226e284a0e9Sdando_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}}
227e284a0e9Sdan
228e284a0e9Sdandb_swap test.db2 test.db
229e284a0e9Sdando_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}}
230e284a0e9Sdando_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}}
231e284a0e9Sdan
2329c5e3680Sdando_execsql_test  6.6 { PRAGMA wal_checkpoint } {0 0 0}
233e284a0e9Sdan
234e284a0e9Sdandb_swap test.db2 test.db
235e284a0e9Sdando_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}}
236e284a0e9Sdando_catchsql_test 6.8 { SELECT * FROM t2 } {0 {a b}}
237e284a0e9Sdan
238e284a0e9Sdandb_swap test.db2 test.db
239e284a0e9Sdando_catchsql_test 6.9 { SELECT * FROM t1 } {0 {1 2 3 4}}
240e284a0e9Sdando_catchsql_test 6.10 { SELECT * FROM t2 } {1 {no such table: t2}}
241e284a0e9Sdan
242*4a8a6467Sdrhif {[nonzero_reserved_bytes]} {
243*4a8a6467Sdrh  # Vacuum with a size change is not allowed with the codec
244*4a8a6467Sdrh  do_execsql_test  6.11codec {
245*4a8a6467Sdrh    PRAGMA journal_mode = delete;
246*4a8a6467Sdrh    VACUUM;
247*4a8a6467Sdrh    PRAGMA journal_mode = wal;
248*4a8a6467Sdrh    INSERT INTO t1 VALUES(5, 6);
249*4a8a6467Sdrh  } {delete wal}
250*4a8a6467Sdrh} else {
251e284a0e9Sdan  do_execsql_test  6.11 {
252e284a0e9Sdan    PRAGMA journal_mode = delete;
253e284a0e9Sdan    PRAGMA page_size = 512;
254e284a0e9Sdan    VACUUM;
255e284a0e9Sdan    PRAGMA journal_mode = wal;
256e284a0e9Sdan    INSERT INTO t1 VALUES(5, 6);
257e284a0e9Sdan  } {delete wal}
258*4a8a6467Sdrh}
259e284a0e9Sdan
260e284a0e9Sdandb_swap test.db2 test.db
261e284a0e9Sdando_catchsql_test 6.12 { SELECT * FROM t1 } {1 {no such table: t1}}
262e284a0e9Sdando_catchsql_test 6.13 { SELECT * FROM t2 } {0 {a b}}
263e284a0e9Sdan
264e284a0e9Sdandb_swap test.db2 test.db
265e284a0e9Sdando_catchsql_test 6.14 { SELECT * FROM t1 } {0 {1 2 3 4 5 6}}
266e284a0e9Sdando_catchsql_test 6.15 { SELECT * FROM t2 } {1 {no such table: t2}}
267e336b001Sdan
268e336b001Sdanfinish_test
269