xref: /sqlite-3.40.0/test/pager1.test (revision 53f04f3b)
1e91a54e1Sdan# 2010 June 15
2e91a54e1Sdan#
3e91a54e1Sdan# The author disclaims copyright to this source code.  In place of
4e91a54e1Sdan# a legal notice, here is a blessing:
5e91a54e1Sdan#
6e91a54e1Sdan#    May you do good and not evil.
7e91a54e1Sdan#    May you find forgiveness for yourself and forgive others.
8e91a54e1Sdan#    May you share freely, never taking more than you give.
9e91a54e1Sdan#
10e91a54e1Sdan#***********************************************************************
11e91a54e1Sdan#
12e91a54e1Sdan
13e91a54e1Sdanset testdir [file dirname $argv0]
14e91a54e1Sdansource $testdir/tester.tcl
15e91a54e1Sdansource $testdir/lock_common.tcl
16e91a54e1Sdansource $testdir/malloc_common.tcl
17e91a54e1Sdan
18b0ac3e3aSdan#
19b0ac3e3aSdan# pager1-1.*: Test inter-process locking (clients in multiple processes).
20b0ac3e3aSdan#
21b0ac3e3aSdan# pager1-2.*: Test intra-process locking (multiple clients in this process).
22b0ac3e3aSdan#
23b0ac3e3aSdan# pager1-3.*: Savepoint related tests.
24b0ac3e3aSdan#
25b0ac3e3aSdan
26b0ac3e3aSdanproc do_execsql_test {testname sql result} {
27b0ac3e3aSdan  uplevel do_test $testname [list "execsql {$sql}"] [list $result]
28b0ac3e3aSdan}
29b0ac3e3aSdanproc do_catchsql_test {testname sql result} {
30b0ac3e3aSdan  uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
31b0ac3e3aSdan}
32b0ac3e3aSdan
33*53f04f3bSdanset a_string_counter 1
34*53f04f3bSdanproc a_string {n} {
35*53f04f3bSdan  global a_string_counter
36*53f04f3bSdan  incr a_string_counter
37*53f04f3bSdan  string range [string repeat "${a_string_counter}." $n] 1 $n
38*53f04f3bSdan}
39*53f04f3bSdandb func a_string a_string
40*53f04f3bSdan
41a4a9095eSdando_multiclient_test tn {
42e91a54e1Sdan
43e91a54e1Sdan  # Create and populate a database table using connection [db]. Check
44e91a54e1Sdan  # that connections [db2] and [db3] can see the schema and content.
45e91a54e1Sdan  #
46e91a54e1Sdan  do_test pager1-$tn.1 {
47e91a54e1Sdan    sql1 {
48e91a54e1Sdan      CREATE TABLE t1(a PRIMARY KEY, b);
49e91a54e1Sdan      CREATE INDEX i1 ON t1(b);
50e91a54e1Sdan      INSERT INTO t1 VALUES(1, 'one');
51e91a54e1Sdan      INSERT INTO t1 VALUES(2, 'two');
52e91a54e1Sdan    }
53e91a54e1Sdan  } {}
54e91a54e1Sdan  do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
55e91a54e1Sdan  do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
56e91a54e1Sdan
57e91a54e1Sdan  # Open a transaction and add a row using [db]. This puts [db] in
58e91a54e1Sdan  # RESERVED state. Check that connections [db2] and [db3] can still
59e91a54e1Sdan  # read the database content as it was before the transaction was
60e91a54e1Sdan  # opened. [db] should see the inserted row.
61e91a54e1Sdan  #
62e91a54e1Sdan  do_test pager1-$tn.4 {
63e91a54e1Sdan    sql1 {
64e91a54e1Sdan      BEGIN;
65e91a54e1Sdan        INSERT INTO t1 VALUES(3, 'three');
66e91a54e1Sdan    }
67e91a54e1Sdan  } {}
68e91a54e1Sdan  do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
69e91a54e1Sdan  do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
70e91a54e1Sdan
71e91a54e1Sdan  # [db] still has an open write transaction. Check that this prevents
72e91a54e1Sdan  # other connections (specifically [db2]) from writing to the database.
73e91a54e1Sdan  #
74e91a54e1Sdan  # Even if [db2] opens a transaction first, it may not write to the
75e91a54e1Sdan  # database. After the attempt to write the db within a transaction,
76e91a54e1Sdan  # [db2] is left with an open transaction, but not a read-lock on
77e91a54e1Sdan  # the main database. So it does not prevent [db] from committing.
78e91a54e1Sdan  #
79e91a54e1Sdan  do_test pager1-$tn.8 {
80e91a54e1Sdan    csql2 { UPDATE t1 SET a = a + 10 }
81e91a54e1Sdan  } {1 {database is locked}}
82e91a54e1Sdan  do_test pager1-$tn.9 {
83e91a54e1Sdan    csql2 {
84e91a54e1Sdan      BEGIN;
85e91a54e1Sdan      UPDATE t1 SET a = a + 10;
86e91a54e1Sdan    }
87e91a54e1Sdan  } {1 {database is locked}}
88e91a54e1Sdan
89e91a54e1Sdan  # Have [db] commit its transactions. Check the other connections can
90e91a54e1Sdan  # now see the new database content.
91e91a54e1Sdan  #
92e91a54e1Sdan  do_test pager1-$tn.10 { sql1 { COMMIT } } {}
93e91a54e1Sdan  do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
94e91a54e1Sdan  do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
95e91a54e1Sdan  do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
96e91a54e1Sdan
97e91a54e1Sdan  # Check that, as noted above, [db2] really did keep an open transaction
98e91a54e1Sdan  # after the attempt to write the database failed.
99e91a54e1Sdan  #
100e91a54e1Sdan  do_test pager1-$tn.14 {
101e91a54e1Sdan    csql2 { BEGIN }
102e91a54e1Sdan  } {1 {cannot start a transaction within a transaction}}
103e91a54e1Sdan  do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}
104e91a54e1Sdan
105e91a54e1Sdan  # Have [db2] open a transaction and take a read-lock on the database.
106e91a54e1Sdan  # Check that this prevents [db] from writing to the database (outside
107e91a54e1Sdan  # of any transaction). After this fails, check that [db3] can read
108e91a54e1Sdan  # the db (showing that [db] did not take a PENDING lock etc.)
109e91a54e1Sdan  #
110e91a54e1Sdan  do_test pager1-$tn.15 {
111e91a54e1Sdan    sql2 { BEGIN; SELECT * FROM t1; }
112e91a54e1Sdan  } {1 one 2 two 3 three}
113e91a54e1Sdan  do_test pager1-$tn.16 {
114e91a54e1Sdan    csql1 { UPDATE t1 SET a = a + 10 }
115e91a54e1Sdan  } {1 {database is locked}}
116e91a54e1Sdan  do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
117e91a54e1Sdan
118e91a54e1Sdan  # This time, have [db] open a transaction before writing the database.
119e91a54e1Sdan  # This works - [db] gets a RESERVED lock which does not conflict with
120e91a54e1Sdan  # the SHARED lock [db2] is holding.
121e91a54e1Sdan  #
122e91a54e1Sdan  do_test pager1-$tn.18 {
123e91a54e1Sdan    sql1 {
124e91a54e1Sdan      BEGIN;
125e91a54e1Sdan      UPDATE t1 SET a = a + 10;
126e91a54e1Sdan    }
127e91a54e1Sdan  } {}
128e91a54e1Sdan  do_test pager1-$tn-19 {
129e91a54e1Sdan    sql1 { PRAGMA lock_status }
130e91a54e1Sdan  } {main reserved temp closed}
131e91a54e1Sdan  do_test pager1-$tn-20 {
132e91a54e1Sdan    sql2 { PRAGMA lock_status }
133e91a54e1Sdan  } {main shared temp closed}
134e91a54e1Sdan
135e91a54e1Sdan  # Check that all connections can still read the database. Only [db] sees
136e91a54e1Sdan  # the updated content (as the transaction has not been committed yet).
137e91a54e1Sdan  #
138e91a54e1Sdan  do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
139e91a54e1Sdan  do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
140e91a54e1Sdan  do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
141e91a54e1Sdan
142e91a54e1Sdan  # Because [db2] still has the SHARED lock, [db] is unable to commit the
143e91a54e1Sdan  # transaction. If it tries, an error is returned and the connection
144e91a54e1Sdan  # upgrades to a PENDING lock.
145e91a54e1Sdan  #
146e91a54e1Sdan  # Once this happens, [db] can read the database and see the new content,
147e91a54e1Sdan  # [db2] (still holding SHARED) can still read the old content, but [db3]
148e91a54e1Sdan  # (not holding any lock) is prevented by [db]'s PENDING from reading
149e91a54e1Sdan  # the database.
150e91a54e1Sdan  #
151e91a54e1Sdan  do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
152e91a54e1Sdan  do_test pager1-$tn-25 {
153e91a54e1Sdan    sql1 { PRAGMA lock_status }
154e91a54e1Sdan  } {main pending temp closed}
155e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { SELECT * FROM t1  } } {11 one 12 two 13 three}
156e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1  } } {1 one 2 two 3 three}
157e91a54e1Sdan  do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
158e91a54e1Sdan
159e91a54e1Sdan  # Have [db2] commit its read transaction, releasing the SHARED lock it
160e91a54e1Sdan  # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
161e91a54e1Sdan  # is still holding a PENDING).
162e91a54e1Sdan  #
163e91a54e1Sdan  do_test pager1-$tn.29 { sql2 { COMMIT } } {}
164e91a54e1Sdan  do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
165e91a54e1Sdan  do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
166e91a54e1Sdan
167e91a54e1Sdan  # [db] is now able to commit the transaction. Once the transaction is
168e91a54e1Sdan  # committed, all three connections can read the new content.
169e91a54e1Sdan  #
170e91a54e1Sdan  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
171e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
172e91a54e1Sdan  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
173e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
174e91a54e1Sdan  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
175e91a54e1Sdan}
176e91a54e1Sdan
177*53f04f3bSdan#-------------------------------------------------------------------------
178*53f04f3bSdan# Savepoint related test cases.
179*53f04f3bSdan#
180b0ac3e3aSdando_test pager1-3.1 {
181b0ac3e3aSdan  faultsim_delete_and_reopen
182b0ac3e3aSdan  execsql {
183b0ac3e3aSdan    CREATE TABLE t1(a PRIMARY KEY, b);
184b0ac3e3aSdan    CREATE TABLE counter(
185b0ac3e3aSdan      i CHECK (i<5),
186b0ac3e3aSdan      u CHECK (u<10)
187b0ac3e3aSdan    );
188b0ac3e3aSdan    INSERT INTO counter VALUES(0, 0);
189b0ac3e3aSdan    CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
190b0ac3e3aSdan      UPDATE counter SET i = i+1;
191b0ac3e3aSdan    END;
192b0ac3e3aSdan    CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
193b0ac3e3aSdan      UPDATE counter SET u = u+1;
194b0ac3e3aSdan    END;
195b0ac3e3aSdan  }
196b0ac3e3aSdan  execsql { SELECT * FROM counter }
197b0ac3e3aSdan} {0 0}
198b0ac3e3aSdan
199b0ac3e3aSdando_execsql_test pager1-3.2 {
200b0ac3e3aSdan  BEGIN;
201b0ac3e3aSdan    INSERT INTO t1 VALUES(1, randomblob(1500));
202b0ac3e3aSdan    INSERT INTO t1 VALUES(2, randomblob(1500));
203b0ac3e3aSdan    INSERT INTO t1 VALUES(3, randomblob(1500));
204b0ac3e3aSdan    SELECT * FROM counter;
205b0ac3e3aSdan} {3 0}
206b0ac3e3aSdando_catchsql_test pager1-3.3 {
207b0ac3e3aSdan    INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
208b0ac3e3aSdan} {1 {constraint failed}}
209b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
210b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
211b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {}
212b0ac3e3aSdan
213*53f04f3bSdan#-------------------------------------------------------------------------
214*53f04f3bSdan# Hot journal rollback related test cases.
215*53f04f3bSdan#
216*53f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid
217*53f04f3bSdan#               journal files.
218*53f04f3bSdan#
219*53f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a
220*53f04f3bSdan#               hot-journal file appears to be corrupt (checksum does not
221*53f04f3bSdan#               compute) the associated journal is rolled back (and no
222*53f04f3bSdan#               xAccess() call to check for the presence of any master
223*53f04f3bSdan#               journal file is made).
224*53f04f3bSdan#
225*53f04f3bSdando_test pager1.4.1.1 {
226*53f04f3bSdan  faultsim_delete_and_reopen
227*53f04f3bSdan  execsql {
228*53f04f3bSdan    CREATE TABLE x(y, z);
229*53f04f3bSdan    INSERT INTO x VALUES(1, 2);
230*53f04f3bSdan  }
231*53f04f3bSdan  set fd [open test.db-journal w]
232*53f04f3bSdan  puts -nonewline $fd "helloworld"
233*53f04f3bSdan  close $fd
234*53f04f3bSdan  file exists test.db-journal
235*53f04f3bSdan} {1}
236*53f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
237*53f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0}
238*53f04f3bSdan
239*53f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite
240*53f04f3bSdan# deletes the master-journal to commit a multi-file transaction.
241*53f04f3bSdan#
242*53f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
243*53f04f3bSdan# up the file system to contain two databases, two hot-journal files and
244*53f04f3bSdan# a master-journal.
245*53f04f3bSdan#
246*53f04f3bSdando_test pager1.4.2.1 {
247*53f04f3bSdan  testvfs tstvfs -default 1
248*53f04f3bSdan  tstvfs filter xDelete
249*53f04f3bSdan  tstvfs script xDeleteCallback
250*53f04f3bSdan  proc xDeleteCallback {method file args} {
251*53f04f3bSdan    set file [file tail $file]
252*53f04f3bSdan    if { [string match *mj* $file] } { faultsim_save }
253*53f04f3bSdan  }
254*53f04f3bSdan  faultsim_delete_and_reopen
255*53f04f3bSdan  db func a_string a_string
256*53f04f3bSdan  execsql {
257*53f04f3bSdan    ATTACH 'test.db2' AS aux;
258*53f04f3bSdan    PRAGMA journal_mode = DELETE;
259*53f04f3bSdan    PRAGMA main.cache_size = 10;
260*53f04f3bSdan    PRAGMA aux.cache_size = 10;
261*53f04f3bSdan    CREATE TABLE t1(a UNIQUE, b UNIQUE);
262*53f04f3bSdan    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
263*53f04f3bSdan    INSERT INTO t1 VALUES(a_string(200), a_string(300));
264*53f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
265*53f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
266*53f04f3bSdan    INSERT INTO t2 SELECT * FROM t1;
267*53f04f3bSdan    BEGIN;
268*53f04f3bSdan      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
269*53f04f3bSdan      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
270*53f04f3bSdan      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
271*53f04f3bSdan      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
272*53f04f3bSdan      REPLACE INTO t2 SELECT * FROM t1;
273*53f04f3bSdan    COMMIT;
274*53f04f3bSdan  }
275*53f04f3bSdan  db close
276*53f04f3bSdan  tstvfs delete
277*53f04f3bSdan} {}
278*53f04f3bSdando_test pager1.4.2.2 {
279*53f04f3bSdan  faultsim_restore_and_reopen
280*53f04f3bSdan  execsql {
281*53f04f3bSdan    SELECT count(*) FROM t1;
282*53f04f3bSdan    PRAGMA integrity_check;
283*53f04f3bSdan  }
284*53f04f3bSdan} {4 ok}
285*53f04f3bSdando_test pager1.4.2.3 {
286*53f04f3bSdan  faultsim_restore_and_reopen
287*53f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
288*53f04f3bSdan  execsql {
289*53f04f3bSdan    SELECT count(*) FROM t1;
290*53f04f3bSdan    PRAGMA integrity_check;
291*53f04f3bSdan  }
292*53f04f3bSdan} {64 ok}
293*53f04f3bSdando_test pager1.4.2.4 {
294*53f04f3bSdan  faultsim_restore_and_reopen
295*53f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
296*53f04f3bSdan  execsql {
297*53f04f3bSdan    SELECT count(*) FROM t1;
298*53f04f3bSdan    PRAGMA integrity_check;
299*53f04f3bSdan  }
300*53f04f3bSdan} {4 ok}
301*53f04f3bSdando_test pager1.4.2.5 {
302*53f04f3bSdan  faultsim_restore_and_reopen
303*53f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
304*53f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
305*53f04f3bSdan  execsql {
306*53f04f3bSdan    SELECT count(*) FROM t1;
307*53f04f3bSdan    PRAGMA integrity_check;
308*53f04f3bSdan  }
309*53f04f3bSdan} {4 ok}
310*53f04f3bSdan
311e91a54e1Sdanfinish_test
312e91a54e1Sdan
313