xref: /sqlite-3.40.0/test/pager1.test (revision c8ce3972)
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#
52*c8ce3972Sdan# pager1-15.*: Varying sqlite3_vfs.szOsFile
53*c8ce3972Sdan#
54*c8ce3972Sdan# pager1-16.*: Varying sqlite3_vfs.mxPathname
55*c8ce3972Sdan#
56*c8ce3972Sdan# pager1-17.*: Tests related to "PRAGMA omit_readlock"
57*c8ce3972Sdan#
58*c8ce3972Sdan# pager1-18.*: Test that the pager layer responds correctly if the b-tree
59*c8ce3972Sdan#              requests an invalid page number (due to db corruption).
60*c8ce3972Sdan#
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  }
284273f3f0dSdan} {
285273f3f0dSdan  do_test pager1-3.$tn.1 {
286273f3f0dSdan    eval $tcl
287dca321aeSdan    faultsim_delete_and_reopen
288dca321aeSdan    db func a_string a_string
289273f3f0dSdan    execsql $sql
290dca321aeSdan    execsql {
291dca321aeSdan      PRAGMA auto_vacuum = 2;
292dca321aeSdan      PRAGMA cache_size = 10;
293dca321aeSdan      CREATE TABLE z(x INTEGER PRIMARY KEY, y);
294dca321aeSdan      BEGIN;
295dca321aeSdan        INSERT INTO z VALUES(NULL, a_string(800));
296dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   2
297dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   4
298dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   8
299dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  16
300dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  32
301dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  64
302dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 128
303dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 256
304dca321aeSdan      COMMIT;
305dca321aeSdan    }
306dca321aeSdan    execsql { PRAGMA auto_vacuum }
307dca321aeSdan  } {2}
308273f3f0dSdan  do_execsql_test pager1-3.$tn.2 {
309dca321aeSdan    BEGIN;
310dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
311dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
312dca321aeSdan      SAVEPOINT one;
313273f3f0dSdan        UPDATE z SET y = NULL WHERE x>256;
314dca321aeSdan        PRAGMA incremental_vacuum;
315dca321aeSdan        SELECT count(*) FROM z WHERE x < 100;
316dca321aeSdan      ROLLBACK TO one;
317dca321aeSdan    COMMIT;
318dca321aeSdan  } {99}
319dca321aeSdan
320273f3f0dSdan  do_execsql_test pager1-3.$tn.3 {
321273f3f0dSdan    BEGIN;
322273f3f0dSdan      SAVEPOINT one;
323273f3f0dSdan        UPDATE z SET y = y||x;
324273f3f0dSdan      ROLLBACK TO one;
325273f3f0dSdan    COMMIT;
326273f3f0dSdan    SELECT count(*) FROM z;
327273f3f0dSdan  } {258}
328273f3f0dSdan
329273f3f0dSdan  do_execsql_test pager1-3.$tn.4 {
330273f3f0dSdan    SAVEPOINT one;
331273f3f0dSdan      UPDATE z SET y = y||x;
332273f3f0dSdan    ROLLBACK TO one;
333273f3f0dSdan  } {}
334273f3f0dSdan  do_execsql_test pager1-3.$tn.5 {
335273f3f0dSdan    SELECT count(*) FROM z;
336273f3f0dSdan    RELEASE one;
337273f3f0dSdan    PRAGMA integrity_check;
338273f3f0dSdan  } {258 ok}
339273f3f0dSdan
340273f3f0dSdan  db close
341273f3f0dSdan  catch { tv delete }
342273f3f0dSdan}
343273f3f0dSdan
34453f04f3bSdan#-------------------------------------------------------------------------
34553f04f3bSdan# Hot journal rollback related test cases.
34653f04f3bSdan#
34753f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid
34853f04f3bSdan#               journal files.
34953f04f3bSdan#
35053f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a
35153f04f3bSdan#               hot-journal file appears to be corrupt (checksum does not
35253f04f3bSdan#               compute) the associated journal is rolled back (and no
35353f04f3bSdan#               xAccess() call to check for the presence of any master
35453f04f3bSdan#               journal file is made).
35553f04f3bSdan#
356de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
357de4996e2Sdan#               page-size or sector-size in the journal header appear to
358de4996e2Sdan#               be invalid (too large, too small or not a power of 2).
359de4996e2Sdan#
360e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master
361e08341c6Sdan#               journal pointer generated in various "PRAGMA synchronous"
362e08341c6Sdan#               modes.
363e08341c6Sdan#
364e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
365e08341c6Sdan#               journal-record for which the checksum fails.
366e08341c6Sdan#
367ec6ffc1aSdan# pager1.4.6.*: Test that when rolling back a hot-journal that contains a
368ec6ffc1aSdan#               master journal pointer, the master journal file is deleted
369ec6ffc1aSdan#               after all the hot-journals that refer to it are deleted.
370ec6ffc1aSdan#
371*c8ce3972Sdan# pager1.4.7.*: Test that if a hot-journal file exists but a client can
372*c8ce3972Sdan#               open it for reading only, the database cannot be accessed and
373*c8ce3972Sdan#               SQLITE_CANTOPEN is returned.
374*c8ce3972Sdan#
37553f04f3bSdando_test pager1.4.1.1 {
37653f04f3bSdan  faultsim_delete_and_reopen
37753f04f3bSdan  execsql {
37853f04f3bSdan    CREATE TABLE x(y, z);
37953f04f3bSdan    INSERT INTO x VALUES(1, 2);
38053f04f3bSdan  }
38153f04f3bSdan  set fd [open test.db-journal w]
38253f04f3bSdan  puts -nonewline $fd "helloworld"
38353f04f3bSdan  close $fd
38453f04f3bSdan  file exists test.db-journal
38553f04f3bSdan} {1}
38653f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
38753f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0}
38853f04f3bSdan
38953f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite
39053f04f3bSdan# deletes the master-journal to commit a multi-file transaction.
39153f04f3bSdan#
39253f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
39353f04f3bSdan# up the file system to contain two databases, two hot-journal files and
39453f04f3bSdan# a master-journal.
39553f04f3bSdan#
39653f04f3bSdando_test pager1.4.2.1 {
39753f04f3bSdan  testvfs tstvfs -default 1
39853f04f3bSdan  tstvfs filter xDelete
39953f04f3bSdan  tstvfs script xDeleteCallback
40053f04f3bSdan  proc xDeleteCallback {method file args} {
40153f04f3bSdan    set file [file tail $file]
40253f04f3bSdan    if { [string match *mj* $file] } { faultsim_save }
40353f04f3bSdan  }
40453f04f3bSdan  faultsim_delete_and_reopen
40553f04f3bSdan  db func a_string a_string
40653f04f3bSdan  execsql {
40753f04f3bSdan    ATTACH 'test.db2' AS aux;
40853f04f3bSdan    PRAGMA journal_mode = DELETE;
40953f04f3bSdan    PRAGMA main.cache_size = 10;
41053f04f3bSdan    PRAGMA aux.cache_size = 10;
41153f04f3bSdan    CREATE TABLE t1(a UNIQUE, b UNIQUE);
41253f04f3bSdan    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
41353f04f3bSdan    INSERT INTO t1 VALUES(a_string(200), a_string(300));
41453f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
41553f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
41653f04f3bSdan    INSERT INTO t2 SELECT * FROM t1;
41753f04f3bSdan    BEGIN;
41853f04f3bSdan      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
41953f04f3bSdan      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
42053f04f3bSdan      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
42153f04f3bSdan      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
42253f04f3bSdan      REPLACE INTO t2 SELECT * FROM t1;
42353f04f3bSdan    COMMIT;
42453f04f3bSdan  }
42553f04f3bSdan  db close
42653f04f3bSdan  tstvfs delete
42753f04f3bSdan} {}
42853f04f3bSdando_test pager1.4.2.2 {
42953f04f3bSdan  faultsim_restore_and_reopen
43053f04f3bSdan  execsql {
43153f04f3bSdan    SELECT count(*) FROM t1;
43253f04f3bSdan    PRAGMA integrity_check;
43353f04f3bSdan  }
43453f04f3bSdan} {4 ok}
43553f04f3bSdando_test pager1.4.2.3 {
43653f04f3bSdan  faultsim_restore_and_reopen
43753f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
43853f04f3bSdan  execsql {
43953f04f3bSdan    SELECT count(*) FROM t1;
44053f04f3bSdan    PRAGMA integrity_check;
44153f04f3bSdan  }
44253f04f3bSdan} {64 ok}
44353f04f3bSdando_test pager1.4.2.4 {
44453f04f3bSdan  faultsim_restore_and_reopen
44553f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
44653f04f3bSdan  execsql {
44753f04f3bSdan    SELECT count(*) FROM t1;
44853f04f3bSdan    PRAGMA integrity_check;
44953f04f3bSdan  }
45053f04f3bSdan} {4 ok}
45153f04f3bSdando_test pager1.4.2.5 {
45253f04f3bSdan  faultsim_restore_and_reopen
45353f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
45453f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
45553f04f3bSdan  execsql {
45653f04f3bSdan    SELECT count(*) FROM t1;
45753f04f3bSdan    PRAGMA integrity_check;
45853f04f3bSdan  }
45953f04f3bSdan} {4 ok}
46053f04f3bSdan
461de4996e2Sdando_test pager1.4.3.1 {
462de4996e2Sdan  testvfs tstvfs -default 1
463de4996e2Sdan  tstvfs filter xSync
464de4996e2Sdan  tstvfs script xSyncCallback
465de4996e2Sdan  proc xSyncCallback {method file args} {
466de4996e2Sdan    set file [file tail $file]
467de4996e2Sdan    if { 0==[string match *journal $file] } { faultsim_save }
468de4996e2Sdan  }
469de4996e2Sdan  faultsim_delete_and_reopen
470de4996e2Sdan  execsql {
471de4996e2Sdan    PRAGMA journal_mode = DELETE;
472de4996e2Sdan    CREATE TABLE t1(a, b);
473de4996e2Sdan    INSERT INTO t1 VALUES(1, 2);
474de4996e2Sdan    INSERT INTO t1 VALUES(3, 4);
475de4996e2Sdan  }
476de4996e2Sdan  db close
477de4996e2Sdan  tstvfs delete
478de4996e2Sdan} {}
479de4996e2Sdan
480de4996e2Sdanforeach {tn ofst value result} {
481de4996e2Sdan          2   20    31       {1 2 3 4}
482de4996e2Sdan          3   20    32       {1 2 3 4}
483de4996e2Sdan          4   20    33       {1 2 3 4}
484de4996e2Sdan          5   20    65536    {1 2 3 4}
485de4996e2Sdan          6   20    131072   {1 2 3 4}
486de4996e2Sdan
487de4996e2Sdan          7   24    511      {1 2 3 4}
488de4996e2Sdan          8   24    513      {1 2 3 4}
489de4996e2Sdan          9   24    65536    {1 2 3 4}
490de4996e2Sdan
491de4996e2Sdan         10   32    65536    {1 2}
492de4996e2Sdan} {
493de4996e2Sdan  do_test pager1.4.3.$tn {
494de4996e2Sdan    faultsim_restore_and_reopen
495de4996e2Sdan    hexio_write test.db-journal $ofst [format %.8x $value]
496de4996e2Sdan    execsql { SELECT * FROM t1 }
497de4996e2Sdan  } $result
498de4996e2Sdan}
499de4996e2Sdandb close
500de4996e2Sdan
501e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal
502e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the
503e08341c6Sdan# file-system is saved just before the xDelete() call to remove the
504e08341c6Sdan# master journal file from the file-system.
505e08341c6Sdan#
506e08341c6Sdantestvfs tv -default 1
507e08341c6Sdantv script copy_on_mj_delete
508e08341c6Sdanset ::mj_filename_length 0
509e08341c6Sdanproc copy_on_mj_delete {method filename args} {
510e08341c6Sdan  if {[string match *mj* [file tail $filename]]} {
511e08341c6Sdan    set ::mj_filename_length [string length $filename]
512e08341c6Sdan    faultsim_save
513e08341c6Sdan  }
514e08341c6Sdan  return SQLITE_OK
515e08341c6Sdan}
516e08341c6Sdan
517e08341c6Sdanset pwd [pwd]
518e08341c6Sdanforeach {tn1 tcl} {
519e08341c6Sdan  1 { set prefix "test.db" }
520e08341c6Sdan  2 {
521e08341c6Sdan    # This test depends on the underlying VFS being able to open paths
522e08341c6Sdan    # 512 bytes in length. The idea is to create a hot-journal file that
523e08341c6Sdan    # contains a master-journal pointer so large that it could contain
524e08341c6Sdan    # a valid page record (if the file page-size is 512 bytes). So as to
525e08341c6Sdan    # make sure SQLite doesn't get confused by this.
526e08341c6Sdan    #
527e08341c6Sdan    set nPadding [expr 511 - $::mj_filename_length]
528e08341c6Sdan
529e08341c6Sdan    # We cannot just create a really long database file name to open, as
530e08341c6Sdan    # Linux limits a single component of a path to 255 bytes by default
531e08341c6Sdan    # (and presumably other systems have limits too). So create a directory
532e08341c6Sdan    # hierarchy to work in.
533e08341c6Sdan    #
534e08341c6Sdan    set dirname "d123456789012345678901234567890/"
535e08341c6Sdan    set nDir [expr $nPadding / 32]
536e08341c6Sdan    if { $nDir } {
537e08341c6Sdan      set p [string repeat $dirname $nDir]
538e08341c6Sdan      file mkdir $p
539e08341c6Sdan      cd $p
540e08341c6Sdan    }
541e08341c6Sdan
542e08341c6Sdan    set padding [string repeat x [expr $nPadding %32]]
543e08341c6Sdan    set prefix "test.db${padding}"
544e08341c6Sdan  }
545e08341c6Sdan} {
546e08341c6Sdan  eval $tcl
547e08341c6Sdan  foreach {tn2 sql} {
548e08341c6Sdan    o {
549e08341c6Sdan      PRAGMA main.synchronous=OFF;
550e08341c6Sdan      PRAGMA aux.synchronous=OFF;
551e08341c6Sdan    }
552e08341c6Sdan    o512 {
553e08341c6Sdan      PRAGMA main.synchronous=OFF;
554e08341c6Sdan      PRAGMA aux.synchronous=OFF;
555e08341c6Sdan      PRAGMA main.page_size = 512;
556e08341c6Sdan      PRAGMA aux.page_size = 512;
557e08341c6Sdan    }
558e08341c6Sdan    n {
559e08341c6Sdan      PRAGMA main.synchronous=NORMAL;
560e08341c6Sdan      PRAGMA aux.synchronous=NORMAL;
561e08341c6Sdan    }
562e08341c6Sdan    f {
563e08341c6Sdan      PRAGMA main.synchronous=FULL;
564e08341c6Sdan      PRAGMA aux.synchronous=FULL;
565e08341c6Sdan    }
566e08341c6Sdan  } {
567e08341c6Sdan
568e08341c6Sdan    set tn "${tn1}.${tn2}"
569e08341c6Sdan
570e08341c6Sdan    # Set up a connection to have two databases, test.db (main) and
571e08341c6Sdan    # test.db2 (aux). Then run a multi-file transaction on them. The
572e08341c6Sdan    # VFS will snapshot the file-system just before the master-journal
573e08341c6Sdan    # file is deleted to commit the transaction.
574e08341c6Sdan    #
575e08341c6Sdan    tv filter xDelete
576e08341c6Sdan    do_test pager1-4.4.$tn.1 {
577e08341c6Sdan      faultsim_delete_and_reopen $prefix
578e08341c6Sdan      execsql "
579e08341c6Sdan        ATTACH '${prefix}2' AS aux;
580e08341c6Sdan        $sql
581e08341c6Sdan        CREATE TABLE a(x);
582e08341c6Sdan        CREATE TABLE aux.b(x);
583e08341c6Sdan        INSERT INTO a VALUES('double-you');
584e08341c6Sdan        INSERT INTO a VALUES('why');
585e08341c6Sdan        INSERT INTO a VALUES('zed');
586e08341c6Sdan        INSERT INTO b VALUES('won');
587e08341c6Sdan        INSERT INTO b VALUES('too');
588e08341c6Sdan        INSERT INTO b VALUES('free');
589e08341c6Sdan      "
590e08341c6Sdan      execsql {
591e08341c6Sdan        BEGIN;
592e08341c6Sdan          INSERT INTO a SELECT * FROM b WHERE rowid<=3;
593e08341c6Sdan          INSERT INTO b SELECT * FROM a WHERE rowid<=3;
594e08341c6Sdan        COMMIT;
595e08341c6Sdan      }
596e08341c6Sdan    } {}
597e08341c6Sdan    tv filter {}
598e08341c6Sdan
599e08341c6Sdan    # Check that the transaction was committed successfully.
600e08341c6Sdan    #
601e08341c6Sdan    do_execsql_test pager1-4.4.$tn.2 {
602e08341c6Sdan      SELECT * FROM a
603e08341c6Sdan    } {double-you why zed won too free}
604e08341c6Sdan    do_execsql_test pager1-4.4.$tn.3 {
605e08341c6Sdan      SELECT * FROM b
606e08341c6Sdan    } {won too free double-you why zed}
607e08341c6Sdan
608e08341c6Sdan    # Restore the file-system and reopen the databases. Check that it now
609e08341c6Sdan    # appears that the transaction was not committed (because the file-system
610e08341c6Sdan    # was restored to the state where it had not been).
611e08341c6Sdan    #
612e08341c6Sdan    do_test pager1-4.4.$tn.4 {
613e08341c6Sdan      faultsim_restore_and_reopen $prefix
614e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
615e08341c6Sdan    } {}
616e08341c6Sdan    do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
617e08341c6Sdan    do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
618e08341c6Sdan
619e08341c6Sdan    # Restore the file-system again. This time, before reopening the databases,
620e08341c6Sdan    # delete the master-journal file from the file-system. It now appears that
621e08341c6Sdan    # the transaction was committed (no master-journal file == no rollback).
622e08341c6Sdan    #
623e08341c6Sdan    do_test pager1-4.4.$tn.7 {
624e08341c6Sdan      faultsim_restore_and_reopen $prefix
625e08341c6Sdan      foreach f [glob ${prefix}-mj*] { file delete -force $f }
626e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
627e08341c6Sdan    } {}
628e08341c6Sdan    do_execsql_test pager1-4.4.$tn.8 {
629e08341c6Sdan      SELECT * FROM a
630e08341c6Sdan    } {double-you why zed won too free}
631e08341c6Sdan    do_execsql_test pager1-4.4.$tn.9 {
632e08341c6Sdan      SELECT * FROM b
633e08341c6Sdan    } {won too free double-you why zed}
634e08341c6Sdan  }
635e08341c6Sdan
636e08341c6Sdan  cd $pwd
637e08341c6Sdan}
638e08341c6Sdandb close
639e08341c6Sdantv delete
6400e986f51Sdanfile delete -force $dirname
6410e986f51Sdan
6420e986f51Sdan
6430e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a
6440e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly
6450e986f51Sdan# two database pages (and page 1 - the change counter).
6460e986f51Sdan#
6470e986f51Sdantestvfs tv -default 1
6480e986f51Sdantv sectorsize 512
6490e986f51Sdantv script copy_on_journal_delete
6500e986f51Sdantv filter xDelete
6510e986f51Sdanproc copy_on_journal_delete {method filename args} {
6520e986f51Sdan  if {[string match *journal $filename]} faultsim_save
6530e986f51Sdan  return SQLITE_OK
6540e986f51Sdan}
6550e986f51Sdanfaultsim_delete_and_reopen
6560e986f51Sdando_execsql_test pager1.4.5.1 {
6570e986f51Sdan  PRAGMA page_size = 1024;
6580e986f51Sdan  CREATE TABLE t1(a, b);
6590e986f51Sdan  CREATE TABLE t2(a, b);
6600e986f51Sdan  INSERT INTO t1 VALUES('I', 'II');
6610e986f51Sdan  INSERT INTO t2 VALUES('III', 'IV');
6620e986f51Sdan  BEGIN;
6630e986f51Sdan    INSERT INTO t1 VALUES(1, 2);
6640e986f51Sdan    INSERT INTO t2 VALUES(3, 4);
6650e986f51Sdan  COMMIT;
6660e986f51Sdan} {}
6670e986f51Sdantv filter {}
6680e986f51Sdan
6690e986f51Sdan# Check the transaction was committed:
6700e986f51Sdan#
6710e986f51Sdando_execsql_test pager1.4.5.2 {
6720e986f51Sdan  SELECT * FROM t1;
6730e986f51Sdan  SELECT * FROM t2;
6740e986f51Sdan} {I II 1 2 III IV 3 4}
6750e986f51Sdan
676ec6ffc1aSdan# Now try four tests:
6770e986f51Sdan#
6780e986f51Sdan#  pager1-4.5.3: Restore the file-system. Check that the whole transaction
6790e986f51Sdan#                is rolled back.
6800e986f51Sdan#
6810e986f51Sdan#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
6820e986f51Sdan#                journal. Check the transaction is not rolled back.
6830e986f51Sdan#
6840e986f51Sdan#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
6850e986f51Sdan#                journal. Check that the first record in the transaction is
6860e986f51Sdan#                played back, but not the second.
6870e986f51Sdan#
68810f5a50eSdan#  pager1-4.5.6: Restore the file-system. Try to open the database with a
68910f5a50eSdan#                readonly connection. This should fail, as a read-only
69010f5a50eSdan#                connection cannot roll back the database file.
69110f5a50eSdan#
6920e986f51Sdanfaultsim_restore_and_reopen
6930e986f51Sdando_execsql_test pager1.4.5.3 {
6940e986f51Sdan  SELECT * FROM t1;
6950e986f51Sdan  SELECT * FROM t2;
6960e986f51Sdan} {I II III IV}
6970e986f51Sdanfaultsim_restore_and_reopen
6980e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
6990e986f51Sdando_execsql_test pager1.4.5.4 {
7000e986f51Sdan  SELECT * FROM t1;
7010e986f51Sdan  SELECT * FROM t2;
7020e986f51Sdan} {I II 1 2 III IV 3 4}
7030e986f51Sdanfaultsim_restore_and_reopen
7040e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
7050e986f51Sdando_execsql_test pager1.4.5.5 {
7060e986f51Sdan  SELECT * FROM t1;
7070e986f51Sdan  SELECT * FROM t2;
7080e986f51Sdan} {I II III IV 3 4}
7090e986f51Sdan
71010f5a50eSdanfaultsim_restore_and_reopen
71110f5a50eSdandb close
71210f5a50eSdansqlite3 db test.db -readonly 1
71310f5a50eSdando_catchsql_test pager1.4.5.6 {
71410f5a50eSdan  SELECT * FROM t1;
71510f5a50eSdan  SELECT * FROM t2;
71610f5a50eSdan} {1 {disk I/O error}}
717ec6ffc1aSdandb close
718ec6ffc1aSdan
719ec6ffc1aSdan# Snapshot the file-system just before multi-file commit. Save the name
720ec6ffc1aSdan# of the master journal file in $::mj_filename.
721ec6ffc1aSdan#
722ec6ffc1aSdantv script copy_on_mj_delete
723ec6ffc1aSdantv filter xDelete
724ec6ffc1aSdanproc copy_on_mj_delete {method filename args} {
725ec6ffc1aSdan  if {[string match *mj* [file tail $filename]]} {
726ec6ffc1aSdan    set ::mj_filename $filename
727ec6ffc1aSdan    faultsim_save
728ec6ffc1aSdan  }
729ec6ffc1aSdan  return SQLITE_OK
730ec6ffc1aSdan}
731ec6ffc1aSdando_test pager1.4.6.1 {
732ec6ffc1aSdan  faultsim_delete_and_reopen
733ec6ffc1aSdan  execsql {
734ec6ffc1aSdan    ATTACH 'test.db2' AS two;
735ec6ffc1aSdan    CREATE TABLE t1(a, b);
736ec6ffc1aSdan    CREATE TABLE two.t2(a, b);
737ec6ffc1aSdan    INSERT INTO t1 VALUES(1, 't1.1');
738ec6ffc1aSdan    INSERT INTO t2 VALUES(1, 't2.1');
739ec6ffc1aSdan    BEGIN;
740ec6ffc1aSdan      UPDATE t1 SET b = 't1.2';
741ec6ffc1aSdan      UPDATE t2 SET b = 't2.2';
742ec6ffc1aSdan    COMMIT;
743ec6ffc1aSdan  }
744ec6ffc1aSdan  tv filter {}
745ec6ffc1aSdan  db close
746ec6ffc1aSdan} {}
747ec6ffc1aSdan
748ec6ffc1aSdanfaultsim_restore_and_reopen
749ec6ffc1aSdando_execsql_test pager1.4.6.2 { SELECT * FROM t1 }           {1 t1.1}
750ec6ffc1aSdando_test         pager1.4.6.3 { file exists $::mj_filename } {1}
751ec6ffc1aSdando_execsql_test pager1.4.6.4 {
752ec6ffc1aSdan  ATTACH 'test.db2' AS two;
753ec6ffc1aSdan  SELECT * FROM t2;
754ec6ffc1aSdan} {1 t2.1}
755ec6ffc1aSdando_test pager1.4.6.5 { file exists $::mj_filename } {0}
756ec6ffc1aSdan
757ec6ffc1aSdanfaultsim_restore_and_reopen
758ec6ffc1aSdandb close
759ec6ffc1aSdando_test pager1.4.6.8 {
760ec6ffc1aSdan  set ::mj_filename1 $::mj_filename
761ec6ffc1aSdan  tv filter xDelete
762ec6ffc1aSdan  sqlite3 db test.db2
763ec6ffc1aSdan  execsql {
764ec6ffc1aSdan    ATTACH 'test.db3' AS three;
765ec6ffc1aSdan    CREATE TABLE three.t3(a, b);
766ec6ffc1aSdan    INSERT INTO t3 VALUES(1, 't3.1');
767ec6ffc1aSdan    BEGIN;
768ec6ffc1aSdan      UPDATE t2 SET b = 't2.3';
769ec6ffc1aSdan      UPDATE t3 SET b = 't3.3';
770ec6ffc1aSdan    COMMIT;
771ec6ffc1aSdan  }
772ec6ffc1aSdan  expr {$::mj_filename1 != $::mj_filename}
773ec6ffc1aSdan} {1}
774ec6ffc1aSdanfaultsim_restore_and_reopen
775ec6ffc1aSdantv filter {}
776ec6ffc1aSdan
777ec6ffc1aSdan# The file-system now contains:
778ec6ffc1aSdan#
779ec6ffc1aSdan#   * three databases
780ec6ffc1aSdan#   * three hot-journal files
781ec6ffc1aSdan#   * two master-journal files.
782ec6ffc1aSdan#
783ec6ffc1aSdan# The hot-journals associated with test.db2 and test.db3 point to
784ec6ffc1aSdan# master journal $::mj_filename. The hot-journal file associated with
785ec6ffc1aSdan# test.db points to master journal $::mj_filename1. So reading from
786ec6ffc1aSdan# test.db should delete $::mj_filename1.
787ec6ffc1aSdan#
788ec6ffc1aSdando_test pager1.4.6.9 {
789ec6ffc1aSdan  lsort [glob test.db*]
790ec6ffc1aSdan} [lsort [list                                           \
791ec6ffc1aSdan  test.db test.db2 test.db3                              \
792ec6ffc1aSdan  test.db-journal test.db2-journal test.db3-journal      \
793ec6ffc1aSdan  [file tail $::mj_filename] [file tail $::mj_filename1]
794ec6ffc1aSdan]]
795ec6ffc1aSdan
796ec6ffc1aSdan# The master-journal $::mj_filename1 contains pointers to test.db and
797ec6ffc1aSdan# test.db2. However the hot-journal associated with test.db2 points to
798ec6ffc1aSdan# a different master-journal. Therefore, reading from test.db only should
799ec6ffc1aSdan# be enough to cause SQLite to delete $::mj_filename1.
800ec6ffc1aSdan#
801ec6ffc1aSdando_test         pager1.4.6.10 { file exists $::mj_filename  } {1}
802ec6ffc1aSdando_test         pager1.4.6.11 { file exists $::mj_filename1 } {1}
803ec6ffc1aSdando_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1}
804ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename  } {1}
805ec6ffc1aSdando_test         pager1.4.6.14 { file exists $::mj_filename1 } {0}
806ec6ffc1aSdan
807ec6ffc1aSdando_execsql_test pager1.4.6.12 {
808ec6ffc1aSdan  ATTACH 'test.db2' AS two;
809ec6ffc1aSdan  SELECT * FROM t2;
810ec6ffc1aSdan} {1 t2.1}
811ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename }  {1}
812ec6ffc1aSdando_execsql_test pager1.4.6.14 {
813ec6ffc1aSdan  ATTACH 'test.db3' AS three;
814ec6ffc1aSdan  SELECT * FROM t3;
815ec6ffc1aSdan} {1 t3.1}
816ec6ffc1aSdando_test         pager1.4.6.15 { file exists $::mj_filename }  {0}
81710f5a50eSdan
8180e986f51Sdandb close
8190e986f51Sdantv delete
820e08341c6Sdan
821*c8ce3972Sdantestvfs tv -default 1
822*c8ce3972Sdantv sectorsize 512
823*c8ce3972Sdantv script copy_on_journal_delete
824*c8ce3972Sdantv filter xDelete
825*c8ce3972Sdanproc copy_on_journal_delete {method filename args} {
826*c8ce3972Sdan  if {[string match *journal $filename]} faultsim_save
827*c8ce3972Sdan  return SQLITE_OK
828*c8ce3972Sdan}
829*c8ce3972Sdanfaultsim_delete_and_reopen
830*c8ce3972Sdando_execsql_test pager1.4.7.1 {
831*c8ce3972Sdan  CREATE TABLE t1(x PRIMARY KEY, y);
832*c8ce3972Sdan  CREATE INDEX i1 ON t1(y);
833*c8ce3972Sdan  INSERT INTO t1 VALUES('I',   'one');
834*c8ce3972Sdan  INSERT INTO t1 VALUES('II',  'four');
835*c8ce3972Sdan  INSERT INTO t1 VALUES('III', 'nine');
836*c8ce3972Sdan  BEGIN;
837*c8ce3972Sdan    INSERT INTO t1 VALUES('IV', 'sixteen');
838*c8ce3972Sdan    INSERT INTO t1 VALUES('V' , 'twentyfive');
839*c8ce3972Sdan  COMMIT;
840*c8ce3972Sdan} {}
841*c8ce3972Sdantv filter {}
842*c8ce3972Sdandb close
843*c8ce3972Sdantv delete
844*c8ce3972Sdando_test pager1.4.7.2 {
845*c8ce3972Sdan  faultsim_restore_and_reopen
846*c8ce3972Sdan  catch {file attributes test.db-journal -permissions r--------}
847*c8ce3972Sdan  catch {file attributes test.db-journal -readonly 1}
848*c8ce3972Sdan  catchsql { SELECT * FROM t1 }
849*c8ce3972Sdan} {1 {unable to open database file}}
850*c8ce3972Sdando_test pager1.4.7.3 {
851*c8ce3972Sdan  db close
852*c8ce3972Sdan  catch {file attributes test.db-journal -permissions rw-rw-rw-}
853*c8ce3972Sdan  catch {file attributes test.db-journal -readonly 0}
854*c8ce3972Sdan  file delete test.db-journal
855*c8ce3972Sdan  file exists test.db-journal
856*c8ce3972Sdan} {0}
857*c8ce3972Sdan
858de4996e2Sdan#-------------------------------------------------------------------------
859146ed78bSdan# The following tests deal with multi-file commits.
860de4996e2Sdan#
861146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because
862146ed78bSdan#               another connection is holding a SHARED lock on one of the
863146ed78bSdan#               files. After the SHARED lock is removed, the COMMIT succeeds.
864146ed78bSdan#
865146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory.
866146ed78bSdan#
867146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory.
868146ed78bSdan#
869146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
870146ed78bSdan#               name is added to a journal file immediately after the last
871146ed78bSdan#               journal record. But with synchronous=full, extra unused space
872146ed78bSdan#               is allocated between the last journal record and the
873146ed78bSdan#               master-journal file name so that the master-journal file
874146ed78bSdan#               name does not lie on the same sector as the last journal file
875146ed78bSdan#               record.
876146ed78bSdan#
877e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
878e08341c6Sdan#               truncated to zero bytes when a multi-file transaction is
879e08341c6Sdan#               committed (instead of the first couple of bytes being zeroed).
880e08341c6Sdan#
881146ed78bSdan#
882146ed78bSdando_test pager1-5.1.1 {
883146ed78bSdan  faultsim_delete_and_reopen
884146ed78bSdan  execsql {
885146ed78bSdan    ATTACH 'test.db2' AS aux;
886146ed78bSdan    CREATE TABLE t1(a, b);
887146ed78bSdan    CREATE TABLE aux.t2(a, b);
888146ed78bSdan    INSERT INTO t1 VALUES(17, 'Lenin');
889146ed78bSdan    INSERT INTO t1 VALUES(22, 'Stalin');
890146ed78bSdan    INSERT INTO t1 VALUES(53, 'Khrushchev');
891146ed78bSdan  }
892146ed78bSdan} {}
893146ed78bSdando_test pager1-5.1.2 {
894146ed78bSdan  execsql {
895146ed78bSdan    BEGIN;
896146ed78bSdan      INSERT INTO t1 VALUES(64, 'Brezhnev');
897146ed78bSdan      INSERT INTO t2 SELECT * FROM t1;
898146ed78bSdan  }
899146ed78bSdan  sqlite3 db2 test.db2
900146ed78bSdan  execsql {
901146ed78bSdan    BEGIN;
902146ed78bSdan      SELECT * FROM t2;
903146ed78bSdan  } db2
904146ed78bSdan} {}
905146ed78bSdando_test pager1-5.1.3 {
906146ed78bSdan  catchsql COMMIT
907146ed78bSdan} {1 {database is locked}}
908146ed78bSdando_test pager1-5.1.4 {
909146ed78bSdan  execsql COMMIT db2
910146ed78bSdan  execsql COMMIT
911146ed78bSdan  execsql { SELECT * FROM t2 } db2
912146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
913146ed78bSdando_test pager1-5.1.5 {
914146ed78bSdan  db2 close
915146ed78bSdan} {}
916146ed78bSdan
917146ed78bSdando_test pager1-5.2.1 {
918146ed78bSdan  execsql {
919146ed78bSdan    PRAGMA journal_mode = memory;
920146ed78bSdan    BEGIN;
921146ed78bSdan      INSERT INTO t1 VALUES(84, 'Andropov');
922146ed78bSdan      INSERT INTO t2 VALUES(84, 'Andropov');
923146ed78bSdan    COMMIT;
924146ed78bSdan  }
925146ed78bSdan} {memory}
926146ed78bSdando_test pager1-5.3.1 {
927146ed78bSdan  execsql {
928146ed78bSdan    PRAGMA journal_mode = off;
929146ed78bSdan    BEGIN;
930146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
931146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
932146ed78bSdan    COMMIT;
933146ed78bSdan  }
934146ed78bSdan} {off}
935146ed78bSdan
936146ed78bSdando_test pager1-5.4.1 {
937146ed78bSdan  db close
938146ed78bSdan  testvfs tv
939146ed78bSdan  sqlite3 db test.db -vfs tv
940146ed78bSdan  execsql { ATTACH 'test.db2' AS aux }
941146ed78bSdan
942146ed78bSdan  tv filter xDelete
943146ed78bSdan  tv script max_journal_size
944146ed78bSdan  tv sectorsize 512
945146ed78bSdan  set ::max_journal 0
946146ed78bSdan  proc max_journal_size {method args} {
947146ed78bSdan    set sz 0
948146ed78bSdan    catch { set sz [file size test.db-journal] }
949146ed78bSdan    if {$sz > $::max_journal} {
950146ed78bSdan      set ::max_journal $sz
951146ed78bSdan    }
952146ed78bSdan    return SQLITE_OK
953146ed78bSdan  }
954146ed78bSdan  execsql {
955146ed78bSdan    PRAGMA journal_mode = DELETE;
956146ed78bSdan    PRAGMA synchronous = NORMAL;
957146ed78bSdan    BEGIN;
958146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
959146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
960146ed78bSdan    COMMIT;
961146ed78bSdan  }
962146ed78bSdan  set ::max_journal
963146ed78bSdan} [expr 2615+[string length [pwd]]]
964146ed78bSdando_test pager1-5.4.2 {
965146ed78bSdan  set ::max_journal 0
966146ed78bSdan  execsql {
967146ed78bSdan    PRAGMA synchronous = full;
968146ed78bSdan    BEGIN;
969146ed78bSdan      DELETE FROM t1 WHERE b = 'Lenin';
970146ed78bSdan      DELETE FROM t2 WHERE b = 'Lenin';
971146ed78bSdan    COMMIT;
972146ed78bSdan  }
973146ed78bSdan  set ::max_journal
974146ed78bSdan} [expr 3111+[string length [pwd]]]
975146ed78bSdandb close
976146ed78bSdantv delete
977146ed78bSdan
978146ed78bSdando_test pager1-5.5.1 {
979146ed78bSdan  sqlite3 db test.db
980146ed78bSdan  execsql {
981146ed78bSdan    ATTACH 'test.db2' AS aux;
982146ed78bSdan    PRAGMA journal_mode = PERSIST;
983146ed78bSdan    CREATE TABLE t3(a, b);
984146ed78bSdan    INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
985146ed78bSdan    UPDATE t3 SET b = randomblob(1500);
986146ed78bSdan  }
987146ed78bSdan  expr [file size test.db-journal] > 15000
988146ed78bSdan} {1}
989146ed78bSdando_test pager1-5.5.2 {
990146ed78bSdan  execsql {
991146ed78bSdan    PRAGMA synchronous = full;
992146ed78bSdan    BEGIN;
993146ed78bSdan      DELETE FROM t1 WHERE b = 'Stalin';
994146ed78bSdan      DELETE FROM t2 WHERE b = 'Stalin';
995146ed78bSdan    COMMIT;
996146ed78bSdan  }
997146ed78bSdan  file size test.db-journal
998146ed78bSdan} {0}
999146ed78bSdan
1000146ed78bSdan
1001146ed78bSdan#-------------------------------------------------------------------------
1002146ed78bSdan# The following tests work with "PRAGMA max_page_count"
1003146ed78bSdan#
1004146ed78bSdando_test pager1-6.1 {
1005146ed78bSdan  faultsim_delete_and_reopen
1006146ed78bSdan  execsql {
1007146ed78bSdan    PRAGMA max_page_count = 10;
1008146ed78bSdan    CREATE TABLE t2(a, b);
1009146ed78bSdan    CREATE TABLE t3(a, b);
1010146ed78bSdan    CREATE TABLE t4(a, b);
1011146ed78bSdan    CREATE TABLE t5(a, b);
1012146ed78bSdan    CREATE TABLE t6(a, b);
1013146ed78bSdan    CREATE TABLE t7(a, b);
1014146ed78bSdan    CREATE TABLE t8(a, b);
1015146ed78bSdan    CREATE TABLE t9(a, b);
1016146ed78bSdan    CREATE TABLE t10(a, b);
1017146ed78bSdan  }
1018146ed78bSdan} {10}
1019d353331aSdando_catchsql_test pager1-6.2 {
1020d353331aSdan  CREATE TABLE t11(a, b)
1021146ed78bSdan} {1 {database or disk is full}}
1022d353331aSdando_execsql_test pager1-6.4 { PRAGMA max_page_count      } {10}
1023d353331aSdando_execsql_test pager1-6.5 { PRAGMA max_page_count = 15 } {15}
1024d353331aSdando_execsql_test pager1-6.6 { CREATE TABLE t11(a, b)     } {}
1025d353331aSdando_execsql_test pager1-6.7 {
1026d353331aSdan  BEGIN;
1027d353331aSdan    INSERT INTO t11 VALUES(1, 2);
1028d353331aSdan    PRAGMA max_page_count = 13;
1029d353331aSdan} {13}
1030d353331aSdando_execsql_test pager1-6.8 {
1031d353331aSdan    INSERT INTO t11 VALUES(3, 4);
1032d353331aSdan    PRAGMA max_page_count = 10;
1033d353331aSdan} {11}
1034d353331aSdando_execsql_test pager1-6.9 { COMMIT } {}
1035de4996e2Sdan
1036153eda0aSdan
1037153eda0aSdan#-------------------------------------------------------------------------
1038153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
1039153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE".
1040153eda0aSdan#
1041153eda0aSdan# Each test is specified with 5 variables. As follows:
1042153eda0aSdan#
1043153eda0aSdan#   $tn:  Test Number. Used as part of the [do_test] test names.
1044153eda0aSdan#   $sql: SQL to execute.
1045153eda0aSdan#   $res: Expected result of executing $sql.
1046153eda0aSdan#   $js:  The expected size of the journal file, in bytes, after executing
1047153eda0aSdan#         the SQL script. Or -1 if the journal is not expected to exist.
1048153eda0aSdan#   $ws:  The expected size of the WAL file, in bytes, after executing
1049153eda0aSdan#         the SQL script. Or -1 if the WAL is not expected to exist.
1050153eda0aSdan#
105138e1a279Sdanifcapable wal {
1052153eda0aSdan  faultsim_delete_and_reopen
1053153eda0aSdan  foreach {tn sql res js ws} [subst {
1054153eda0aSdan
1055153eda0aSdan    1  {
1056153eda0aSdan      CREATE TABLE t1(a, b);
1057153eda0aSdan      PRAGMA auto_vacuum=OFF;
1058153eda0aSdan      PRAGMA synchronous=NORMAL;
1059153eda0aSdan      PRAGMA page_size=1024;
1060153eda0aSdan      PRAGMA locking_mode=EXCLUSIVE;
1061153eda0aSdan      PRAGMA journal_mode=TRUNCATE;
1062153eda0aSdan      INSERT INTO t1 VALUES(1, 2);
1063153eda0aSdan    } {exclusive truncate} 0 -1
1064153eda0aSdan
1065153eda0aSdan    2  {
1066153eda0aSdan      BEGIN IMMEDIATE;
1067153eda0aSdan        SELECT * FROM t1;
1068153eda0aSdan      COMMIT;
1069153eda0aSdan    } {1 2} 0 -1
1070153eda0aSdan
1071153eda0aSdan    3  {
1072153eda0aSdan      BEGIN;
1073153eda0aSdan        SELECT * FROM t1;
1074153eda0aSdan      COMMIT;
1075153eda0aSdan    } {1 2} 0 -1
1076153eda0aSdan
1077153eda0aSdan    4  { PRAGMA journal_mode = WAL }    wal    -1 -1
1078153eda0aSdan    5  { INSERT INTO t1 VALUES(3, 4) }  {}     -1 [wal_file_size 1 1024]
1079153eda0aSdan    6  { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
1080153eda0aSdan    7  { INSERT INTO t1 VALUES(5, 6); } {}     -1 [wal_file_size 2 1024]
1081153eda0aSdan
1082153eda0aSdan    8  { PRAGMA journal_mode = TRUNCATE } truncate          0 -1
1083153eda0aSdan    9  { INSERT INTO t1 VALUES(7, 8) }    {}                0 -1
1084153eda0aSdan    10 { SELECT * FROM t1 }               {1 2 3 4 5 6 7 8} 0 -1
1085153eda0aSdan
1086153eda0aSdan  }] {
1087153eda0aSdan    do_execsql_test pager1-7.1.$tn.1 $sql $res
1088153eda0aSdan    catch { set J -1 ; set J [file size test.db-journal] }
1089153eda0aSdan    catch { set W -1 ; set W [file size test.db-wal] }
1090153eda0aSdan    do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
1091153eda0aSdan  }
109238e1a279Sdan}
1093153eda0aSdan
1094d353331aSdan#-------------------------------------------------------------------------
1095d353331aSdan# The following tests, pager1-8.*, test that the special filenames
1096d353331aSdan# ":memory:" and "" open temporary databases.
1097d353331aSdan#
10980e986f51Sdanforeach {tn filename} {
10990e986f51Sdan  1 :memory:
11000e986f51Sdan  2 ""
11010e986f51Sdan} {
11020e986f51Sdan  do_test pager1-8.$tn.1 {
1103153eda0aSdan    faultsim_delete_and_reopen
1104153eda0aSdan    db close
11050e986f51Sdan    sqlite3 db $filename
1106153eda0aSdan    execsql {
1107*c8ce3972Sdan      PRAGMA auto_vacuum = 1;
1108153eda0aSdan      CREATE TABLE x1(x);
1109153eda0aSdan      INSERT INTO x1 VALUES('Charles');
1110153eda0aSdan      INSERT INTO x1 VALUES('James');
1111153eda0aSdan      INSERT INTO x1 VALUES('Mary');
1112153eda0aSdan      SELECT * FROM x1;
1113153eda0aSdan    }
1114153eda0aSdan  } {Charles James Mary}
11150e986f51Sdan
11160e986f51Sdan  do_test pager1-8.$tn.2 {
11170e986f51Sdan    sqlite3 db2 $filename
11180e986f51Sdan    catchsql { SELECT * FROM x1 } db2
1119153eda0aSdan  } {1 {no such table: x1}}
1120153eda0aSdan
11210e986f51Sdan  do_execsql_test pager1-8.$tn.3 {
11220e986f51Sdan    BEGIN;
11230e986f51Sdan      INSERT INTO x1 VALUES('William');
11240e986f51Sdan      INSERT INTO x1 VALUES('Anne');
11250e986f51Sdan    ROLLBACK;
11260e986f51Sdan  } {}
11270e986f51Sdan}
11280e986f51Sdan
1129dca321aeSdan#-------------------------------------------------------------------------
1130dca321aeSdan# The next block of tests - pager1-9.* - deal with interactions between
1131dca321aeSdan# the pager and the backup API. Test cases:
1132dca321aeSdan#
1133dca321aeSdan#   pager1-9.1.*: Test that a backup completes successfully even if the
1134dca321aeSdan#                 source db is written to during the backup op.
1135dca321aeSdan#
1136dca321aeSdan#   pager1-9.2.*: Test that a backup completes successfully even if the
1137dca321aeSdan#                 source db is written to and then rolled back during a
1138dca321aeSdan#                 backup operation.
1139dca321aeSdan#
1140dca321aeSdando_test pager1-9.0.1 {
1141dca321aeSdan  faultsim_delete_and_reopen
1142dca321aeSdan  db func a_string a_string
1143dca321aeSdan  execsql {
1144dca321aeSdan    PRAGMA cache_size = 10;
1145dca321aeSdan    BEGIN;
1146dca321aeSdan      CREATE TABLE ab(a, b, UNIQUE(a, b));
1147dca321aeSdan      INSERT INTO ab VALUES( a_string(200), a_string(300) );
1148dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1149dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
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    COMMIT;
1156dca321aeSdan  }
1157dca321aeSdan} {}
1158dca321aeSdando_test pager1-9.0.2 {
1159dca321aeSdan  sqlite3 db2 test.db2
1160dca321aeSdan  db2 eval { PRAGMA cache_size = 10 }
1161dca321aeSdan  sqlite3_backup B db2 main db main
1162dca321aeSdan  list [B step 10000] [B finish]
1163dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1164dca321aeSdando_test pager1-9.0.3 {
1165dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1166dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1167dca321aeSdan
1168dca321aeSdando_test pager1-9.1.1 {
1169dca321aeSdan  execsql { UPDATE ab SET a = a_string(201) }
1170dca321aeSdan  sqlite3_backup B db2 main db main
1171dca321aeSdan  B step 30
1172dca321aeSdan} {SQLITE_OK}
1173dca321aeSdando_test pager1-9.1.2 {
1174dca321aeSdan  execsql { UPDATE ab SET b = a_string(301) }
1175dca321aeSdan  list [B step 10000] [B finish]
1176dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1177dca321aeSdando_test pager1-9.1.3 {
1178dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1179dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1180dca321aeSdando_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128}
1181dca321aeSdan
1182dca321aeSdando_test pager1-9.2.1 {
1183dca321aeSdan  execsql { UPDATE ab SET a = a_string(202) }
1184dca321aeSdan  sqlite3_backup B db2 main db main
1185dca321aeSdan  B step 30
1186dca321aeSdan} {SQLITE_OK}
1187dca321aeSdando_test pager1-9.2.2 {
1188dca321aeSdan  execsql {
1189dca321aeSdan    BEGIN;
1190dca321aeSdan      UPDATE ab SET b = a_string(301);
1191dca321aeSdan    ROLLBACK;
1192dca321aeSdan  }
1193dca321aeSdan  list [B step 10000] [B finish]
1194dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1195dca321aeSdando_test pager1-9.2.3 {
1196dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1197dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1198dca321aeSdando_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128}
1199ec6ffc1aSdandb close
1200ec6ffc1aSdandb2 close
1201ec6ffc1aSdan
1202ec6ffc1aSdan#-------------------------------------------------------------------------
1203ec6ffc1aSdan# Test that regardless of the value returned by xSectorSize(), the
1204ec6ffc1aSdan# minimum effective sector-size is 512 and the maximum 65536 bytes.
1205ec6ffc1aSdan#
1206ec6ffc1aSdantestvfs tv -default 1
1207ec6ffc1aSdanforeach sectorsize {
1208ec6ffc1aSdan    32   64   128   256   512   1024   2048
1209ec6ffc1aSdan    4096 8192 16384 32768 65536 131072 262144
1210ec6ffc1aSdan} {
1211ec6ffc1aSdan  tv sectorsize $sectorsize
1212ec6ffc1aSdan  set eff $sectorsize
1213ec6ffc1aSdan  if {$sectorsize < 512}   { set eff 512 }
1214ec6ffc1aSdan  if {$sectorsize > 65536} { set eff 65536 }
1215ec6ffc1aSdan
1216ec6ffc1aSdan  do_test pager1-10.$sectorsize {
1217ec6ffc1aSdan    faultsim_delete_and_reopen
1218ec6ffc1aSdan    execsql {
1219ec6ffc1aSdan      PRAGMA journal_mode = PERSIST;
1220ec6ffc1aSdan      PRAGMA page_size = 1024;
1221ec6ffc1aSdan      CREATE TABLE t1(a, b);
1222ec6ffc1aSdan    }
1223ec6ffc1aSdan    file size test.db-journal
1224ec6ffc1aSdan  } [expr $sectorsize > 65536 ? 65536 : $sectorsize]
1225ec6ffc1aSdan}
1226ec6ffc1aSdandb close
1227ec6ffc1aSdantv delete
1228ec6ffc1aSdan
1229ec6ffc1aSdantestvfs tv -default 1
1230ec6ffc1aSdanfaultsim_delete_and_reopen
1231ec6ffc1aSdandb func a_string a_string
1232ec6ffc1aSdando_execsql_test pager1-11.1 {
1233ec6ffc1aSdan  PRAGMA cache_size = 10;
1234ec6ffc1aSdan  BEGIN;
1235ec6ffc1aSdan    CREATE TABLE zz(top PRIMARY KEY);
1236ec6ffc1aSdan    INSERT INTO zz VALUES(a_string(222));
1237ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1238ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
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  COMMIT;
1243ec6ffc1aSdan  BEGIN;
1244ec6ffc1aSdan    UPDATE zz SET top = a_string(345);
1245ec6ffc1aSdan} {}
1246ec6ffc1aSdan
1247ec6ffc1aSdanproc lockout {method args} { return SQLITE_IOERR }
1248ec6ffc1aSdantv script lockout
1249ec6ffc1aSdantv filter {xWrite xTruncate xSync}
1250ec6ffc1aSdando_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}}
1251ec6ffc1aSdan
1252ec6ffc1aSdantv script {}
1253ec6ffc1aSdando_test pager1-11.3 {
1254ec6ffc1aSdan  sqlite3 db2 test.db
1255ec6ffc1aSdan  execsql {
1256ec6ffc1aSdan    PRAGMA journal_mode = TRUNCATE;
1257ec6ffc1aSdan    PRAGMA integrity_check;
1258ec6ffc1aSdan  } db2
1259ec6ffc1aSdan} {truncate ok}
1260ec6ffc1aSdando_test pager1-11.4 {
1261ec6ffc1aSdan  db2 close
1262ec6ffc1aSdan  file size test.db-journal
1263ec6ffc1aSdan} {0}
1264ec6ffc1aSdando_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32}
1265f9b4419dSdandb close
1266f9b4419dSdantv delete
1267ec6ffc1aSdan
1268f9b4419dSdan#-------------------------------------------------------------------------
1269f9b4419dSdan# Test "PRAGMA page_size"
1270f9b4419dSdan#
1271f9b4419dSdanforeach pagesize {
1272f9b4419dSdan    512   1024   2048 4096 8192 16384 32768
1273f9b4419dSdan} {
1274f9b4419dSdan  faultsim_delete_and_reopen
1275f9b4419dSdan
1276f9b4419dSdan  do_test pager1-12.$pagesize.1 {
1277f9b4419dSdan    sqlite3 db2 test.db
1278f9b4419dSdan    execsql "
1279f9b4419dSdan      PRAGMA page_size = $pagesize;
1280f9b4419dSdan      CREATE VIEW v AS SELECT * FROM sqlite_master;
1281f9b4419dSdan    " db2
1282f9b4419dSdan    file size test.db
1283f9b4419dSdan  } $pagesize
1284f9b4419dSdan  do_test pager1-12.$pagesize.2 {
1285f9b4419dSdan    sqlite3 db2 test.db
1286f9b4419dSdan    execsql {
1287f9b4419dSdan      SELECT count(*) FROM v;
1288f9b4419dSdan      PRAGMA main.page_size;
1289f9b4419dSdan    } db2
1290f9b4419dSdan  } [list 1 $pagesize]
1291f9b4419dSdan  do_test pager1-12.$pagesize.3 {
1292f9b4419dSdan    execsql {
1293f9b4419dSdan      SELECT count(*) FROM v;
1294f9b4419dSdan      PRAGMA main.page_size;
1295f9b4419dSdan    }
1296f9b4419dSdan  } [list 1 $pagesize]
1297f9b4419dSdan  db2 close
1298f9b4419dSdan}
1299dca321aeSdan
1300d353331aSdan#-------------------------------------------------------------------------
1301d353331aSdan# Test specal "PRAGMA journal_mode=PERSIST" test cases.
1302d353331aSdan#
1303d353331aSdan# pager1-13.1.*: This tests a special case encountered in persistent
1304d353331aSdan#                journal mode: If the journal associated with a transaction
1305d353331aSdan#                is smaller than the journal file (because a previous
1306d353331aSdan#                transaction left a very large non-hot journal file in the
1307d353331aSdan#                file-system), then SQLite has to be careful that there is
1308d353331aSdan#                not a journal-header left over from a previous transaction
1309d353331aSdan#                immediately following the journal content just written.
1310d353331aSdan#                If there is, and the process crashes so that the journal
1311d353331aSdan#                becomes a hot-journal and must be rolled back by another
1312d353331aSdan#                process, there is a danger that the other process may roll
1313d353331aSdan#                back the aborted transaction, then continue copying data
1314d353331aSdan#                from an older transaction from the remainder of the journal.
1315d353331aSdan#                See the syncJournal() function for details.
1316d353331aSdan#
1317d353331aSdan# pager1-13.2.*: Same test as the previous. This time, throw an index into
1318d353331aSdan#                the mix to make the integrity-check more likely to catch
1319d353331aSdan#                errors.
1320d353331aSdan#
1321d353331aSdantestvfs tv -default 1
1322d353331aSdantv script xSyncCb
1323d353331aSdantv filter xSync
1324d353331aSdanproc xSyncCb {method filename args} {
1325d353331aSdan  set t [file tail $filename]
1326d353331aSdan  if {$t == "test.db"} faultsim_save
1327d353331aSdan  return SQLITE_OK
1328d353331aSdan}
1329d353331aSdanfaultsim_delete_and_reopen
1330d353331aSdandb func a_string a_string
1331e91a54e1Sdan
1332d353331aSdan# The UPDATE statement at the end of this test case creates a really big
1333d353331aSdan# journal. Since the cache-size is only 10 pages, the journal contains
1334d353331aSdan# frequent journal headers.
1335d353331aSdan#
1336d353331aSdando_execsql_test pager1-13.1.1 {
1337d353331aSdan  PRAGMA page_size = 1024;
1338d353331aSdan  PRAGMA journal_mode = PERSIST;
1339d353331aSdan  PRAGMA cache_size = 10;
1340d353331aSdan  BEGIN;
1341d353331aSdan    CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
1342d353331aSdan    INSERT INTO t1 VALUES(NULL, a_string(400));
1343d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   2 */
1344d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   4 */
1345d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*   8 */
1346d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  16 */
1347d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  32 */
1348d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /*  64 */
1349d353331aSdan    INSERT INTO t1 SELECT NULL, a_string(400) FROM t1;          /* 128 */
1350d353331aSdan  COMMIT;
1351d353331aSdan  UPDATE t1 SET b = a_string(400);
1352d353331aSdan} {persist}
1353d353331aSdan
1354d353331aSdan# Run transactions of increasing sizes. Eventually, one (or more than one)
1355d353331aSdan# of these will write just enough content that one of the old headers created
1356d353331aSdan# by the transaction in the block above lies immediately after the content
1357d353331aSdan# journalled by the current transaction.
1358d353331aSdan#
1359d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1360d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.1 {
1361d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1362d353331aSdan  } {}
1363d353331aSdan  do_execsql_test pager1-13.1.2.$nUp.2 { PRAGMA integrity_check } {ok}
1364d353331aSdan
1365d353331aSdan  # Try to access the snapshot of the file-system.
1366d353331aSdan  #
1367d353331aSdan  sqlite3 db2 sv_test.db
1368d353331aSdan  do_test pager1-13.1.2.$nUp.3 {
1369d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1370d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1371d353331aSdan  do_test pager1-13.1.2.$nUp.4 {
1372d353331aSdan    execsql { PRAGMA integrity_check } db2
1373d353331aSdan  } {ok}
1374d353331aSdan  db2 close
1375d353331aSdan}
1376d353331aSdan
1377d353331aSdan# Same test as above. But this time with an index on the table.
1378d353331aSdan#
1379d353331aSdando_execsql_test pager1-13.2.1 {
1380d353331aSdan  CREATE INDEX i1 ON t1(b);
1381d353331aSdan  UPDATE t1 SET b = a_string(400);
1382d353331aSdan} {}
1383d353331aSdanfor {set nUp 1} {$nUp<64} {incr nUp} {
1384d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.1 {
1385d353331aSdan    UPDATE t1 SET b = a_string(399) WHERE a <= $nUp
1386d353331aSdan  } {}
1387d353331aSdan  do_execsql_test pager1-13.2.2.$nUp.2 { PRAGMA integrity_check } {ok}
1388d353331aSdan  sqlite3 db2 sv_test.db
1389d353331aSdan  do_test pager1-13.2.2.$nUp.3 {
1390d353331aSdan    execsql { SELECT sum(length(b)) FROM t1 } db2
1391d353331aSdan  } [expr {128*400 - ($nUp-1)}]
1392d353331aSdan  do_test pager1-13.2.2.$nUp.4 {
1393d353331aSdan    execsql { PRAGMA integrity_check } db2
1394d353331aSdan  } {ok}
1395d353331aSdan  db2 close
1396d353331aSdan}
1397d353331aSdan
1398d353331aSdandb close
1399d353331aSdantv delete
1400d353331aSdan
1401d353331aSdan#-------------------------------------------------------------------------
1402d353331aSdan# Test specal "PRAGMA journal_mode=OFF" test cases.
1403d353331aSdan#
1404d353331aSdanfaultsim_delete_and_reopen
1405d353331aSdando_execsql_test pager1-14.1.1 {
1406d353331aSdan  PRAGMA journal_mode = OFF;
1407d353331aSdan  CREATE TABLE t1(a, b);
1408d353331aSdan  BEGIN;
1409d353331aSdan    INSERT INTO t1 VALUES(1, 2);
1410d353331aSdan  COMMIT;
1411d353331aSdan  SELECT * FROM t1;
1412d353331aSdan} {off 1 2}
1413d353331aSdando_catchsql_test pager1-14.1.2 {
1414d353331aSdan  BEGIN;
1415d353331aSdan    INSERT INTO t1 VALUES(3, 4);
1416d353331aSdan  ROLLBACK;
1417d353331aSdan} {0 {}}
1418d353331aSdando_execsql_test pager1-14.1.3 {
1419d353331aSdan  SELECT * FROM t1;
1420d353331aSdan} {1 2 3 4}
1421d353331aSdando_catchsql_test pager1-14.1.4 {
1422d353331aSdan  BEGIN;
1423d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1424d353331aSdan    INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
1425d353331aSdan} {1 {PRIMARY KEY must be unique}}
1426d353331aSdando_execsql_test pager1-14.1.5 {
1427d353331aSdan  COMMIT;
1428d353331aSdan  SELECT * FROM t1;
1429d353331aSdan} {1 2 3 4 2 2 4 4}
1430d353331aSdan
1431*c8ce3972Sdan#-------------------------------------------------------------------------
1432*c8ce3972Sdan# Test opening and closing the pager sub-system with different values
1433*c8ce3972Sdan# for the sqlite3_vfs.szOsFile variable.
1434*c8ce3972Sdan#
1435*c8ce3972Sdanfaultsim_delete_and_reopen
1436*c8ce3972Sdando_execsql_test pager1-15.0 {
1437*c8ce3972Sdan  CREATE TABLE tx(y, z);
1438*c8ce3972Sdan  INSERT INTO tx VALUES('Ayutthaya', 'Beijing');
1439*c8ce3972Sdan  INSERT INTO tx VALUES('London', 'Tokyo');
1440*c8ce3972Sdan} {}
1441*c8ce3972Sdandb close
1442*c8ce3972Sdanfor {set i 0} {$i<513} {incr i 3} {
1443*c8ce3972Sdan  testvfs tv -default 1 -szosfile $i
1444*c8ce3972Sdan  sqlite3 db test.db
1445*c8ce3972Sdan  do_execsql_test pager1-15.$i.1 {
1446*c8ce3972Sdan    SELECT * FROM tx;
1447*c8ce3972Sdan  } {Ayutthaya Beijing London Tokyo}
1448*c8ce3972Sdan  db close
1449*c8ce3972Sdan  tv delete
1450*c8ce3972Sdan}
1451*c8ce3972Sdan
1452*c8ce3972Sdan#-------------------------------------------------------------------------
1453*c8ce3972Sdan# Check that it is not possible to open a database file if the full path
1454*c8ce3972Sdan# to the associated journal file will be longer than sqlite3_vfs.mxPathname.
1455*c8ce3972Sdan#
1456*c8ce3972Sdantestvfs tv -default 1
1457*c8ce3972Sdantv script xOpenCb
1458*c8ce3972Sdantv filter xOpen
1459*c8ce3972Sdanproc xOpenCb {method filename} {
1460*c8ce3972Sdan  set ::file_len [string length $filename]
1461*c8ce3972Sdan}
1462*c8ce3972Sdansqlite3 db test.db
1463*c8ce3972Sdandb close
1464*c8ce3972Sdantv delete
1465*c8ce3972Sdan
1466*c8ce3972Sdanfor {set ii [expr $::file_len-5]} {$ii < [expr $::file_len+20]} {incr ii} {
1467*c8ce3972Sdan  testvfs tv -default 1 -mxpathname $ii
1468*c8ce3972Sdan
1469*c8ce3972Sdan  # The length of the full path to file "test.db-journal" is ($::file_len+8).
1470*c8ce3972Sdan  # If the configured sqlite3_vfs.mxPathname value greater than or equal to
1471*c8ce3972Sdan  # this, then the file can be opened. Otherwise, it cannot.
1472*c8ce3972Sdan  #
1473*c8ce3972Sdan  if {$ii >= [expr $::file_len+8]} {
1474*c8ce3972Sdan    set res {0 {}}
1475*c8ce3972Sdan  } else {
1476*c8ce3972Sdan    set res {1 {unable to open database file}}
1477*c8ce3972Sdan  }
1478*c8ce3972Sdan
1479*c8ce3972Sdan  do_test pager1-16.1.$ii {
1480*c8ce3972Sdan    list [catch { sqlite3 db test.db } msg] $msg
1481*c8ce3972Sdan  } $res
1482*c8ce3972Sdan
1483*c8ce3972Sdan  catch {db close}
1484*c8ce3972Sdan  tv delete
1485*c8ce3972Sdan}
1486*c8ce3972Sdan
1487*c8ce3972Sdan#-------------------------------------------------------------------------
1488*c8ce3972Sdan# Test "PRAGMA omit_readlock".
1489*c8ce3972Sdan#
1490*c8ce3972Sdan#   pager1-17.$tn.1.*: Test that if a second connection has an open
1491*c8ce3972Sdan#                      read-transaction, it is not usually possible to write
1492*c8ce3972Sdan#                      the database.
1493*c8ce3972Sdan#
1494*c8ce3972Sdan#   pager1-17.$tn.2.*: Test that if the second connection was opened with
1495*c8ce3972Sdan#                      the SQLITE_OPEN_READONLY flag, and
1496*c8ce3972Sdan#                      "PRAGMA omit_readlock = 1" is executed before attaching
1497*c8ce3972Sdan#                      the database and opening a read-transaction on it, it is
1498*c8ce3972Sdan#                      possible to write the db.
1499*c8ce3972Sdan#
1500*c8ce3972Sdan#   pager1-17.$tn.3.*: Test that if the second connection was *not* opened with
1501*c8ce3972Sdan#                      the SQLITE_OPEN_READONLY flag, executing
1502*c8ce3972Sdan#                      "PRAGMA omit_readlock = 1" has no effect.
1503*c8ce3972Sdan#
1504*c8ce3972Sdando_multiclient_test tn {
1505*c8ce3972Sdan  do_test pager1-17.$tn.1.1 {
1506*c8ce3972Sdan    sql1 {
1507*c8ce3972Sdan      CREATE TABLE t1(a, b);
1508*c8ce3972Sdan      INSERT INTO t1 VALUES(1, 2);
1509*c8ce3972Sdan    }
1510*c8ce3972Sdan    sql2 {
1511*c8ce3972Sdan      BEGIN;
1512*c8ce3972Sdan      SELECT * FROM t1;
1513*c8ce3972Sdan    }
1514*c8ce3972Sdan  } {1 2}
1515*c8ce3972Sdan  do_test pager1-17.$tn.1.2 {
1516*c8ce3972Sdan    csql1 { INSERT INTO t1 VALUES(3, 4) }
1517*c8ce3972Sdan  } {1 {database is locked}}
1518*c8ce3972Sdan  do_test pager1-17.$tn.1.3 {
1519*c8ce3972Sdan    sql2 { COMMIT }
1520*c8ce3972Sdan    sql1 { INSERT INTO t1 VALUES(3, 4) }
1521*c8ce3972Sdan  } {}
1522*c8ce3972Sdan
1523*c8ce3972Sdan  do_test pager1-17.$tn.2.1 {
1524*c8ce3972Sdan    code2 {
1525*c8ce3972Sdan      db2 close
1526*c8ce3972Sdan      sqlite3 db2 :memory: -readonly 1
1527*c8ce3972Sdan    }
1528*c8ce3972Sdan    sql2 {
1529*c8ce3972Sdan      PRAGMA omit_readlock = 1;
1530*c8ce3972Sdan      ATTACH 'test.db' AS two;
1531*c8ce3972Sdan      BEGIN;
1532*c8ce3972Sdan      SELECT * FROM t1;
1533*c8ce3972Sdan    }
1534*c8ce3972Sdan  } {1 2 3 4}
1535*c8ce3972Sdan  do_test pager1-17.$tn.2.2 { sql1 "INSERT INTO t1 VALUES(5, 6)" } {}
1536*c8ce3972Sdan  do_test pager1-17.$tn.2.3 { sql2 "SELECT * FROM t1" }            {1 2 3 4}
1537*c8ce3972Sdan  do_test pager1-17.$tn.2.4 { sql2 "COMMIT ; SELECT * FROM t1" }   {1 2 3 4 5 6}
1538*c8ce3972Sdan
1539*c8ce3972Sdan  do_test pager1-17.$tn.3.1 {
1540*c8ce3972Sdan    code2 {
1541*c8ce3972Sdan      db2 close
1542*c8ce3972Sdan      sqlite3 db2 :memory:
1543*c8ce3972Sdan    }
1544*c8ce3972Sdan    sql2 {
1545*c8ce3972Sdan      PRAGMA omit_readlock = 1;
1546*c8ce3972Sdan      ATTACH 'test.db' AS two;
1547*c8ce3972Sdan      BEGIN;
1548*c8ce3972Sdan      SELECT * FROM t1;
1549*c8ce3972Sdan    }
1550*c8ce3972Sdan  } {1 2 3 4 5 6}
1551*c8ce3972Sdan  do_test pager1-17.$tn.3.2 {
1552*c8ce3972Sdan    csql1 { INSERT INTO t1 VALUES(3, 4) }
1553*c8ce3972Sdan  } {1 {database is locked}}
1554*c8ce3972Sdan  do_test pager1-17.$tn.3.3 { sql2 COMMIT } {}
1555*c8ce3972Sdan}
1556*c8ce3972Sdan
1557*c8ce3972Sdan#-------------------------------------------------------------------------
1558*c8ce3972Sdan# Test the pagers response to the b-tree layer requesting illegal page
1559*c8ce3972Sdan# numbers:
1560*c8ce3972Sdan#
1561*c8ce3972Sdan#   + The locking page,
1562*c8ce3972Sdan#   + Page 0,
1563*c8ce3972Sdan#   + A page with a page number greater than (2^31-1).
1564*c8ce3972Sdan#
1565*c8ce3972Sdando_test pager1-18.1 {
1566*c8ce3972Sdan  faultsim_delete_and_reopen
1567*c8ce3972Sdan  db func a_string a_string
1568*c8ce3972Sdan  execsql {
1569*c8ce3972Sdan    PRAGMA page_size = 1024;
1570*c8ce3972Sdan    CREATE TABLE t1(a, b);
1571*c8ce3972Sdan    INSERT INTO t1 VALUES(a_string(500), a_string(200));
1572*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1573*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1574*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1575*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1576*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1577*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1578*c8ce3972Sdan    INSERT INTO t1 SELECT a_string(500), a_string(200) FROM t1;
1579*c8ce3972Sdan  }
1580*c8ce3972Sdan} {}
1581*c8ce3972Sdando_test pager1-18.2 {
1582*c8ce3972Sdan  set root [db one "SELECT rootpage FROM sqlite_master"]
1583*c8ce3972Sdan  set lockingpage [expr (0x10000/1024) + 1]
1584*c8ce3972Sdan  execsql {
1585*c8ce3972Sdan    PRAGMA writable_schema = 1;
1586*c8ce3972Sdan    UPDATE sqlite_master SET rootpage = $lockingpage;
1587*c8ce3972Sdan  }
1588*c8ce3972Sdan  sqlite3 db2 test.db
1589*c8ce3972Sdan  catchsql { SELECT count(*) FROM t1 } db2
1590*c8ce3972Sdan} {1 {database disk image is malformed}}
1591*c8ce3972Sdandb2 close
1592*c8ce3972Sdando_test pager1-18.3 {
1593*c8ce3972Sdan  execsql {
1594*c8ce3972Sdan    CREATE TABLE t2(x);
1595*c8ce3972Sdan    INSERT INTO t2 VALUES(a_string(5000));
1596*c8ce3972Sdan  }
1597*c8ce3972Sdan  set pgno [expr ([file size test.db] / 1024)-2]
1598*c8ce3972Sdan  hexio_write test.db [expr ($pgno-1)*1024] 00000000
1599*c8ce3972Sdan  sqlite3 db2 test.db
1600*c8ce3972Sdan  catchsql { SELECT length(x) FROM t2 } db2
1601*c8ce3972Sdan} {1 {database disk image is malformed}}
1602*c8ce3972Sdandb2 close
1603*c8ce3972Sdando_test pager1-18.4 {
1604*c8ce3972Sdan  hexio_write test.db [expr ($pgno-1)*1024] 90000000
1605*c8ce3972Sdan  sqlite3 db2 test.db
1606*c8ce3972Sdan  catchsql { SELECT length(x) FROM t2 } db2
1607*c8ce3972Sdan} {1 {database disk image is malformed}}
1608*c8ce3972Sdandb2 close
1609*c8ce3972Sdando_test pager1-18.5 {
1610*c8ce3972Sdan  sqlite3 db ""
1611*c8ce3972Sdan  execsql {
1612*c8ce3972Sdan    CREATE TABLE t1(a, b);
1613*c8ce3972Sdan    CREATE TABLE t2(a, b);
1614*c8ce3972Sdan    PRAGMA writable_schema = 1;
1615*c8ce3972Sdan    UPDATE sqlite_master SET rootpage=5 WHERE tbl_name = 't1';
1616*c8ce3972Sdan    PRAGMA writable_schema = 0;
1617*c8ce3972Sdan    ALTER TABLE t1 RENAME TO x1;
1618*c8ce3972Sdan  }
1619*c8ce3972Sdan  catchsql { SELECT * FROM x1 }
1620*c8ce3972Sdan} {}
1621*c8ce3972Sdandb close
1622*c8ce3972Sdan
1623d353331aSdanfinish_test
1624*c8ce3972Sdan
1625