xref: /sqlite-3.40.0/test/pager1.test (revision d353331a)
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
17153eda0aSdansource $testdir/wal_common.tcl
1868928b6cSdan
1968928b6cSdan# Do not use a codec for tests in this file, as the database file is
2068928b6cSdan# manipulated directly using tcl scripts (using the [hexio_write] command).
2168928b6cSdan#
2268928b6cSdando_not_use_codec
23e91a54e1Sdan
24b0ac3e3aSdan#
25b0ac3e3aSdan# pager1-1.*: Test inter-process locking (clients in multiple processes).
26b0ac3e3aSdan#
27b0ac3e3aSdan# pager1-2.*: Test intra-process locking (multiple clients in this process).
28b0ac3e3aSdan#
29b0ac3e3aSdan# pager1-3.*: Savepoint related tests.
30b0ac3e3aSdan#
311f4cb65aSdan# pager1-4.*: Hot-journal related tests.
321f4cb65aSdan#
33de4996e2Sdan# pager1-5.*: Cases related to multi-file commits.
34de4996e2Sdan#
35146ed78bSdan# pager1-6.*: Cases related to "PRAGMA max_page_count"
36146ed78bSdan#
37153eda0aSdan# pager1-7.*: Cases specific to "PRAGMA journal_mode=TRUNCATE"
38153eda0aSdan#
390e986f51Sdan# pager1-8.*: Cases using temporary and in-memory databases.
400e986f51Sdan#
41dca321aeSdan# pager1-9.*: Tests related to the backup API.
42dca321aeSdan#
43ec6ffc1aSdan# pager1-10.*: Test that the assumed file-system sector-size is limited to
44ec6ffc1aSdan#              64KB.
45ec6ffc1aSdan#
46*d353331aSdan# pager1-12.*: Tests involving "PRAGMA page_size"
47*d353331aSdan#
48*d353331aSdan# pager1-13.*: Cases specific to "PRAGMA journal_mode=PERSIST"
49*d353331aSdan#
50*d353331aSdan# pager1-14.*: Cases specific to "PRAGMA journal_mode=OFF"
51ec6ffc1aSdan#
52b0ac3e3aSdan
5353f04f3bSdanset a_string_counter 1
5453f04f3bSdanproc a_string {n} {
5553f04f3bSdan  global a_string_counter
5653f04f3bSdan  incr a_string_counter
5753f04f3bSdan  string range [string repeat "${a_string_counter}." $n] 1 $n
5853f04f3bSdan}
5953f04f3bSdandb func a_string a_string
6053f04f3bSdan
61a4a9095eSdando_multiclient_test tn {
62e91a54e1Sdan
63e91a54e1Sdan  # Create and populate a database table using connection [db]. Check
64e91a54e1Sdan  # that connections [db2] and [db3] can see the schema and content.
65e91a54e1Sdan  #
66e91a54e1Sdan  do_test pager1-$tn.1 {
67e91a54e1Sdan    sql1 {
68e91a54e1Sdan      CREATE TABLE t1(a PRIMARY KEY, b);
69e91a54e1Sdan      CREATE INDEX i1 ON t1(b);
70*d353331aSdan      INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two');
71e91a54e1Sdan    }
72e91a54e1Sdan  } {}
73e91a54e1Sdan  do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
74e91a54e1Sdan  do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
75e91a54e1Sdan
76e91a54e1Sdan  # Open a transaction and add a row using [db]. This puts [db] in
77e91a54e1Sdan  # RESERVED state. Check that connections [db2] and [db3] can still
78e91a54e1Sdan  # read the database content as it was before the transaction was
79e91a54e1Sdan  # opened. [db] should see the inserted row.
80e91a54e1Sdan  #
81e91a54e1Sdan  do_test pager1-$tn.4 {
82e91a54e1Sdan    sql1 {
83e91a54e1Sdan      BEGIN;
84e91a54e1Sdan        INSERT INTO t1 VALUES(3, 'three');
85e91a54e1Sdan    }
86e91a54e1Sdan  } {}
87e91a54e1Sdan  do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
88e91a54e1Sdan  do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
89e91a54e1Sdan
90e91a54e1Sdan  # [db] still has an open write transaction. Check that this prevents
91e91a54e1Sdan  # other connections (specifically [db2]) from writing to the database.
92e91a54e1Sdan  #
93e91a54e1Sdan  # Even if [db2] opens a transaction first, it may not write to the
94e91a54e1Sdan  # database. After the attempt to write the db within a transaction,
95e91a54e1Sdan  # [db2] is left with an open transaction, but not a read-lock on
96e91a54e1Sdan  # the main database. So it does not prevent [db] from committing.
97e91a54e1Sdan  #
98e91a54e1Sdan  do_test pager1-$tn.8 {
99e91a54e1Sdan    csql2 { UPDATE t1 SET a = a + 10 }
100e91a54e1Sdan  } {1 {database is locked}}
101e91a54e1Sdan  do_test pager1-$tn.9 {
102e91a54e1Sdan    csql2 {
103e91a54e1Sdan      BEGIN;
104e91a54e1Sdan      UPDATE t1 SET a = a + 10;
105e91a54e1Sdan    }
106e91a54e1Sdan  } {1 {database is locked}}
107e91a54e1Sdan
108e91a54e1Sdan  # Have [db] commit its transactions. Check the other connections can
109e91a54e1Sdan  # now see the new database content.
110e91a54e1Sdan  #
111e91a54e1Sdan  do_test pager1-$tn.10 { sql1 { COMMIT } } {}
112e91a54e1Sdan  do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
113e91a54e1Sdan  do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
114e91a54e1Sdan  do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
115e91a54e1Sdan
116e91a54e1Sdan  # Check that, as noted above, [db2] really did keep an open transaction
117e91a54e1Sdan  # after the attempt to write the database failed.
118e91a54e1Sdan  #
119e91a54e1Sdan  do_test pager1-$tn.14 {
120e91a54e1Sdan    csql2 { BEGIN }
121e91a54e1Sdan  } {1 {cannot start a transaction within a transaction}}
122e91a54e1Sdan  do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}
123e91a54e1Sdan
124e91a54e1Sdan  # Have [db2] open a transaction and take a read-lock on the database.
125e91a54e1Sdan  # Check that this prevents [db] from writing to the database (outside
126e91a54e1Sdan  # of any transaction). After this fails, check that [db3] can read
127e91a54e1Sdan  # the db (showing that [db] did not take a PENDING lock etc.)
128e91a54e1Sdan  #
129e91a54e1Sdan  do_test pager1-$tn.15 {
130e91a54e1Sdan    sql2 { BEGIN; SELECT * FROM t1; }
131e91a54e1Sdan  } {1 one 2 two 3 three}
132e91a54e1Sdan  do_test pager1-$tn.16 {
133e91a54e1Sdan    csql1 { UPDATE t1 SET a = a + 10 }
134e91a54e1Sdan  } {1 {database is locked}}
135e91a54e1Sdan  do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
136e91a54e1Sdan
137e91a54e1Sdan  # This time, have [db] open a transaction before writing the database.
138e91a54e1Sdan  # This works - [db] gets a RESERVED lock which does not conflict with
139e91a54e1Sdan  # the SHARED lock [db2] is holding.
140e91a54e1Sdan  #
141e91a54e1Sdan  do_test pager1-$tn.18 {
142e91a54e1Sdan    sql1 {
143e91a54e1Sdan      BEGIN;
144e91a54e1Sdan      UPDATE t1 SET a = a + 10;
145e91a54e1Sdan    }
146e91a54e1Sdan  } {}
147e91a54e1Sdan  do_test pager1-$tn-19 {
148e91a54e1Sdan    sql1 { PRAGMA lock_status }
149e91a54e1Sdan  } {main reserved temp closed}
150e91a54e1Sdan  do_test pager1-$tn-20 {
151e91a54e1Sdan    sql2 { PRAGMA lock_status }
152e91a54e1Sdan  } {main shared temp closed}
153e91a54e1Sdan
154e91a54e1Sdan  # Check that all connections can still read the database. Only [db] sees
155e91a54e1Sdan  # the updated content (as the transaction has not been committed yet).
156e91a54e1Sdan  #
157e91a54e1Sdan  do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
158e91a54e1Sdan  do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
159e91a54e1Sdan  do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
160e91a54e1Sdan
161e91a54e1Sdan  # Because [db2] still has the SHARED lock, [db] is unable to commit the
162e91a54e1Sdan  # transaction. If it tries, an error is returned and the connection
163e91a54e1Sdan  # upgrades to a PENDING lock.
164e91a54e1Sdan  #
165e91a54e1Sdan  # Once this happens, [db] can read the database and see the new content,
166e91a54e1Sdan  # [db2] (still holding SHARED) can still read the old content, but [db3]
167e91a54e1Sdan  # (not holding any lock) is prevented by [db]'s PENDING from reading
168e91a54e1Sdan  # the database.
169e91a54e1Sdan  #
170e91a54e1Sdan  do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
171e91a54e1Sdan  do_test pager1-$tn-25 {
172e91a54e1Sdan    sql1 { PRAGMA lock_status }
173e91a54e1Sdan  } {main pending temp closed}
174e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { SELECT * FROM t1  } } {11 one 12 two 13 three}
175e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1  } } {1 one 2 two 3 three}
176e91a54e1Sdan  do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
177e91a54e1Sdan
178e91a54e1Sdan  # Have [db2] commit its read transaction, releasing the SHARED lock it
179e91a54e1Sdan  # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
180e91a54e1Sdan  # is still holding a PENDING).
181e91a54e1Sdan  #
182e91a54e1Sdan  do_test pager1-$tn.29 { sql2 { COMMIT } } {}
183e91a54e1Sdan  do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
184e91a54e1Sdan  do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
185e91a54e1Sdan
186e91a54e1Sdan  # [db] is now able to commit the transaction. Once the transaction is
187e91a54e1Sdan  # committed, all three connections can read the new content.
188e91a54e1Sdan  #
189e91a54e1Sdan  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
190e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
191e91a54e1Sdan  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
192e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
193e91a54e1Sdan  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
194*d353331aSdan
195*d353331aSdan  # Install a busy-handler for connection [db].
196*d353331aSdan  #
197*d353331aSdan  set ::nbusy [list]
198*d353331aSdan  proc busy {n} {
199*d353331aSdan    lappend ::nbusy $n
200*d353331aSdan    if {$n>5} { sql2 COMMIT }
201*d353331aSdan    return 0
202*d353331aSdan  }
203*d353331aSdan  db busy busy
204*d353331aSdan
205*d353331aSdan  do_test pager1-$tn.29 {
206*d353331aSdan    sql1 { BEGIN ; INSERT INTO t1 VALUES('x', 'y') }
207*d353331aSdan  } {}
208*d353331aSdan  do_test pager1-$tn.30 {
209*d353331aSdan    sql2 { BEGIN ; SELECT * FROM t1 }
210*d353331aSdan  } {21 one 22 two 23 three}
211*d353331aSdan  do_test pager1-$tn.31 { sql1 COMMIT } {}
212*d353331aSdan  do_test pager1-$tn.32 { set ::nbusy } {0 1 2 3 4 5 6}
213e91a54e1Sdan}
214e91a54e1Sdan
21553f04f3bSdan#-------------------------------------------------------------------------
21653f04f3bSdan# Savepoint related test cases.
21753f04f3bSdan#
218dca321aeSdan# pager1-3.1.2.*: Force a savepoint rollback to cause the database file
219dca321aeSdan#                 to grow.
220dca321aeSdan#
221273f3f0dSdan# pager1-3.1.3.*: Use a journal created in synchronous=off mode as part
222273f3f0dSdan#                 of a savepoint rollback.
223273f3f0dSdan#
2240e986f51Sdando_test pager1-3.1.1 {
225b0ac3e3aSdan  faultsim_delete_and_reopen
226b0ac3e3aSdan  execsql {
227b0ac3e3aSdan    CREATE TABLE t1(a PRIMARY KEY, b);
228b0ac3e3aSdan    CREATE TABLE counter(
229b0ac3e3aSdan      i CHECK (i<5),
230b0ac3e3aSdan      u CHECK (u<10)
231b0ac3e3aSdan    );
232b0ac3e3aSdan    INSERT INTO counter VALUES(0, 0);
233b0ac3e3aSdan    CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
234b0ac3e3aSdan      UPDATE counter SET i = i+1;
235b0ac3e3aSdan    END;
236b0ac3e3aSdan    CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
237b0ac3e3aSdan      UPDATE counter SET u = u+1;
238b0ac3e3aSdan    END;
239b0ac3e3aSdan  }
240b0ac3e3aSdan  execsql { SELECT * FROM counter }
241b0ac3e3aSdan} {0 0}
242b0ac3e3aSdan
2430e986f51Sdando_execsql_test pager1-3.1.2 {
244*d353331aSdan  PRAGMA cache_size = 10;
245b0ac3e3aSdan  BEGIN;
246b0ac3e3aSdan    INSERT INTO t1 VALUES(1, randomblob(1500));
247b0ac3e3aSdan    INSERT INTO t1 VALUES(2, randomblob(1500));
248b0ac3e3aSdan    INSERT INTO t1 VALUES(3, randomblob(1500));
249b0ac3e3aSdan    SELECT * FROM counter;
250b0ac3e3aSdan} {3 0}
2510e986f51Sdando_catchsql_test pager1-3.1.3 {
252b0ac3e3aSdan    INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
253b0ac3e3aSdan} {1 {constraint failed}}
254b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
255b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
256b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {}
257b0ac3e3aSdan
258273f3f0dSdanforeach {tn sql tcl} {
259*d353331aSdan  7  { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 0 } {
260273f3f0dSdan    testvfs tv -default 1
261273f3f0dSdan    tv devchar safe_append
262273f3f0dSdan  }
263*d353331aSdan  8  { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 2 } {
264*d353331aSdan    testvfs tv -default 1
265*d353331aSdan    tv devchar sequential
266*d353331aSdan  }
267*d353331aSdan  9  { PRAGMA synchronous = FULL } { }
268*d353331aSdan  10 { PRAGMA synchronous = NORMAL } { }
269*d353331aSdan  11 { PRAGMA synchronous = OFF } { }
270*d353331aSdan  12 { PRAGMA synchronous = FULL ; PRAGMA fullfsync = 1 } { }
271*d353331aSdan  13 { PRAGMA synchronous = FULL } {
272*d353331aSdan    testvfs tv -default 1
273*d353331aSdan    tv devchar sequential
274*d353331aSdan  }
275273f3f0dSdan} {
276273f3f0dSdan  do_test pager1-3.$tn.1 {
277273f3f0dSdan    eval $tcl
278dca321aeSdan    faultsim_delete_and_reopen
279dca321aeSdan    db func a_string a_string
280273f3f0dSdan    execsql $sql
281dca321aeSdan    execsql {
282dca321aeSdan      PRAGMA auto_vacuum = 2;
283dca321aeSdan      PRAGMA cache_size = 10;
284dca321aeSdan      CREATE TABLE z(x INTEGER PRIMARY KEY, y);
285dca321aeSdan      BEGIN;
286dca321aeSdan        INSERT INTO z VALUES(NULL, a_string(800));
287dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   2
288dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   4
289dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   8
290dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  16
291dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  32
292dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  64
293dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 128
294dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 256
295dca321aeSdan      COMMIT;
296dca321aeSdan    }
297dca321aeSdan    execsql { PRAGMA auto_vacuum }
298dca321aeSdan  } {2}
299273f3f0dSdan  do_execsql_test pager1-3.$tn.2 {
300dca321aeSdan    BEGIN;
301dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
302dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
303dca321aeSdan      SAVEPOINT one;
304273f3f0dSdan        UPDATE z SET y = NULL WHERE x>256;
305dca321aeSdan        PRAGMA incremental_vacuum;
306dca321aeSdan        SELECT count(*) FROM z WHERE x < 100;
307dca321aeSdan      ROLLBACK TO one;
308dca321aeSdan    COMMIT;
309dca321aeSdan  } {99}
310dca321aeSdan
311273f3f0dSdan  do_execsql_test pager1-3.$tn.3 {
312273f3f0dSdan    BEGIN;
313273f3f0dSdan      SAVEPOINT one;
314273f3f0dSdan        UPDATE z SET y = y||x;
315273f3f0dSdan      ROLLBACK TO one;
316273f3f0dSdan    COMMIT;
317273f3f0dSdan    SELECT count(*) FROM z;
318273f3f0dSdan  } {258}
319273f3f0dSdan
320273f3f0dSdan  do_execsql_test pager1-3.$tn.4 {
321273f3f0dSdan    SAVEPOINT one;
322273f3f0dSdan      UPDATE z SET y = y||x;
323273f3f0dSdan    ROLLBACK TO one;
324273f3f0dSdan  } {}
325273f3f0dSdan  do_execsql_test pager1-3.$tn.5 {
326273f3f0dSdan    SELECT count(*) FROM z;
327273f3f0dSdan    RELEASE one;
328273f3f0dSdan    PRAGMA integrity_check;
329273f3f0dSdan  } {258 ok}
330273f3f0dSdan
331273f3f0dSdan  db close
332273f3f0dSdan  catch { tv delete }
333273f3f0dSdan}
334273f3f0dSdan
33553f04f3bSdan#-------------------------------------------------------------------------
33653f04f3bSdan# Hot journal rollback related test cases.
33753f04f3bSdan#
33853f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid
33953f04f3bSdan#               journal files.
34053f04f3bSdan#
34153f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a
34253f04f3bSdan#               hot-journal file appears to be corrupt (checksum does not
34353f04f3bSdan#               compute) the associated journal is rolled back (and no
34453f04f3bSdan#               xAccess() call to check for the presence of any master
34553f04f3bSdan#               journal file is made).
34653f04f3bSdan#
347de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
348de4996e2Sdan#               page-size or sector-size in the journal header appear to
349de4996e2Sdan#               be invalid (too large, too small or not a power of 2).
350de4996e2Sdan#
351e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master
352e08341c6Sdan#               journal pointer generated in various "PRAGMA synchronous"
353e08341c6Sdan#               modes.
354e08341c6Sdan#
355e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
356e08341c6Sdan#               journal-record for which the checksum fails.
357e08341c6Sdan#
358ec6ffc1aSdan# pager1.4.6.*: Test that when rolling back a hot-journal that contains a
359ec6ffc1aSdan#               master journal pointer, the master journal file is deleted
360ec6ffc1aSdan#               after all the hot-journals that refer to it are deleted.
361ec6ffc1aSdan#
36253f04f3bSdando_test pager1.4.1.1 {
36353f04f3bSdan  faultsim_delete_and_reopen
36453f04f3bSdan  execsql {
36553f04f3bSdan    CREATE TABLE x(y, z);
36653f04f3bSdan    INSERT INTO x VALUES(1, 2);
36753f04f3bSdan  }
36853f04f3bSdan  set fd [open test.db-journal w]
36953f04f3bSdan  puts -nonewline $fd "helloworld"
37053f04f3bSdan  close $fd
37153f04f3bSdan  file exists test.db-journal
37253f04f3bSdan} {1}
37353f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
37453f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0}
37553f04f3bSdan
37653f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite
37753f04f3bSdan# deletes the master-journal to commit a multi-file transaction.
37853f04f3bSdan#
37953f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
38053f04f3bSdan# up the file system to contain two databases, two hot-journal files and
38153f04f3bSdan# a master-journal.
38253f04f3bSdan#
38353f04f3bSdando_test pager1.4.2.1 {
38453f04f3bSdan  testvfs tstvfs -default 1
38553f04f3bSdan  tstvfs filter xDelete
38653f04f3bSdan  tstvfs script xDeleteCallback
38753f04f3bSdan  proc xDeleteCallback {method file args} {
38853f04f3bSdan    set file [file tail $file]
38953f04f3bSdan    if { [string match *mj* $file] } { faultsim_save }
39053f04f3bSdan  }
39153f04f3bSdan  faultsim_delete_and_reopen
39253f04f3bSdan  db func a_string a_string
39353f04f3bSdan  execsql {
39453f04f3bSdan    ATTACH 'test.db2' AS aux;
39553f04f3bSdan    PRAGMA journal_mode = DELETE;
39653f04f3bSdan    PRAGMA main.cache_size = 10;
39753f04f3bSdan    PRAGMA aux.cache_size = 10;
39853f04f3bSdan    CREATE TABLE t1(a UNIQUE, b UNIQUE);
39953f04f3bSdan    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
40053f04f3bSdan    INSERT INTO t1 VALUES(a_string(200), a_string(300));
40153f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
40253f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
40353f04f3bSdan    INSERT INTO t2 SELECT * FROM t1;
40453f04f3bSdan    BEGIN;
40553f04f3bSdan      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
40653f04f3bSdan      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
40753f04f3bSdan      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
40853f04f3bSdan      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
40953f04f3bSdan      REPLACE INTO t2 SELECT * FROM t1;
41053f04f3bSdan    COMMIT;
41153f04f3bSdan  }
41253f04f3bSdan  db close
41353f04f3bSdan  tstvfs delete
41453f04f3bSdan} {}
41553f04f3bSdando_test pager1.4.2.2 {
41653f04f3bSdan  faultsim_restore_and_reopen
41753f04f3bSdan  execsql {
41853f04f3bSdan    SELECT count(*) FROM t1;
41953f04f3bSdan    PRAGMA integrity_check;
42053f04f3bSdan  }
42153f04f3bSdan} {4 ok}
42253f04f3bSdando_test pager1.4.2.3 {
42353f04f3bSdan  faultsim_restore_and_reopen
42453f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
42553f04f3bSdan  execsql {
42653f04f3bSdan    SELECT count(*) FROM t1;
42753f04f3bSdan    PRAGMA integrity_check;
42853f04f3bSdan  }
42953f04f3bSdan} {64 ok}
43053f04f3bSdando_test pager1.4.2.4 {
43153f04f3bSdan  faultsim_restore_and_reopen
43253f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
43353f04f3bSdan  execsql {
43453f04f3bSdan    SELECT count(*) FROM t1;
43553f04f3bSdan    PRAGMA integrity_check;
43653f04f3bSdan  }
43753f04f3bSdan} {4 ok}
43853f04f3bSdando_test pager1.4.2.5 {
43953f04f3bSdan  faultsim_restore_and_reopen
44053f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
44153f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
44253f04f3bSdan  execsql {
44353f04f3bSdan    SELECT count(*) FROM t1;
44453f04f3bSdan    PRAGMA integrity_check;
44553f04f3bSdan  }
44653f04f3bSdan} {4 ok}
44753f04f3bSdan
448de4996e2Sdando_test pager1.4.3.1 {
449de4996e2Sdan  testvfs tstvfs -default 1
450de4996e2Sdan  tstvfs filter xSync
451de4996e2Sdan  tstvfs script xSyncCallback
452de4996e2Sdan  proc xSyncCallback {method file args} {
453de4996e2Sdan    set file [file tail $file]
454de4996e2Sdan    if { 0==[string match *journal $file] } { faultsim_save }
455de4996e2Sdan  }
456de4996e2Sdan  faultsim_delete_and_reopen
457de4996e2Sdan  execsql {
458de4996e2Sdan    PRAGMA journal_mode = DELETE;
459de4996e2Sdan    CREATE TABLE t1(a, b);
460de4996e2Sdan    INSERT INTO t1 VALUES(1, 2);
461de4996e2Sdan    INSERT INTO t1 VALUES(3, 4);
462de4996e2Sdan  }
463de4996e2Sdan  db close
464de4996e2Sdan  tstvfs delete
465de4996e2Sdan} {}
466de4996e2Sdan
467de4996e2Sdanforeach {tn ofst value result} {
468de4996e2Sdan          2   20    31       {1 2 3 4}
469de4996e2Sdan          3   20    32       {1 2 3 4}
470de4996e2Sdan          4   20    33       {1 2 3 4}
471de4996e2Sdan          5   20    65536    {1 2 3 4}
472de4996e2Sdan          6   20    131072   {1 2 3 4}
473de4996e2Sdan
474de4996e2Sdan          7   24    511      {1 2 3 4}
475de4996e2Sdan          8   24    513      {1 2 3 4}
476de4996e2Sdan          9   24    65536    {1 2 3 4}
477de4996e2Sdan
478de4996e2Sdan         10   32    65536    {1 2}
479de4996e2Sdan} {
480de4996e2Sdan  do_test pager1.4.3.$tn {
481de4996e2Sdan    faultsim_restore_and_reopen
482de4996e2Sdan    hexio_write test.db-journal $ofst [format %.8x $value]
483de4996e2Sdan    execsql { SELECT * FROM t1 }
484de4996e2Sdan  } $result
485de4996e2Sdan}
486de4996e2Sdandb close
487de4996e2Sdan
488e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal
489e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the
490e08341c6Sdan# file-system is saved just before the xDelete() call to remove the
491e08341c6Sdan# master journal file from the file-system.
492e08341c6Sdan#
493e08341c6Sdantestvfs tv -default 1
494e08341c6Sdantv script copy_on_mj_delete
495e08341c6Sdanset ::mj_filename_length 0
496e08341c6Sdanproc copy_on_mj_delete {method filename args} {
497e08341c6Sdan  if {[string match *mj* [file tail $filename]]} {
498e08341c6Sdan    set ::mj_filename_length [string length $filename]
499e08341c6Sdan    faultsim_save
500e08341c6Sdan  }
501e08341c6Sdan  return SQLITE_OK
502e08341c6Sdan}
503e08341c6Sdan
504e08341c6Sdanset pwd [pwd]
505e08341c6Sdanforeach {tn1 tcl} {
506e08341c6Sdan  1 { set prefix "test.db" }
507e08341c6Sdan  2 {
508e08341c6Sdan    # This test depends on the underlying VFS being able to open paths
509e08341c6Sdan    # 512 bytes in length. The idea is to create a hot-journal file that
510e08341c6Sdan    # contains a master-journal pointer so large that it could contain
511e08341c6Sdan    # a valid page record (if the file page-size is 512 bytes). So as to
512e08341c6Sdan    # make sure SQLite doesn't get confused by this.
513e08341c6Sdan    #
514e08341c6Sdan    set nPadding [expr 511 - $::mj_filename_length]
515e08341c6Sdan
516e08341c6Sdan    # We cannot just create a really long database file name to open, as
517e08341c6Sdan    # Linux limits a single component of a path to 255 bytes by default
518e08341c6Sdan    # (and presumably other systems have limits too). So create a directory
519e08341c6Sdan    # hierarchy to work in.
520e08341c6Sdan    #
521e08341c6Sdan    set dirname "d123456789012345678901234567890/"
522e08341c6Sdan    set nDir [expr $nPadding / 32]
523e08341c6Sdan    if { $nDir } {
524e08341c6Sdan      set p [string repeat $dirname $nDir]
525e08341c6Sdan      file mkdir $p
526e08341c6Sdan      cd $p
527e08341c6Sdan    }
528e08341c6Sdan
529e08341c6Sdan    set padding [string repeat x [expr $nPadding %32]]
530e08341c6Sdan    set prefix "test.db${padding}"
531e08341c6Sdan  }
532e08341c6Sdan} {
533e08341c6Sdan  eval $tcl
534e08341c6Sdan  foreach {tn2 sql} {
535e08341c6Sdan    o {
536e08341c6Sdan      PRAGMA main.synchronous=OFF;
537e08341c6Sdan      PRAGMA aux.synchronous=OFF;
538e08341c6Sdan    }
539e08341c6Sdan    o512 {
540e08341c6Sdan      PRAGMA main.synchronous=OFF;
541e08341c6Sdan      PRAGMA aux.synchronous=OFF;
542e08341c6Sdan      PRAGMA main.page_size = 512;
543e08341c6Sdan      PRAGMA aux.page_size = 512;
544e08341c6Sdan    }
545e08341c6Sdan    n {
546e08341c6Sdan      PRAGMA main.synchronous=NORMAL;
547e08341c6Sdan      PRAGMA aux.synchronous=NORMAL;
548e08341c6Sdan    }
549e08341c6Sdan    f {
550e08341c6Sdan      PRAGMA main.synchronous=FULL;
551e08341c6Sdan      PRAGMA aux.synchronous=FULL;
552e08341c6Sdan    }
553e08341c6Sdan  } {
554e08341c6Sdan
555e08341c6Sdan    set tn "${tn1}.${tn2}"
556e08341c6Sdan
557e08341c6Sdan    # Set up a connection to have two databases, test.db (main) and
558e08341c6Sdan    # test.db2 (aux). Then run a multi-file transaction on them. The
559e08341c6Sdan    # VFS will snapshot the file-system just before the master-journal
560e08341c6Sdan    # file is deleted to commit the transaction.
561e08341c6Sdan    #
562e08341c6Sdan    tv filter xDelete
563e08341c6Sdan    do_test pager1-4.4.$tn.1 {
564e08341c6Sdan      faultsim_delete_and_reopen $prefix
565e08341c6Sdan      execsql "
566e08341c6Sdan        ATTACH '${prefix}2' AS aux;
567e08341c6Sdan        $sql
568e08341c6Sdan        CREATE TABLE a(x);
569e08341c6Sdan        CREATE TABLE aux.b(x);
570e08341c6Sdan        INSERT INTO a VALUES('double-you');
571e08341c6Sdan        INSERT INTO a VALUES('why');
572e08341c6Sdan        INSERT INTO a VALUES('zed');
573e08341c6Sdan        INSERT INTO b VALUES('won');
574e08341c6Sdan        INSERT INTO b VALUES('too');
575e08341c6Sdan        INSERT INTO b VALUES('free');
576e08341c6Sdan      "
577e08341c6Sdan      execsql {
578e08341c6Sdan        BEGIN;
579e08341c6Sdan          INSERT INTO a SELECT * FROM b WHERE rowid<=3;
580e08341c6Sdan          INSERT INTO b SELECT * FROM a WHERE rowid<=3;
581e08341c6Sdan        COMMIT;
582e08341c6Sdan      }
583e08341c6Sdan    } {}
584e08341c6Sdan    tv filter {}
585e08341c6Sdan
586e08341c6Sdan    # Check that the transaction was committed successfully.
587e08341c6Sdan    #
588e08341c6Sdan    do_execsql_test pager1-4.4.$tn.2 {
589e08341c6Sdan      SELECT * FROM a
590e08341c6Sdan    } {double-you why zed won too free}
591e08341c6Sdan    do_execsql_test pager1-4.4.$tn.3 {
592e08341c6Sdan      SELECT * FROM b
593e08341c6Sdan    } {won too free double-you why zed}
594e08341c6Sdan
595e08341c6Sdan    # Restore the file-system and reopen the databases. Check that it now
596e08341c6Sdan    # appears that the transaction was not committed (because the file-system
597e08341c6Sdan    # was restored to the state where it had not been).
598e08341c6Sdan    #
599e08341c6Sdan    do_test pager1-4.4.$tn.4 {
600e08341c6Sdan      faultsim_restore_and_reopen $prefix
601e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
602e08341c6Sdan    } {}
603e08341c6Sdan    do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
604e08341c6Sdan    do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
605e08341c6Sdan
606e08341c6Sdan    # Restore the file-system again. This time, before reopening the databases,
607e08341c6Sdan    # delete the master-journal file from the file-system. It now appears that
608e08341c6Sdan    # the transaction was committed (no master-journal file == no rollback).
609e08341c6Sdan    #
610e08341c6Sdan    do_test pager1-4.4.$tn.7 {
611e08341c6Sdan      faultsim_restore_and_reopen $prefix
612e08341c6Sdan      foreach f [glob ${prefix}-mj*] { file delete -force $f }
613e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
614e08341c6Sdan    } {}
615e08341c6Sdan    do_execsql_test pager1-4.4.$tn.8 {
616e08341c6Sdan      SELECT * FROM a
617e08341c6Sdan    } {double-you why zed won too free}
618e08341c6Sdan    do_execsql_test pager1-4.4.$tn.9 {
619e08341c6Sdan      SELECT * FROM b
620e08341c6Sdan    } {won too free double-you why zed}
621e08341c6Sdan  }
622e08341c6Sdan
623e08341c6Sdan  cd $pwd
624e08341c6Sdan}
625e08341c6Sdandb close
626e08341c6Sdantv delete
6270e986f51Sdanfile delete -force $dirname
6280e986f51Sdan
6290e986f51Sdan
6300e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a
6310e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly
6320e986f51Sdan# two database pages (and page 1 - the change counter).
6330e986f51Sdan#
6340e986f51Sdantestvfs tv -default 1
6350e986f51Sdantv sectorsize 512
6360e986f51Sdantv script copy_on_journal_delete
6370e986f51Sdantv filter xDelete
6380e986f51Sdanset ::mj_filename_length 0
6390e986f51Sdanproc copy_on_journal_delete {method filename args} {
6400e986f51Sdan  if {[string match *journal $filename]} faultsim_save
6410e986f51Sdan  return SQLITE_OK
6420e986f51Sdan}
6430e986f51Sdanfaultsim_delete_and_reopen
6440e986f51Sdando_execsql_test pager1.4.5.1 {
6450e986f51Sdan  PRAGMA page_size = 1024;
6460e986f51Sdan  CREATE TABLE t1(a, b);
6470e986f51Sdan  CREATE TABLE t2(a, b);
6480e986f51Sdan  INSERT INTO t1 VALUES('I', 'II');
6490e986f51Sdan  INSERT INTO t2 VALUES('III', 'IV');
6500e986f51Sdan  BEGIN;
6510e986f51Sdan    INSERT INTO t1 VALUES(1, 2);
6520e986f51Sdan    INSERT INTO t2 VALUES(3, 4);
6530e986f51Sdan  COMMIT;
6540e986f51Sdan} {}
6550e986f51Sdantv filter {}
6560e986f51Sdan
6570e986f51Sdan# Check the transaction was committed:
6580e986f51Sdan#
6590e986f51Sdando_execsql_test pager1.4.5.2 {
6600e986f51Sdan  SELECT * FROM t1;
6610e986f51Sdan  SELECT * FROM t2;
6620e986f51Sdan} {I II 1 2 III IV 3 4}
6630e986f51Sdan
664ec6ffc1aSdan# Now try four tests:
6650e986f51Sdan#
6660e986f51Sdan#  pager1-4.5.3: Restore the file-system. Check that the whole transaction
6670e986f51Sdan#                is rolled back.
6680e986f51Sdan#
6690e986f51Sdan#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
6700e986f51Sdan#                journal. Check the transaction is not rolled back.
6710e986f51Sdan#
6720e986f51Sdan#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
6730e986f51Sdan#                journal. Check that the first record in the transaction is
6740e986f51Sdan#                played back, but not the second.
6750e986f51Sdan#
67610f5a50eSdan#  pager1-4.5.6: Restore the file-system. Try to open the database with a
67710f5a50eSdan#                readonly connection. This should fail, as a read-only
67810f5a50eSdan#                connection cannot roll back the database file.
67910f5a50eSdan#
6800e986f51Sdanfaultsim_restore_and_reopen
6810e986f51Sdando_execsql_test pager1.4.5.3 {
6820e986f51Sdan  SELECT * FROM t1;
6830e986f51Sdan  SELECT * FROM t2;
6840e986f51Sdan} {I II III IV}
6850e986f51Sdanfaultsim_restore_and_reopen
6860e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
6870e986f51Sdando_execsql_test pager1.4.5.4 {
6880e986f51Sdan  SELECT * FROM t1;
6890e986f51Sdan  SELECT * FROM t2;
6900e986f51Sdan} {I II 1 2 III IV 3 4}
6910e986f51Sdanfaultsim_restore_and_reopen
6920e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
6930e986f51Sdando_execsql_test pager1.4.5.5 {
6940e986f51Sdan  SELECT * FROM t1;
6950e986f51Sdan  SELECT * FROM t2;
6960e986f51Sdan} {I II III IV 3 4}
6970e986f51Sdan
69810f5a50eSdanfaultsim_restore_and_reopen
69910f5a50eSdandb close
70010f5a50eSdansqlite3 db test.db -readonly 1
70110f5a50eSdando_catchsql_test pager1.4.5.6 {
70210f5a50eSdan  SELECT * FROM t1;
70310f5a50eSdan  SELECT * FROM t2;
70410f5a50eSdan} {1 {disk I/O error}}
705ec6ffc1aSdandb close
706ec6ffc1aSdan
707ec6ffc1aSdan# Snapshot the file-system just before multi-file commit. Save the name
708ec6ffc1aSdan# of the master journal file in $::mj_filename.
709ec6ffc1aSdan#
710ec6ffc1aSdantv script copy_on_mj_delete
711ec6ffc1aSdantv filter xDelete
712ec6ffc1aSdanproc copy_on_mj_delete {method filename args} {
713ec6ffc1aSdan  if {[string match *mj* [file tail $filename]]} {
714ec6ffc1aSdan    set ::mj_filename $filename
715ec6ffc1aSdan    faultsim_save
716ec6ffc1aSdan  }
717ec6ffc1aSdan  return SQLITE_OK
718ec6ffc1aSdan}
719ec6ffc1aSdando_test pager1.4.6.1 {
720ec6ffc1aSdan  faultsim_delete_and_reopen
721ec6ffc1aSdan  execsql {
722ec6ffc1aSdan    ATTACH 'test.db2' AS two;
723ec6ffc1aSdan    CREATE TABLE t1(a, b);
724ec6ffc1aSdan    CREATE TABLE two.t2(a, b);
725ec6ffc1aSdan    INSERT INTO t1 VALUES(1, 't1.1');
726ec6ffc1aSdan    INSERT INTO t2 VALUES(1, 't2.1');
727ec6ffc1aSdan    BEGIN;
728ec6ffc1aSdan      UPDATE t1 SET b = 't1.2';
729ec6ffc1aSdan      UPDATE t2 SET b = 't2.2';
730ec6ffc1aSdan    COMMIT;
731ec6ffc1aSdan  }
732ec6ffc1aSdan  tv filter {}
733ec6ffc1aSdan  db close
734ec6ffc1aSdan} {}
735ec6ffc1aSdan
736ec6ffc1aSdanfaultsim_restore_and_reopen
737ec6ffc1aSdando_execsql_test pager1.4.6.2 { SELECT * FROM t1 }           {1 t1.1}
738ec6ffc1aSdando_test         pager1.4.6.3 { file exists $::mj_filename } {1}
739ec6ffc1aSdando_execsql_test pager1.4.6.4 {
740ec6ffc1aSdan  ATTACH 'test.db2' AS two;
741ec6ffc1aSdan  SELECT * FROM t2;
742ec6ffc1aSdan} {1 t2.1}
743ec6ffc1aSdando_test pager1.4.6.5 { file exists $::mj_filename } {0}
744ec6ffc1aSdan
745ec6ffc1aSdanfaultsim_restore_and_reopen
746ec6ffc1aSdandb close
747ec6ffc1aSdando_test pager1.4.6.8 {
748ec6ffc1aSdan  set ::mj_filename1 $::mj_filename
749ec6ffc1aSdan  tv filter xDelete
750ec6ffc1aSdan  sqlite3 db test.db2
751ec6ffc1aSdan  execsql {
752ec6ffc1aSdan    ATTACH 'test.db3' AS three;
753ec6ffc1aSdan    CREATE TABLE three.t3(a, b);
754ec6ffc1aSdan    INSERT INTO t3 VALUES(1, 't3.1');
755ec6ffc1aSdan    BEGIN;
756ec6ffc1aSdan      UPDATE t2 SET b = 't2.3';
757ec6ffc1aSdan      UPDATE t3 SET b = 't3.3';
758ec6ffc1aSdan    COMMIT;
759ec6ffc1aSdan  }
760ec6ffc1aSdan  expr {$::mj_filename1 != $::mj_filename}
761ec6ffc1aSdan} {1}
762ec6ffc1aSdanfaultsim_restore_and_reopen
763ec6ffc1aSdantv filter {}
764ec6ffc1aSdan
765ec6ffc1aSdan# The file-system now contains:
766ec6ffc1aSdan#
767ec6ffc1aSdan#   * three databases
768ec6ffc1aSdan#   * three hot-journal files
769ec6ffc1aSdan#   * two master-journal files.
770ec6ffc1aSdan#
771ec6ffc1aSdan# The hot-journals associated with test.db2 and test.db3 point to
772ec6ffc1aSdan# master journal $::mj_filename. The hot-journal file associated with
773ec6ffc1aSdan# test.db points to master journal $::mj_filename1. So reading from
774ec6ffc1aSdan# test.db should delete $::mj_filename1.
775ec6ffc1aSdan#
776ec6ffc1aSdando_test pager1.4.6.9 {
777ec6ffc1aSdan  lsort [glob test.db*]
778ec6ffc1aSdan} [lsort [list                                           \
779ec6ffc1aSdan  test.db test.db2 test.db3                              \
780ec6ffc1aSdan  test.db-journal test.db2-journal test.db3-journal      \
781ec6ffc1aSdan  [file tail $::mj_filename] [file tail $::mj_filename1]
782ec6ffc1aSdan]]
783ec6ffc1aSdan
784ec6ffc1aSdan# The master-journal $::mj_filename1 contains pointers to test.db and
785ec6ffc1aSdan# test.db2. However the hot-journal associated with test.db2 points to
786ec6ffc1aSdan# a different master-journal. Therefore, reading from test.db only should
787ec6ffc1aSdan# be enough to cause SQLite to delete $::mj_filename1.
788ec6ffc1aSdan#
789ec6ffc1aSdando_test         pager1.4.6.10 { file exists $::mj_filename  } {1}
790ec6ffc1aSdando_test         pager1.4.6.11 { file exists $::mj_filename1 } {1}
791ec6ffc1aSdando_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1}
792ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename  } {1}
793ec6ffc1aSdando_test         pager1.4.6.14 { file exists $::mj_filename1 } {0}
794ec6ffc1aSdan
795ec6ffc1aSdando_execsql_test pager1.4.6.12 {
796ec6ffc1aSdan  ATTACH 'test.db2' AS two;
797ec6ffc1aSdan  SELECT * FROM t2;
798ec6ffc1aSdan} {1 t2.1}
799ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename }  {1}
800ec6ffc1aSdando_execsql_test pager1.4.6.14 {
801ec6ffc1aSdan  ATTACH 'test.db3' AS three;
802ec6ffc1aSdan  SELECT * FROM t3;
803ec6ffc1aSdan} {1 t3.1}
804ec6ffc1aSdando_test         pager1.4.6.15 { file exists $::mj_filename }  {0}
80510f5a50eSdan
8060e986f51Sdandb close
8070e986f51Sdantv delete
808e08341c6Sdan
809de4996e2Sdan#-------------------------------------------------------------------------
810146ed78bSdan# The following tests deal with multi-file commits.
811de4996e2Sdan#
812146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because
813146ed78bSdan#               another connection is holding a SHARED lock on one of the
814146ed78bSdan#               files. After the SHARED lock is removed, the COMMIT succeeds.
815146ed78bSdan#
816146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory.
817146ed78bSdan#
818146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory.
819146ed78bSdan#
820146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
821146ed78bSdan#               name is added to a journal file immediately after the last
822146ed78bSdan#               journal record. But with synchronous=full, extra unused space
823146ed78bSdan#               is allocated between the last journal record and the
824146ed78bSdan#               master-journal file name so that the master-journal file
825146ed78bSdan#               name does not lie on the same sector as the last journal file
826146ed78bSdan#               record.
827146ed78bSdan#
828e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
829e08341c6Sdan#               truncated to zero bytes when a multi-file transaction is
830e08341c6Sdan#               committed (instead of the first couple of bytes being zeroed).
831e08341c6Sdan#
832146ed78bSdan#
833146ed78bSdando_test pager1-5.1.1 {
834146ed78bSdan  faultsim_delete_and_reopen
835146ed78bSdan  execsql {
836146ed78bSdan    ATTACH 'test.db2' AS aux;
837146ed78bSdan    CREATE TABLE t1(a, b);
838146ed78bSdan    CREATE TABLE aux.t2(a, b);
839146ed78bSdan    INSERT INTO t1 VALUES(17, 'Lenin');
840146ed78bSdan    INSERT INTO t1 VALUES(22, 'Stalin');
841146ed78bSdan    INSERT INTO t1 VALUES(53, 'Khrushchev');
842146ed78bSdan  }
843146ed78bSdan} {}
844146ed78bSdando_test pager1-5.1.2 {
845146ed78bSdan  execsql {
846146ed78bSdan    BEGIN;
847146ed78bSdan      INSERT INTO t1 VALUES(64, 'Brezhnev');
848146ed78bSdan      INSERT INTO t2 SELECT * FROM t1;
849146ed78bSdan  }
850146ed78bSdan  sqlite3 db2 test.db2
851146ed78bSdan  execsql {
852146ed78bSdan    BEGIN;
853146ed78bSdan      SELECT * FROM t2;
854146ed78bSdan  } db2
855146ed78bSdan} {}
856146ed78bSdando_test pager1-5.1.3 {
857146ed78bSdan  catchsql COMMIT
858146ed78bSdan} {1 {database is locked}}
859146ed78bSdando_test pager1-5.1.4 {
860146ed78bSdan  execsql COMMIT db2
861146ed78bSdan  execsql COMMIT
862146ed78bSdan  execsql { SELECT * FROM t2 } db2
863146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
864146ed78bSdando_test pager1-5.1.5 {
865146ed78bSdan  db2 close
866146ed78bSdan} {}
867146ed78bSdan
868146ed78bSdando_test pager1-5.2.1 {
869146ed78bSdan  execsql {
870146ed78bSdan    PRAGMA journal_mode = memory;
871146ed78bSdan    BEGIN;
872146ed78bSdan      INSERT INTO t1 VALUES(84, 'Andropov');
873146ed78bSdan      INSERT INTO t2 VALUES(84, 'Andropov');
874146ed78bSdan    COMMIT;
875146ed78bSdan  }
876146ed78bSdan} {memory}
877146ed78bSdando_test pager1-5.3.1 {
878146ed78bSdan  execsql {
879146ed78bSdan    PRAGMA journal_mode = off;
880146ed78bSdan    BEGIN;
881146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
882146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
883146ed78bSdan    COMMIT;
884146ed78bSdan  }
885146ed78bSdan} {off}
886146ed78bSdan
887146ed78bSdando_test pager1-5.4.1 {
888146ed78bSdan  db close
889146ed78bSdan  testvfs tv
890146ed78bSdan  sqlite3 db test.db -vfs tv
891146ed78bSdan  execsql { ATTACH 'test.db2' AS aux }
892146ed78bSdan
893146ed78bSdan  tv filter xDelete
894146ed78bSdan  tv script max_journal_size
895146ed78bSdan  tv sectorsize 512
896146ed78bSdan  set ::max_journal 0
897146ed78bSdan  proc max_journal_size {method args} {
898146ed78bSdan    set sz 0
899146ed78bSdan    catch { set sz [file size test.db-journal] }
900146ed78bSdan    if {$sz > $::max_journal} {
901146ed78bSdan      set ::max_journal $sz
902146ed78bSdan    }
903146ed78bSdan    return SQLITE_OK
904146ed78bSdan  }
905146ed78bSdan  execsql {
906146ed78bSdan    PRAGMA journal_mode = DELETE;
907146ed78bSdan    PRAGMA synchronous = NORMAL;
908146ed78bSdan    BEGIN;
909146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
910146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
911146ed78bSdan    COMMIT;
912146ed78bSdan  }
913146ed78bSdan  set ::max_journal
914146ed78bSdan} [expr 2615+[string length [pwd]]]
915146ed78bSdando_test pager1-5.4.2 {
916146ed78bSdan  set ::max_journal 0
917146ed78bSdan  execsql {
918146ed78bSdan    PRAGMA synchronous = full;
919146ed78bSdan    BEGIN;
920146ed78bSdan      DELETE FROM t1 WHERE b = 'Lenin';
921146ed78bSdan      DELETE FROM t2 WHERE b = 'Lenin';
922146ed78bSdan    COMMIT;
923146ed78bSdan  }
924146ed78bSdan  set ::max_journal
925146ed78bSdan} [expr 3111+[string length [pwd]]]
926146ed78bSdandb close
927146ed78bSdantv delete
928146ed78bSdan
929146ed78bSdando_test pager1-5.5.1 {
930146ed78bSdan  sqlite3 db test.db
931146ed78bSdan  execsql {
932146ed78bSdan    ATTACH 'test.db2' AS aux;
933146ed78bSdan    PRAGMA journal_mode = PERSIST;
934146ed78bSdan    CREATE TABLE t3(a, b);
935146ed78bSdan    INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
936146ed78bSdan    UPDATE t3 SET b = randomblob(1500);
937146ed78bSdan  }
938146ed78bSdan  expr [file size test.db-journal] > 15000
939146ed78bSdan} {1}
940146ed78bSdando_test pager1-5.5.2 {
941146ed78bSdan  execsql {
942146ed78bSdan    PRAGMA synchronous = full;
943146ed78bSdan    BEGIN;
944146ed78bSdan      DELETE FROM t1 WHERE b = 'Stalin';
945146ed78bSdan      DELETE FROM t2 WHERE b = 'Stalin';
946146ed78bSdan    COMMIT;
947146ed78bSdan  }
948146ed78bSdan  file size test.db-journal
949146ed78bSdan} {0}
950146ed78bSdan
951146ed78bSdan
952146ed78bSdan#-------------------------------------------------------------------------
953146ed78bSdan# The following tests work with "PRAGMA max_page_count"
954146ed78bSdan#
955146ed78bSdando_test pager1-6.1 {
956146ed78bSdan  faultsim_delete_and_reopen
957146ed78bSdan  execsql {
958146ed78bSdan    PRAGMA max_page_count = 10;
959146ed78bSdan    CREATE TABLE t2(a, b);
960146ed78bSdan    CREATE TABLE t3(a, b);
961146ed78bSdan    CREATE TABLE t4(a, b);
962146ed78bSdan    CREATE TABLE t5(a, b);
963146ed78bSdan    CREATE TABLE t6(a, b);
964146ed78bSdan    CREATE TABLE t7(a, b);
965146ed78bSdan    CREATE TABLE t8(a, b);
966146ed78bSdan    CREATE TABLE t9(a, b);
967146ed78bSdan    CREATE TABLE t10(a, b);
968146ed78bSdan  }
969146ed78bSdan} {10}
970*d353331aSdando_catchsql_test pager1-6.2 {
971*d353331aSdan  CREATE TABLE t11(a, b)
972146ed78bSdan} {1 {database or disk is full}}
973*d353331aSdando_execsql_test pager1-6.4 { PRAGMA max_page_count      } {10}
974*d353331aSdando_execsql_test pager1-6.5 { PRAGMA max_page_count = 15 } {15}
975*d353331aSdando_execsql_test pager1-6.6 { CREATE TABLE t11(a, b)     } {}
976*d353331aSdando_execsql_test pager1-6.7 {
977*d353331aSdan  BEGIN;
978*d353331aSdan    INSERT INTO t11 VALUES(1, 2);
979*d353331aSdan    PRAGMA max_page_count = 13;
980*d353331aSdan} {13}
981*d353331aSdando_execsql_test pager1-6.8 {
982*d353331aSdan    INSERT INTO t11 VALUES(3, 4);
983*d353331aSdan    PRAGMA max_page_count = 10;
984*d353331aSdan} {11}
985*d353331aSdando_execsql_test pager1-6.9 { COMMIT } {}
986de4996e2Sdan
987153eda0aSdan
988153eda0aSdan#-------------------------------------------------------------------------
989153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
990153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE".
991153eda0aSdan#
992153eda0aSdan# Each test is specified with 5 variables. As follows:
993153eda0aSdan#
994153eda0aSdan#   $tn:  Test Number. Used as part of the [do_test] test names.
995153eda0aSdan#   $sql: SQL to execute.
996153eda0aSdan#   $res: Expected result of executing $sql.
997153eda0aSdan#   $js:  The expected size of the journal file, in bytes, after executing
998153eda0aSdan#         the SQL script. Or -1 if the journal is not expected to exist.
999153eda0aSdan#   $ws:  The expected size of the WAL file, in bytes, after executing
1000153eda0aSdan#         the SQL script. Or -1 if the WAL is not expected to exist.
1001153eda0aSdan#
100238e1a279Sdanifcapable wal {
1003153eda0aSdan  faultsim_delete_and_reopen
1004153eda0aSdan  foreach {tn sql res js ws} [subst {
1005153eda0aSdan
1006153eda0aSdan    1  {
1007153eda0aSdan      CREATE TABLE t1(a, b);
1008153eda0aSdan      PRAGMA auto_vacuum=OFF;
1009153eda0aSdan      PRAGMA synchronous=NORMAL;
1010153eda0aSdan      PRAGMA page_size=1024;
1011153eda0aSdan      PRAGMA locking_mode=EXCLUSIVE;
1012153eda0aSdan      PRAGMA journal_mode=TRUNCATE;
1013153eda0aSdan      INSERT INTO t1 VALUES(1, 2);
1014153eda0aSdan    } {exclusive truncate} 0 -1
1015153eda0aSdan
1016153eda0aSdan    2  {
1017153eda0aSdan      BEGIN IMMEDIATE;
1018153eda0aSdan        SELECT * FROM t1;
1019153eda0aSdan      COMMIT;
1020153eda0aSdan    } {1 2} 0 -1
1021153eda0aSdan
1022153eda0aSdan    3  {
1023153eda0aSdan      BEGIN;
1024153eda0aSdan        SELECT * FROM t1;
1025153eda0aSdan      COMMIT;
1026153eda0aSdan    } {1 2} 0 -1
1027153eda0aSdan
1028153eda0aSdan    4  { PRAGMA journal_mode = WAL }    wal    -1 -1
1029153eda0aSdan    5  { INSERT INTO t1 VALUES(3, 4) }  {}     -1 [wal_file_size 1 1024]
1030153eda0aSdan    6  { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
1031153eda0aSdan    7  { INSERT INTO t1 VALUES(5, 6); } {}     -1 [wal_file_size 2 1024]
1032153eda0aSdan
1033153eda0aSdan    8  { PRAGMA journal_mode = TRUNCATE } truncate          0 -1
1034153eda0aSdan    9  { INSERT INTO t1 VALUES(7, 8) }    {}                0 -1
1035153eda0aSdan    10 { SELECT * FROM t1 }               {1 2 3 4 5 6 7 8} 0 -1
1036153eda0aSdan
1037153eda0aSdan  }] {
1038153eda0aSdan    do_execsql_test pager1-7.1.$tn.1 $sql $res
1039153eda0aSdan    catch { set J -1 ; set J [file size test.db-journal] }
1040153eda0aSdan    catch { set W -1 ; set W [file size test.db-wal] }
1041153eda0aSdan    do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
1042153eda0aSdan  }
104338e1a279Sdan}
1044153eda0aSdan
1045*d353331aSdan#-------------------------------------------------------------------------
1046*d353331aSdan# The following tests, pager1-8.*, test that the special filenames
1047*d353331aSdan# ":memory:" and "" open temporary databases.
1048*d353331aSdan#
10490e986f51Sdanforeach {tn filename} {
10500e986f51Sdan  1 :memory:
10510e986f51Sdan  2 ""
10520e986f51Sdan} {
10530e986f51Sdan  do_test pager1-8.$tn.1 {
1054153eda0aSdan    faultsim_delete_and_reopen
1055153eda0aSdan    db close
10560e986f51Sdan    sqlite3 db $filename
1057153eda0aSdan    execsql {
1058153eda0aSdan      CREATE TABLE x1(x);
1059153eda0aSdan      INSERT INTO x1 VALUES('Charles');
1060153eda0aSdan      INSERT INTO x1 VALUES('James');
1061153eda0aSdan      INSERT INTO x1 VALUES('Mary');
1062153eda0aSdan      SELECT * FROM x1;
1063153eda0aSdan    }
1064153eda0aSdan  } {Charles James Mary}
10650e986f51Sdan
10660e986f51Sdan  do_test pager1-8.$tn.2 {
10670e986f51Sdan    sqlite3 db2 $filename
10680e986f51Sdan    catchsql { SELECT * FROM x1 } db2
1069153eda0aSdan  } {1 {no such table: x1}}
1070153eda0aSdan
10710e986f51Sdan  do_execsql_test pager1-8.$tn.3 {
10720e986f51Sdan    BEGIN;
10730e986f51Sdan      INSERT INTO x1 VALUES('William');
10740e986f51Sdan      INSERT INTO x1 VALUES('Anne');
10750e986f51Sdan    ROLLBACK;
10760e986f51Sdan  } {}
10770e986f51Sdan}
10780e986f51Sdan
1079dca321aeSdan#-------------------------------------------------------------------------
1080dca321aeSdan# The next block of tests - pager1-9.* - deal with interactions between
1081dca321aeSdan# the pager and the backup API. Test cases:
1082dca321aeSdan#
1083dca321aeSdan#   pager1-9.1.*: Test that a backup completes successfully even if the
1084dca321aeSdan#                 source db is written to during the backup op.
1085dca321aeSdan#
1086dca321aeSdan#   pager1-9.2.*: Test that a backup completes successfully even if the
1087dca321aeSdan#                 source db is written to and then rolled back during a
1088dca321aeSdan#                 backup operation.
1089dca321aeSdan#
1090dca321aeSdando_test pager1-9.0.1 {
1091dca321aeSdan  faultsim_delete_and_reopen
1092dca321aeSdan  db func a_string a_string
1093dca321aeSdan  execsql {
1094dca321aeSdan    PRAGMA cache_size = 10;
1095dca321aeSdan    BEGIN;
1096dca321aeSdan      CREATE TABLE ab(a, b, UNIQUE(a, b));
1097dca321aeSdan      INSERT INTO ab VALUES( a_string(200), a_string(300) );
1098dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1099dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1100dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1101dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1102dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1103dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1104dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1105dca321aeSdan    COMMIT;
1106dca321aeSdan  }
1107dca321aeSdan} {}
1108dca321aeSdando_test pager1-9.0.2 {
1109dca321aeSdan  sqlite3 db2 test.db2
1110dca321aeSdan  db2 eval { PRAGMA cache_size = 10 }
1111dca321aeSdan  sqlite3_backup B db2 main db main
1112dca321aeSdan  list [B step 10000] [B finish]
1113dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1114dca321aeSdando_test pager1-9.0.3 {
1115dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1116dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1117dca321aeSdan
1118dca321aeSdando_test pager1-9.1.1 {
1119dca321aeSdan  execsql { UPDATE ab SET a = a_string(201) }
1120dca321aeSdan  sqlite3_backup B db2 main db main
1121dca321aeSdan  B step 30
1122dca321aeSdan} {SQLITE_OK}
1123dca321aeSdando_test pager1-9.1.2 {
1124dca321aeSdan  execsql { UPDATE ab SET b = a_string(301) }
1125dca321aeSdan  list [B step 10000] [B finish]
1126dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1127dca321aeSdando_test pager1-9.1.3 {
1128dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1129dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1130dca321aeSdando_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128}
1131dca321aeSdan
1132dca321aeSdando_test pager1-9.2.1 {
1133dca321aeSdan  execsql { UPDATE ab SET a = a_string(202) }
1134dca321aeSdan  sqlite3_backup B db2 main db main
1135dca321aeSdan  B step 30
1136dca321aeSdan} {SQLITE_OK}
1137dca321aeSdando_test pager1-9.2.2 {
1138dca321aeSdan  execsql {
1139dca321aeSdan    BEGIN;
1140dca321aeSdan      UPDATE ab SET b = a_string(301);
1141dca321aeSdan    ROLLBACK;
1142dca321aeSdan  }
1143dca321aeSdan  list [B step 10000] [B finish]
1144dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1145dca321aeSdando_test pager1-9.2.3 {
1146dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1147dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1148dca321aeSdando_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128}
1149ec6ffc1aSdandb close
1150ec6ffc1aSdandb2 close
1151ec6ffc1aSdan
1152ec6ffc1aSdan#-------------------------------------------------------------------------
1153ec6ffc1aSdan# Test that regardless of the value returned by xSectorSize(), the
1154ec6ffc1aSdan# minimum effective sector-size is 512 and the maximum 65536 bytes.
1155ec6ffc1aSdan#
1156ec6ffc1aSdantestvfs tv -default 1
1157ec6ffc1aSdanforeach sectorsize {
1158ec6ffc1aSdan    32   64   128   256   512   1024   2048
1159ec6ffc1aSdan    4096 8192 16384 32768 65536 131072 262144
1160ec6ffc1aSdan} {
1161ec6ffc1aSdan  tv sectorsize $sectorsize
1162ec6ffc1aSdan  set eff $sectorsize
1163ec6ffc1aSdan  if {$sectorsize < 512}   { set eff 512 }
1164ec6ffc1aSdan  if {$sectorsize > 65536} { set eff 65536 }
1165ec6ffc1aSdan
1166ec6ffc1aSdan  do_test pager1-10.$sectorsize {
1167ec6ffc1aSdan    faultsim_delete_and_reopen
1168ec6ffc1aSdan    execsql {
1169ec6ffc1aSdan      PRAGMA journal_mode = PERSIST;
1170ec6ffc1aSdan      PRAGMA page_size = 1024;
1171ec6ffc1aSdan      CREATE TABLE t1(a, b);
1172ec6ffc1aSdan    }
1173ec6ffc1aSdan    file size test.db-journal
1174ec6ffc1aSdan  } [expr $sectorsize > 65536 ? 65536 : $sectorsize]
1175ec6ffc1aSdan}
1176ec6ffc1aSdandb close
1177ec6ffc1aSdantv delete
1178ec6ffc1aSdan
1179ec6ffc1aSdantestvfs tv -default 1
1180ec6ffc1aSdanfaultsim_delete_and_reopen
1181ec6ffc1aSdandb func a_string a_string
1182ec6ffc1aSdando_execsql_test pager1-11.1 {
1183ec6ffc1aSdan  PRAGMA cache_size = 10;
1184ec6ffc1aSdan  BEGIN;
1185ec6ffc1aSdan    CREATE TABLE zz(top PRIMARY KEY);
1186ec6ffc1aSdan    INSERT INTO zz VALUES(a_string(222));
1187ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1188ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1189ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1190ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1191ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1192ec6ffc1aSdan  COMMIT;
1193ec6ffc1aSdan  BEGIN;
1194ec6ffc1aSdan    UPDATE zz SET top = a_string(345);
1195ec6ffc1aSdan} {}
1196ec6ffc1aSdan
1197ec6ffc1aSdanproc lockout {method args} { return SQLITE_IOERR }
1198ec6ffc1aSdantv script lockout
1199ec6ffc1aSdantv filter {xWrite xTruncate xSync}
1200ec6ffc1aSdando_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}}
1201ec6ffc1aSdan
1202ec6ffc1aSdantv script {}
1203ec6ffc1aSdando_test pager1-11.3 {
1204ec6ffc1aSdan  sqlite3 db2 test.db
1205ec6ffc1aSdan  execsql {
1206ec6ffc1aSdan    PRAGMA journal_mode = TRUNCATE;
1207ec6ffc1aSdan    PRAGMA integrity_check;
1208ec6ffc1aSdan  } db2
1209ec6ffc1aSdan} {truncate ok}
1210ec6ffc1aSdando_test pager1-11.4 {
1211ec6ffc1aSdan  db2 close
1212ec6ffc1aSdan  file size test.db-journal
1213ec6ffc1aSdan} {0}
1214ec6ffc1aSdando_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32}
1215f9b4419dSdandb close
1216f9b4419dSdantv delete
1217ec6ffc1aSdan
1218f9b4419dSdan#-------------------------------------------------------------------------
1219f9b4419dSdan# Test "PRAGMA page_size"
1220f9b4419dSdan#
1221f9b4419dSdanforeach pagesize {
1222f9b4419dSdan    512   1024   2048 4096 8192 16384 32768
1223f9b4419dSdan} {
1224f9b4419dSdan  faultsim_delete_and_reopen
1225f9b4419dSdan
1226f9b4419dSdan  do_test pager1-12.$pagesize.1 {
1227f9b4419dSdan    sqlite3 db2 test.db
1228f9b4419dSdan    execsql "
1229f9b4419dSdan      PRAGMA page_size = $pagesize;
1230f9b4419dSdan      CREATE VIEW v AS SELECT * FROM sqlite_master;
1231f9b4419dSdan    " db2
1232f9b4419dSdan    file size test.db
1233f9b4419dSdan  } $pagesize
1234f9b4419dSdan  do_test pager1-12.$pagesize.2 {
1235f9b4419dSdan    sqlite3 db2 test.db
1236f9b4419dSdan    execsql {
1237f9b4419dSdan      SELECT count(*) FROM v;
1238f9b4419dSdan      PRAGMA main.page_size;
1239f9b4419dSdan    } db2
1240f9b4419dSdan  } [list 1 $pagesize]
1241f9b4419dSdan  do_test pager1-12.$pagesize.3 {
1242f9b4419dSdan    execsql {
1243f9b4419dSdan      SELECT count(*) FROM v;
1244f9b4419dSdan      PRAGMA main.page_size;
1245f9b4419dSdan    }
1246f9b4419dSdan  } [list 1 $pagesize]
1247f9b4419dSdan  db2 close
1248f9b4419dSdan}
1249dca321aeSdan
1250*d353331aSdan#-------------------------------------------------------------------------
1251*d353331aSdan# Test specal "PRAGMA journal_mode=PERSIST" test cases.
1252*d353331aSdan#
1253*d353331aSdan# pager1-13.1.*: This tests a special case encountered in persistent
1254*d353331aSdan#                journal mode: If the journal associated with a transaction
1255*d353331aSdan#                is smaller than the journal file (because a previous
1256*d353331aSdan#                transaction left a very large non-hot journal file in the
1257*d353331aSdan#                file-system), then SQLite has to be careful that there is
1258*d353331aSdan#                not a journal-header left over from a previous transaction
1259*d353331aSdan#                immediately following the journal content just written.
1260*d353331aSdan#                If there is, and the process crashes so that the journal
1261*d353331aSdan#                becomes a hot-journal and must be rolled back by another
1262*d353331aSdan#                process, there is a danger that the other process may roll
1263*d353331aSdan#                back the aborted transaction, then continue copying data
1264*d353331aSdan#                from an older transaction from the remainder of the journal.
1265*d353331aSdan#                See the syncJournal() function for details.
1266*d353331aSdan#
1267*d353331aSdan# pager1-13.2.*: Same test as the previous. This time, throw an index into
1268*d353331aSdan#                the mix to make the integrity-check more likely to catch
1269*d353331aSdan#                errors.
1270*d353331aSdan#
1271*d353331aSdantestvfs tv -default 1
1272*d353331aSdantv script xSyncCb
1273*d353331aSdantv filter xSync
1274*d353331aSdanproc xSyncCb {method filename args} {
1275*d353331aSdan  set t [file tail $filename]
1276*d353331aSdan  if {$t == "test.db"} faultsim_save
1277*d353331aSdan  return SQLITE_OK
1278*d353331aSdan}
1279*d353331aSdanfaultsim_delete_and_reopen
1280*d353331aSdandb func a_string a_string
1281e91a54e1Sdan
1282*d353331aSdan# The UPDATE statement at the end of this test case creates a really big
1283*d353331aSdan# journal. Since the cache-size is only 10 pages, the journal contains
1284*d353331aSdan# frequent journal headers.
1285*d353331aSdan#
1286*d353331aSdando_execsql_test pager1-13.1.1 {
1287*d353331aSdan  PRAGMA page_size = 1024;
1288*d353331aSdan  PRAGMA journal_mode = PERSIST;
1289*d353331aSdan  PRAGMA cache_size = 10;
1290*d353331aSdan  BEGIN;
1291*d353331aSdan    CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
1292*d353331aSdan    INSERT INTO t1 VALUES(NULL, a_string(400));
1293*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   2 */
1294*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   4 */
1295*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   8 */
1296*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  16 */
1297*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  32 */
1298*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  64 */
1299*d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /* 128 */
1300*d353331aSdan  COMMIT;
1301*d353331aSdan  UPDATE t1 SET b = a_string(400);
1302*d353331aSdan} {persist}
1303*d353331aSdan
1304*d353331aSdan# Run transactions of increasing sizes. Eventually, one (or more than one)
1305*d353331aSdan# of these will write just enough content that one of the old headers created
1306*d353331aSdan# by the transaction in the block above lies immediately after the content
1307*d353331aSdan# journalled by the current transaction.
1308*d353331aSdan#
1309*d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1310*d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.1 {
1311*d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1312*d353331aSdan  } {}
1313*d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.2 { PRAGMA integrity_check } {ok}
1314*d353331aSdan
1315*d353331aSdan  # Try to access the snapshot of the file-system.
1316*d353331aSdan  #
1317*d353331aSdan  sqlite3 db2 sv_test.db
1318*d353331aSdan  do_test pager1-13.1.2.$nUp.3 {
1319*d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1320*d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1321*d353331aSdan  do_test pager1-13.1.2.$nUp.4 {
1322*d353331aSdan    execsql { PRAGMA integrity_check } db2
1323*d353331aSdan  } {ok}
1324*d353331aSdan  db2 close
1325*d353331aSdan}
1326*d353331aSdan
1327*d353331aSdan# Same test as above. But this time with an index on the table.
1328*d353331aSdan#
1329*d353331aSdando_execsql_test pager1-13.2.1 {
1330*d353331aSdan  CREATE INDEX i1 ON t1(b);
1331*d353331aSdan  UPDATE t1 SET b = a_string(400);
1332*d353331aSdan} {}
1333*d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1334*d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.1 {
1335*d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1336*d353331aSdan  } {}
1337*d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.2 { PRAGMA integrity_check } {ok}
1338*d353331aSdan  sqlite3 db2 sv_test.db
1339*d353331aSdan  do_test pager1-13.2.2.$nUp.3 {
1340*d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1341*d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1342*d353331aSdan  do_test pager1-13.2.2.$nUp.4 {
1343*d353331aSdan    execsql { PRAGMA integrity_check } db2
1344*d353331aSdan  } {ok}
1345*d353331aSdan  db2 close
1346*d353331aSdan}
1347*d353331aSdan
1348*d353331aSdandb close
1349*d353331aSdantv delete
1350*d353331aSdan
1351*d353331aSdan#-------------------------------------------------------------------------
1352*d353331aSdan# Test specal "PRAGMA journal_mode=OFF" test cases.
1353*d353331aSdan#
1354*d353331aSdanfaultsim_delete_and_reopen
1355*d353331aSdando_execsql_test pager1-14.1.1 {
1356*d353331aSdan  PRAGMA journal_mode = OFF;
1357*d353331aSdan  CREATE TABLE t1(a, b);
1358*d353331aSdan  BEGIN;
1359*d353331aSdan    INSERT INTO t1 VALUES(1, 2);
1360*d353331aSdan  COMMIT;
1361*d353331aSdan  SELECT * FROM t1;
1362*d353331aSdan} {off 1 2}
1363*d353331aSdando_catchsql_test pager1-14.1.2 {
1364*d353331aSdan  BEGIN;
1365*d353331aSdan    INSERT INTO t1 VALUES(3, 4);
1366*d353331aSdan  ROLLBACK;
1367*d353331aSdan} {0 {}}
1368*d353331aSdando_execsql_test pager1-14.1.3 {
1369*d353331aSdan  SELECT * FROM t1;
1370*d353331aSdan} {1 2 3 4}
1371*d353331aSdando_catchsql_test pager1-14.1.4 {
1372*d353331aSdan  BEGIN;
1373*d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1374*d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1375*d353331aSdan} {1 {PRIMARY KEY must be unique}}
1376*d353331aSdando_execsql_test pager1-14.1.5 {
1377*d353331aSdan  COMMIT;
1378*d353331aSdan  SELECT * FROM t1;
1379*d353331aSdan} {1 2 3 4 2 2 4 4}
1380*d353331aSdan
1381*d353331aSdanfinish_test
1382