xref: /sqlite-3.40.0/test/pager1.test (revision 273f3f0d)
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#
46ec6ffc1aSdan#
47b0ac3e3aSdan
4853f04f3bSdanset a_string_counter 1
4953f04f3bSdanproc a_string {n} {
5053f04f3bSdan  global a_string_counter
5153f04f3bSdan  incr a_string_counter
5253f04f3bSdan  string range [string repeat "${a_string_counter}." $n] 1 $n
5353f04f3bSdan}
5453f04f3bSdandb func a_string a_string
5553f04f3bSdan
56a4a9095eSdando_multiclient_test tn {
57e91a54e1Sdan
58e91a54e1Sdan  # Create and populate a database table using connection [db]. Check
59e91a54e1Sdan  # that connections [db2] and [db3] can see the schema and content.
60e91a54e1Sdan  #
61e91a54e1Sdan  do_test pager1-$tn.1 {
62e91a54e1Sdan    sql1 {
63e91a54e1Sdan      CREATE TABLE t1(a PRIMARY KEY, b);
64e91a54e1Sdan      CREATE INDEX i1 ON t1(b);
65e91a54e1Sdan      INSERT INTO t1 VALUES(1, 'one');
66e91a54e1Sdan      INSERT INTO t1 VALUES(2, 'two');
67e91a54e1Sdan    }
68e91a54e1Sdan  } {}
69e91a54e1Sdan  do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
70e91a54e1Sdan  do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two}
71e91a54e1Sdan
72e91a54e1Sdan  # Open a transaction and add a row using [db]. This puts [db] in
73e91a54e1Sdan  # RESERVED state. Check that connections [db2] and [db3] can still
74e91a54e1Sdan  # read the database content as it was before the transaction was
75e91a54e1Sdan  # opened. [db] should see the inserted row.
76e91a54e1Sdan  #
77e91a54e1Sdan  do_test pager1-$tn.4 {
78e91a54e1Sdan    sql1 {
79e91a54e1Sdan      BEGIN;
80e91a54e1Sdan        INSERT INTO t1 VALUES(3, 'three');
81e91a54e1Sdan    }
82e91a54e1Sdan  } {}
83e91a54e1Sdan  do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two}
84e91a54e1Sdan  do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
85e91a54e1Sdan
86e91a54e1Sdan  # [db] still has an open write transaction. Check that this prevents
87e91a54e1Sdan  # other connections (specifically [db2]) from writing to the database.
88e91a54e1Sdan  #
89e91a54e1Sdan  # Even if [db2] opens a transaction first, it may not write to the
90e91a54e1Sdan  # database. After the attempt to write the db within a transaction,
91e91a54e1Sdan  # [db2] is left with an open transaction, but not a read-lock on
92e91a54e1Sdan  # the main database. So it does not prevent [db] from committing.
93e91a54e1Sdan  #
94e91a54e1Sdan  do_test pager1-$tn.8 {
95e91a54e1Sdan    csql2 { UPDATE t1 SET a = a + 10 }
96e91a54e1Sdan  } {1 {database is locked}}
97e91a54e1Sdan  do_test pager1-$tn.9 {
98e91a54e1Sdan    csql2 {
99e91a54e1Sdan      BEGIN;
100e91a54e1Sdan      UPDATE t1 SET a = a + 10;
101e91a54e1Sdan    }
102e91a54e1Sdan  } {1 {database is locked}}
103e91a54e1Sdan
104e91a54e1Sdan  # Have [db] commit its transactions. Check the other connections can
105e91a54e1Sdan  # now see the new database content.
106e91a54e1Sdan  #
107e91a54e1Sdan  do_test pager1-$tn.10 { sql1 { COMMIT } } {}
108e91a54e1Sdan  do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three}
109e91a54e1Sdan  do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
110e91a54e1Sdan  do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
111e91a54e1Sdan
112e91a54e1Sdan  # Check that, as noted above, [db2] really did keep an open transaction
113e91a54e1Sdan  # after the attempt to write the database failed.
114e91a54e1Sdan  #
115e91a54e1Sdan  do_test pager1-$tn.14 {
116e91a54e1Sdan    csql2 { BEGIN }
117e91a54e1Sdan  } {1 {cannot start a transaction within a transaction}}
118e91a54e1Sdan  do_test pager1-$tn.15 { sql2 { ROLLBACK } } {}
119e91a54e1Sdan
120e91a54e1Sdan  # Have [db2] open a transaction and take a read-lock on the database.
121e91a54e1Sdan  # Check that this prevents [db] from writing to the database (outside
122e91a54e1Sdan  # of any transaction). After this fails, check that [db3] can read
123e91a54e1Sdan  # the db (showing that [db] did not take a PENDING lock etc.)
124e91a54e1Sdan  #
125e91a54e1Sdan  do_test pager1-$tn.15 {
126e91a54e1Sdan    sql2 { BEGIN; SELECT * FROM t1; }
127e91a54e1Sdan  } {1 one 2 two 3 three}
128e91a54e1Sdan  do_test pager1-$tn.16 {
129e91a54e1Sdan    csql1 { UPDATE t1 SET a = a + 10 }
130e91a54e1Sdan  } {1 {database is locked}}
131e91a54e1Sdan  do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
132e91a54e1Sdan
133e91a54e1Sdan  # This time, have [db] open a transaction before writing the database.
134e91a54e1Sdan  # This works - [db] gets a RESERVED lock which does not conflict with
135e91a54e1Sdan  # the SHARED lock [db2] is holding.
136e91a54e1Sdan  #
137e91a54e1Sdan  do_test pager1-$tn.18 {
138e91a54e1Sdan    sql1 {
139e91a54e1Sdan      BEGIN;
140e91a54e1Sdan      UPDATE t1 SET a = a + 10;
141e91a54e1Sdan    }
142e91a54e1Sdan  } {}
143e91a54e1Sdan  do_test pager1-$tn-19 {
144e91a54e1Sdan    sql1 { PRAGMA lock_status }
145e91a54e1Sdan  } {main reserved temp closed}
146e91a54e1Sdan  do_test pager1-$tn-20 {
147e91a54e1Sdan    sql2 { PRAGMA lock_status }
148e91a54e1Sdan  } {main shared temp closed}
149e91a54e1Sdan
150e91a54e1Sdan  # Check that all connections can still read the database. Only [db] sees
151e91a54e1Sdan  # the updated content (as the transaction has not been committed yet).
152e91a54e1Sdan  #
153e91a54e1Sdan  do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three}
154e91a54e1Sdan  do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three}
155e91a54e1Sdan  do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three}
156e91a54e1Sdan
157e91a54e1Sdan  # Because [db2] still has the SHARED lock, [db] is unable to commit the
158e91a54e1Sdan  # transaction. If it tries, an error is returned and the connection
159e91a54e1Sdan  # upgrades to a PENDING lock.
160e91a54e1Sdan  #
161e91a54e1Sdan  # Once this happens, [db] can read the database and see the new content,
162e91a54e1Sdan  # [db2] (still holding SHARED) can still read the old content, but [db3]
163e91a54e1Sdan  # (not holding any lock) is prevented by [db]'s PENDING from reading
164e91a54e1Sdan  # the database.
165e91a54e1Sdan  #
166e91a54e1Sdan  do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}}
167e91a54e1Sdan  do_test pager1-$tn-25 {
168e91a54e1Sdan    sql1 { PRAGMA lock_status }
169e91a54e1Sdan  } {main pending temp closed}
170e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { SELECT * FROM t1  } } {11 one 12 two 13 three}
171e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1  } } {1 one 2 two 3 three}
172e91a54e1Sdan  do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
173e91a54e1Sdan
174e91a54e1Sdan  # Have [db2] commit its read transaction, releasing the SHARED lock it
175e91a54e1Sdan  # is holding. Now, neither [db2] nor [db3] may read the database (as [db]
176e91a54e1Sdan  # is still holding a PENDING).
177e91a54e1Sdan  #
178e91a54e1Sdan  do_test pager1-$tn.29 { sql2 { COMMIT } } {}
179e91a54e1Sdan  do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
180e91a54e1Sdan  do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}}
181e91a54e1Sdan
182e91a54e1Sdan  # [db] is now able to commit the transaction. Once the transaction is
183e91a54e1Sdan  # committed, all three connections can read the new content.
184e91a54e1Sdan  #
185e91a54e1Sdan  do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {}
186e91a54e1Sdan  do_test pager1-$tn.26 { sql1 { COMMIT } } {}
187e91a54e1Sdan  do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three}
188e91a54e1Sdan  do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three}
189e91a54e1Sdan  do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three}
190e91a54e1Sdan}
191e91a54e1Sdan
19253f04f3bSdan#-------------------------------------------------------------------------
19353f04f3bSdan# Savepoint related test cases.
19453f04f3bSdan#
195dca321aeSdan# pager1-3.1.2.*: Force a savepoint rollback to cause the database file
196dca321aeSdan#                 to grow.
197dca321aeSdan#
198*273f3f0dSdan# pager1-3.1.3.*: Use a journal created in synchronous=off mode as part
199*273f3f0dSdan#                 of a savepoint rollback.
200*273f3f0dSdan#
2010e986f51Sdando_test pager1-3.1.1 {
202b0ac3e3aSdan  faultsim_delete_and_reopen
203b0ac3e3aSdan  execsql {
204b0ac3e3aSdan    CREATE TABLE t1(a PRIMARY KEY, b);
205b0ac3e3aSdan    CREATE TABLE counter(
206b0ac3e3aSdan      i CHECK (i<5),
207b0ac3e3aSdan      u CHECK (u<10)
208b0ac3e3aSdan    );
209b0ac3e3aSdan    INSERT INTO counter VALUES(0, 0);
210b0ac3e3aSdan    CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
211b0ac3e3aSdan      UPDATE counter SET i = i+1;
212b0ac3e3aSdan    END;
213b0ac3e3aSdan    CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
214b0ac3e3aSdan      UPDATE counter SET u = u+1;
215b0ac3e3aSdan    END;
216b0ac3e3aSdan  }
217b0ac3e3aSdan  execsql { SELECT * FROM counter }
218b0ac3e3aSdan} {0 0}
219b0ac3e3aSdan
2200e986f51Sdando_execsql_test pager1-3.1.2 {
221b0ac3e3aSdan  BEGIN;
222b0ac3e3aSdan    INSERT INTO t1 VALUES(1, randomblob(1500));
223b0ac3e3aSdan    INSERT INTO t1 VALUES(2, randomblob(1500));
224b0ac3e3aSdan    INSERT INTO t1 VALUES(3, randomblob(1500));
225b0ac3e3aSdan    SELECT * FROM counter;
226b0ac3e3aSdan} {3 0}
2270e986f51Sdando_catchsql_test pager1-3.1.3 {
228b0ac3e3aSdan    INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
229b0ac3e3aSdan} {1 {constraint failed}}
230b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
231b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
232b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {}
233b0ac3e3aSdan
234*273f3f0dSdanforeach {tn sql tcl} {
235*273f3f0dSdan  9  { PRAGMA synchronous = NORMAL } { }
236*273f3f0dSdan
237*273f3f0dSdan  7  { PRAGMA synchronous = NORMAL } {
238*273f3f0dSdan    testvfs tv -default 1
239*273f3f0dSdan    tv devchar safe_append
240*273f3f0dSdan  }
241*273f3f0dSdan  8  { PRAGMA synchronous = FULL } { }
242*273f3f0dSdan  10 { PRAGMA synchronous = OFF } { }
243*273f3f0dSdan  11 { PRAGMA synchronous = FULL ; PRAGMA fullfsync = 1 } { }
244*273f3f0dSdan} {
245*273f3f0dSdan  do_test pager1-3.$tn.1 {
246*273f3f0dSdan    eval $tcl
247dca321aeSdan    faultsim_delete_and_reopen
248dca321aeSdan    db func a_string a_string
249*273f3f0dSdan    execsql $sql
250dca321aeSdan    execsql {
251dca321aeSdan      PRAGMA auto_vacuum = 2;
252dca321aeSdan      PRAGMA cache_size = 10;
253dca321aeSdan      CREATE TABLE z(x INTEGER PRIMARY KEY, y);
254dca321aeSdan      BEGIN;
255dca321aeSdan        INSERT INTO z VALUES(NULL, a_string(800));
256dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   2
257dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   4
258dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --   8
259dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  16
260dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  32
261dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     --  64
262dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 128
263dca321aeSdan        INSERT INTO z SELECT NULL, a_string(800) FROM z;     -- 256
264dca321aeSdan      COMMIT;
265dca321aeSdan    }
266dca321aeSdan    execsql { PRAGMA auto_vacuum }
267dca321aeSdan  } {2}
268*273f3f0dSdan  do_execsql_test pager1-3.$tn.2 {
269dca321aeSdan    BEGIN;
270dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
271dca321aeSdan      INSERT INTO z VALUES(NULL, a_string(800));
272dca321aeSdan      SAVEPOINT one;
273*273f3f0dSdan        UPDATE z SET y = NULL WHERE x>256;
274dca321aeSdan        PRAGMA incremental_vacuum;
275dca321aeSdan        SELECT count(*) FROM z WHERE x < 100;
276dca321aeSdan      ROLLBACK TO one;
277dca321aeSdan    COMMIT;
278dca321aeSdan  } {99}
279dca321aeSdan
280*273f3f0dSdan  do_execsql_test pager1-3.$tn.3 {
281*273f3f0dSdan    BEGIN;
282*273f3f0dSdan      SAVEPOINT one;
283*273f3f0dSdan        UPDATE z SET y = y||x;
284*273f3f0dSdan      ROLLBACK TO one;
285*273f3f0dSdan    COMMIT;
286*273f3f0dSdan    SELECT count(*) FROM z;
287*273f3f0dSdan  } {258}
288*273f3f0dSdan
289*273f3f0dSdan  do_execsql_test pager1-3.$tn.4 {
290*273f3f0dSdan    SAVEPOINT one;
291*273f3f0dSdan      UPDATE z SET y = y||x;
292*273f3f0dSdan    ROLLBACK TO one;
293*273f3f0dSdan  } {}
294*273f3f0dSdan  do_execsql_test pager1-3.$tn.5 {
295*273f3f0dSdan    SELECT count(*) FROM z;
296*273f3f0dSdan    RELEASE one;
297*273f3f0dSdan    PRAGMA integrity_check;
298*273f3f0dSdan  } {258 ok}
299*273f3f0dSdan
300*273f3f0dSdan  db close
301*273f3f0dSdan  catch { tv delete }
302*273f3f0dSdan}
303*273f3f0dSdan
30453f04f3bSdan#-------------------------------------------------------------------------
30553f04f3bSdan# Hot journal rollback related test cases.
30653f04f3bSdan#
30753f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid
30853f04f3bSdan#               journal files.
30953f04f3bSdan#
31053f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a
31153f04f3bSdan#               hot-journal file appears to be corrupt (checksum does not
31253f04f3bSdan#               compute) the associated journal is rolled back (and no
31353f04f3bSdan#               xAccess() call to check for the presence of any master
31453f04f3bSdan#               journal file is made).
31553f04f3bSdan#
316de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
317de4996e2Sdan#               page-size or sector-size in the journal header appear to
318de4996e2Sdan#               be invalid (too large, too small or not a power of 2).
319de4996e2Sdan#
320e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master
321e08341c6Sdan#               journal pointer generated in various "PRAGMA synchronous"
322e08341c6Sdan#               modes.
323e08341c6Sdan#
324e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
325e08341c6Sdan#               journal-record for which the checksum fails.
326e08341c6Sdan#
327ec6ffc1aSdan# pager1.4.6.*: Test that when rolling back a hot-journal that contains a
328ec6ffc1aSdan#               master journal pointer, the master journal file is deleted
329ec6ffc1aSdan#               after all the hot-journals that refer to it are deleted.
330ec6ffc1aSdan#
33153f04f3bSdando_test pager1.4.1.1 {
33253f04f3bSdan  faultsim_delete_and_reopen
33353f04f3bSdan  execsql {
33453f04f3bSdan    CREATE TABLE x(y, z);
33553f04f3bSdan    INSERT INTO x VALUES(1, 2);
33653f04f3bSdan  }
33753f04f3bSdan  set fd [open test.db-journal w]
33853f04f3bSdan  puts -nonewline $fd "helloworld"
33953f04f3bSdan  close $fd
34053f04f3bSdan  file exists test.db-journal
34153f04f3bSdan} {1}
34253f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2}
34353f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0}
34453f04f3bSdan
34553f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite
34653f04f3bSdan# deletes the master-journal to commit a multi-file transaction.
34753f04f3bSdan#
34853f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets
34953f04f3bSdan# up the file system to contain two databases, two hot-journal files and
35053f04f3bSdan# a master-journal.
35153f04f3bSdan#
35253f04f3bSdando_test pager1.4.2.1 {
35353f04f3bSdan  testvfs tstvfs -default 1
35453f04f3bSdan  tstvfs filter xDelete
35553f04f3bSdan  tstvfs script xDeleteCallback
35653f04f3bSdan  proc xDeleteCallback {method file args} {
35753f04f3bSdan    set file [file tail $file]
35853f04f3bSdan    if { [string match *mj* $file] } { faultsim_save }
35953f04f3bSdan  }
36053f04f3bSdan  faultsim_delete_and_reopen
36153f04f3bSdan  db func a_string a_string
36253f04f3bSdan  execsql {
36353f04f3bSdan    ATTACH 'test.db2' AS aux;
36453f04f3bSdan    PRAGMA journal_mode = DELETE;
36553f04f3bSdan    PRAGMA main.cache_size = 10;
36653f04f3bSdan    PRAGMA aux.cache_size = 10;
36753f04f3bSdan    CREATE TABLE t1(a UNIQUE, b UNIQUE);
36853f04f3bSdan    CREATE TABLE aux.t2(a UNIQUE, b UNIQUE);
36953f04f3bSdan    INSERT INTO t1 VALUES(a_string(200), a_string(300));
37053f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
37153f04f3bSdan    INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
37253f04f3bSdan    INSERT INTO t2 SELECT * FROM t1;
37353f04f3bSdan    BEGIN;
37453f04f3bSdan      INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1;
37553f04f3bSdan      INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1;
37653f04f3bSdan      INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1;
37753f04f3bSdan      INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1;
37853f04f3bSdan      REPLACE INTO t2 SELECT * FROM t1;
37953f04f3bSdan    COMMIT;
38053f04f3bSdan  }
38153f04f3bSdan  db close
38253f04f3bSdan  tstvfs delete
38353f04f3bSdan} {}
38453f04f3bSdando_test pager1.4.2.2 {
38553f04f3bSdan  faultsim_restore_and_reopen
38653f04f3bSdan  execsql {
38753f04f3bSdan    SELECT count(*) FROM t1;
38853f04f3bSdan    PRAGMA integrity_check;
38953f04f3bSdan  }
39053f04f3bSdan} {4 ok}
39153f04f3bSdando_test pager1.4.2.3 {
39253f04f3bSdan  faultsim_restore_and_reopen
39353f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
39453f04f3bSdan  execsql {
39553f04f3bSdan    SELECT count(*) FROM t1;
39653f04f3bSdan    PRAGMA integrity_check;
39753f04f3bSdan  }
39853f04f3bSdan} {64 ok}
39953f04f3bSdando_test pager1.4.2.4 {
40053f04f3bSdan  faultsim_restore_and_reopen
40153f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
40253f04f3bSdan  execsql {
40353f04f3bSdan    SELECT count(*) FROM t1;
40453f04f3bSdan    PRAGMA integrity_check;
40553f04f3bSdan  }
40653f04f3bSdan} {4 ok}
40753f04f3bSdando_test pager1.4.2.5 {
40853f04f3bSdan  faultsim_restore_and_reopen
40953f04f3bSdan  hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456
41053f04f3bSdan  foreach f [glob test.db-mj*] { file delete -force $f }
41153f04f3bSdan  execsql {
41253f04f3bSdan    SELECT count(*) FROM t1;
41353f04f3bSdan    PRAGMA integrity_check;
41453f04f3bSdan  }
41553f04f3bSdan} {4 ok}
41653f04f3bSdan
417de4996e2Sdando_test pager1.4.3.1 {
418de4996e2Sdan  testvfs tstvfs -default 1
419de4996e2Sdan  tstvfs filter xSync
420de4996e2Sdan  tstvfs script xSyncCallback
421de4996e2Sdan  proc xSyncCallback {method file args} {
422de4996e2Sdan    set file [file tail $file]
423de4996e2Sdan    if { 0==[string match *journal $file] } { faultsim_save }
424de4996e2Sdan  }
425de4996e2Sdan  faultsim_delete_and_reopen
426de4996e2Sdan  execsql {
427de4996e2Sdan    PRAGMA journal_mode = DELETE;
428de4996e2Sdan    CREATE TABLE t1(a, b);
429de4996e2Sdan    INSERT INTO t1 VALUES(1, 2);
430de4996e2Sdan    INSERT INTO t1 VALUES(3, 4);
431de4996e2Sdan  }
432de4996e2Sdan  db close
433de4996e2Sdan  tstvfs delete
434de4996e2Sdan} {}
435de4996e2Sdan
436de4996e2Sdanforeach {tn ofst value result} {
437de4996e2Sdan          2   20    31       {1 2 3 4}
438de4996e2Sdan          3   20    32       {1 2 3 4}
439de4996e2Sdan          4   20    33       {1 2 3 4}
440de4996e2Sdan          5   20    65536    {1 2 3 4}
441de4996e2Sdan          6   20    131072   {1 2 3 4}
442de4996e2Sdan
443de4996e2Sdan          7   24    511      {1 2 3 4}
444de4996e2Sdan          8   24    513      {1 2 3 4}
445de4996e2Sdan          9   24    65536    {1 2 3 4}
446de4996e2Sdan
447de4996e2Sdan         10   32    65536    {1 2}
448de4996e2Sdan} {
449de4996e2Sdan  do_test pager1.4.3.$tn {
450de4996e2Sdan    faultsim_restore_and_reopen
451de4996e2Sdan    hexio_write test.db-journal $ofst [format %.8x $value]
452de4996e2Sdan    execsql { SELECT * FROM t1 }
453de4996e2Sdan  } $result
454de4996e2Sdan}
455de4996e2Sdandb close
456de4996e2Sdan
457e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal
458e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the
459e08341c6Sdan# file-system is saved just before the xDelete() call to remove the
460e08341c6Sdan# master journal file from the file-system.
461e08341c6Sdan#
462e08341c6Sdantestvfs tv -default 1
463e08341c6Sdantv script copy_on_mj_delete
464e08341c6Sdanset ::mj_filename_length 0
465e08341c6Sdanproc copy_on_mj_delete {method filename args} {
466e08341c6Sdan  if {[string match *mj* [file tail $filename]]} {
467e08341c6Sdan    set ::mj_filename_length [string length $filename]
468e08341c6Sdan    faultsim_save
469e08341c6Sdan  }
470e08341c6Sdan  return SQLITE_OK
471e08341c6Sdan}
472e08341c6Sdan
473e08341c6Sdanset pwd [pwd]
474e08341c6Sdanforeach {tn1 tcl} {
475e08341c6Sdan  1 { set prefix "test.db" }
476e08341c6Sdan  2 {
477e08341c6Sdan    # This test depends on the underlying VFS being able to open paths
478e08341c6Sdan    # 512 bytes in length. The idea is to create a hot-journal file that
479e08341c6Sdan    # contains a master-journal pointer so large that it could contain
480e08341c6Sdan    # a valid page record (if the file page-size is 512 bytes). So as to
481e08341c6Sdan    # make sure SQLite doesn't get confused by this.
482e08341c6Sdan    #
483e08341c6Sdan    set nPadding [expr 511 - $::mj_filename_length]
484e08341c6Sdan
485e08341c6Sdan    # We cannot just create a really long database file name to open, as
486e08341c6Sdan    # Linux limits a single component of a path to 255 bytes by default
487e08341c6Sdan    # (and presumably other systems have limits too). So create a directory
488e08341c6Sdan    # hierarchy to work in.
489e08341c6Sdan    #
490e08341c6Sdan    set dirname "d123456789012345678901234567890/"
491e08341c6Sdan    set nDir [expr $nPadding / 32]
492e08341c6Sdan    if { $nDir } {
493e08341c6Sdan      set p [string repeat $dirname $nDir]
494e08341c6Sdan      file mkdir $p
495e08341c6Sdan      cd $p
496e08341c6Sdan    }
497e08341c6Sdan
498e08341c6Sdan    set padding [string repeat x [expr $nPadding %32]]
499e08341c6Sdan    set prefix "test.db${padding}"
500e08341c6Sdan  }
501e08341c6Sdan} {
502e08341c6Sdan  eval $tcl
503e08341c6Sdan  foreach {tn2 sql} {
504e08341c6Sdan    o {
505e08341c6Sdan      PRAGMA main.synchronous=OFF;
506e08341c6Sdan      PRAGMA aux.synchronous=OFF;
507e08341c6Sdan    }
508e08341c6Sdan    o512 {
509e08341c6Sdan      PRAGMA main.synchronous=OFF;
510e08341c6Sdan      PRAGMA aux.synchronous=OFF;
511e08341c6Sdan      PRAGMA main.page_size = 512;
512e08341c6Sdan      PRAGMA aux.page_size = 512;
513e08341c6Sdan    }
514e08341c6Sdan    n {
515e08341c6Sdan      PRAGMA main.synchronous=NORMAL;
516e08341c6Sdan      PRAGMA aux.synchronous=NORMAL;
517e08341c6Sdan    }
518e08341c6Sdan    f {
519e08341c6Sdan      PRAGMA main.synchronous=FULL;
520e08341c6Sdan      PRAGMA aux.synchronous=FULL;
521e08341c6Sdan    }
522e08341c6Sdan  } {
523e08341c6Sdan
524e08341c6Sdan    set tn "${tn1}.${tn2}"
525e08341c6Sdan
526e08341c6Sdan    # Set up a connection to have two databases, test.db (main) and
527e08341c6Sdan    # test.db2 (aux). Then run a multi-file transaction on them. The
528e08341c6Sdan    # VFS will snapshot the file-system just before the master-journal
529e08341c6Sdan    # file is deleted to commit the transaction.
530e08341c6Sdan    #
531e08341c6Sdan    tv filter xDelete
532e08341c6Sdan    do_test pager1-4.4.$tn.1 {
533e08341c6Sdan      faultsim_delete_and_reopen $prefix
534e08341c6Sdan      execsql "
535e08341c6Sdan        ATTACH '${prefix}2' AS aux;
536e08341c6Sdan        $sql
537e08341c6Sdan        CREATE TABLE a(x);
538e08341c6Sdan        CREATE TABLE aux.b(x);
539e08341c6Sdan        INSERT INTO a VALUES('double-you');
540e08341c6Sdan        INSERT INTO a VALUES('why');
541e08341c6Sdan        INSERT INTO a VALUES('zed');
542e08341c6Sdan        INSERT INTO b VALUES('won');
543e08341c6Sdan        INSERT INTO b VALUES('too');
544e08341c6Sdan        INSERT INTO b VALUES('free');
545e08341c6Sdan      "
546e08341c6Sdan      execsql {
547e08341c6Sdan        BEGIN;
548e08341c6Sdan          INSERT INTO a SELECT * FROM b WHERE rowid<=3;
549e08341c6Sdan          INSERT INTO b SELECT * FROM a WHERE rowid<=3;
550e08341c6Sdan        COMMIT;
551e08341c6Sdan      }
552e08341c6Sdan    } {}
553e08341c6Sdan    tv filter {}
554e08341c6Sdan
555e08341c6Sdan    # Check that the transaction was committed successfully.
556e08341c6Sdan    #
557e08341c6Sdan    do_execsql_test pager1-4.4.$tn.2 {
558e08341c6Sdan      SELECT * FROM a
559e08341c6Sdan    } {double-you why zed won too free}
560e08341c6Sdan    do_execsql_test pager1-4.4.$tn.3 {
561e08341c6Sdan      SELECT * FROM b
562e08341c6Sdan    } {won too free double-you why zed}
563e08341c6Sdan
564e08341c6Sdan    # Restore the file-system and reopen the databases. Check that it now
565e08341c6Sdan    # appears that the transaction was not committed (because the file-system
566e08341c6Sdan    # was restored to the state where it had not been).
567e08341c6Sdan    #
568e08341c6Sdan    do_test pager1-4.4.$tn.4 {
569e08341c6Sdan      faultsim_restore_and_reopen $prefix
570e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
571e08341c6Sdan    } {}
572e08341c6Sdan    do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
573e08341c6Sdan    do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
574e08341c6Sdan
575e08341c6Sdan    # Restore the file-system again. This time, before reopening the databases,
576e08341c6Sdan    # delete the master-journal file from the file-system. It now appears that
577e08341c6Sdan    # the transaction was committed (no master-journal file == no rollback).
578e08341c6Sdan    #
579e08341c6Sdan    do_test pager1-4.4.$tn.7 {
580e08341c6Sdan      faultsim_restore_and_reopen $prefix
581e08341c6Sdan      foreach f [glob ${prefix}-mj*] { file delete -force $f }
582e08341c6Sdan      execsql "ATTACH '${prefix}2' AS aux"
583e08341c6Sdan    } {}
584e08341c6Sdan    do_execsql_test pager1-4.4.$tn.8 {
585e08341c6Sdan      SELECT * FROM a
586e08341c6Sdan    } {double-you why zed won too free}
587e08341c6Sdan    do_execsql_test pager1-4.4.$tn.9 {
588e08341c6Sdan      SELECT * FROM b
589e08341c6Sdan    } {won too free double-you why zed}
590e08341c6Sdan  }
591e08341c6Sdan
592e08341c6Sdan  cd $pwd
593e08341c6Sdan}
594e08341c6Sdandb close
595e08341c6Sdantv delete
5960e986f51Sdanfile delete -force $dirname
5970e986f51Sdan
5980e986f51Sdan
5990e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a
6000e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly
6010e986f51Sdan# two database pages (and page 1 - the change counter).
6020e986f51Sdan#
6030e986f51Sdantestvfs tv -default 1
6040e986f51Sdantv sectorsize 512
6050e986f51Sdantv script copy_on_journal_delete
6060e986f51Sdantv filter xDelete
6070e986f51Sdanset ::mj_filename_length 0
6080e986f51Sdanproc copy_on_journal_delete {method filename args} {
6090e986f51Sdan  if {[string match *journal $filename]} faultsim_save
6100e986f51Sdan  return SQLITE_OK
6110e986f51Sdan}
6120e986f51Sdanfaultsim_delete_and_reopen
6130e986f51Sdando_execsql_test pager1.4.5.1 {
6140e986f51Sdan  PRAGMA page_size = 1024;
6150e986f51Sdan  CREATE TABLE t1(a, b);
6160e986f51Sdan  CREATE TABLE t2(a, b);
6170e986f51Sdan  INSERT INTO t1 VALUES('I', 'II');
6180e986f51Sdan  INSERT INTO t2 VALUES('III', 'IV');
6190e986f51Sdan  BEGIN;
6200e986f51Sdan    INSERT INTO t1 VALUES(1, 2);
6210e986f51Sdan    INSERT INTO t2 VALUES(3, 4);
6220e986f51Sdan  COMMIT;
6230e986f51Sdan} {}
6240e986f51Sdantv filter {}
6250e986f51Sdan
6260e986f51Sdan# Check the transaction was committed:
6270e986f51Sdan#
6280e986f51Sdando_execsql_test pager1.4.5.2 {
6290e986f51Sdan  SELECT * FROM t1;
6300e986f51Sdan  SELECT * FROM t2;
6310e986f51Sdan} {I II 1 2 III IV 3 4}
6320e986f51Sdan
633ec6ffc1aSdan# Now try four tests:
6340e986f51Sdan#
6350e986f51Sdan#  pager1-4.5.3: Restore the file-system. Check that the whole transaction
6360e986f51Sdan#                is rolled back.
6370e986f51Sdan#
6380e986f51Sdan#  pager1-4.5.4: Restore the file-system. Corrupt the first record in the
6390e986f51Sdan#                journal. Check the transaction is not rolled back.
6400e986f51Sdan#
6410e986f51Sdan#  pager1-4.5.5: Restore the file-system. Corrupt the second record in the
6420e986f51Sdan#                journal. Check that the first record in the transaction is
6430e986f51Sdan#                played back, but not the second.
6440e986f51Sdan#
64510f5a50eSdan#  pager1-4.5.6: Restore the file-system. Try to open the database with a
64610f5a50eSdan#                readonly connection. This should fail, as a read-only
64710f5a50eSdan#                connection cannot roll back the database file.
64810f5a50eSdan#
6490e986f51Sdanfaultsim_restore_and_reopen
6500e986f51Sdando_execsql_test pager1.4.5.3 {
6510e986f51Sdan  SELECT * FROM t1;
6520e986f51Sdan  SELECT * FROM t2;
6530e986f51Sdan} {I II III IV}
6540e986f51Sdanfaultsim_restore_and_reopen
6550e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF
6560e986f51Sdando_execsql_test pager1.4.5.4 {
6570e986f51Sdan  SELECT * FROM t1;
6580e986f51Sdan  SELECT * FROM t2;
6590e986f51Sdan} {I II 1 2 III IV 3 4}
6600e986f51Sdanfaultsim_restore_and_reopen
6610e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF
6620e986f51Sdando_execsql_test pager1.4.5.5 {
6630e986f51Sdan  SELECT * FROM t1;
6640e986f51Sdan  SELECT * FROM t2;
6650e986f51Sdan} {I II III IV 3 4}
6660e986f51Sdan
66710f5a50eSdanfaultsim_restore_and_reopen
66810f5a50eSdandb close
66910f5a50eSdansqlite3 db test.db -readonly 1
67010f5a50eSdando_catchsql_test pager1.4.5.6 {
67110f5a50eSdan  SELECT * FROM t1;
67210f5a50eSdan  SELECT * FROM t2;
67310f5a50eSdan} {1 {disk I/O error}}
674ec6ffc1aSdandb close
675ec6ffc1aSdan
676ec6ffc1aSdan# Snapshot the file-system just before multi-file commit. Save the name
677ec6ffc1aSdan# of the master journal file in $::mj_filename.
678ec6ffc1aSdan#
679ec6ffc1aSdantv script copy_on_mj_delete
680ec6ffc1aSdantv filter xDelete
681ec6ffc1aSdanproc copy_on_mj_delete {method filename args} {
682ec6ffc1aSdan  if {[string match *mj* [file tail $filename]]} {
683ec6ffc1aSdan    set ::mj_filename $filename
684ec6ffc1aSdan    faultsim_save
685ec6ffc1aSdan  }
686ec6ffc1aSdan  return SQLITE_OK
687ec6ffc1aSdan}
688ec6ffc1aSdando_test pager1.4.6.1 {
689ec6ffc1aSdan  faultsim_delete_and_reopen
690ec6ffc1aSdan  execsql {
691ec6ffc1aSdan    ATTACH 'test.db2' AS two;
692ec6ffc1aSdan    CREATE TABLE t1(a, b);
693ec6ffc1aSdan    CREATE TABLE two.t2(a, b);
694ec6ffc1aSdan    INSERT INTO t1 VALUES(1, 't1.1');
695ec6ffc1aSdan    INSERT INTO t2 VALUES(1, 't2.1');
696ec6ffc1aSdan    BEGIN;
697ec6ffc1aSdan      UPDATE t1 SET b = 't1.2';
698ec6ffc1aSdan      UPDATE t2 SET b = 't2.2';
699ec6ffc1aSdan    COMMIT;
700ec6ffc1aSdan  }
701ec6ffc1aSdan  tv filter {}
702ec6ffc1aSdan  db close
703ec6ffc1aSdan} {}
704ec6ffc1aSdan
705ec6ffc1aSdanfaultsim_restore_and_reopen
706ec6ffc1aSdando_execsql_test pager1.4.6.2 { SELECT * FROM t1 }           {1 t1.1}
707ec6ffc1aSdando_test         pager1.4.6.3 { file exists $::mj_filename } {1}
708ec6ffc1aSdando_execsql_test pager1.4.6.4 {
709ec6ffc1aSdan  ATTACH 'test.db2' AS two;
710ec6ffc1aSdan  SELECT * FROM t2;
711ec6ffc1aSdan} {1 t2.1}
712ec6ffc1aSdando_test pager1.4.6.5 { file exists $::mj_filename } {0}
713ec6ffc1aSdan
714ec6ffc1aSdanfaultsim_restore_and_reopen
715ec6ffc1aSdandb close
716ec6ffc1aSdando_test pager1.4.6.8 {
717ec6ffc1aSdan  set ::mj_filename1 $::mj_filename
718ec6ffc1aSdan  tv filter xDelete
719ec6ffc1aSdan  sqlite3 db test.db2
720ec6ffc1aSdan  execsql {
721ec6ffc1aSdan    ATTACH 'test.db3' AS three;
722ec6ffc1aSdan    CREATE TABLE three.t3(a, b);
723ec6ffc1aSdan    INSERT INTO t3 VALUES(1, 't3.1');
724ec6ffc1aSdan    BEGIN;
725ec6ffc1aSdan      UPDATE t2 SET b = 't2.3';
726ec6ffc1aSdan      UPDATE t3 SET b = 't3.3';
727ec6ffc1aSdan    COMMIT;
728ec6ffc1aSdan  }
729ec6ffc1aSdan  expr {$::mj_filename1 != $::mj_filename}
730ec6ffc1aSdan} {1}
731ec6ffc1aSdanfaultsim_restore_and_reopen
732ec6ffc1aSdantv filter {}
733ec6ffc1aSdan
734ec6ffc1aSdan# The file-system now contains:
735ec6ffc1aSdan#
736ec6ffc1aSdan#   * three databases
737ec6ffc1aSdan#   * three hot-journal files
738ec6ffc1aSdan#   * two master-journal files.
739ec6ffc1aSdan#
740ec6ffc1aSdan# The hot-journals associated with test.db2 and test.db3 point to
741ec6ffc1aSdan# master journal $::mj_filename. The hot-journal file associated with
742ec6ffc1aSdan# test.db points to master journal $::mj_filename1. So reading from
743ec6ffc1aSdan# test.db should delete $::mj_filename1.
744ec6ffc1aSdan#
745ec6ffc1aSdando_test pager1.4.6.9 {
746ec6ffc1aSdan  lsort [glob test.db*]
747ec6ffc1aSdan} [lsort [list                                           \
748ec6ffc1aSdan  test.db test.db2 test.db3                              \
749ec6ffc1aSdan  test.db-journal test.db2-journal test.db3-journal      \
750ec6ffc1aSdan  [file tail $::mj_filename] [file tail $::mj_filename1]
751ec6ffc1aSdan]]
752ec6ffc1aSdan
753ec6ffc1aSdan# The master-journal $::mj_filename1 contains pointers to test.db and
754ec6ffc1aSdan# test.db2. However the hot-journal associated with test.db2 points to
755ec6ffc1aSdan# a different master-journal. Therefore, reading from test.db only should
756ec6ffc1aSdan# be enough to cause SQLite to delete $::mj_filename1.
757ec6ffc1aSdan#
758ec6ffc1aSdando_test         pager1.4.6.10 { file exists $::mj_filename  } {1}
759ec6ffc1aSdando_test         pager1.4.6.11 { file exists $::mj_filename1 } {1}
760ec6ffc1aSdando_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1}
761ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename  } {1}
762ec6ffc1aSdando_test         pager1.4.6.14 { file exists $::mj_filename1 } {0}
763ec6ffc1aSdan
764ec6ffc1aSdando_execsql_test pager1.4.6.12 {
765ec6ffc1aSdan  ATTACH 'test.db2' AS two;
766ec6ffc1aSdan  SELECT * FROM t2;
767ec6ffc1aSdan} {1 t2.1}
768ec6ffc1aSdando_test         pager1.4.6.13 { file exists $::mj_filename }  {1}
769ec6ffc1aSdando_execsql_test pager1.4.6.14 {
770ec6ffc1aSdan  ATTACH 'test.db3' AS three;
771ec6ffc1aSdan  SELECT * FROM t3;
772ec6ffc1aSdan} {1 t3.1}
773ec6ffc1aSdando_test         pager1.4.6.15 { file exists $::mj_filename }  {0}
77410f5a50eSdan
7750e986f51Sdandb close
7760e986f51Sdantv delete
777e08341c6Sdan
778de4996e2Sdan#-------------------------------------------------------------------------
779146ed78bSdan# The following tests deal with multi-file commits.
780de4996e2Sdan#
781146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because
782146ed78bSdan#               another connection is holding a SHARED lock on one of the
783146ed78bSdan#               files. After the SHARED lock is removed, the COMMIT succeeds.
784146ed78bSdan#
785146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory.
786146ed78bSdan#
787146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory.
788146ed78bSdan#
789146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
790146ed78bSdan#               name is added to a journal file immediately after the last
791146ed78bSdan#               journal record. But with synchronous=full, extra unused space
792146ed78bSdan#               is allocated between the last journal record and the
793146ed78bSdan#               master-journal file name so that the master-journal file
794146ed78bSdan#               name does not lie on the same sector as the last journal file
795146ed78bSdan#               record.
796146ed78bSdan#
797e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
798e08341c6Sdan#               truncated to zero bytes when a multi-file transaction is
799e08341c6Sdan#               committed (instead of the first couple of bytes being zeroed).
800e08341c6Sdan#
801146ed78bSdan#
802146ed78bSdando_test pager1-5.1.1 {
803146ed78bSdan  faultsim_delete_and_reopen
804146ed78bSdan  execsql {
805146ed78bSdan    ATTACH 'test.db2' AS aux;
806146ed78bSdan    CREATE TABLE t1(a, b);
807146ed78bSdan    CREATE TABLE aux.t2(a, b);
808146ed78bSdan    INSERT INTO t1 VALUES(17, 'Lenin');
809146ed78bSdan    INSERT INTO t1 VALUES(22, 'Stalin');
810146ed78bSdan    INSERT INTO t1 VALUES(53, 'Khrushchev');
811146ed78bSdan  }
812146ed78bSdan} {}
813146ed78bSdando_test pager1-5.1.2 {
814146ed78bSdan  execsql {
815146ed78bSdan    BEGIN;
816146ed78bSdan      INSERT INTO t1 VALUES(64, 'Brezhnev');
817146ed78bSdan      INSERT INTO t2 SELECT * FROM t1;
818146ed78bSdan  }
819146ed78bSdan  sqlite3 db2 test.db2
820146ed78bSdan  execsql {
821146ed78bSdan    BEGIN;
822146ed78bSdan      SELECT * FROM t2;
823146ed78bSdan  } db2
824146ed78bSdan} {}
825146ed78bSdando_test pager1-5.1.3 {
826146ed78bSdan  catchsql COMMIT
827146ed78bSdan} {1 {database is locked}}
828146ed78bSdando_test pager1-5.1.4 {
829146ed78bSdan  execsql COMMIT db2
830146ed78bSdan  execsql COMMIT
831146ed78bSdan  execsql { SELECT * FROM t2 } db2
832146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
833146ed78bSdando_test pager1-5.1.5 {
834146ed78bSdan  db2 close
835146ed78bSdan} {}
836146ed78bSdan
837146ed78bSdando_test pager1-5.2.1 {
838146ed78bSdan  execsql {
839146ed78bSdan    PRAGMA journal_mode = memory;
840146ed78bSdan    BEGIN;
841146ed78bSdan      INSERT INTO t1 VALUES(84, 'Andropov');
842146ed78bSdan      INSERT INTO t2 VALUES(84, 'Andropov');
843146ed78bSdan    COMMIT;
844146ed78bSdan  }
845146ed78bSdan} {memory}
846146ed78bSdando_test pager1-5.3.1 {
847146ed78bSdan  execsql {
848146ed78bSdan    PRAGMA journal_mode = off;
849146ed78bSdan    BEGIN;
850146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
851146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
852146ed78bSdan    COMMIT;
853146ed78bSdan  }
854146ed78bSdan} {off}
855146ed78bSdan
856146ed78bSdando_test pager1-5.4.1 {
857146ed78bSdan  db close
858146ed78bSdan  testvfs tv
859146ed78bSdan  sqlite3 db test.db -vfs tv
860146ed78bSdan  execsql { ATTACH 'test.db2' AS aux }
861146ed78bSdan
862146ed78bSdan  tv filter xDelete
863146ed78bSdan  tv script max_journal_size
864146ed78bSdan  tv sectorsize 512
865146ed78bSdan  set ::max_journal 0
866146ed78bSdan  proc max_journal_size {method args} {
867146ed78bSdan    set sz 0
868146ed78bSdan    catch { set sz [file size test.db-journal] }
869146ed78bSdan    if {$sz > $::max_journal} {
870146ed78bSdan      set ::max_journal $sz
871146ed78bSdan    }
872146ed78bSdan    return SQLITE_OK
873146ed78bSdan  }
874146ed78bSdan  execsql {
875146ed78bSdan    PRAGMA journal_mode = DELETE;
876146ed78bSdan    PRAGMA synchronous = NORMAL;
877146ed78bSdan    BEGIN;
878146ed78bSdan      INSERT INTO t1 VALUES(85, 'Gorbachev');
879146ed78bSdan      INSERT INTO t2 VALUES(85, 'Gorbachev');
880146ed78bSdan    COMMIT;
881146ed78bSdan  }
882146ed78bSdan  set ::max_journal
883146ed78bSdan} [expr 2615+[string length [pwd]]]
884146ed78bSdando_test pager1-5.4.2 {
885146ed78bSdan  set ::max_journal 0
886146ed78bSdan  execsql {
887146ed78bSdan    PRAGMA synchronous = full;
888146ed78bSdan    BEGIN;
889146ed78bSdan      DELETE FROM t1 WHERE b = 'Lenin';
890146ed78bSdan      DELETE FROM t2 WHERE b = 'Lenin';
891146ed78bSdan    COMMIT;
892146ed78bSdan  }
893146ed78bSdan  set ::max_journal
894146ed78bSdan} [expr 3111+[string length [pwd]]]
895146ed78bSdandb close
896146ed78bSdantv delete
897146ed78bSdan
898146ed78bSdando_test pager1-5.5.1 {
899146ed78bSdan  sqlite3 db test.db
900146ed78bSdan  execsql {
901146ed78bSdan    ATTACH 'test.db2' AS aux;
902146ed78bSdan    PRAGMA journal_mode = PERSIST;
903146ed78bSdan    CREATE TABLE t3(a, b);
904146ed78bSdan    INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
905146ed78bSdan    UPDATE t3 SET b = randomblob(1500);
906146ed78bSdan  }
907146ed78bSdan  expr [file size test.db-journal] > 15000
908146ed78bSdan} {1}
909146ed78bSdando_test pager1-5.5.2 {
910146ed78bSdan  execsql {
911146ed78bSdan    PRAGMA synchronous = full;
912146ed78bSdan    BEGIN;
913146ed78bSdan      DELETE FROM t1 WHERE b = 'Stalin';
914146ed78bSdan      DELETE FROM t2 WHERE b = 'Stalin';
915146ed78bSdan    COMMIT;
916146ed78bSdan  }
917146ed78bSdan  file size test.db-journal
918146ed78bSdan} {0}
919146ed78bSdan
920146ed78bSdan
921146ed78bSdan#-------------------------------------------------------------------------
922146ed78bSdan# The following tests work with "PRAGMA max_page_count"
923146ed78bSdan#
924146ed78bSdando_test pager1-6.1 {
925146ed78bSdan  faultsim_delete_and_reopen
926146ed78bSdan  execsql {
927146ed78bSdan    PRAGMA max_page_count = 10;
928146ed78bSdan    CREATE TABLE t2(a, b);
929146ed78bSdan    CREATE TABLE t3(a, b);
930146ed78bSdan    CREATE TABLE t4(a, b);
931146ed78bSdan    CREATE TABLE t5(a, b);
932146ed78bSdan    CREATE TABLE t6(a, b);
933146ed78bSdan    CREATE TABLE t7(a, b);
934146ed78bSdan    CREATE TABLE t8(a, b);
935146ed78bSdan    CREATE TABLE t9(a, b);
936146ed78bSdan    CREATE TABLE t10(a, b);
937146ed78bSdan  }
938146ed78bSdan} {10}
939146ed78bSdando_test pager1-6.2 {
940146ed78bSdan  catchsql {
941146ed78bSdan    CREATE TABLE t11(a, b);
942146ed78bSdan  }
943146ed78bSdan} {1 {database or disk is full}}
944de4996e2Sdan
945153eda0aSdan
946153eda0aSdan#-------------------------------------------------------------------------
947153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
948153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE".
949153eda0aSdan#
950153eda0aSdan# Each test is specified with 5 variables. As follows:
951153eda0aSdan#
952153eda0aSdan#   $tn:  Test Number. Used as part of the [do_test] test names.
953153eda0aSdan#   $sql: SQL to execute.
954153eda0aSdan#   $res: Expected result of executing $sql.
955153eda0aSdan#   $js:  The expected size of the journal file, in bytes, after executing
956153eda0aSdan#         the SQL script. Or -1 if the journal is not expected to exist.
957153eda0aSdan#   $ws:  The expected size of the WAL file, in bytes, after executing
958153eda0aSdan#         the SQL script. Or -1 if the WAL is not expected to exist.
959153eda0aSdan#
960153eda0aSdanfaultsim_delete_and_reopen
961153eda0aSdanforeach {tn sql res js ws} [subst {
962153eda0aSdan
963153eda0aSdan  1  {
964153eda0aSdan    CREATE TABLE t1(a, b);
965153eda0aSdan    PRAGMA auto_vacuum=OFF;
966153eda0aSdan    PRAGMA synchronous=NORMAL;
967153eda0aSdan    PRAGMA page_size=1024;
968153eda0aSdan    PRAGMA locking_mode=EXCLUSIVE;
969153eda0aSdan    PRAGMA journal_mode=TRUNCATE;
970153eda0aSdan    INSERT INTO t1 VALUES(1, 2);
971153eda0aSdan  } {exclusive truncate} 0 -1
972153eda0aSdan
973153eda0aSdan  2  {
974153eda0aSdan    BEGIN IMMEDIATE;
975153eda0aSdan      SELECT * FROM t1;
976153eda0aSdan    COMMIT;
977153eda0aSdan  } {1 2} 0 -1
978153eda0aSdan
979153eda0aSdan  3  {
980153eda0aSdan    BEGIN;
981153eda0aSdan      SELECT * FROM t1;
982153eda0aSdan    COMMIT;
983153eda0aSdan  } {1 2} 0 -1
984153eda0aSdan
985153eda0aSdan  4  { PRAGMA journal_mode = WAL }    wal    -1 -1
986153eda0aSdan  5  { INSERT INTO t1 VALUES(3, 4) }  {}     -1 [wal_file_size 1 1024]
987153eda0aSdan  6  { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
988153eda0aSdan  7  { INSERT INTO t1 VALUES(5, 6); } {}     -1 [wal_file_size 2 1024]
989153eda0aSdan
990153eda0aSdan  8  { PRAGMA journal_mode = TRUNCATE } truncate          0 -1
991153eda0aSdan  9  { INSERT INTO t1 VALUES(7, 8) }    {}                0 -1
992153eda0aSdan  10 { SELECT * FROM t1 }               {1 2 3 4 5 6 7 8} 0 -1
993153eda0aSdan
994153eda0aSdan}] {
995153eda0aSdan  do_execsql_test pager1-7.1.$tn.1 $sql $res
996153eda0aSdan  catch { set J -1 ; set J [file size test.db-journal] }
997153eda0aSdan  catch { set W -1 ; set W [file size test.db-wal] }
998153eda0aSdan  do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
999153eda0aSdan}
1000153eda0aSdan
10010e986f51Sdanforeach {tn filename} {
10020e986f51Sdan  1 :memory:
10030e986f51Sdan  2 ""
10040e986f51Sdan} {
10050e986f51Sdan  do_test pager1-8.$tn.1 {
1006153eda0aSdan    faultsim_delete_and_reopen
1007153eda0aSdan    db close
10080e986f51Sdan    sqlite3 db $filename
1009153eda0aSdan    execsql {
1010153eda0aSdan      CREATE TABLE x1(x);
1011153eda0aSdan      INSERT INTO x1 VALUES('Charles');
1012153eda0aSdan      INSERT INTO x1 VALUES('James');
1013153eda0aSdan      INSERT INTO x1 VALUES('Mary');
1014153eda0aSdan      SELECT * FROM x1;
1015153eda0aSdan    }
1016153eda0aSdan  } {Charles James Mary}
10170e986f51Sdan
10180e986f51Sdan  do_test pager1-8.$tn.2 {
10190e986f51Sdan    sqlite3 db2 $filename
10200e986f51Sdan    catchsql { SELECT * FROM x1 } db2
1021153eda0aSdan  } {1 {no such table: x1}}
1022153eda0aSdan
10230e986f51Sdan  do_execsql_test pager1-8.$tn.3 {
10240e986f51Sdan    BEGIN;
10250e986f51Sdan      INSERT INTO x1 VALUES('William');
10260e986f51Sdan      INSERT INTO x1 VALUES('Anne');
10270e986f51Sdan    ROLLBACK;
10280e986f51Sdan  } {}
10290e986f51Sdan}
10300e986f51Sdan
1031dca321aeSdan#-------------------------------------------------------------------------
1032dca321aeSdan# The next block of tests - pager1-9.* - deal with interactions between
1033dca321aeSdan# the pager and the backup API. Test cases:
1034dca321aeSdan#
1035dca321aeSdan#   pager1-9.1.*: Test that a backup completes successfully even if the
1036dca321aeSdan#                 source db is written to during the backup op.
1037dca321aeSdan#
1038dca321aeSdan#   pager1-9.2.*: Test that a backup completes successfully even if the
1039dca321aeSdan#                 source db is written to and then rolled back during a
1040dca321aeSdan#                 backup operation.
1041dca321aeSdan#
1042dca321aeSdando_test pager1-9.0.1 {
1043dca321aeSdan  faultsim_delete_and_reopen
1044dca321aeSdan  db func a_string a_string
1045dca321aeSdan  execsql {
1046dca321aeSdan    PRAGMA cache_size = 10;
1047dca321aeSdan    BEGIN;
1048dca321aeSdan      CREATE TABLE ab(a, b, UNIQUE(a, b));
1049dca321aeSdan      INSERT INTO ab VALUES( a_string(200), a_string(300) );
1050dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1051dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1052dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1053dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1054dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1055dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1056dca321aeSdan      INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab;
1057dca321aeSdan    COMMIT;
1058dca321aeSdan  }
1059dca321aeSdan} {}
1060dca321aeSdando_test pager1-9.0.2 {
1061dca321aeSdan  sqlite3 db2 test.db2
1062dca321aeSdan  db2 eval { PRAGMA cache_size = 10 }
1063dca321aeSdan  sqlite3_backup B db2 main db main
1064dca321aeSdan  list [B step 10000] [B finish]
1065dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1066dca321aeSdando_test pager1-9.0.3 {
1067dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1068dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1069dca321aeSdan
1070dca321aeSdando_test pager1-9.1.1 {
1071dca321aeSdan  execsql { UPDATE ab SET a = a_string(201) }
1072dca321aeSdan  sqlite3_backup B db2 main db main
1073dca321aeSdan  B step 30
1074dca321aeSdan} {SQLITE_OK}
1075dca321aeSdando_test pager1-9.1.2 {
1076dca321aeSdan  execsql { UPDATE ab SET b = a_string(301) }
1077dca321aeSdan  list [B step 10000] [B finish]
1078dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1079dca321aeSdando_test pager1-9.1.3 {
1080dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1081dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1082dca321aeSdando_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128}
1083dca321aeSdan
1084dca321aeSdando_test pager1-9.2.1 {
1085dca321aeSdan  execsql { UPDATE ab SET a = a_string(202) }
1086dca321aeSdan  sqlite3_backup B db2 main db main
1087dca321aeSdan  B step 30
1088dca321aeSdan} {SQLITE_OK}
1089dca321aeSdando_test pager1-9.2.2 {
1090dca321aeSdan  execsql {
1091dca321aeSdan    BEGIN;
1092dca321aeSdan      UPDATE ab SET b = a_string(301);
1093dca321aeSdan    ROLLBACK;
1094dca321aeSdan  }
1095dca321aeSdan  list [B step 10000] [B finish]
1096dca321aeSdan} {SQLITE_DONE SQLITE_OK}
1097dca321aeSdando_test pager1-9.2.3 {
1098dca321aeSdan db one {SELECT md5sum(a, b) FROM ab}
1099dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}]
1100dca321aeSdando_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128}
1101ec6ffc1aSdandb close
1102ec6ffc1aSdandb2 close
1103ec6ffc1aSdan
1104ec6ffc1aSdan#-------------------------------------------------------------------------
1105ec6ffc1aSdan# Test that regardless of the value returned by xSectorSize(), the
1106ec6ffc1aSdan# minimum effective sector-size is 512 and the maximum 65536 bytes.
1107ec6ffc1aSdan#
1108ec6ffc1aSdantestvfs tv -default 1
1109ec6ffc1aSdanforeach sectorsize {
1110ec6ffc1aSdan    32   64   128   256   512   1024   2048
1111ec6ffc1aSdan    4096 8192 16384 32768 65536 131072 262144
1112ec6ffc1aSdan} {
1113ec6ffc1aSdan  tv sectorsize $sectorsize
1114ec6ffc1aSdan  set eff $sectorsize
1115ec6ffc1aSdan  if {$sectorsize < 512}   { set eff 512 }
1116ec6ffc1aSdan  if {$sectorsize > 65536} { set eff 65536 }
1117ec6ffc1aSdan
1118ec6ffc1aSdan  do_test pager1-10.$sectorsize {
1119ec6ffc1aSdan    faultsim_delete_and_reopen
1120ec6ffc1aSdan    execsql {
1121ec6ffc1aSdan      PRAGMA journal_mode = PERSIST;
1122ec6ffc1aSdan      PRAGMA page_size = 1024;
1123ec6ffc1aSdan      CREATE TABLE t1(a, b);
1124ec6ffc1aSdan    }
1125ec6ffc1aSdan    file size test.db-journal
1126ec6ffc1aSdan  } [expr $sectorsize > 65536 ? 65536 : $sectorsize]
1127ec6ffc1aSdan}
1128ec6ffc1aSdandb close
1129ec6ffc1aSdantv delete
1130ec6ffc1aSdan
1131ec6ffc1aSdantestvfs tv -default 1
1132ec6ffc1aSdanfaultsim_delete_and_reopen
1133ec6ffc1aSdandb func a_string a_string
1134ec6ffc1aSdando_execsql_test pager1-11.1 {
1135ec6ffc1aSdan  PRAGMA cache_size = 10;
1136ec6ffc1aSdan  BEGIN;
1137ec6ffc1aSdan    CREATE TABLE zz(top PRIMARY KEY);
1138ec6ffc1aSdan    INSERT INTO zz VALUES(a_string(222));
1139ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1140ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1141ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1142ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1143ec6ffc1aSdan    INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz;
1144ec6ffc1aSdan  COMMIT;
1145ec6ffc1aSdan  BEGIN;
1146ec6ffc1aSdan    UPDATE zz SET top = a_string(345);
1147ec6ffc1aSdan} {}
1148ec6ffc1aSdan
1149ec6ffc1aSdanproc lockout {method args} { return SQLITE_IOERR }
1150ec6ffc1aSdantv script lockout
1151ec6ffc1aSdantv filter {xWrite xTruncate xSync}
1152ec6ffc1aSdando_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}}
1153ec6ffc1aSdan
1154ec6ffc1aSdantv script {}
1155ec6ffc1aSdando_test pager1-11.3 {
1156ec6ffc1aSdan  sqlite3 db2 test.db
1157ec6ffc1aSdan  execsql {
1158ec6ffc1aSdan    PRAGMA journal_mode = TRUNCATE;
1159ec6ffc1aSdan    PRAGMA integrity_check;
1160ec6ffc1aSdan  } db2
1161ec6ffc1aSdan} {truncate ok}
1162ec6ffc1aSdando_test pager1-11.4 {
1163ec6ffc1aSdan  db2 close
1164ec6ffc1aSdan  file size test.db-journal
1165ec6ffc1aSdan} {0}
1166ec6ffc1aSdanbreakpoint
1167ec6ffc1aSdando_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32}
1168f9b4419dSdandb close
1169f9b4419dSdantv delete
1170ec6ffc1aSdan
1171f9b4419dSdan#-------------------------------------------------------------------------
1172f9b4419dSdan# Test "PRAGMA page_size"
1173f9b4419dSdan#
1174f9b4419dSdanforeach pagesize {
1175f9b4419dSdan    512   1024   2048 4096 8192 16384 32768
1176f9b4419dSdan} {
1177f9b4419dSdan  faultsim_delete_and_reopen
1178f9b4419dSdan
1179f9b4419dSdan  do_test pager1-12.$pagesize.1 {
1180f9b4419dSdan    sqlite3 db2 test.db
1181f9b4419dSdan    execsql "
1182f9b4419dSdan      PRAGMA page_size = $pagesize;
1183f9b4419dSdan      CREATE VIEW v AS SELECT * FROM sqlite_master;
1184f9b4419dSdan    " db2
1185f9b4419dSdan    file size test.db
1186f9b4419dSdan  } $pagesize
1187f9b4419dSdan  do_test pager1-12.$pagesize.2 {
1188f9b4419dSdan    sqlite3 db2 test.db
1189f9b4419dSdan    execsql {
1190f9b4419dSdan      SELECT count(*) FROM v;
1191f9b4419dSdan      PRAGMA main.page_size;
1192f9b4419dSdan    } db2
1193f9b4419dSdan  } [list 1 $pagesize]
1194f9b4419dSdan  do_test pager1-12.$pagesize.3 {
1195f9b4419dSdan    execsql {
1196f9b4419dSdan      SELECT count(*) FROM v;
1197f9b4419dSdan      PRAGMA main.page_size;
1198f9b4419dSdan    }
1199f9b4419dSdan  } [list 1 $pagesize]
1200f9b4419dSdan  db2 close
1201f9b4419dSdan}
1202dca321aeSdan
1203e91a54e1Sdanfinish_test
1204e91a54e1Sdan
1205