xref: /sqlite-3.40.0/test/pager1.test (revision 6b63ab47)
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#
46d353331aSdan# pager1-12.*: Tests involving "PRAGMA page_size"
47d353331aSdan#
48d353331aSdan# pager1-13.*: Cases specific to "PRAGMA journal_mode=PERSIST"
49d353331aSdan#
50d353331aSdan# pager1-14.*: Cases specific to "PRAGMA journal_mode=OFF"
51ec6ffc1aSdan#
52c8ce3972Sdan# pager1-15.*: Varying sqlite3_vfs.szOsFile
53c8ce3972Sdan#
54c8ce3972Sdan# pager1-16.*: Varying sqlite3_vfs.mxPathname
55c8ce3972Sdan#
56c8ce3972Sdan# pager1-17.*: Tests related to "PRAGMA omit_readlock"
57c8ce3972Sdan#
58c8ce3972Sdan# pager1-18.*: Test that the pager layer responds correctly if the b-tree
59c8ce3972Sdan#              requests an invalid page number (due to db corruption).
60c8ce3972Sdan#
61b0ac3e3aSdan
6253f04f3bSdanset a_string_counter 1
6353f04f3bSdanproc a_string {n} {
6453f04f3bSdan  global a_string_counter
6553f04f3bSdan  incr a_string_counter
6653f04f3bSdan  string range [string repeat "${a_string_counter}." $n] 1 $n
6753f04f3bSdan}
6853f04f3bSdandb func a_string a_string
6953f04f3bSdan
70a4a9095eSdando_multiclient_test tn {
71e91a54e1Sdan
72e91a54e1Sdan  # Create and populate a database table using connection [db]. Check
73e91a54e1Sdan  # that connections [db2] and [db3] can see the schema and content.
74e91a54e1Sdan  #
75e91a54e1Sdan  do_test pager1-$tn.1 {
76e91a54e1Sdan    sql1 {
77e91a54e1Sdan      CREATE TABLE t1(a PRIMARY KEY, b);
78e91a54e1Sdan      CREATE INDEX i1 ON t1(b);
79d353331aSdan      INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two');
80e91a54e1Sdan    }
81e91a54e1Sdan  } {}
82e91a54e1Sdan  do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
83e91a54e1Sdan  do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
84e91a54e1Sdan
85e91a54e1Sdan  # Open a transaction and add a row using [db]. This puts [db] in
86e91a54e1Sdan  # RESERVED state. Check that connections [db2] and [db3] can still
87e91a54e1Sdan  # read the database content as it was before the transaction was
88e91a54e1Sdan  # opened. [db] should see the inserted row.
89e91a54e1Sdan  #
90e91a54e1Sdan  do_test pager1-$tn.4 {
91e91a54e1Sdan    sql1 {
92e91a54e1Sdan      BEGIN;
93e91a54e1Sdan        INSERT INTO t1 VALUES(3, 'three');
94e91a54e1Sdan    }
95e91a54e1Sdan  } {}
96e91a54e1Sdan  do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
97e91a54e1Sdan  do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
98e91a54e1Sdan
99e91a54e1Sdan  # [db] still has an open write transaction. Check that this prevents
100e91a54e1Sdan  # other connections (specifically [db2]) from writing to the database.
101e91a54e1Sdan  #
102e91a54e1Sdan  # Even if [db2] opens a transaction first, it may not write to the
103e91a54e1Sdan  # database. After the attempt to write the db within a transaction,
104e91a54e1Sdan  # [db2] is left with an open transaction, but not a read-lock on
105e91a54e1Sdan  # the main database. So it does not prevent [db] from committing.
106e91a54e1Sdan  #
107e91a54e1Sdan  do_test pager1-$tn.8 {
108e91a54e1Sdan    csql2 { UPDATE t1 SET a = a + 10 }
109e91a54e1Sdan  } {1 {database is locked}}
110e91a54e1Sdan  do_test pager1-$tn.9 {
111e91a54e1Sdan    csql2 {
112e91a54e1Sdan      BEGIN;
113e91a54e1Sdan      UPDATE t1 SET a = a + 10;
114e91a54e1Sdan    }
115e91a54e1Sdan  } {1 {database is locked}}
116e91a54e1Sdan
117e91a54e1Sdan  # Have [db] commit its transactions. Check the other connections can
118e91a54e1Sdan  # now see the new database content.
119e91a54e1Sdan  #
120e91a54e1Sdan  do_test pager1-$tn.10 { sql1 { COMMIT } } {}
121e91a54e1Sdan  do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
122e91a54e1Sdan  do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
123e91a54e1Sdan  do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
124e91a54e1Sdan
125e91a54e1Sdan  # Check that, as noted above, [db2] really did keep an open transaction
126e91a54e1Sdan  # after the attempt to write the database failed.
127e91a54e1Sdan  #
128e91a54e1Sdan  do_test pager1-$tn.14 {
129e91a54e1Sdan    csql2 { BEGIN }
130e91a54e1Sdan  } {1 {cannot start a transaction within a transaction}}
131e91a54e1Sdan  do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}
132e91a54e1Sdan
133e91a54e1Sdan  # Have [db2] open a transaction and take a read-lock on the database.
134e91a54e1Sdan  # Check that this prevents [db] from writing to the database (outside
135e91a54e1Sdan  # of any transaction). After this fails, check that [db3] can read
136e91a54e1Sdan  # the db (showing that [db] did not take a PENDING lock etc.)
137e91a54e1Sdan  #
138e91a54e1Sdan  do_test pager1-$tn.15 {
139e91a54e1Sdan    sql2 { BEGIN; SELECT * FROM t1; }
140e91a54e1Sdan  } {1 one 2 two 3 three}
141e91a54e1Sdan  do_test pager1-$tn.16 {
142e91a54e1Sdan    csql1 { UPDATE t1 SET a = a + 10 }
143e91a54e1Sdan  } {1 {database is locked}}
144e91a54e1Sdan  do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
145e91a54e1Sdan
146e91a54e1Sdan  # This time, have [db] open a transaction before writing the database.
147e91a54e1Sdan  # This works - [db] gets a RESERVED lock which does not conflict with
148e91a54e1Sdan  # the SHARED lock [db2] is holding.
149e91a54e1Sdan  #
150e91a54e1Sdan  do_test pager1-$tn.18 {
151e91a54e1Sdan    sql1 {
152e91a54e1Sdan      BEGIN;
153e91a54e1Sdan      UPDATE t1 SET a = a + 10;
154e91a54e1Sdan    }
155e91a54e1Sdan  } {}
156e91a54e1Sdan  do_test pager1-$tn-19 {
157e91a54e1Sdan    sql1 { PRAGMA lock_status }
158e91a54e1Sdan  } {main reserved temp closed}
159e91a54e1Sdan  do_test pager1-$tn-20 {
160e91a54e1Sdan    sql2 { PRAGMA lock_status }
161e91a54e1Sdan  } {main shared temp closed}
162e91a54e1Sdan
163e91a54e1Sdan  # Check that all connections can still read the database. Only [db] sees
164e91a54e1Sdan  # the updated content (as the transaction has not been committed yet).
165e91a54e1Sdan  #
166e91a54e1Sdan  do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
167e91a54e1Sdan  do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
168e91a54e1Sdan  do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
169e91a54e1Sdan
170e91a54e1Sdan  # Because [db2] still has the SHARED lock, [db] is unable to commit the
171e91a54e1Sdan  # transaction. If it tries, an error is returned and the connection
172e91a54e1Sdan  # upgrades to a PENDING lock.
173e91a54e1Sdan  #
174e91a54e1Sdan  # Once this happens, [db] can read the database and see the new content,
175e91a54e1Sdan  # [db2] (still holding SHARED) can still read the old content, but [db3]
176e91a54e1Sdan  # (not holding any lock) is prevented by [db]'s PENDING from reading
177e91a54e1Sdan  # the database.
178e91a54e1Sdan  #
179e91a54e1Sdan  do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
180e91a54e1Sdan  do_test pager1-$tn-25 {
181e91a54e1Sdan    sql1 { PRAGMA lock_status }
182e91a54e1Sdan  } {main pending temp closed}
183e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { SELECT * FROM t1  } } {11 one 12 two 13 three}
184e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1  } } {1 one 2 two 3 three}
185e91a54e1Sdan  do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
186e91a54e1Sdan
187e91a54e1Sdan  # Have [db2] commit its read transaction, releasing the SHARED lock it
188e91a54e1Sdan  # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
189e91a54e1Sdan  # is still holding a PENDING).
190e91a54e1Sdan  #
191e91a54e1Sdan  do_test pager1-$tn.29 { sql2 { COMMIT } } {}
192e91a54e1Sdan  do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
193e91a54e1Sdan  do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
194e91a54e1Sdan
195e91a54e1Sdan  # [db] is now able to commit the transaction. Once the transaction is
196e91a54e1Sdan  # committed, all three connections can read the new content.
197e91a54e1Sdan  #
198e91a54e1Sdan  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
199e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
200e91a54e1Sdan  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
201e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
202e91a54e1Sdan  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
203d353331aSdan
204d353331aSdan  # Install a busy-handler for connection [db].
205d353331aSdan  #
206d353331aSdan  set ::nbusy [list]
207d353331aSdan  proc busy {n} {
208d353331aSdan    lappend ::nbusy $n
209d353331aSdan    if {$n>5} { sql2 COMMIT }
210d353331aSdan    return 0
211d353331aSdan  }
212d353331aSdan  db busy busy
213d353331aSdan
214d353331aSdan  do_test pager1-$tn.29 {
215d353331aSdan    sql1 { BEGIN ; INSERT INTO t1 VALUES('x', 'y') }
216d353331aSdan  } {}
217d353331aSdan  do_test pager1-$tn.30 {
218d353331aSdan    sql2 { BEGIN ; SELECT * FROM t1 }
219d353331aSdan  } {21 one 22 two 23 three}
220d353331aSdan  do_test pager1-$tn.31 { sql1 COMMIT } {}
221d353331aSdan  do_test pager1-$tn.32 { set ::nbusy } {0 1 2 3 4 5 6}
222e91a54e1Sdan}
223e91a54e1Sdan
22453f04f3bSdan#-------------------------------------------------------------------------
22553f04f3bSdan# Savepoint related test cases.
22653f04f3bSdan#
227dca321aeSdan# pager1-3.1.2.*: Force a savepoint rollback to cause the database file
228dca321aeSdan#                 to grow.
229dca321aeSdan#
230273f3f0dSdan# pager1-3.1.3.*: Use a journal created in synchronous=off mode as part
231273f3f0dSdan#                 of a savepoint rollback.
232273f3f0dSdan#
2330e986f51Sdando_test pager1-3.1.1 {
234b0ac3e3aSdan  faultsim_delete_and_reopen
235b0ac3e3aSdan  execsql {
236b0ac3e3aSdan    CREATE TABLE t1(a PRIMARY KEY, b);
237b0ac3e3aSdan    CREATE TABLE counter(
238b0ac3e3aSdan      i CHECK (i<5),
239b0ac3e3aSdan      u CHECK (u<10)
240b0ac3e3aSdan    );
241b0ac3e3aSdan    INSERT INTO counter VALUES(0, 0);
242b0ac3e3aSdan    CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
243b0ac3e3aSdan      UPDATE counter SET i = i+1;
244b0ac3e3aSdan    END;
245b0ac3e3aSdan    CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
246b0ac3e3aSdan      UPDATE counter SET u = u+1;
247b0ac3e3aSdan    END;
248b0ac3e3aSdan  }
249b0ac3e3aSdan  execsql { SELECT * FROM counter }
250b0ac3e3aSdan} {0 0}
251b0ac3e3aSdan
2520e986f51Sdando_execsql_test pager1-3.1.2 {
253d353331aSdan  PRAGMA cache_size = 10;
254b0ac3e3aSdan  BEGIN;
255b0ac3e3aSdan    INSERT INTO t1 VALUES(1, randomblob(1500));
256b0ac3e3aSdan    INSERT INTO t1 VALUES(2, randomblob(1500));
257b0ac3e3aSdan    INSERT INTO t1 VALUES(3, randomblob(1500));
258b0ac3e3aSdan    SELECT * FROM counter;
259b0ac3e3aSdan} {3 0}
2600e986f51Sdando_catchsql_test pager1-3.1.3 {
261b0ac3e3aSdan    INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
262b0ac3e3aSdan} {1 {constraint failed}}
263b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
264b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
265b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {}
266b0ac3e3aSdan
267273f3f0dSdanforeach {tn sql tcl} {
268d353331aSdan  7  { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 0 } {
269273f3f0dSdan    testvfs tv -default 1
270273f3f0dSdan    tv devchar safe_append
271273f3f0dSdan  }
272d353331aSdan  8  { PRAGMA synchronous = NORMAL ; PRAGMA temp_store = 2 } {
273d353331aSdan    testvfs tv -default 1
274d353331aSdan    tv devchar sequential
275d353331aSdan  }
276d353331aSdan  9  { PRAGMA synchronous = FULL } { }
277d353331aSdan  10 { PRAGMA synchronous = NORMAL } { }
278d353331aSdan  11 { PRAGMA synchronous = OFF } { }
279d353331aSdan  12 { PRAGMA synchronous = FULL ; PRAGMA fullfsync = 1 } { }
280d353331aSdan  13 { PRAGMA synchronous = FULL } {
281d353331aSdan    testvfs tv -default 1
282d353331aSdan    tv devchar sequential
283d353331aSdan  }
284*6b63ab47Sdan  14 { PRAGMA locking_mode = EXCLUSIVE } {
285*6b63ab47Sdan  }
286273f3f0dSdan} {
287273f3f0dSdan  do_test pager1-3.$tn.1 {
288273f3f0dSdan    eval $tcl
289dca321aeSdan    faultsim_delete_and_reopen
290dca321aeSdan    db func a_string a_string
291273f3f0dSdan    execsql $sql
292dca321aeSdan    execsql {
293dca321aeSdan      PRAGMA auto_vacuum = 2;
294dca321aeSdan      PRAGMA cache_size = 10;
295dca321aeSdan      CREATE TABLE z(x INTEGER PRIMARY KEY, y);
296dca321aeSdan      BEGIN;
297dca321aeSdan        INSERT INTO z VALUES(NULL, a_string(800));
298dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   2
299dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   4
300dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   8
301dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  16
302dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  32
303dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  64
304dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 128
305dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 256
306dca321aeSdan      COMMIT;
307dca321aeSdan    }
308dca321aeSdan    execsql { PRAGMA auto_vacuum }
309dca321aeSdan  } {2}
310273f3f0dSdan  do_execsql_test pager1-3.$tn.2 {
311dca321aeSdan    BEGIN;
312dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
313dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
314dca321aeSdan      SAVEPOINT one;
315273f3f0dSdan        UPDATE z SET y = NULL WHERE x>256;
316dca321aeSdan        PRAGMA incremental_vacuum;
317dca321aeSdan        SELECT count(*) FROM z WHERE x < 100;
318dca321aeSdan      ROLLBACK TO one;
319dca321aeSdan    COMMIT;
320dca321aeSdan  } {99}
321dca321aeSdan
322273f3f0dSdan  do_execsql_test pager1-3.$tn.3 {
323273f3f0dSdan    BEGIN;
324273f3f0dSdan      SAVEPOINT one;
325273f3f0dSdan        UPDATE z SET y = y||x;
326273f3f0dSdan      ROLLBACK TO one;
327273f3f0dSdan    COMMIT;
328273f3f0dSdan    SELECT count(*) FROM z;
329273f3f0dSdan  } {258}
330273f3f0dSdan
331273f3f0dSdan  do_execsql_test pager1-3.$tn.4 {
332273f3f0dSdan    SAVEPOINT one;
333273f3f0dSdan      UPDATE z SET y = y||x;
334273f3f0dSdan    ROLLBACK TO one;
335273f3f0dSdan  } {}
336273f3f0dSdan  do_execsql_test pager1-3.$tn.5 {
337273f3f0dSdan    SELECT count(*) FROM z;
338273f3f0dSdan    RELEASE one;
339273f3f0dSdan    PRAGMA integrity_check;
340273f3f0dSdan  } {258 ok}
341273f3f0dSdan
342273f3f0dSdan  db close
343273f3f0dSdan  catch { tv delete }
344273f3f0dSdan}
345273f3f0dSdan
34653f04f3bSdan#-------------------------------------------------------------------------
34753f04f3bSdan# Hot journal rollback related test cases.
34853f04f3bSdan#
34953f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid
35053f04f3bSdan#               journal files.
35153f04f3bSdan#
35253f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a
35353f04f3bSdan#               hot-journal file appears to be corrupt (checksum does not
35453f04f3bSdan#               compute) the associated journal is rolled back (and no
35553f04f3bSdan#               xAccess() call to check for the presence of any master
35653f04f3bSdan#               journal file is made).
35753f04f3bSdan#
358de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
359de4996e2Sdan#               page-size or sector-size in the journal header appear to
360de4996e2Sdan#               be invalid (too large, too small or not a power of 2).
361de4996e2Sdan#
362e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master
363e08341c6Sdan#               journal pointer generated in various "PRAGMA synchronous"
364e08341c6Sdan#               modes.
365e08341c6Sdan#
366e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
367e08341c6Sdan#               journal-record for which the checksum fails.
368e08341c6Sdan#
369ec6ffc1aSdan# pager1.4.6.*: Test that when rolling back a hot-journal that contains a
370ec6ffc1aSdan#               master journal pointer, the master journal file is deleted
371ec6ffc1aSdan#               after all the hot-journals that refer to it are deleted.
372ec6ffc1aSdan#
373c8ce3972Sdan# pager1.4.7.*: Test that if a hot-journal file exists but a client can
374c8ce3972Sdan#               open it for reading only, the database cannot be accessed and
375c8ce3972Sdan#               SQLITE_CANTOPEN is returned.
376c8ce3972Sdan#
37753f04f3bSdando_test pager1.4.1.1 {
37853f04f3bSdan  faultsim_delete_and_reopen
37953f04f3bSdan  execsql {
38053f04f3bSdan    CREATE TABLE x(y, z);
38153f04f3bSdan    INSERT INTO x VALUES(1, 2);
38253f04f3bSdan  }
38353f04f3bSdan  set fd [open test.db-journal w]
38453f04f3bSdan  puts -nonewline $fd "helloworld"
38553f04f3bSdan  close $fd
38653f04f3bSdan  file exists test.db-journal
38753f04f3bSdan} {1}
38853f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
38953f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0}
39053f04f3bSdan
39153f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite
39253f04f3bSdan# deletes the master-journal to commit a multi-file transaction.
39353f04f3bSdan#
39453f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
39553f04f3bSdan# up the file system to contain two databases, two hot-journal files and
39653f04f3bSdan# a master-journal.
39753f04f3bSdan#
39853f04f3bSdando_test pager1.4.2.1 {
39953f04f3bSdan  testvfs tstvfs -default 1
40053f04f3bSdan  tstvfs filter xDelete
40153f04f3bSdan  tstvfs script xDeleteCallback
40253f04f3bSdan  proc xDeleteCallback {method file args} {
40353f04f3bSdan    set file [file tail $file]
40453f04f3bSdan    if { [string match *mj* $file] } { faultsim_save }
40553f04f3bSdan  }
40653f04f3bSdan  faultsim_delete_and_reopen
40753f04f3bSdan  db func a_string a_string
40853f04f3bSdan  execsql {
40953f04f3bSdan    ATTACH 'test.db2' AS aux;
41053f04f3bSdan    PRAGMA journal_mode = DELETE;
41153f04f3bSdan    PRAGMA main.cache_size = 10;
41253f04f3bSdan    PRAGMA aux.cache_size = 10;
41353f04f3bSdan    CREATE TABLE t1(a UNIQUE, b UNIQUE);
41453f04f3bSdan    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
41553f04f3bSdan    INSERT INTO t1 VALUES(a_string(200), a_string(300));
41653f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
41753f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
41853f04f3bSdan    INSERT INTO t2 SELECT * FROM t1;
41953f04f3bSdan    BEGIN;
42053f04f3bSdan      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
42153f04f3bSdan      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
42253f04f3bSdan      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
42353f04f3bSdan      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
42453f04f3bSdan      REPLACE INTO t2 SELECT * FROM t1;
42553f04f3bSdan    COMMIT;
42653f04f3bSdan  }
42753f04f3bSdan  db close
42853f04f3bSdan  tstvfs delete
42953f04f3bSdan} {}
43053f04f3bSdando_test pager1.4.2.2 {
43153f04f3bSdan  faultsim_restore_and_reopen
43253f04f3bSdan  execsql {
43353f04f3bSdan    SELECT count(*) FROM t1;
43453f04f3bSdan    PRAGMA integrity_check;
43553f04f3bSdan  }
43653f04f3bSdan} {4 ok}
43753f04f3bSdando_test pager1.4.2.3 {
43853f04f3bSdan  faultsim_restore_and_reopen
43953f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
44053f04f3bSdan  execsql {
44153f04f3bSdan    SELECT count(*) FROM t1;
44253f04f3bSdan    PRAGMA integrity_check;
44353f04f3bSdan  }
44453f04f3bSdan} {64 ok}
44553f04f3bSdando_test pager1.4.2.4 {
44653f04f3bSdan  faultsim_restore_and_reopen
44753f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
44853f04f3bSdan  execsql {
44953f04f3bSdan    SELECT count(*) FROM t1;
45053f04f3bSdan    PRAGMA integrity_check;
45153f04f3bSdan  }
45253f04f3bSdan} {4 ok}
45353f04f3bSdando_test pager1.4.2.5 {
45453f04f3bSdan  faultsim_restore_and_reopen
45553f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
45653f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
45753f04f3bSdan  execsql {
45853f04f3bSdan    SELECT count(*) FROM t1;
45953f04f3bSdan    PRAGMA integrity_check;
46053f04f3bSdan  }
46153f04f3bSdan} {4 ok}
46253f04f3bSdan
463de4996e2Sdando_test pager1.4.3.1 {
464de4996e2Sdan  testvfs tstvfs -default 1
465de4996e2Sdan  tstvfs filter xSync
466de4996e2Sdan  tstvfs script xSyncCallback
467de4996e2Sdan  proc xSyncCallback {method file args} {
468de4996e2Sdan    set file [file tail $file]
469de4996e2Sdan    if { 0==[string match *journal $file] } { faultsim_save }
470de4996e2Sdan  }
471de4996e2Sdan  faultsim_delete_and_reopen
472de4996e2Sdan  execsql {
473de4996e2Sdan    PRAGMA journal_mode = DELETE;
474de4996e2Sdan    CREATE TABLE t1(a, b);
475de4996e2Sdan    INSERT INTO t1 VALUES(1, 2);
476de4996e2Sdan    INSERT INTO t1 VALUES(3, 4);
477de4996e2Sdan  }
478de4996e2Sdan  db close
479de4996e2Sdan  tstvfs delete
480de4996e2Sdan} {}
481de4996e2Sdan
482de4996e2Sdanforeach {tn ofst value result} {
483de4996e2Sdan          2   20    31       {1 2 3 4}
484de4996e2Sdan          3   20    32       {1 2 3 4}
485de4996e2Sdan          4   20    33       {1 2 3 4}
486de4996e2Sdan          5   20    65536    {1 2 3 4}
487de4996e2Sdan          6   20    131072   {1 2 3 4}
488de4996e2Sdan
489de4996e2Sdan          7   24    511      {1 2 3 4}
490de4996e2Sdan          8   24    513      {1 2 3 4}
491de4996e2Sdan          9   24    65536    {1 2 3 4}
492de4996e2Sdan
493de4996e2Sdan         10   32    65536    {1 2}
494de4996e2Sdan} {
495de4996e2Sdan  do_test pager1.4.3.$tn {
496de4996e2Sdan    faultsim_restore_and_reopen
497de4996e2Sdan    hexio_write test.db-journal $ofst [format %.8x $value]
498de4996e2Sdan    execsql { SELECT * FROM t1 }
499de4996e2Sdan  } $result
500de4996e2Sdan}
501de4996e2Sdandb close
502de4996e2Sdan
503e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal
504e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the
505e08341c6Sdan# file-system is saved just before the xDelete() call to remove the
506e08341c6Sdan# master journal file from the file-system.
507e08341c6Sdan#
508e08341c6Sdantestvfs tv -default 1
509e08341c6Sdantv script copy_on_mj_delete
510e08341c6Sdanset ::mj_filename_length 0
511e08341c6Sdanproc copy_on_mj_delete {method filename args} {
512e08341c6Sdan  if {[string match *mj* [file tail $filename]]} {
513e08341c6Sdan    set ::mj_filename_length [string length $filename]
514e08341c6Sdan    faultsim_save
515e08341c6Sdan  }
516e08341c6Sdan  return SQLITE_OK
517e08341c6Sdan}
518e08341c6Sdan
519e08341c6Sdanset pwd [pwd]
520e08341c6Sdanforeach {tn1 tcl} {
521e08341c6Sdan  1 { set prefix "test.db" }
522e08341c6Sdan  2 {
523e08341c6Sdan    # This test depends on the underlying VFS being able to open paths
524e08341c6Sdan    # 512 bytes in length. The idea is to create a hot-journal file that
525e08341c6Sdan    # contains a master-journal pointer so large that it could contain
526e08341c6Sdan    # a valid page record (if the file page-size is 512 bytes). So as to
527e08341c6Sdan    # make sure SQLite doesn't get confused by this.
528e08341c6Sdan    #
529e08341c6Sdan    set nPadding [expr 511 - $::mj_filename_length]
530e08341c6Sdan
531e08341c6Sdan    # We cannot just create a really long database file name to open, as
532e08341c6Sdan    # Linux limits a single component of a path to 255 bytes by default
533e08341c6Sdan    # (and presumably other systems have limits too). So create a directory
534e08341c6Sdan    # hierarchy to work in.
535e08341c6Sdan    #
536e08341c6Sdan    set dirname "d123456789012345678901234567890/"
537e08341c6Sdan    set nDir [expr $nPadding / 32]
538e08341c6Sdan    if { $nDir } {
539e08341c6Sdan      set p [string repeat $dirname $nDir]
540e08341c6Sdan      file mkdir $p
541e08341c6Sdan      cd $p
542e08341c6Sdan    }
543e08341c6Sdan
544e08341c6Sdan    set padding [string repeat x [expr $nPadding %32]]
545e08341c6Sdan    set prefix "test.db${padding}"
546e08341c6Sdan  }
547e08341c6Sdan} {
548e08341c6Sdan  eval $tcl
549e08341c6Sdan  foreach {tn2 sql} {
550e08341c6Sdan    o {
551e08341c6Sdan      PRAGMA main.synchronous=OFF;
552e08341c6Sdan      PRAGMA aux.synchronous=OFF;
553e08341c6Sdan    }
554e08341c6Sdan    o512 {
555e08341c6Sdan      PRAGMA main.synchronous=OFF;
556e08341c6Sdan      PRAGMA aux.synchronous=OFF;
557e08341c6Sdan      PRAGMA main.page_size = 512;
558e08341c6Sdan      PRAGMA aux.page_size = 512;
559e08341c6Sdan    }
560e08341c6Sdan    n {
561e08341c6Sdan      PRAGMA main.synchronous=NORMAL;
562e08341c6Sdan      PRAGMA aux.synchronous=NORMAL;
563e08341c6Sdan    }
564e08341c6Sdan    f {
565e08341c6Sdan      PRAGMA main.synchronous=FULL;
566e08341c6Sdan      PRAGMA aux.synchronous=FULL;
567e08341c6Sdan    }
568e08341c6Sdan  } {
569e08341c6Sdan
570e08341c6Sdan    set tn "${tn1}.${tn2}"
571e08341c6Sdan
572e08341c6Sdan    # Set up a connection to have two databases, test.db (main) and
573e08341c6Sdan    # test.db2 (aux). Then run a multi-file transaction on them. The
574e08341c6Sdan    # VFS will snapshot the file-system just before the master-journal
575e08341c6Sdan    # file is deleted to commit the transaction.
576e08341c6Sdan    #
577e08341c6Sdan    tv filter xDelete
578e08341c6Sdan    do_test pager1-4.4.$tn.1 {
579e08341c6Sdan      faultsim_delete_and_reopen $prefix
580e08341c6Sdan      execsql "
581e08341c6Sdan        ATTACH '${prefix}2' AS aux;
582e08341c6Sdan        $sql
583e08341c6Sdan        CREATE TABLE a(x);
584e08341c6Sdan        CREATE TABLE aux.b(x);
585e08341c6Sdan        INSERT INTO a VALUES('double-you');
586e08341c6Sdan        INSERT INTO a VALUES('why');
587e08341c6Sdan        INSERT INTO a VALUES('zed');
588e08341c6Sdan        INSERT INTO b VALUES('won');
589e08341c6Sdan        INSERT INTO b VALUES('too');
590e08341c6Sdan        INSERT INTO b VALUES('free');
591e08341c6Sdan      "
592e08341c6Sdan      execsql {
593e08341c6Sdan        BEGIN;
594e08341c6Sdan          INSERT INTO a SELECT * FROM b WHERE rowid<=3;
595e08341c6Sdan          INSERT INTO b SELECT * FROM a WHERE rowid<=3;
596e08341c6Sdan        COMMIT;
597e08341c6Sdan      }
598e08341c6Sdan    } {}
599e08341c6Sdan    tv filter {}
600e08341c6Sdan
601e08341c6Sdan    # Check that the transaction was committed successfully.
602e08341c6Sdan    #
603e08341c6Sdan    do_execsql_test pager1-4.4.$tn.2 {
604e08341c6Sdan      SELECT * FROM a
605e08341c6Sdan    } {double-you why zed won too free}
606e08341c6Sdan    do_execsql_test pager1-4.4.$tn.3 {
607e08341c6Sdan      SELECT * FROM b
608e08341c6Sdan    } {won too free double-you why zed}
609e08341c6Sdan
610e08341c6Sdan    # Restore the file-system and reopen the databases. Check that it now
611e08341c6Sdan    # appears that the transaction was not committed (because the file-system
612e08341c6Sdan    # was restored to the state where it had not been).
613e08341c6Sdan    #
614e08341c6Sdan    do_test pager1-4.4.$tn.4 {
615e08341c6Sdan      faultsim_restore_and_reopen $prefix
616e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
617e08341c6Sdan    } {}
618e08341c6Sdan    do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
619e08341c6Sdan    do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
620e08341c6Sdan
621e08341c6Sdan    # Restore the file-system again. This time, before reopening the databases,
622e08341c6Sdan    # delete the master-journal file from the file-system. It now appears that
623e08341c6Sdan    # the transaction was committed (no master-journal file == no rollback).
624e08341c6Sdan    #
625e08341c6Sdan    do_test pager1-4.4.$tn.7 {
626e08341c6Sdan      faultsim_restore_and_reopen $prefix
627e08341c6Sdan      foreach f [glob ${prefix}-mj*] { file delete -force $f }
628e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
629e08341c6Sdan    } {}
630e08341c6Sdan    do_execsql_test pager1-4.4.$tn.8 {
631e08341c6Sdan      SELECT * FROM a
632e08341c6Sdan    } {double-you why zed won too free}
633e08341c6Sdan    do_execsql_test pager1-4.4.$tn.9 {
634e08341c6Sdan      SELECT * FROM b
635e08341c6Sdan    } {won too free double-you why zed}
636e08341c6Sdan  }
637e08341c6Sdan
638e08341c6Sdan  cd $pwd
639e08341c6Sdan}
640e08341c6Sdandb close
641e08341c6Sdantv delete
6420e986f51Sdanfile delete -force $dirname
6430e986f51Sdan
6440e986f51Sdan
6450e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a
6460e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly
6470e986f51Sdan# two database pages (and page 1 - the change counter).
6480e986f51Sdan#
6490e986f51Sdantestvfs tv -default 1
6500e986f51Sdantv sectorsize 512
6510e986f51Sdantv script copy_on_journal_delete
6520e986f51Sdantv filter xDelete
6530e986f51Sdanproc copy_on_journal_delete {method filename args} {
6540e986f51Sdan  if {[string match *journal $filename]} faultsim_save
6550e986f51Sdan  return SQLITE_OK
6560e986f51Sdan}
6570e986f51Sdanfaultsim_delete_and_reopen
6580e986f51Sdando_execsql_test pager1.4.5.1 {
6590e986f51Sdan  PRAGMA page_size = 1024;
6600e986f51Sdan  CREATE TABLE t1(a, b);
6610e986f51Sdan  CREATE TABLE t2(a, b);
6620e986f51Sdan  INSERT INTO t1 VALUES('I', 'II');
6630e986f51Sdan  INSERT INTO t2 VALUES('III', 'IV');
6640e986f51Sdan  BEGIN;
6650e986f51Sdan    INSERT INTO t1 VALUES(1, 2);
6660e986f51Sdan    INSERT INTO t2 VALUES(3, 4);
6670e986f51Sdan  COMMIT;
6680e986f51Sdan} {}
6690e986f51Sdantv filter {}
6700e986f51Sdan
6710e986f51Sdan# Check the transaction was committed:
6720e986f51Sdan#
6730e986f51Sdando_execsql_test pager1.4.5.2 {
6740e986f51Sdan  SELECT * FROM t1;
6750e986f51Sdan  SELECT * FROM t2;
6760e986f51Sdan} {I II 1 2 III IV 3 4}
6770e986f51Sdan
678ec6ffc1aSdan# Now try four tests:
6790e986f51Sdan#
6800e986f51Sdan#  pager1-4.5.3: Restore the file-system. Check that the whole transaction
6810e986f51Sdan#                is rolled back.
6820e986f51Sdan#
6830e986f51Sdan#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
6840e986f51Sdan#                journal. Check the transaction is not rolled back.
6850e986f51Sdan#
6860e986f51Sdan#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
6870e986f51Sdan#                journal. Check that the first record in the transaction is
6880e986f51Sdan#                played back, but not the second.
6890e986f51Sdan#
69010f5a50eSdan#  pager1-4.5.6: Restore the file-system. Try to open the database with a
69110f5a50eSdan#                readonly connection. This should fail, as a read-only
69210f5a50eSdan#                connection cannot roll back the database file.
69310f5a50eSdan#
6940e986f51Sdanfaultsim_restore_and_reopen
6950e986f51Sdando_execsql_test pager1.4.5.3 {
6960e986f51Sdan  SELECT * FROM t1;
6970e986f51Sdan  SELECT * FROM t2;
6980e986f51Sdan} {I II III IV}
6990e986f51Sdanfaultsim_restore_and_reopen
7000e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
7010e986f51Sdando_execsql_test pager1.4.5.4 {
7020e986f51Sdan  SELECT * FROM t1;
7030e986f51Sdan  SELECT * FROM t2;
7040e986f51Sdan} {I II 1 2 III IV 3 4}
7050e986f51Sdanfaultsim_restore_and_reopen
7060e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
7070e986f51Sdando_execsql_test pager1.4.5.5 {
7080e986f51Sdan  SELECT * FROM t1;
7090e986f51Sdan  SELECT * FROM t2;
7100e986f51Sdan} {I II III IV 3 4}
7110e986f51Sdan
71210f5a50eSdanfaultsim_restore_and_reopen
71310f5a50eSdandb close
71410f5a50eSdansqlite3 db test.db -readonly 1
71510f5a50eSdando_catchsql_test pager1.4.5.6 {
71610f5a50eSdan  SELECT * FROM t1;
71710f5a50eSdan  SELECT * FROM t2;
71810f5a50eSdan} {1 {disk I/O error}}
719ec6ffc1aSdandb close
720ec6ffc1aSdan
721ec6ffc1aSdan# Snapshot the file-system just before multi-file commit. Save the name
722ec6ffc1aSdan# of the master journal file in $::mj_filename.
723ec6ffc1aSdan#
724ec6ffc1aSdantv script copy_on_mj_delete
725ec6ffc1aSdantv filter xDelete
726ec6ffc1aSdanproc copy_on_mj_delete {method filename args} {
727ec6ffc1aSdan  if {[string match *mj* [file tail $filename]]} {
728ec6ffc1aSdan    set ::mj_filename $filename
729ec6ffc1aSdan    faultsim_save
730ec6ffc1aSdan  }
731ec6ffc1aSdan  return SQLITE_OK
732ec6ffc1aSdan}
733ec6ffc1aSdando_test pager1.4.6.1 {
734ec6ffc1aSdan  faultsim_delete_and_reopen
735ec6ffc1aSdan  execsql {
736ec6ffc1aSdan    ATTACH 'test.db2' AS two;
737ec6ffc1aSdan    CREATE TABLE t1(a, b);
738ec6ffc1aSdan    CREATE TABLE two.t2(a, b);
739ec6ffc1aSdan    INSERT INTO t1 VALUES(1, 't1.1');
740ec6ffc1aSdan    INSERT INTO t2 VALUES(1, 't2.1');
741ec6ffc1aSdan    BEGIN;
742ec6ffc1aSdan      UPDATE t1 SET b = 't1.2';
743ec6ffc1aSdan      UPDATE t2 SET b = 't2.2';
744ec6ffc1aSdan    COMMIT;
745ec6ffc1aSdan  }
746ec6ffc1aSdan  tv filter {}
747ec6ffc1aSdan  db close
748ec6ffc1aSdan} {}
749ec6ffc1aSdan
750ec6ffc1aSdanfaultsim_restore_and_reopen
751ec6ffc1aSdando_execsql_test pager1.4.6.2 { SELECT * FROM t1 }           {1 t1.1}
752ec6ffc1aSdando_test         pager1.4.6.3 { file exists $::mj_filename } {1}
753ec6ffc1aSdando_execsql_test pager1.4.6.4 {
754ec6ffc1aSdan  ATTACH 'test.db2' AS two;
755ec6ffc1aSdan  SELECT * FROM t2;
756ec6ffc1aSdan} {1 t2.1}
757ec6ffc1aSdando_test pager1.4.6.5 { file exists $::mj_filename } {0}
758ec6ffc1aSdan
759ec6ffc1aSdanfaultsim_restore_and_reopen
760ec6ffc1aSdandb close
761ec6ffc1aSdando_test pager1.4.6.8 {
762ec6ffc1aSdan  set ::mj_filename1 $::mj_filename
763ec6ffc1aSdan  tv filter xDelete
764ec6ffc1aSdan  sqlite3 db test.db2
765ec6ffc1aSdan  execsql {
766ec6ffc1aSdan    ATTACH 'test.db3' AS three;
767ec6ffc1aSdan    CREATE TABLE three.t3(a, b);
768ec6ffc1aSdan    INSERT INTO t3 VALUES(1, 't3.1');
769ec6ffc1aSdan    BEGIN;
770ec6ffc1aSdan      UPDATE t2 SET b = 't2.3';
771ec6ffc1aSdan      UPDATE t3 SET b = 't3.3';
772ec6ffc1aSdan    COMMIT;
773ec6ffc1aSdan  }
774ec6ffc1aSdan  expr {$::mj_filename1 != $::mj_filename}
775ec6ffc1aSdan} {1}
776ec6ffc1aSdanfaultsim_restore_and_reopen
777ec6ffc1aSdantv filter {}
778ec6ffc1aSdan
779ec6ffc1aSdan# The file-system now contains:
780ec6ffc1aSdan#
781ec6ffc1aSdan#   * three databases
782ec6ffc1aSdan#   * three hot-journal files
783ec6ffc1aSdan#   * two master-journal files.
784ec6ffc1aSdan#
785ec6ffc1aSdan# The hot-journals associated with test.db2 and test.db3 point to
786ec6ffc1aSdan# master journal $::mj_filename. The hot-journal file associated with
787ec6ffc1aSdan# test.db points to master journal $::mj_filename1. So reading from
788ec6ffc1aSdan# test.db should delete $::mj_filename1.
789ec6ffc1aSdan#
790ec6ffc1aSdando_test pager1.4.6.9 {
791ec6ffc1aSdan  lsort [glob test.db*]
792ec6ffc1aSdan} [lsort [list                                           \
793ec6ffc1aSdan  test.db test.db2 test.db3                              \
794ec6ffc1aSdan  test.db-journal test.db2-journal test.db3-journal      \
795ec6ffc1aSdan  [file tail $::mj_filename] [file tail $::mj_filename1]
796ec6ffc1aSdan]]
797ec6ffc1aSdan
798ec6ffc1aSdan# The master-journal $::mj_filename1 contains pointers to test.db and
799ec6ffc1aSdan# test.db2. However the hot-journal associated with test.db2 points to
800ec6ffc1aSdan# a different master-journal. Therefore, reading from test.db only should
801ec6ffc1aSdan# be enough to cause SQLite to delete $::mj_filename1.
802ec6ffc1aSdan#
803ec6ffc1aSdando_test         pager1.4.6.10 { file exists $::mj_filename  } {1}
804ec6ffc1aSdando_test         pager1.4.6.11 { file exists $::mj_filename1 } {1}
805ec6ffc1aSdando_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1}
806ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename  } {1}
807ec6ffc1aSdando_test         pager1.4.6.14 { file exists $::mj_filename1 } {0}
808ec6ffc1aSdan
809ec6ffc1aSdando_execsql_test pager1.4.6.12 {
810ec6ffc1aSdan  ATTACH 'test.db2' AS two;
811ec6ffc1aSdan  SELECT * FROM t2;
812ec6ffc1aSdan} {1 t2.1}
813ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename }  {1}
814ec6ffc1aSdando_execsql_test pager1.4.6.14 {
815ec6ffc1aSdan  ATTACH 'test.db3' AS three;
816ec6ffc1aSdan  SELECT * FROM t3;
817ec6ffc1aSdan} {1 t3.1}
818ec6ffc1aSdando_test         pager1.4.6.15 { file exists $::mj_filename }  {0}
81910f5a50eSdan
8200e986f51Sdandb close
8210e986f51Sdantv delete
822e08341c6Sdan
823c8ce3972Sdantestvfs tv -default 1
824c8ce3972Sdantv sectorsize 512
825c8ce3972Sdantv script copy_on_journal_delete
826c8ce3972Sdantv filter xDelete
827c8ce3972Sdanproc copy_on_journal_delete {method filename args} {
828c8ce3972Sdan  if {[string match *journal $filename]} faultsim_save
829c8ce3972Sdan  return SQLITE_OK
830c8ce3972Sdan}
831c8ce3972Sdanfaultsim_delete_and_reopen
832c8ce3972Sdando_execsql_test pager1.4.7.1 {
833c8ce3972Sdan  CREATE TABLE t1(x PRIMARY KEY, y);
834c8ce3972Sdan  CREATE INDEX i1 ON t1(y);
835c8ce3972Sdan  INSERT INTO t1 VALUES('I',   'one');
836c8ce3972Sdan  INSERT INTO t1 VALUES('II',  'four');
837c8ce3972Sdan  INSERT INTO t1 VALUES('III', 'nine');
838c8ce3972Sdan  BEGIN;
839c8ce3972Sdan    INSERT INTO t1 VALUES('IV', 'sixteen');
840c8ce3972Sdan    INSERT INTO t1 VALUES('V' , 'twentyfive');
841c8ce3972Sdan  COMMIT;
842c8ce3972Sdan} {}
843c8ce3972Sdantv filter {}
844c8ce3972Sdandb close
845c8ce3972Sdantv delete
846c8ce3972Sdando_test pager1.4.7.2 {
847c8ce3972Sdan  faultsim_restore_and_reopen
848c8ce3972Sdan  catch {file attributes test.db-journal -permissions r--------}
849c8ce3972Sdan  catch {file attributes test.db-journal -readonly 1}
850c8ce3972Sdan  catchsql { SELECT * FROM t1 }
851c8ce3972Sdan} {1 {unable to open database file}}
852c8ce3972Sdando_test pager1.4.7.3 {
853c8ce3972Sdan  db close
854c8ce3972Sdan  catch {file attributes test.db-journal -permissions rw-rw-rw-}
855c8ce3972Sdan  catch {file attributes test.db-journal -readonly 0}
856c8ce3972Sdan  file delete test.db-journal
857c8ce3972Sdan  file exists test.db-journal
858c8ce3972Sdan} {0}
859c8ce3972Sdan
860de4996e2Sdan#-------------------------------------------------------------------------
861146ed78bSdan# The following tests deal with multi-file commits.
862de4996e2Sdan#
863146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because
864146ed78bSdan#               another connection is holding a SHARED lock on one of the
865146ed78bSdan#               files. After the SHARED lock is removed, the COMMIT succeeds.
866146ed78bSdan#
867146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory.
868146ed78bSdan#
869146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory.
870146ed78bSdan#
871146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
872146ed78bSdan#               name is added to a journal file immediately after the last
873146ed78bSdan#               journal record. But with synchronous=full, extra unused space
874146ed78bSdan#               is allocated between the last journal record and the
875146ed78bSdan#               master-journal file name so that the master-journal file
876146ed78bSdan#               name does not lie on the same sector as the last journal file
877146ed78bSdan#               record.
878146ed78bSdan#
879e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
880e08341c6Sdan#               truncated to zero bytes when a multi-file transaction is
881e08341c6Sdan#               committed (instead of the first couple of bytes being zeroed).
882e08341c6Sdan#
883146ed78bSdan#
884146ed78bSdando_test pager1-5.1.1 {
885146ed78bSdan  faultsim_delete_and_reopen
886146ed78bSdan  execsql {
887146ed78bSdan    ATTACH 'test.db2' AS aux;
888146ed78bSdan    CREATE TABLE t1(a, b);
889146ed78bSdan    CREATE TABLE aux.t2(a, b);
890146ed78bSdan    INSERT INTO t1 VALUES(17, 'Lenin');
891146ed78bSdan    INSERT INTO t1 VALUES(22, 'Stalin');
892146ed78bSdan    INSERT INTO t1 VALUES(53, 'Khrushchev');
893146ed78bSdan  }
894146ed78bSdan} {}
895146ed78bSdando_test pager1-5.1.2 {
896146ed78bSdan  execsql {
897146ed78bSdan    BEGIN;
898146ed78bSdan      INSERT INTO t1 VALUES(64, 'Brezhnev');
899146ed78bSdan      INSERT INTO t2 SELECT * FROM t1;
900146ed78bSdan  }
901146ed78bSdan  sqlite3 db2 test.db2
902146ed78bSdan  execsql {
903146ed78bSdan    BEGIN;
904146ed78bSdan      SELECT * FROM t2;
905146ed78bSdan  } db2
906146ed78bSdan} {}
907146ed78bSdando_test pager1-5.1.3 {
908146ed78bSdan  catchsql COMMIT
909146ed78bSdan} {1 {database is locked}}
910146ed78bSdando_test pager1-5.1.4 {
911146ed78bSdan  execsql COMMIT db2
912146ed78bSdan  execsql COMMIT
913146ed78bSdan  execsql { SELECT * FROM t2 } db2
914146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
915146ed78bSdando_test pager1-5.1.5 {
916146ed78bSdan  db2 close
917146ed78bSdan} {}
918146ed78bSdan
919146ed78bSdando_test pager1-5.2.1 {
920146ed78bSdan  execsql {
921146ed78bSdan    PRAGMA journal_mode = memory;
922146ed78bSdan    BEGIN;
923146ed78bSdan      INSERT INTO t1 VALUES(84, 'Andropov');
924146ed78bSdan      INSERT INTO t2 VALUES(84, 'Andropov');
925146ed78bSdan    COMMIT;
926146ed78bSdan  }
927146ed78bSdan} {memory}
928146ed78bSdando_test pager1-5.3.1 {
929146ed78bSdan  execsql {
930146ed78bSdan    PRAGMA journal_mode = off;
931146ed78bSdan    BEGIN;
932146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
933146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
934146ed78bSdan    COMMIT;
935146ed78bSdan  }
936146ed78bSdan} {off}
937146ed78bSdan
938146ed78bSdando_test pager1-5.4.1 {
939146ed78bSdan  db close
940146ed78bSdan  testvfs tv
941146ed78bSdan  sqlite3 db test.db -vfs tv
942146ed78bSdan  execsql { ATTACH 'test.db2' AS aux }
943146ed78bSdan
944146ed78bSdan  tv filter xDelete
945146ed78bSdan  tv script max_journal_size
946146ed78bSdan  tv sectorsize 512
947146ed78bSdan  set ::max_journal 0
948146ed78bSdan  proc max_journal_size {method args} {
949146ed78bSdan    set sz 0
950146ed78bSdan    catch { set sz [file size test.db-journal] }
951146ed78bSdan    if {$sz > $::max_journal} {
952146ed78bSdan      set ::max_journal $sz
953146ed78bSdan    }
954146ed78bSdan    return SQLITE_OK
955146ed78bSdan  }
956146ed78bSdan  execsql {
957146ed78bSdan    PRAGMA journal_mode = DELETE;
958146ed78bSdan    PRAGMA synchronous = NORMAL;
959146ed78bSdan    BEGIN;
960146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
961146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
962146ed78bSdan    COMMIT;
963146ed78bSdan  }
964146ed78bSdan  set ::max_journal
965146ed78bSdan} [expr 2615+[string length [pwd]]]
966146ed78bSdando_test pager1-5.4.2 {
967146ed78bSdan  set ::max_journal 0
968146ed78bSdan  execsql {
969146ed78bSdan    PRAGMA synchronous = full;
970146ed78bSdan    BEGIN;
971146ed78bSdan      DELETE FROM t1 WHERE b = 'Lenin';
972146ed78bSdan      DELETE FROM t2 WHERE b = 'Lenin';
973146ed78bSdan    COMMIT;
974146ed78bSdan  }
975146ed78bSdan  set ::max_journal
976146ed78bSdan} [expr 3111+[string length [pwd]]]
977146ed78bSdandb close
978146ed78bSdantv delete
979146ed78bSdan
980146ed78bSdando_test pager1-5.5.1 {
981146ed78bSdan  sqlite3 db test.db
982146ed78bSdan  execsql {
983146ed78bSdan    ATTACH 'test.db2' AS aux;
984146ed78bSdan    PRAGMA journal_mode = PERSIST;
985146ed78bSdan    CREATE TABLE t3(a, b);
986146ed78bSdan    INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
987146ed78bSdan    UPDATE t3 SET b = randomblob(1500);
988146ed78bSdan  }
989146ed78bSdan  expr [file size test.db-journal] > 15000
990146ed78bSdan} {1}
991146ed78bSdando_test pager1-5.5.2 {
992146ed78bSdan  execsql {
993146ed78bSdan    PRAGMA synchronous = full;
994146ed78bSdan    BEGIN;
995146ed78bSdan      DELETE FROM t1 WHERE b = 'Stalin';
996146ed78bSdan      DELETE FROM t2 WHERE b = 'Stalin';
997146ed78bSdan    COMMIT;
998146ed78bSdan  }
999146ed78bSdan  file size test.db-journal
1000146ed78bSdan} {0}
1001146ed78bSdan
1002146ed78bSdan
1003146ed78bSdan#-------------------------------------------------------------------------
1004146ed78bSdan# The following tests work with "PRAGMA max_page_count"
1005146ed78bSdan#
1006146ed78bSdando_test pager1-6.1 {
1007146ed78bSdan  faultsim_delete_and_reopen
1008146ed78bSdan  execsql {
1009146ed78bSdan    PRAGMA max_page_count = 10;
1010146ed78bSdan    CREATE TABLE t2(a, b);
1011146ed78bSdan    CREATE TABLE t3(a, b);
1012146ed78bSdan    CREATE TABLE t4(a, b);
1013146ed78bSdan    CREATE TABLE t5(a, b);
1014146ed78bSdan    CREATE TABLE t6(a, b);
1015146ed78bSdan    CREATE TABLE t7(a, b);
1016146ed78bSdan    CREATE TABLE t8(a, b);
1017146ed78bSdan    CREATE TABLE t9(a, b);
1018146ed78bSdan    CREATE TABLE t10(a, b);
1019146ed78bSdan  }
1020146ed78bSdan} {10}
1021d353331aSdando_catchsql_test pager1-6.2 {
1022d353331aSdan  CREATE TABLE t11(a, b)
1023146ed78bSdan} {1 {database or disk is full}}
1024d353331aSdando_execsql_test pager1-6.4 { PRAGMA max_page_count      } {10}
1025d353331aSdando_execsql_test pager1-6.5 { PRAGMA max_page_count = 15 } {15}
1026d353331aSdando_execsql_test pager1-6.6 { CREATE TABLE t11(a, b)     } {}
1027d353331aSdando_execsql_test pager1-6.7 {
1028d353331aSdan  BEGIN;
1029d353331aSdan    INSERT INTO t11 VALUES(1, 2);
1030d353331aSdan    PRAGMA max_page_count = 13;
1031d353331aSdan} {13}
1032d353331aSdando_execsql_test pager1-6.8 {
1033d353331aSdan    INSERT INTO t11 VALUES(3, 4);
1034d353331aSdan    PRAGMA max_page_count = 10;
1035d353331aSdan} {11}
1036d353331aSdando_execsql_test pager1-6.9 { COMMIT } {}
1037de4996e2Sdan
1038153eda0aSdan
1039153eda0aSdan#-------------------------------------------------------------------------
1040153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
1041153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE".
1042153eda0aSdan#
1043153eda0aSdan# Each test is specified with 5 variables. As follows:
1044153eda0aSdan#
1045153eda0aSdan#   $tn:  Test Number. Used as part of the [do_test] test names.
1046153eda0aSdan#   $sql: SQL to execute.
1047153eda0aSdan#   $res: Expected result of executing $sql.
1048153eda0aSdan#   $js:  The expected size of the journal file, in bytes, after executing
1049153eda0aSdan#         the SQL script. Or -1 if the journal is not expected to exist.
1050153eda0aSdan#   $ws:  The expected size of the WAL file, in bytes, after executing
1051153eda0aSdan#         the SQL script. Or -1 if the WAL is not expected to exist.
1052153eda0aSdan#
105338e1a279Sdanifcapable wal {
1054153eda0aSdan  faultsim_delete_and_reopen
1055153eda0aSdan  foreach {tn sql res js ws} [subst {
1056153eda0aSdan
1057153eda0aSdan    1  {
1058153eda0aSdan      CREATE TABLE t1(a, b);
1059153eda0aSdan      PRAGMA auto_vacuum=OFF;
1060153eda0aSdan      PRAGMA synchronous=NORMAL;
1061153eda0aSdan      PRAGMA page_size=1024;
1062153eda0aSdan      PRAGMA locking_mode=EXCLUSIVE;
1063153eda0aSdan      PRAGMA journal_mode=TRUNCATE;
1064153eda0aSdan      INSERT INTO t1 VALUES(1, 2);
1065153eda0aSdan    } {exclusive truncate} 0 -1
1066153eda0aSdan
1067153eda0aSdan    2  {
1068153eda0aSdan      BEGIN IMMEDIATE;
1069153eda0aSdan        SELECT * FROM t1;
1070153eda0aSdan      COMMIT;
1071153eda0aSdan    } {1 2} 0 -1
1072153eda0aSdan
1073153eda0aSdan    3  {
1074153eda0aSdan      BEGIN;
1075153eda0aSdan        SELECT * FROM t1;
1076153eda0aSdan      COMMIT;
1077153eda0aSdan    } {1 2} 0 -1
1078153eda0aSdan
1079153eda0aSdan    4  { PRAGMA journal_mode = WAL }    wal    -1 -1
1080153eda0aSdan    5  { INSERT INTO t1 VALUES(3, 4) }  {}     -1 [wal_file_size 1 1024]
1081153eda0aSdan    6  { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
1082153eda0aSdan    7  { INSERT INTO t1 VALUES(5, 6); } {}     -1 [wal_file_size 2 1024]
1083153eda0aSdan
1084153eda0aSdan    8  { PRAGMA journal_mode = TRUNCATE } truncate          0 -1
1085153eda0aSdan    9  { INSERT INTO t1 VALUES(7, 8) }    {}                0 -1
1086153eda0aSdan    10 { SELECT * FROM t1 }               {1 2 3 4 5 6 7 8} 0 -1
1087153eda0aSdan
1088153eda0aSdan  }] {
1089153eda0aSdan    do_execsql_test pager1-7.1.$tn.1 $sql $res
1090153eda0aSdan    catch { set J -1 ; set J [file size test.db-journal] }
1091153eda0aSdan    catch { set W -1 ; set W [file size test.db-wal] }
1092153eda0aSdan    do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
1093153eda0aSdan  }
109438e1a279Sdan}
1095153eda0aSdan
1096d353331aSdan#-------------------------------------------------------------------------
1097d353331aSdan# The following tests, pager1-8.*, test that the special filenames
1098d353331aSdan# ":memory:" and "" open temporary databases.
1099d353331aSdan#
11000e986f51Sdanforeach {tn filename} {
11010e986f51Sdan  1 :memory:
11020e986f51Sdan  2 ""
11030e986f51Sdan} {
11040e986f51Sdan  do_test pager1-8.$tn.1 {
1105153eda0aSdan    faultsim_delete_and_reopen
1106153eda0aSdan    db close
11070e986f51Sdan    sqlite3 db $filename
1108153eda0aSdan    execsql {
1109c8ce3972Sdan      PRAGMA auto_vacuum = 1;
1110153eda0aSdan      CREATE TABLE x1(x);
1111153eda0aSdan      INSERT INTO x1 VALUES('Charles');
1112153eda0aSdan      INSERT INTO x1 VALUES('James');
1113153eda0aSdan      INSERT INTO x1 VALUES('Mary');
1114153eda0aSdan      SELECT * FROM x1;
1115153eda0aSdan    }
1116153eda0aSdan  } {Charles James Mary}
11170e986f51Sdan
11180e986f51Sdan  do_test pager1-8.$tn.2 {
11190e986f51Sdan    sqlite3 db2 $filename
11200e986f51Sdan    catchsql { SELECT * FROM x1 } db2
1121153eda0aSdan  } {1 {no such table: x1}}
1122153eda0aSdan
11230e986f51Sdan  do_execsql_test pager1-8.$tn.3 {
11240e986f51Sdan    BEGIN;
11250e986f51Sdan      INSERT INTO x1 VALUES('William');
11260e986f51Sdan      INSERT INTO x1 VALUES('Anne');
11270e986f51Sdan    ROLLBACK;
11280e986f51Sdan  } {}
11290e986f51Sdan}
11300e986f51Sdan
1131dca321aeSdan#-------------------------------------------------------------------------
1132dca321aeSdan# The next block of tests - pager1-9.* - deal with interactions between
1133dca321aeSdan# the pager and the backup API. Test cases:
1134dca321aeSdan#
1135dca321aeSdan#   pager1-9.1.*: Test that a backup completes successfully even if the
1136dca321aeSdan#                 source db is written to during the backup op.
1137dca321aeSdan#
1138dca321aeSdan#   pager1-9.2.*: Test that a backup completes successfully even if the
1139dca321aeSdan#                 source db is written to and then rolled back during a
1140dca321aeSdan#                 backup operation.
1141dca321aeSdan#
1142dca321aeSdando_test pager1-9.0.1 {
1143dca321aeSdan  faultsim_delete_and_reopen
1144dca321aeSdan  db func a_string a_string
1145dca321aeSdan  execsql {
1146dca321aeSdan    PRAGMA cache_size = 10;
1147dca321aeSdan    BEGIN;
1148dca321aeSdan      CREATE TABLE ab(a, b, UNIQUE(a, b));
1149dca321aeSdan      INSERT INTO ab VALUES( a_string(200), a_string(300) );
1150dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1151dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1152dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1153dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1154dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1155dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1156dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1157dca321aeSdan    COMMIT;
1158dca321aeSdan  }
1159dca321aeSdan} {}
1160dca321aeSdando_test pager1-9.0.2 {
1161dca321aeSdan  sqlite3 db2 test.db2
1162dca321aeSdan  db2 eval { PRAGMA cache_size = 10 }
1163dca321aeSdan  sqlite3_backup B db2 main db main
1164dca321aeSdan  list [B step 10000] [B finish]
1165dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1166dca321aeSdando_test pager1-9.0.3 {
1167dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1168dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1169dca321aeSdan
1170dca321aeSdando_test pager1-9.1.1 {
1171dca321aeSdan  execsql { UPDATE ab SET a = a_string(201) }
1172dca321aeSdan  sqlite3_backup B db2 main db main
1173dca321aeSdan  B step 30
1174dca321aeSdan} {SQLITE_OK}
1175dca321aeSdando_test pager1-9.1.2 {
1176dca321aeSdan  execsql { UPDATE ab SET b = a_string(301) }
1177dca321aeSdan  list [B step 10000] [B finish]
1178dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1179dca321aeSdando_test pager1-9.1.3 {
1180dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1181dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1182dca321aeSdando_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128}
1183dca321aeSdan
1184dca321aeSdando_test pager1-9.2.1 {
1185dca321aeSdan  execsql { UPDATE ab SET a = a_string(202) }
1186dca321aeSdan  sqlite3_backup B db2 main db main
1187dca321aeSdan  B step 30
1188dca321aeSdan} {SQLITE_OK}
1189dca321aeSdando_test pager1-9.2.2 {
1190dca321aeSdan  execsql {
1191dca321aeSdan    BEGIN;
1192dca321aeSdan      UPDATE ab SET b = a_string(301);
1193dca321aeSdan    ROLLBACK;
1194dca321aeSdan  }
1195dca321aeSdan  list [B step 10000] [B finish]
1196dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1197dca321aeSdando_test pager1-9.2.3 {
1198dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1199dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1200dca321aeSdando_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128}
1201ec6ffc1aSdandb close
1202ec6ffc1aSdandb2 close
1203ec6ffc1aSdan
1204ec6ffc1aSdan#-------------------------------------------------------------------------
1205ec6ffc1aSdan# Test that regardless of the value returned by xSectorSize(), the
1206ec6ffc1aSdan# minimum effective sector-size is 512 and the maximum 65536 bytes.
1207ec6ffc1aSdan#
1208ec6ffc1aSdantestvfs tv -default 1
1209ec6ffc1aSdanforeach sectorsize {
1210ec6ffc1aSdan    32   64   128   256   512   1024   2048
1211ec6ffc1aSdan    4096 8192 16384 32768 65536 131072 262144
1212ec6ffc1aSdan} {
1213ec6ffc1aSdan  tv sectorsize $sectorsize
1214ec6ffc1aSdan  set eff $sectorsize
1215ec6ffc1aSdan  if {$sectorsize < 512}   { set eff 512 }
1216ec6ffc1aSdan  if {$sectorsize > 65536} { set eff 65536 }
1217ec6ffc1aSdan
1218ec6ffc1aSdan  do_test pager1-10.$sectorsize {
1219ec6ffc1aSdan    faultsim_delete_and_reopen
1220ec6ffc1aSdan    execsql {
1221ec6ffc1aSdan      PRAGMA journal_mode = PERSIST;
1222ec6ffc1aSdan      PRAGMA page_size = 1024;
1223ec6ffc1aSdan      CREATE TABLE t1(a, b);
1224ec6ffc1aSdan    }
1225ec6ffc1aSdan    file size test.db-journal
1226ec6ffc1aSdan  } [expr $sectorsize > 65536 ? 65536 : $sectorsize]
1227ec6ffc1aSdan}
1228ec6ffc1aSdandb close
1229ec6ffc1aSdantv delete
1230ec6ffc1aSdan
1231ec6ffc1aSdantestvfs tv -default 1
1232ec6ffc1aSdanfaultsim_delete_and_reopen
1233ec6ffc1aSdandb func a_string a_string
1234ec6ffc1aSdando_execsql_test pager1-11.1 {
1235ec6ffc1aSdan  PRAGMA cache_size = 10;
1236ec6ffc1aSdan  BEGIN;
1237ec6ffc1aSdan    CREATE TABLE zz(top PRIMARY KEY);
1238ec6ffc1aSdan    INSERT INTO zz VALUES(a_string(222));
1239ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1240ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1241ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1242ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1243ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1244ec6ffc1aSdan  COMMIT;
1245ec6ffc1aSdan  BEGIN;
1246ec6ffc1aSdan    UPDATE zz SET top = a_string(345);
1247ec6ffc1aSdan} {}
1248ec6ffc1aSdan
1249ec6ffc1aSdanproc lockout {method args} { return SQLITE_IOERR }
1250ec6ffc1aSdantv script lockout
1251ec6ffc1aSdantv filter {xWrite xTruncate xSync}
1252ec6ffc1aSdando_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}}
1253ec6ffc1aSdan
1254ec6ffc1aSdantv script {}
1255ec6ffc1aSdando_test pager1-11.3 {
1256ec6ffc1aSdan  sqlite3 db2 test.db
1257ec6ffc1aSdan  execsql {
1258ec6ffc1aSdan    PRAGMA journal_mode = TRUNCATE;
1259ec6ffc1aSdan    PRAGMA integrity_check;
1260ec6ffc1aSdan  } db2
1261ec6ffc1aSdan} {truncate ok}
1262ec6ffc1aSdando_test pager1-11.4 {
1263ec6ffc1aSdan  db2 close
1264ec6ffc1aSdan  file size test.db-journal
1265ec6ffc1aSdan} {0}
1266ec6ffc1aSdando_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32}
1267f9b4419dSdandb close
1268f9b4419dSdantv delete
1269ec6ffc1aSdan
1270f9b4419dSdan#-------------------------------------------------------------------------
1271f9b4419dSdan# Test "PRAGMA page_size"
1272f9b4419dSdan#
1273f9b4419dSdanforeach pagesize {
1274f9b4419dSdan    512   1024   2048 4096 8192 16384 32768
1275f9b4419dSdan} {
1276f9b4419dSdan  faultsim_delete_and_reopen
1277f9b4419dSdan
1278f9b4419dSdan  do_test pager1-12.$pagesize.1 {
1279f9b4419dSdan    sqlite3 db2 test.db
1280f9b4419dSdan    execsql "
1281f9b4419dSdan      PRAGMA page_size = $pagesize;
1282f9b4419dSdan      CREATE VIEW v AS SELECT * FROM sqlite_master;
1283f9b4419dSdan    " db2
1284f9b4419dSdan    file size test.db
1285f9b4419dSdan  } $pagesize
1286f9b4419dSdan  do_test pager1-12.$pagesize.2 {
1287f9b4419dSdan    sqlite3 db2 test.db
1288f9b4419dSdan    execsql {
1289f9b4419dSdan      SELECT count(*) FROM v;
1290f9b4419dSdan      PRAGMA main.page_size;
1291f9b4419dSdan    } db2
1292f9b4419dSdan  } [list 1 $pagesize]
1293f9b4419dSdan  do_test pager1-12.$pagesize.3 {
1294f9b4419dSdan    execsql {
1295f9b4419dSdan      SELECT count(*) FROM v;
1296f9b4419dSdan      PRAGMA main.page_size;
1297f9b4419dSdan    }
1298f9b4419dSdan  } [list 1 $pagesize]
1299f9b4419dSdan  db2 close
1300f9b4419dSdan}
1301dca321aeSdan
1302d353331aSdan#-------------------------------------------------------------------------
1303d353331aSdan# Test specal "PRAGMA journal_mode=PERSIST" test cases.
1304d353331aSdan#
1305d353331aSdan# pager1-13.1.*: This tests a special case encountered in persistent
1306d353331aSdan#                journal mode: If the journal associated with a transaction
1307d353331aSdan#                is smaller than the journal file (because a previous
1308d353331aSdan#                transaction left a very large non-hot journal file in the
1309d353331aSdan#                file-system), then SQLite has to be careful that there is
1310d353331aSdan#                not a journal-header left over from a previous transaction
1311d353331aSdan#                immediately following the journal content just written.
1312d353331aSdan#                If there is, and the process crashes so that the journal
1313d353331aSdan#                becomes a hot-journal and must be rolled back by another
1314d353331aSdan#                process, there is a danger that the other process may roll
1315d353331aSdan#                back the aborted transaction, then continue copying data
1316d353331aSdan#                from an older transaction from the remainder of the journal.
1317d353331aSdan#                See the syncJournal() function for details.
1318d353331aSdan#
1319d353331aSdan# pager1-13.2.*: Same test as the previous. This time, throw an index into
1320d353331aSdan#                the mix to make the integrity-check more likely to catch
1321d353331aSdan#                errors.
1322d353331aSdan#
1323d353331aSdantestvfs tv -default 1
1324d353331aSdantv script xSyncCb
1325d353331aSdantv filter xSync
1326d353331aSdanproc xSyncCb {method filename args} {
1327d353331aSdan  set t [file tail $filename]
1328d353331aSdan  if {$t == "test.db"} faultsim_save
1329d353331aSdan  return SQLITE_OK
1330d353331aSdan}
1331d353331aSdanfaultsim_delete_and_reopen
1332d353331aSdandb func a_string a_string
1333e91a54e1Sdan
1334d353331aSdan# The UPDATE statement at the end of this test case creates a really big
1335d353331aSdan# journal. Since the cache-size is only 10 pages, the journal contains
1336d353331aSdan# frequent journal headers.
1337d353331aSdan#
1338d353331aSdando_execsql_test pager1-13.1.1 {
1339d353331aSdan  PRAGMA page_size = 1024;
1340d353331aSdan  PRAGMA journal_mode = PERSIST;
1341d353331aSdan  PRAGMA cache_size = 10;
1342d353331aSdan  BEGIN;
1343d353331aSdan    CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
1344d353331aSdan    INSERT INTO t1 VALUES(NULL, a_string(400));
1345d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   2 */
1346d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   4 */
1347d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   8 */
1348d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  16 */
1349d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  32 */
1350d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  64 */
1351d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /* 128 */
1352d353331aSdan  COMMIT;
1353d353331aSdan  UPDATE t1 SET b = a_string(400);
1354d353331aSdan} {persist}
1355d353331aSdan
1356d353331aSdan# Run transactions of increasing sizes. Eventually, one (or more than one)
1357d353331aSdan# of these will write just enough content that one of the old headers created
1358d353331aSdan# by the transaction in the block above lies immediately after the content
1359d353331aSdan# journalled by the current transaction.
1360d353331aSdan#
1361d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1362d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.1 {
1363d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1364d353331aSdan  } {}
1365d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.2 { PRAGMA integrity_check } {ok}
1366d353331aSdan
1367d353331aSdan  # Try to access the snapshot of the file-system.
1368d353331aSdan  #
1369d353331aSdan  sqlite3 db2 sv_test.db
1370d353331aSdan  do_test pager1-13.1.2.$nUp.3 {
1371d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1372d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1373d353331aSdan  do_test pager1-13.1.2.$nUp.4 {
1374d353331aSdan    execsql { PRAGMA integrity_check } db2
1375d353331aSdan  } {ok}
1376d353331aSdan  db2 close
1377d353331aSdan}
1378d353331aSdan
1379d353331aSdan# Same test as above. But this time with an index on the table.
1380d353331aSdan#
1381d353331aSdando_execsql_test pager1-13.2.1 {
1382d353331aSdan  CREATE INDEX i1 ON t1(b);
1383d353331aSdan  UPDATE t1 SET b = a_string(400);
1384d353331aSdan} {}
1385d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1386d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.1 {
1387d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1388d353331aSdan  } {}
1389d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.2 { PRAGMA integrity_check } {ok}
1390d353331aSdan  sqlite3 db2 sv_test.db
1391d353331aSdan  do_test pager1-13.2.2.$nUp.3 {
1392d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1393d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1394d353331aSdan  do_test pager1-13.2.2.$nUp.4 {
1395d353331aSdan    execsql { PRAGMA integrity_check } db2
1396d353331aSdan  } {ok}
1397d353331aSdan  db2 close
1398d353331aSdan}
1399d353331aSdan
1400d353331aSdandb close
1401d353331aSdantv delete
1402d353331aSdan
1403d353331aSdan#-------------------------------------------------------------------------
1404d353331aSdan# Test specal "PRAGMA journal_mode=OFF" test cases.
1405d353331aSdan#
1406d353331aSdanfaultsim_delete_and_reopen
1407d353331aSdando_execsql_test pager1-14.1.1 {
1408d353331aSdan  PRAGMA journal_mode = OFF;
1409d353331aSdan  CREATE TABLE t1(a, b);
1410d353331aSdan  BEGIN;
1411d353331aSdan    INSERT INTO t1 VALUES(1, 2);
1412d353331aSdan  COMMIT;
1413d353331aSdan  SELECT * FROM t1;
1414d353331aSdan} {off 1 2}
1415d353331aSdando_catchsql_test pager1-14.1.2 {
1416d353331aSdan  BEGIN;
1417d353331aSdan    INSERT INTO t1 VALUES(3, 4);
1418d353331aSdan  ROLLBACK;
1419d353331aSdan} {0 {}}
1420d353331aSdando_execsql_test pager1-14.1.3 {
1421d353331aSdan  SELECT * FROM t1;
1422d353331aSdan} {1 2 3 4}
1423d353331aSdando_catchsql_test pager1-14.1.4 {
1424d353331aSdan  BEGIN;
1425d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1426d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1427d353331aSdan} {1 {PRIMARY KEY must be unique}}
1428d353331aSdando_execsql_test pager1-14.1.5 {
1429d353331aSdan  COMMIT;
1430d353331aSdan  SELECT * FROM t1;
1431d353331aSdan} {1 2 3 4 2 2 4 4}
1432d353331aSdan
1433c8ce3972Sdan#-------------------------------------------------------------------------
1434c8ce3972Sdan# Test opening and closing the pager sub-system with different values
1435c8ce3972Sdan# for the sqlite3_vfs.szOsFile variable.
1436c8ce3972Sdan#
1437c8ce3972Sdanfaultsim_delete_and_reopen
1438c8ce3972Sdando_execsql_test pager1-15.0 {
1439c8ce3972Sdan  CREATE TABLE tx(y, z);
1440c8ce3972Sdan  INSERT INTO tx VALUES('Ayutthaya', 'Beijing');
1441c8ce3972Sdan  INSERT INTO tx VALUES('London', 'Tokyo');
1442c8ce3972Sdan} {}
1443c8ce3972Sdandb close
1444c8ce3972Sdanfor {set i 0} {$i<513} {incr i 3} {
1445c8ce3972Sdan  testvfs tv -default 1 -szosfile $i
1446c8ce3972Sdan  sqlite3 db test.db
1447c8ce3972Sdan  do_execsql_test pager1-15.$i.1 {
1448c8ce3972Sdan    SELECT * FROM tx;
1449c8ce3972Sdan  } {Ayutthaya Beijing London Tokyo}
1450c8ce3972Sdan  db close
1451c8ce3972Sdan  tv delete
1452c8ce3972Sdan}
1453c8ce3972Sdan
1454c8ce3972Sdan#-------------------------------------------------------------------------
1455c8ce3972Sdan# Check that it is not possible to open a database file if the full path
1456c8ce3972Sdan# to the associated journal file will be longer than sqlite3_vfs.mxPathname.
1457c8ce3972Sdan#
1458c8ce3972Sdantestvfs tv -default 1
1459c8ce3972Sdantv script xOpenCb
1460c8ce3972Sdantv filter xOpen
1461c8ce3972Sdanproc xOpenCb {method filename} {
1462c8ce3972Sdan  set ::file_len [string length $filename]
1463c8ce3972Sdan}
1464c8ce3972Sdansqlite3 db test.db
1465c8ce3972Sdandb close
1466c8ce3972Sdantv delete
1467c8ce3972Sdan
1468c8ce3972Sdanfor {set ii [expr $::file_len-5]} {$ii < [expr $::file_len+20]} {incr ii} {
1469c8ce3972Sdan  testvfs tv -default 1 -mxpathname $ii
1470c8ce3972Sdan
1471c8ce3972Sdan  # The length of the full path to file "test.db-journal" is ($::file_len+8).
1472c8ce3972Sdan  # If the configured sqlite3_vfs.mxPathname value greater than or equal to
1473c8ce3972Sdan  # this, then the file can be opened. Otherwise, it cannot.
1474c8ce3972Sdan  #
1475c8ce3972Sdan  if {$ii >= [expr $::file_len+8]} {
1476c8ce3972Sdan    set res {0 {}}
1477c8ce3972Sdan  } else {
1478c8ce3972Sdan    set res {1 {unable to open database file}}
1479c8ce3972Sdan  }
1480c8ce3972Sdan
1481c8ce3972Sdan  do_test pager1-16.1.$ii {
1482c8ce3972Sdan    list [catch { sqlite3 db test.db } msg] $msg
1483c8ce3972Sdan  } $res
1484c8ce3972Sdan
1485c8ce3972Sdan  catch {db close}
1486c8ce3972Sdan  tv delete
1487c8ce3972Sdan}
1488c8ce3972Sdan
1489c8ce3972Sdan#-------------------------------------------------------------------------
1490c8ce3972Sdan# Test "PRAGMA omit_readlock".
1491c8ce3972Sdan#
1492c8ce3972Sdan#   pager1-17.$tn.1.*: Test that if a second connection has an open
1493c8ce3972Sdan#                      read-transaction, it is not usually possible to write
1494c8ce3972Sdan#                      the database.
1495c8ce3972Sdan#
1496c8ce3972Sdan#   pager1-17.$tn.2.*: Test that if the second connection was opened with
1497c8ce3972Sdan#                      the SQLITE_OPEN_READONLY flag, and
1498c8ce3972Sdan#                      "PRAGMA omit_readlock = 1" is executed before attaching
1499c8ce3972Sdan#                      the database and opening a read-transaction on it, it is
1500c8ce3972Sdan#                      possible to write the db.
1501c8ce3972Sdan#
1502c8ce3972Sdan#   pager1-17.$tn.3.*: Test that if the second connection was *not* opened with
1503c8ce3972Sdan#                      the SQLITE_OPEN_READONLY flag, executing
1504c8ce3972Sdan#                      "PRAGMA omit_readlock = 1" has no effect.
1505c8ce3972Sdan#
1506c8ce3972Sdando_multiclient_test tn {
1507c8ce3972Sdan  do_test pager1-17.$tn.1.1 {
1508c8ce3972Sdan    sql1 {
1509c8ce3972Sdan      CREATE TABLE t1(a, b);
1510c8ce3972Sdan      INSERT INTO t1 VALUES(1, 2);
1511c8ce3972Sdan    }
1512c8ce3972Sdan    sql2 {
1513c8ce3972Sdan      BEGIN;
1514c8ce3972Sdan      SELECT * FROM t1;
1515c8ce3972Sdan    }
1516c8ce3972Sdan  } {1 2}
1517c8ce3972Sdan  do_test pager1-17.$tn.1.2 {
1518c8ce3972Sdan    csql1 { INSERT INTO t1 VALUES(3, 4) }
1519c8ce3972Sdan  } {1 {database is locked}}
1520c8ce3972Sdan  do_test pager1-17.$tn.1.3 {
1521c8ce3972Sdan    sql2 { COMMIT }
1522c8ce3972Sdan    sql1 { INSERT INTO t1 VALUES(3, 4) }
1523c8ce3972Sdan  } {}
1524c8ce3972Sdan
1525c8ce3972Sdan  do_test pager1-17.$tn.2.1 {
1526c8ce3972Sdan    code2 {
1527c8ce3972Sdan      db2 close
1528c8ce3972Sdan      sqlite3 db2 :memory: -readonly 1
1529c8ce3972Sdan    }
1530c8ce3972Sdan    sql2 {
1531c8ce3972Sdan      PRAGMA omit_readlock = 1;
1532c8ce3972Sdan      ATTACH 'test.db' AS two;
1533c8ce3972Sdan      BEGIN;
1534c8ce3972Sdan      SELECT * FROM t1;
1535c8ce3972Sdan    }
1536c8ce3972Sdan  } {1 2 3 4}
1537c8ce3972Sdan  do_test pager1-17.$tn.2.2 { sql1 "INSERT INTO t1 VALUES(5, 6)" } {}
1538c8ce3972Sdan  do_test pager1-17.$tn.2.3 { sql2 "SELECT * FROM t1" }            {1 2 3 4}
1539c8ce3972Sdan  do_test pager1-17.$tn.2.4 { sql2 "COMMIT ; SELECT * FROM t1" }   {1 2 3 4 5 6}
1540c8ce3972Sdan
1541c8ce3972Sdan  do_test pager1-17.$tn.3.1 {
1542c8ce3972Sdan    code2 {
1543c8ce3972Sdan      db2 close
1544c8ce3972Sdan      sqlite3 db2 :memory:
1545c8ce3972Sdan    }
1546c8ce3972Sdan    sql2 {
1547c8ce3972Sdan      PRAGMA omit_readlock = 1;
1548c8ce3972Sdan      ATTACH 'test.db' AS two;
1549c8ce3972Sdan      BEGIN;
1550c8ce3972Sdan      SELECT * FROM t1;
1551c8ce3972Sdan    }
1552c8ce3972Sdan  } {1 2 3 4 5 6}
1553c8ce3972Sdan  do_test pager1-17.$tn.3.2 {
1554c8ce3972Sdan    csql1 { INSERT INTO t1 VALUES(3, 4) }
1555c8ce3972Sdan  } {1 {database is locked}}
1556c8ce3972Sdan  do_test pager1-17.$tn.3.3 { sql2 COMMIT } {}
1557c8ce3972Sdan}
1558c8ce3972Sdan
1559c8ce3972Sdan#-------------------------------------------------------------------------
1560c8ce3972Sdan# Test the pagers response to the b-tree layer requesting illegal page
1561c8ce3972Sdan# numbers:
1562c8ce3972Sdan#
1563c8ce3972Sdan#   + The locking page,
1564c8ce3972Sdan#   + Page 0,
1565c8ce3972Sdan#   + A page with a page number greater than (2^31-1).
1566c8ce3972Sdan#
1567c8ce3972Sdando_test pager1-18.1 {
1568c8ce3972Sdan  faultsim_delete_and_reopen
1569c8ce3972Sdan  db func a_string a_string
1570c8ce3972Sdan  execsql {
1571c8ce3972Sdan    PRAGMA page_size = 1024;
1572c8ce3972Sdan    CREATE TABLE t1(a, b);
1573c8ce3972Sdan    INSERT INTO t1 VALUES(a_string(500), a_string(200));
1574c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1575c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1576c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1577c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1578c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1579c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1580c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1581c8ce3972Sdan  }
1582c8ce3972Sdan} {}
1583c8ce3972Sdando_test pager1-18.2 {
1584c8ce3972Sdan  set root [db one "SELECT rootpage FROM sqlite_master"]
1585c8ce3972Sdan  set lockingpage [expr (0x10000/1024) + 1]
1586c8ce3972Sdan  execsql {
1587c8ce3972Sdan    PRAGMA writable_schema = 1;
1588c8ce3972Sdan    UPDATE sqlite_master SET rootpage = $lockingpage;
1589c8ce3972Sdan  }
1590c8ce3972Sdan  sqlite3 db2 test.db
1591c8ce3972Sdan  catchsql { SELECT count(*) FROM t1 } db2
1592c8ce3972Sdan} {1 {database disk image is malformed}}
1593c8ce3972Sdandb2 close
1594c8ce3972Sdando_test pager1-18.3 {
1595c8ce3972Sdan  execsql {
1596c8ce3972Sdan    CREATE TABLE t2(x);
1597c8ce3972Sdan    INSERT INTO t2 VALUES(a_string(5000));
1598c8ce3972Sdan  }
1599c8ce3972Sdan  set pgno [expr ([file size test.db] / 1024)-2]
1600c8ce3972Sdan  hexio_write test.db [expr ($pgno-1)*1024] 00000000
1601c8ce3972Sdan  sqlite3 db2 test.db
1602c8ce3972Sdan  catchsql { SELECT length(x) FROM t2 } db2
1603c8ce3972Sdan} {1 {database disk image is malformed}}
1604c8ce3972Sdandb2 close
1605c8ce3972Sdando_test pager1-18.4 {
1606c8ce3972Sdan  hexio_write test.db [expr ($pgno-1)*1024] 90000000
1607c8ce3972Sdan  sqlite3 db2 test.db
1608c8ce3972Sdan  catchsql { SELECT length(x) FROM t2 } db2
1609c8ce3972Sdan} {1 {database disk image is malformed}}
1610c8ce3972Sdandb2 close
1611c8ce3972Sdando_test pager1-18.5 {
1612c8ce3972Sdan  sqlite3 db ""
1613c8ce3972Sdan  execsql {
1614c8ce3972Sdan    CREATE TABLE t1(a, b);
1615c8ce3972Sdan    CREATE TABLE t2(a, b);
1616c8ce3972Sdan    PRAGMA writable_schema = 1;
1617c8ce3972Sdan    UPDATE sqlite_master SET rootpage=5 WHERE tbl_name = 't1';
1618c8ce3972Sdan    PRAGMA writable_schema = 0;
1619c8ce3972Sdan    ALTER TABLE t1 RENAME TO x1;
1620c8ce3972Sdan  }
1621c8ce3972Sdan  catchsql { SELECT * FROM x1 }
1622ba3cbf3dSdan} {1 {database disk image is malformed}}
1623c8ce3972Sdandb close
1624c8ce3972Sdan
1625ba3cbf3dSdando_test pager1-18.6 {
1626ba3cbf3dSdan  faultsim_delete_and_reopen
1627ba3cbf3dSdan  db func a_string a_string
1628ba3cbf3dSdan  execsql {
1629ba3cbf3dSdan    PRAGMA page_size = 1024;
1630ba3cbf3dSdan    CREATE TABLE t1(x);
1631ba3cbf3dSdan    INSERT INTO t1 VALUES(a_string(800));
1632ba3cbf3dSdan    INSERT INTO t1 VALUES(a_string(800));
1633ba3cbf3dSdan  }
1634ba3cbf3dSdan
1635ba3cbf3dSdan  set root [db one "SELECT rootpage FROM sqlite_master"]
1636ba3cbf3dSdan  db close
1637ba3cbf3dSdan
1638ba3cbf3dSdan  hexio_write test.db [expr ($root-1)*1024 + 8] 00000000
1639ba3cbf3dSdan  sqlite3 db test.db
1640ba3cbf3dSdan  catchsql { SELECT length(x) FROM t1 }
1641ba3cbf3dSdan} {1 {database disk image is malformed}}
1642ba3cbf3dSdan
1643ba3cbf3dSdando_test pager1-19.1 {
1644ba3cbf3dSdan  sqlite3 db ""
1645ba3cbf3dSdan  db func a_string a_string
1646ba3cbf3dSdan  execsql {
1647ba3cbf3dSdan    PRAGMA page_size = 512;
1648ba3cbf3dSdan    PRAGMA auto_vacuum = 1;
1649ba3cbf3dSdan    CREATE TABLE t1(aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an,
1650ba3cbf3dSdan                    ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn,
1651ba3cbf3dSdan                    ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn,
1652ba3cbf3dSdan                    da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn,
1653ba3cbf3dSdan                    ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en,
1654ba3cbf3dSdan                    fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk, fl, fm, fn,
1655ba3cbf3dSdan                    ga, gb, gc, gd, ge, gf, gg, gh, gi, gj, gk, gl, gm, gn,
1656ba3cbf3dSdan                    ha, hb, hc, hd, he, hf, hg, hh, hi, hj, hk, hl, hm, hn,
1657ba3cbf3dSdan                    ia, ib, ic, id, ie, if, ig, ih, ii, ij, ik, il, im, ix,
1658ba3cbf3dSdan                    ja, jb, jc, jd, je, jf, jg, jh, ji, jj, jk, jl, jm, jn,
1659ba3cbf3dSdan                    ka, kb, kc, kd, ke, kf, kg, kh, ki, kj, kk, kl, km, kn,
1660ba3cbf3dSdan                    la, lb, lc, ld, le, lf, lg, lh, li, lj, lk, ll, lm, ln,
1661ba3cbf3dSdan                    ma, mb, mc, md, me, mf, mg, mh, mi, mj, mk, ml, mm, mn
1662ba3cbf3dSdan    );
1663ba3cbf3dSdan    CREATE TABLE t2(aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an,
1664ba3cbf3dSdan                    ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn,
1665ba3cbf3dSdan                    ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn,
1666ba3cbf3dSdan                    da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn,
1667ba3cbf3dSdan                    ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en,
1668ba3cbf3dSdan                    fa, fb, fc, fd, fe, ff, fg, fh, fi, fj, fk, fl, fm, fn,
1669ba3cbf3dSdan                    ga, gb, gc, gd, ge, gf, gg, gh, gi, gj, gk, gl, gm, gn,
1670ba3cbf3dSdan                    ha, hb, hc, hd, he, hf, hg, hh, hi, hj, hk, hl, hm, hn,
1671ba3cbf3dSdan                    ia, ib, ic, id, ie, if, ig, ih, ii, ij, ik, il, im, ix,
1672ba3cbf3dSdan                    ja, jb, jc, jd, je, jf, jg, jh, ji, jj, jk, jl, jm, jn,
1673ba3cbf3dSdan                    ka, kb, kc, kd, ke, kf, kg, kh, ki, kj, kk, kl, km, kn,
1674ba3cbf3dSdan                    la, lb, lc, ld, le, lf, lg, lh, li, lj, lk, ll, lm, ln,
1675ba3cbf3dSdan                    ma, mb, mc, md, me, mf, mg, mh, mi, mj, mk, ml, mm, mn
1676ba3cbf3dSdan    );
1677ba3cbf3dSdan    INSERT INTO t1(aa) VALUES( a_string(100000) );
1678ba3cbf3dSdan    INSERT INTO t2(aa) VALUES( a_string(100000) );
1679ba3cbf3dSdan    VACUUM;
1680ba3cbf3dSdan  }
1681ba3cbf3dSdan} {}
1682ba3cbf3dSdan
1683*6b63ab47Sdan#-------------------------------------------------------------------------
1684*6b63ab47Sdan# Test a couple of special cases that come up while committing
1685*6b63ab47Sdan# transactions:
1686*6b63ab47Sdan#
1687*6b63ab47Sdan#   pager1-20.1.*: Committing an in-memory database transaction when the
1688*6b63ab47Sdan#                  database has not been modified at all.
1689*6b63ab47Sdan#
1690*6b63ab47Sdan#   pager1-20.2.*: As above, but with a normal db in exclusive-locking mode.
1691*6b63ab47Sdan#
1692*6b63ab47Sdan#   pager1-20.3.*: Committing a transaction in WAL mode where the database has
1693*6b63ab47Sdan#                  been modified, but all dirty pages have been flushed to
1694*6b63ab47Sdan#                  disk before the commit.
1695*6b63ab47Sdan#
1696*6b63ab47Sdando_test pager1-20.1.1 {
1697*6b63ab47Sdan  catch {db close}
1698*6b63ab47Sdan  sqlite3 db :memory:
1699*6b63ab47Sdan  execsql {
1700*6b63ab47Sdan    CREATE TABLE one(two, three);
1701*6b63ab47Sdan    INSERT INTO one VALUES('a', 'b');
1702*6b63ab47Sdan  }
1703*6b63ab47Sdan} {}
1704*6b63ab47Sdando_test pager1-20.1.2 {
1705*6b63ab47Sdan  execsql {
1706*6b63ab47Sdan    BEGIN EXCLUSIVE;
1707*6b63ab47Sdan    COMMIT;
1708*6b63ab47Sdan  }
1709*6b63ab47Sdan} {}
1710*6b63ab47Sdan
1711*6b63ab47Sdando_test pager1-20.2.1 {
1712*6b63ab47Sdan  faultsim_delete_and_reopen
1713*6b63ab47Sdan  execsql {
1714*6b63ab47Sdan    PRAGMA locking_mode = exclusive;
1715*6b63ab47Sdan    PRAGMA journal_mode = persist;
1716*6b63ab47Sdan    CREATE TABLE one(two, three);
1717*6b63ab47Sdan    INSERT INTO one VALUES('a', 'b');
1718*6b63ab47Sdan  }
1719*6b63ab47Sdan} {exclusive persist}
1720*6b63ab47Sdando_test pager1-20.2.2 {
1721*6b63ab47Sdan  execsql {
1722*6b63ab47Sdan    BEGIN EXCLUSIVE;
1723*6b63ab47Sdan    COMMIT;
1724*6b63ab47Sdan  }
1725*6b63ab47Sdan} {}
1726*6b63ab47Sdan
1727*6b63ab47Sdando_test pager1-20.3.1 {
1728*6b63ab47Sdan  faultsim_delete_and_reopen
1729*6b63ab47Sdan  db func a_string a_string
1730*6b63ab47Sdan  execsql {
1731*6b63ab47Sdan    PRAGMA cache_size = 10;
1732*6b63ab47Sdan    PRAGMA journal_mode = wal;
1733*6b63ab47Sdan    BEGIN;
1734*6b63ab47Sdan      CREATE TABLE t1(x);
1735*6b63ab47Sdan      CREATE TABLE t2(y);
1736*6b63ab47Sdan      INSERT INTO t1 VALUES(a_string(800));
1737*6b63ab47Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1;         /*   2 */
1738*6b63ab47Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1;         /*   4 */
1739*6b63ab47Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1;         /*   8 */
1740*6b63ab47Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1;         /*  16 */
1741*6b63ab47Sdan      INSERT INTO t1 SELECT a_string(800) FROM t1;         /*  32 */
1742*6b63ab47Sdan    COMMIT;
1743*6b63ab47Sdan  }
1744*6b63ab47Sdan} {wal}
1745*6b63ab47Sdando_test pager1-20.3.2 {
1746*6b63ab47Sdan  proc recursive_select {id} {
1747*6b63ab47Sdan    db eval {SELECT rowid, x FROM t1 WHERE rowid = ($id-1)} {
1748*6b63ab47Sdan      recursive_select $rowid
1749*6b63ab47Sdan    }
1750*6b63ab47Sdan  }
1751*6b63ab47Sdan  execsql {
1752*6b63ab47Sdan    BEGIN;
1753*6b63ab47Sdan    INSERT INTO t2 VALUES('xxxx');
1754*6b63ab47Sdan  }
1755*6b63ab47Sdan  recursive_select 32
1756*6b63ab47Sdan  execsql COMMIT
1757*6b63ab47Sdan} {}
1758*6b63ab47Sdan
1759*6b63ab47Sdan
1760*6b63ab47Sdan
1761d353331aSdanfinish_test
1762c8ce3972Sdan
1763