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# 43*ec6ffc1aSdan# pager1-10.*: Test that the assumed file-system sector-size is limited to 44*ec6ffc1aSdan# 64KB. 45*ec6ffc1aSdan# 46*ec6ffc1aSdan# 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# 1980e986f51Sdando_test pager1-3.1.1 { 199b0ac3e3aSdan faultsim_delete_and_reopen 200b0ac3e3aSdan execsql { 201b0ac3e3aSdan CREATE TABLE t1(a PRIMARY KEY, b); 202b0ac3e3aSdan CREATE TABLE counter( 203b0ac3e3aSdan i CHECK (i<5), 204b0ac3e3aSdan u CHECK (u<10) 205b0ac3e3aSdan ); 206b0ac3e3aSdan INSERT INTO counter VALUES(0, 0); 207b0ac3e3aSdan CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN 208b0ac3e3aSdan UPDATE counter SET i = i+1; 209b0ac3e3aSdan END; 210b0ac3e3aSdan CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN 211b0ac3e3aSdan UPDATE counter SET u = u+1; 212b0ac3e3aSdan END; 213b0ac3e3aSdan } 214b0ac3e3aSdan execsql { SELECT * FROM counter } 215b0ac3e3aSdan} {0 0} 216b0ac3e3aSdan 2170e986f51Sdando_execsql_test pager1-3.1.2 { 218b0ac3e3aSdan BEGIN; 219b0ac3e3aSdan INSERT INTO t1 VALUES(1, randomblob(1500)); 220b0ac3e3aSdan INSERT INTO t1 VALUES(2, randomblob(1500)); 221b0ac3e3aSdan INSERT INTO t1 VALUES(3, randomblob(1500)); 222b0ac3e3aSdan SELECT * FROM counter; 223b0ac3e3aSdan} {3 0} 2240e986f51Sdando_catchsql_test pager1-3.1.3 { 225b0ac3e3aSdan INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 226b0ac3e3aSdan} {1 {constraint failed}} 227b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} 228b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} 229b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {} 230b0ac3e3aSdan 231dca321aeSdando_test pager1-3.2.1 { 232dca321aeSdan faultsim_delete_and_reopen 233dca321aeSdan db func a_string a_string 234dca321aeSdan execsql { 235dca321aeSdan PRAGMA auto_vacuum = 2; 236dca321aeSdan PRAGMA cache_size = 10; 237dca321aeSdan CREATE TABLE z(x INTEGER PRIMARY KEY, y); 238dca321aeSdan BEGIN; 239dca321aeSdan INSERT INTO z VALUES(NULL, a_string(800)); 240dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 2 241dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 4 242dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 8 243dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 16 244dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 32 245dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 64 246dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 128 247dca321aeSdan INSERT INTO z SELECT NULL, a_string(800) FROM z; -- 256 248dca321aeSdan COMMIT; 249dca321aeSdan } 250dca321aeSdan execsql { PRAGMA auto_vacuum } 251dca321aeSdan} {2} 252dca321aeSdando_execsql_test pager1-3.2.2 { 253dca321aeSdan BEGIN; 254dca321aeSdan INSERT INTO z VALUES(NULL, a_string(800)); 255dca321aeSdan INSERT INTO z VALUES(NULL, a_string(800)); 256dca321aeSdan SAVEPOINT one; 257dca321aeSdan DELETE FROM z WHERE x>256; 258dca321aeSdan PRAGMA incremental_vacuum; 259dca321aeSdan SELECT count(*) FROM z WHERE x < 100; 260dca321aeSdan ROLLBACK TO one; 261dca321aeSdan COMMIT; 262dca321aeSdan} {99} 263dca321aeSdan 26453f04f3bSdan#------------------------------------------------------------------------- 26553f04f3bSdan# Hot journal rollback related test cases. 26653f04f3bSdan# 26753f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid 26853f04f3bSdan# journal files. 26953f04f3bSdan# 27053f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a 27153f04f3bSdan# hot-journal file appears to be corrupt (checksum does not 27253f04f3bSdan# compute) the associated journal is rolled back (and no 27353f04f3bSdan# xAccess() call to check for the presence of any master 27453f04f3bSdan# journal file is made). 27553f04f3bSdan# 276de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the 277de4996e2Sdan# page-size or sector-size in the journal header appear to 278de4996e2Sdan# be invalid (too large, too small or not a power of 2). 279de4996e2Sdan# 280e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master 281e08341c6Sdan# journal pointer generated in various "PRAGMA synchronous" 282e08341c6Sdan# modes. 283e08341c6Sdan# 284e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a 285e08341c6Sdan# journal-record for which the checksum fails. 286e08341c6Sdan# 287*ec6ffc1aSdan# pager1.4.6.*: Test that when rolling back a hot-journal that contains a 288*ec6ffc1aSdan# master journal pointer, the master journal file is deleted 289*ec6ffc1aSdan# after all the hot-journals that refer to it are deleted. 290*ec6ffc1aSdan# 29153f04f3bSdando_test pager1.4.1.1 { 29253f04f3bSdan faultsim_delete_and_reopen 29353f04f3bSdan execsql { 29453f04f3bSdan CREATE TABLE x(y, z); 29553f04f3bSdan INSERT INTO x VALUES(1, 2); 29653f04f3bSdan } 29753f04f3bSdan set fd [open test.db-journal w] 29853f04f3bSdan puts -nonewline $fd "helloworld" 29953f04f3bSdan close $fd 30053f04f3bSdan file exists test.db-journal 30153f04f3bSdan} {1} 30253f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2} 30353f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0} 30453f04f3bSdan 30553f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite 30653f04f3bSdan# deletes the master-journal to commit a multi-file transaction. 30753f04f3bSdan# 30853f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets 30953f04f3bSdan# up the file system to contain two databases, two hot-journal files and 31053f04f3bSdan# a master-journal. 31153f04f3bSdan# 31253f04f3bSdando_test pager1.4.2.1 { 31353f04f3bSdan testvfs tstvfs -default 1 31453f04f3bSdan tstvfs filter xDelete 31553f04f3bSdan tstvfs script xDeleteCallback 31653f04f3bSdan proc xDeleteCallback {method file args} { 31753f04f3bSdan set file [file tail $file] 31853f04f3bSdan if { [string match *mj* $file] } { faultsim_save } 31953f04f3bSdan } 32053f04f3bSdan faultsim_delete_and_reopen 32153f04f3bSdan db func a_string a_string 32253f04f3bSdan execsql { 32353f04f3bSdan ATTACH 'test.db2' AS aux; 32453f04f3bSdan PRAGMA journal_mode = DELETE; 32553f04f3bSdan PRAGMA main.cache_size = 10; 32653f04f3bSdan PRAGMA aux.cache_size = 10; 32753f04f3bSdan CREATE TABLE t1(a UNIQUE, b UNIQUE); 32853f04f3bSdan CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); 32953f04f3bSdan INSERT INTO t1 VALUES(a_string(200), a_string(300)); 33053f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 33153f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 33253f04f3bSdan INSERT INTO t2 SELECT * FROM t1; 33353f04f3bSdan BEGIN; 33453f04f3bSdan INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; 33553f04f3bSdan INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; 33653f04f3bSdan INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; 33753f04f3bSdan INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; 33853f04f3bSdan REPLACE INTO t2 SELECT * FROM t1; 33953f04f3bSdan COMMIT; 34053f04f3bSdan } 34153f04f3bSdan db close 34253f04f3bSdan tstvfs delete 34353f04f3bSdan} {} 34453f04f3bSdando_test pager1.4.2.2 { 34553f04f3bSdan faultsim_restore_and_reopen 34653f04f3bSdan execsql { 34753f04f3bSdan SELECT count(*) FROM t1; 34853f04f3bSdan PRAGMA integrity_check; 34953f04f3bSdan } 35053f04f3bSdan} {4 ok} 35153f04f3bSdando_test pager1.4.2.3 { 35253f04f3bSdan faultsim_restore_and_reopen 35353f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 35453f04f3bSdan execsql { 35553f04f3bSdan SELECT count(*) FROM t1; 35653f04f3bSdan PRAGMA integrity_check; 35753f04f3bSdan } 35853f04f3bSdan} {64 ok} 35953f04f3bSdando_test pager1.4.2.4 { 36053f04f3bSdan faultsim_restore_and_reopen 36153f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 36253f04f3bSdan execsql { 36353f04f3bSdan SELECT count(*) FROM t1; 36453f04f3bSdan PRAGMA integrity_check; 36553f04f3bSdan } 36653f04f3bSdan} {4 ok} 36753f04f3bSdando_test pager1.4.2.5 { 36853f04f3bSdan faultsim_restore_and_reopen 36953f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 37053f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 37153f04f3bSdan execsql { 37253f04f3bSdan SELECT count(*) FROM t1; 37353f04f3bSdan PRAGMA integrity_check; 37453f04f3bSdan } 37553f04f3bSdan} {4 ok} 37653f04f3bSdan 377de4996e2Sdando_test pager1.4.3.1 { 378de4996e2Sdan testvfs tstvfs -default 1 379de4996e2Sdan tstvfs filter xSync 380de4996e2Sdan tstvfs script xSyncCallback 381de4996e2Sdan proc xSyncCallback {method file args} { 382de4996e2Sdan set file [file tail $file] 383de4996e2Sdan if { 0==[string match *journal $file] } { faultsim_save } 384de4996e2Sdan } 385de4996e2Sdan faultsim_delete_and_reopen 386de4996e2Sdan execsql { 387de4996e2Sdan PRAGMA journal_mode = DELETE; 388de4996e2Sdan CREATE TABLE t1(a, b); 389de4996e2Sdan INSERT INTO t1 VALUES(1, 2); 390de4996e2Sdan INSERT INTO t1 VALUES(3, 4); 391de4996e2Sdan } 392de4996e2Sdan db close 393de4996e2Sdan tstvfs delete 394de4996e2Sdan} {} 395de4996e2Sdan 396de4996e2Sdanforeach {tn ofst value result} { 397de4996e2Sdan 2 20 31 {1 2 3 4} 398de4996e2Sdan 3 20 32 {1 2 3 4} 399de4996e2Sdan 4 20 33 {1 2 3 4} 400de4996e2Sdan 5 20 65536 {1 2 3 4} 401de4996e2Sdan 6 20 131072 {1 2 3 4} 402de4996e2Sdan 403de4996e2Sdan 7 24 511 {1 2 3 4} 404de4996e2Sdan 8 24 513 {1 2 3 4} 405de4996e2Sdan 9 24 65536 {1 2 3 4} 406de4996e2Sdan 407de4996e2Sdan 10 32 65536 {1 2} 408de4996e2Sdan} { 409de4996e2Sdan do_test pager1.4.3.$tn { 410de4996e2Sdan faultsim_restore_and_reopen 411de4996e2Sdan hexio_write test.db-journal $ofst [format %.8x $value] 412de4996e2Sdan execsql { SELECT * FROM t1 } 413de4996e2Sdan } $result 414de4996e2Sdan} 415de4996e2Sdandb close 416de4996e2Sdan 417e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal 418e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the 419e08341c6Sdan# file-system is saved just before the xDelete() call to remove the 420e08341c6Sdan# master journal file from the file-system. 421e08341c6Sdan# 422e08341c6Sdantestvfs tv -default 1 423e08341c6Sdantv script copy_on_mj_delete 424e08341c6Sdanset ::mj_filename_length 0 425e08341c6Sdanproc copy_on_mj_delete {method filename args} { 426e08341c6Sdan if {[string match *mj* [file tail $filename]]} { 427e08341c6Sdan set ::mj_filename_length [string length $filename] 428e08341c6Sdan faultsim_save 429e08341c6Sdan } 430e08341c6Sdan return SQLITE_OK 431e08341c6Sdan} 432e08341c6Sdan 433e08341c6Sdanset pwd [pwd] 434e08341c6Sdanforeach {tn1 tcl} { 435e08341c6Sdan 1 { set prefix "test.db" } 436e08341c6Sdan 2 { 437e08341c6Sdan # This test depends on the underlying VFS being able to open paths 438e08341c6Sdan # 512 bytes in length. The idea is to create a hot-journal file that 439e08341c6Sdan # contains a master-journal pointer so large that it could contain 440e08341c6Sdan # a valid page record (if the file page-size is 512 bytes). So as to 441e08341c6Sdan # make sure SQLite doesn't get confused by this. 442e08341c6Sdan # 443e08341c6Sdan set nPadding [expr 511 - $::mj_filename_length] 444e08341c6Sdan 445e08341c6Sdan # We cannot just create a really long database file name to open, as 446e08341c6Sdan # Linux limits a single component of a path to 255 bytes by default 447e08341c6Sdan # (and presumably other systems have limits too). So create a directory 448e08341c6Sdan # hierarchy to work in. 449e08341c6Sdan # 450e08341c6Sdan set dirname "d123456789012345678901234567890/" 451e08341c6Sdan set nDir [expr $nPadding / 32] 452e08341c6Sdan if { $nDir } { 453e08341c6Sdan set p [string repeat $dirname $nDir] 454e08341c6Sdan file mkdir $p 455e08341c6Sdan cd $p 456e08341c6Sdan } 457e08341c6Sdan 458e08341c6Sdan set padding [string repeat x [expr $nPadding %32]] 459e08341c6Sdan set prefix "test.db${padding}" 460e08341c6Sdan } 461e08341c6Sdan} { 462e08341c6Sdan eval $tcl 463e08341c6Sdan foreach {tn2 sql} { 464e08341c6Sdan o { 465e08341c6Sdan PRAGMA main.synchronous=OFF; 466e08341c6Sdan PRAGMA aux.synchronous=OFF; 467e08341c6Sdan } 468e08341c6Sdan o512 { 469e08341c6Sdan PRAGMA main.synchronous=OFF; 470e08341c6Sdan PRAGMA aux.synchronous=OFF; 471e08341c6Sdan PRAGMA main.page_size = 512; 472e08341c6Sdan PRAGMA aux.page_size = 512; 473e08341c6Sdan } 474e08341c6Sdan n { 475e08341c6Sdan PRAGMA main.synchronous=NORMAL; 476e08341c6Sdan PRAGMA aux.synchronous=NORMAL; 477e08341c6Sdan } 478e08341c6Sdan f { 479e08341c6Sdan PRAGMA main.synchronous=FULL; 480e08341c6Sdan PRAGMA aux.synchronous=FULL; 481e08341c6Sdan } 482e08341c6Sdan } { 483e08341c6Sdan 484e08341c6Sdan set tn "${tn1}.${tn2}" 485e08341c6Sdan 486e08341c6Sdan # Set up a connection to have two databases, test.db (main) and 487e08341c6Sdan # test.db2 (aux). Then run a multi-file transaction on them. The 488e08341c6Sdan # VFS will snapshot the file-system just before the master-journal 489e08341c6Sdan # file is deleted to commit the transaction. 490e08341c6Sdan # 491e08341c6Sdan tv filter xDelete 492e08341c6Sdan do_test pager1-4.4.$tn.1 { 493e08341c6Sdan faultsim_delete_and_reopen $prefix 494e08341c6Sdan execsql " 495e08341c6Sdan ATTACH '${prefix}2' AS aux; 496e08341c6Sdan $sql 497e08341c6Sdan CREATE TABLE a(x); 498e08341c6Sdan CREATE TABLE aux.b(x); 499e08341c6Sdan INSERT INTO a VALUES('double-you'); 500e08341c6Sdan INSERT INTO a VALUES('why'); 501e08341c6Sdan INSERT INTO a VALUES('zed'); 502e08341c6Sdan INSERT INTO b VALUES('won'); 503e08341c6Sdan INSERT INTO b VALUES('too'); 504e08341c6Sdan INSERT INTO b VALUES('free'); 505e08341c6Sdan " 506e08341c6Sdan execsql { 507e08341c6Sdan BEGIN; 508e08341c6Sdan INSERT INTO a SELECT * FROM b WHERE rowid<=3; 509e08341c6Sdan INSERT INTO b SELECT * FROM a WHERE rowid<=3; 510e08341c6Sdan COMMIT; 511e08341c6Sdan } 512e08341c6Sdan } {} 513e08341c6Sdan tv filter {} 514e08341c6Sdan 515e08341c6Sdan # Check that the transaction was committed successfully. 516e08341c6Sdan # 517e08341c6Sdan do_execsql_test pager1-4.4.$tn.2 { 518e08341c6Sdan SELECT * FROM a 519e08341c6Sdan } {double-you why zed won too free} 520e08341c6Sdan do_execsql_test pager1-4.4.$tn.3 { 521e08341c6Sdan SELECT * FROM b 522e08341c6Sdan } {won too free double-you why zed} 523e08341c6Sdan 524e08341c6Sdan # Restore the file-system and reopen the databases. Check that it now 525e08341c6Sdan # appears that the transaction was not committed (because the file-system 526e08341c6Sdan # was restored to the state where it had not been). 527e08341c6Sdan # 528e08341c6Sdan do_test pager1-4.4.$tn.4 { 529e08341c6Sdan faultsim_restore_and_reopen $prefix 530e08341c6Sdan execsql "ATTACH '${prefix}2' AS aux" 531e08341c6Sdan } {} 532e08341c6Sdan do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed} 533e08341c6Sdan do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free} 534e08341c6Sdan 535e08341c6Sdan # Restore the file-system again. This time, before reopening the databases, 536e08341c6Sdan # delete the master-journal file from the file-system. It now appears that 537e08341c6Sdan # the transaction was committed (no master-journal file == no rollback). 538e08341c6Sdan # 539e08341c6Sdan do_test pager1-4.4.$tn.7 { 540e08341c6Sdan faultsim_restore_and_reopen $prefix 541e08341c6Sdan foreach f [glob ${prefix}-mj*] { file delete -force $f } 542e08341c6Sdan execsql "ATTACH '${prefix}2' AS aux" 543e08341c6Sdan } {} 544e08341c6Sdan do_execsql_test pager1-4.4.$tn.8 { 545e08341c6Sdan SELECT * FROM a 546e08341c6Sdan } {double-you why zed won too free} 547e08341c6Sdan do_execsql_test pager1-4.4.$tn.9 { 548e08341c6Sdan SELECT * FROM b 549e08341c6Sdan } {won too free double-you why zed} 550e08341c6Sdan } 551e08341c6Sdan 552e08341c6Sdan cd $pwd 553e08341c6Sdan} 554e08341c6Sdandb close 555e08341c6Sdantv delete 5560e986f51Sdanfile delete -force $dirname 5570e986f51Sdan 5580e986f51Sdan 5590e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a 5600e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly 5610e986f51Sdan# two database pages (and page 1 - the change counter). 5620e986f51Sdan# 5630e986f51Sdantestvfs tv -default 1 5640e986f51Sdantv sectorsize 512 5650e986f51Sdantv script copy_on_journal_delete 5660e986f51Sdantv filter xDelete 5670e986f51Sdanset ::mj_filename_length 0 5680e986f51Sdanproc copy_on_journal_delete {method filename args} { 5690e986f51Sdan if {[string match *journal $filename]} faultsim_save 5700e986f51Sdan return SQLITE_OK 5710e986f51Sdan} 5720e986f51Sdanfaultsim_delete_and_reopen 5730e986f51Sdando_execsql_test pager1.4.5.1 { 5740e986f51Sdan PRAGMA page_size = 1024; 5750e986f51Sdan CREATE TABLE t1(a, b); 5760e986f51Sdan CREATE TABLE t2(a, b); 5770e986f51Sdan INSERT INTO t1 VALUES('I', 'II'); 5780e986f51Sdan INSERT INTO t2 VALUES('III', 'IV'); 5790e986f51Sdan BEGIN; 5800e986f51Sdan INSERT INTO t1 VALUES(1, 2); 5810e986f51Sdan INSERT INTO t2 VALUES(3, 4); 5820e986f51Sdan COMMIT; 5830e986f51Sdan} {} 5840e986f51Sdantv filter {} 5850e986f51Sdan 5860e986f51Sdan# Check the transaction was committed: 5870e986f51Sdan# 5880e986f51Sdando_execsql_test pager1.4.5.2 { 5890e986f51Sdan SELECT * FROM t1; 5900e986f51Sdan SELECT * FROM t2; 5910e986f51Sdan} {I II 1 2 III IV 3 4} 5920e986f51Sdan 593*ec6ffc1aSdan# Now try four tests: 5940e986f51Sdan# 5950e986f51Sdan# pager1-4.5.3: Restore the file-system. Check that the whole transaction 5960e986f51Sdan# is rolled back. 5970e986f51Sdan# 5980e986f51Sdan# pager1-4.5.4: Restore the file-system. Corrupt the first record in the 5990e986f51Sdan# journal. Check the transaction is not rolled back. 6000e986f51Sdan# 6010e986f51Sdan# pager1-4.5.5: Restore the file-system. Corrupt the second record in the 6020e986f51Sdan# journal. Check that the first record in the transaction is 6030e986f51Sdan# played back, but not the second. 6040e986f51Sdan# 60510f5a50eSdan# pager1-4.5.6: Restore the file-system. Try to open the database with a 60610f5a50eSdan# readonly connection. This should fail, as a read-only 60710f5a50eSdan# connection cannot roll back the database file. 60810f5a50eSdan# 6090e986f51Sdanfaultsim_restore_and_reopen 6100e986f51Sdando_execsql_test pager1.4.5.3 { 6110e986f51Sdan SELECT * FROM t1; 6120e986f51Sdan SELECT * FROM t2; 6130e986f51Sdan} {I II III IV} 6140e986f51Sdanfaultsim_restore_and_reopen 6150e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF 6160e986f51Sdando_execsql_test pager1.4.5.4 { 6170e986f51Sdan SELECT * FROM t1; 6180e986f51Sdan SELECT * FROM t2; 6190e986f51Sdan} {I II 1 2 III IV 3 4} 6200e986f51Sdanfaultsim_restore_and_reopen 6210e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF 6220e986f51Sdando_execsql_test pager1.4.5.5 { 6230e986f51Sdan SELECT * FROM t1; 6240e986f51Sdan SELECT * FROM t2; 6250e986f51Sdan} {I II III IV 3 4} 6260e986f51Sdan 62710f5a50eSdanfaultsim_restore_and_reopen 62810f5a50eSdandb close 62910f5a50eSdansqlite3 db test.db -readonly 1 63010f5a50eSdando_catchsql_test pager1.4.5.6 { 63110f5a50eSdan SELECT * FROM t1; 63210f5a50eSdan SELECT * FROM t2; 63310f5a50eSdan} {1 {disk I/O error}} 634*ec6ffc1aSdandb close 635*ec6ffc1aSdan 636*ec6ffc1aSdan# Snapshot the file-system just before multi-file commit. Save the name 637*ec6ffc1aSdan# of the master journal file in $::mj_filename. 638*ec6ffc1aSdan# 639*ec6ffc1aSdantv script copy_on_mj_delete 640*ec6ffc1aSdantv filter xDelete 641*ec6ffc1aSdanproc copy_on_mj_delete {method filename args} { 642*ec6ffc1aSdan if {[string match *mj* [file tail $filename]]} { 643*ec6ffc1aSdan set ::mj_filename $filename 644*ec6ffc1aSdan faultsim_save 645*ec6ffc1aSdan } 646*ec6ffc1aSdan return SQLITE_OK 647*ec6ffc1aSdan} 648*ec6ffc1aSdando_test pager1.4.6.1 { 649*ec6ffc1aSdan faultsim_delete_and_reopen 650*ec6ffc1aSdan execsql { 651*ec6ffc1aSdan ATTACH 'test.db2' AS two; 652*ec6ffc1aSdan CREATE TABLE t1(a, b); 653*ec6ffc1aSdan CREATE TABLE two.t2(a, b); 654*ec6ffc1aSdan INSERT INTO t1 VALUES(1, 't1.1'); 655*ec6ffc1aSdan INSERT INTO t2 VALUES(1, 't2.1'); 656*ec6ffc1aSdan BEGIN; 657*ec6ffc1aSdan UPDATE t1 SET b = 't1.2'; 658*ec6ffc1aSdan UPDATE t2 SET b = 't2.2'; 659*ec6ffc1aSdan COMMIT; 660*ec6ffc1aSdan } 661*ec6ffc1aSdan tv filter {} 662*ec6ffc1aSdan db close 663*ec6ffc1aSdan} {} 664*ec6ffc1aSdan 665*ec6ffc1aSdanfaultsim_restore_and_reopen 666*ec6ffc1aSdando_execsql_test pager1.4.6.2 { SELECT * FROM t1 } {1 t1.1} 667*ec6ffc1aSdando_test pager1.4.6.3 { file exists $::mj_filename } {1} 668*ec6ffc1aSdando_execsql_test pager1.4.6.4 { 669*ec6ffc1aSdan ATTACH 'test.db2' AS two; 670*ec6ffc1aSdan SELECT * FROM t2; 671*ec6ffc1aSdan} {1 t2.1} 672*ec6ffc1aSdando_test pager1.4.6.5 { file exists $::mj_filename } {0} 673*ec6ffc1aSdan 674*ec6ffc1aSdanfaultsim_restore_and_reopen 675*ec6ffc1aSdandb close 676*ec6ffc1aSdando_test pager1.4.6.8 { 677*ec6ffc1aSdan set ::mj_filename1 $::mj_filename 678*ec6ffc1aSdan tv filter xDelete 679*ec6ffc1aSdan sqlite3 db test.db2 680*ec6ffc1aSdan execsql { 681*ec6ffc1aSdan ATTACH 'test.db3' AS three; 682*ec6ffc1aSdan CREATE TABLE three.t3(a, b); 683*ec6ffc1aSdan INSERT INTO t3 VALUES(1, 't3.1'); 684*ec6ffc1aSdan BEGIN; 685*ec6ffc1aSdan UPDATE t2 SET b = 't2.3'; 686*ec6ffc1aSdan UPDATE t3 SET b = 't3.3'; 687*ec6ffc1aSdan COMMIT; 688*ec6ffc1aSdan } 689*ec6ffc1aSdan expr {$::mj_filename1 != $::mj_filename} 690*ec6ffc1aSdan} {1} 691*ec6ffc1aSdanfaultsim_restore_and_reopen 692*ec6ffc1aSdantv filter {} 693*ec6ffc1aSdan 694*ec6ffc1aSdan# The file-system now contains: 695*ec6ffc1aSdan# 696*ec6ffc1aSdan# * three databases 697*ec6ffc1aSdan# * three hot-journal files 698*ec6ffc1aSdan# * two master-journal files. 699*ec6ffc1aSdan# 700*ec6ffc1aSdan# The hot-journals associated with test.db2 and test.db3 point to 701*ec6ffc1aSdan# master journal $::mj_filename. The hot-journal file associated with 702*ec6ffc1aSdan# test.db points to master journal $::mj_filename1. So reading from 703*ec6ffc1aSdan# test.db should delete $::mj_filename1. 704*ec6ffc1aSdan# 705*ec6ffc1aSdando_test pager1.4.6.9 { 706*ec6ffc1aSdan lsort [glob test.db*] 707*ec6ffc1aSdan} [lsort [list \ 708*ec6ffc1aSdan test.db test.db2 test.db3 \ 709*ec6ffc1aSdan test.db-journal test.db2-journal test.db3-journal \ 710*ec6ffc1aSdan [file tail $::mj_filename] [file tail $::mj_filename1] 711*ec6ffc1aSdan]] 712*ec6ffc1aSdan 713*ec6ffc1aSdan# The master-journal $::mj_filename1 contains pointers to test.db and 714*ec6ffc1aSdan# test.db2. However the hot-journal associated with test.db2 points to 715*ec6ffc1aSdan# a different master-journal. Therefore, reading from test.db only should 716*ec6ffc1aSdan# be enough to cause SQLite to delete $::mj_filename1. 717*ec6ffc1aSdan# 718*ec6ffc1aSdando_test pager1.4.6.10 { file exists $::mj_filename } {1} 719*ec6ffc1aSdando_test pager1.4.6.11 { file exists $::mj_filename1 } {1} 720*ec6ffc1aSdando_execsql_test pager1.4.6.12 { SELECT * FROM t1 } {1 t1.1} 721*ec6ffc1aSdando_test pager1.4.6.13 { file exists $::mj_filename } {1} 722*ec6ffc1aSdando_test pager1.4.6.14 { file exists $::mj_filename1 } {0} 723*ec6ffc1aSdan 724*ec6ffc1aSdando_execsql_test pager1.4.6.12 { 725*ec6ffc1aSdan ATTACH 'test.db2' AS two; 726*ec6ffc1aSdan SELECT * FROM t2; 727*ec6ffc1aSdan} {1 t2.1} 728*ec6ffc1aSdando_test pager1.4.6.13 { file exists $::mj_filename } {1} 729*ec6ffc1aSdando_execsql_test pager1.4.6.14 { 730*ec6ffc1aSdan ATTACH 'test.db3' AS three; 731*ec6ffc1aSdan SELECT * FROM t3; 732*ec6ffc1aSdan} {1 t3.1} 733*ec6ffc1aSdando_test pager1.4.6.15 { file exists $::mj_filename } {0} 73410f5a50eSdan 7350e986f51Sdandb close 7360e986f51Sdantv delete 737e08341c6Sdan 738de4996e2Sdan#------------------------------------------------------------------------- 739146ed78bSdan# The following tests deal with multi-file commits. 740de4996e2Sdan# 741146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because 742146ed78bSdan# another connection is holding a SHARED lock on one of the 743146ed78bSdan# files. After the SHARED lock is removed, the COMMIT succeeds. 744146ed78bSdan# 745146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory. 746146ed78bSdan# 747146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory. 748146ed78bSdan# 749146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file 750146ed78bSdan# name is added to a journal file immediately after the last 751146ed78bSdan# journal record. But with synchronous=full, extra unused space 752146ed78bSdan# is allocated between the last journal record and the 753146ed78bSdan# master-journal file name so that the master-journal file 754146ed78bSdan# name does not lie on the same sector as the last journal file 755146ed78bSdan# record. 756146ed78bSdan# 757e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is 758e08341c6Sdan# truncated to zero bytes when a multi-file transaction is 759e08341c6Sdan# committed (instead of the first couple of bytes being zeroed). 760e08341c6Sdan# 761146ed78bSdan# 762146ed78bSdando_test pager1-5.1.1 { 763146ed78bSdan faultsim_delete_and_reopen 764146ed78bSdan execsql { 765146ed78bSdan ATTACH 'test.db2' AS aux; 766146ed78bSdan CREATE TABLE t1(a, b); 767146ed78bSdan CREATE TABLE aux.t2(a, b); 768146ed78bSdan INSERT INTO t1 VALUES(17, 'Lenin'); 769146ed78bSdan INSERT INTO t1 VALUES(22, 'Stalin'); 770146ed78bSdan INSERT INTO t1 VALUES(53, 'Khrushchev'); 771146ed78bSdan } 772146ed78bSdan} {} 773146ed78bSdando_test pager1-5.1.2 { 774146ed78bSdan execsql { 775146ed78bSdan BEGIN; 776146ed78bSdan INSERT INTO t1 VALUES(64, 'Brezhnev'); 777146ed78bSdan INSERT INTO t2 SELECT * FROM t1; 778146ed78bSdan } 779146ed78bSdan sqlite3 db2 test.db2 780146ed78bSdan execsql { 781146ed78bSdan BEGIN; 782146ed78bSdan SELECT * FROM t2; 783146ed78bSdan } db2 784146ed78bSdan} {} 785146ed78bSdando_test pager1-5.1.3 { 786146ed78bSdan catchsql COMMIT 787146ed78bSdan} {1 {database is locked}} 788146ed78bSdando_test pager1-5.1.4 { 789146ed78bSdan execsql COMMIT db2 790146ed78bSdan execsql COMMIT 791146ed78bSdan execsql { SELECT * FROM t2 } db2 792146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev} 793146ed78bSdando_test pager1-5.1.5 { 794146ed78bSdan db2 close 795146ed78bSdan} {} 796146ed78bSdan 797146ed78bSdando_test pager1-5.2.1 { 798146ed78bSdan execsql { 799146ed78bSdan PRAGMA journal_mode = memory; 800146ed78bSdan BEGIN; 801146ed78bSdan INSERT INTO t1 VALUES(84, 'Andropov'); 802146ed78bSdan INSERT INTO t2 VALUES(84, 'Andropov'); 803146ed78bSdan COMMIT; 804146ed78bSdan } 805146ed78bSdan} {memory} 806146ed78bSdando_test pager1-5.3.1 { 807146ed78bSdan execsql { 808146ed78bSdan PRAGMA journal_mode = off; 809146ed78bSdan BEGIN; 810146ed78bSdan INSERT INTO t1 VALUES(85, 'Gorbachev'); 811146ed78bSdan INSERT INTO t2 VALUES(85, 'Gorbachev'); 812146ed78bSdan COMMIT; 813146ed78bSdan } 814146ed78bSdan} {off} 815146ed78bSdan 816146ed78bSdando_test pager1-5.4.1 { 817146ed78bSdan db close 818146ed78bSdan testvfs tv 819146ed78bSdan sqlite3 db test.db -vfs tv 820146ed78bSdan execsql { ATTACH 'test.db2' AS aux } 821146ed78bSdan 822146ed78bSdan tv filter xDelete 823146ed78bSdan tv script max_journal_size 824146ed78bSdan tv sectorsize 512 825146ed78bSdan set ::max_journal 0 826146ed78bSdan proc max_journal_size {method args} { 827146ed78bSdan set sz 0 828146ed78bSdan catch { set sz [file size test.db-journal] } 829146ed78bSdan if {$sz > $::max_journal} { 830146ed78bSdan set ::max_journal $sz 831146ed78bSdan } 832146ed78bSdan return SQLITE_OK 833146ed78bSdan } 834146ed78bSdan execsql { 835146ed78bSdan PRAGMA journal_mode = DELETE; 836146ed78bSdan PRAGMA synchronous = NORMAL; 837146ed78bSdan BEGIN; 838146ed78bSdan INSERT INTO t1 VALUES(85, 'Gorbachev'); 839146ed78bSdan INSERT INTO t2 VALUES(85, 'Gorbachev'); 840146ed78bSdan COMMIT; 841146ed78bSdan } 842146ed78bSdan set ::max_journal 843146ed78bSdan} [expr 2615+[string length [pwd]]] 844146ed78bSdando_test pager1-5.4.2 { 845146ed78bSdan set ::max_journal 0 846146ed78bSdan execsql { 847146ed78bSdan PRAGMA synchronous = full; 848146ed78bSdan BEGIN; 849146ed78bSdan DELETE FROM t1 WHERE b = 'Lenin'; 850146ed78bSdan DELETE FROM t2 WHERE b = 'Lenin'; 851146ed78bSdan COMMIT; 852146ed78bSdan } 853146ed78bSdan set ::max_journal 854146ed78bSdan} [expr 3111+[string length [pwd]]] 855146ed78bSdandb close 856146ed78bSdantv delete 857146ed78bSdan 858146ed78bSdando_test pager1-5.5.1 { 859146ed78bSdan sqlite3 db test.db 860146ed78bSdan execsql { 861146ed78bSdan ATTACH 'test.db2' AS aux; 862146ed78bSdan PRAGMA journal_mode = PERSIST; 863146ed78bSdan CREATE TABLE t3(a, b); 864146ed78bSdan INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1; 865146ed78bSdan UPDATE t3 SET b = randomblob(1500); 866146ed78bSdan } 867146ed78bSdan expr [file size test.db-journal] > 15000 868146ed78bSdan} {1} 869146ed78bSdando_test pager1-5.5.2 { 870146ed78bSdan execsql { 871146ed78bSdan PRAGMA synchronous = full; 872146ed78bSdan BEGIN; 873146ed78bSdan DELETE FROM t1 WHERE b = 'Stalin'; 874146ed78bSdan DELETE FROM t2 WHERE b = 'Stalin'; 875146ed78bSdan COMMIT; 876146ed78bSdan } 877146ed78bSdan file size test.db-journal 878146ed78bSdan} {0} 879146ed78bSdan 880146ed78bSdan 881146ed78bSdan#------------------------------------------------------------------------- 882146ed78bSdan# The following tests work with "PRAGMA max_page_count" 883146ed78bSdan# 884146ed78bSdando_test pager1-6.1 { 885146ed78bSdan faultsim_delete_and_reopen 886146ed78bSdan execsql { 887146ed78bSdan PRAGMA max_page_count = 10; 888146ed78bSdan CREATE TABLE t2(a, b); 889146ed78bSdan CREATE TABLE t3(a, b); 890146ed78bSdan CREATE TABLE t4(a, b); 891146ed78bSdan CREATE TABLE t5(a, b); 892146ed78bSdan CREATE TABLE t6(a, b); 893146ed78bSdan CREATE TABLE t7(a, b); 894146ed78bSdan CREATE TABLE t8(a, b); 895146ed78bSdan CREATE TABLE t9(a, b); 896146ed78bSdan CREATE TABLE t10(a, b); 897146ed78bSdan } 898146ed78bSdan} {10} 899146ed78bSdando_test pager1-6.2 { 900146ed78bSdan catchsql { 901146ed78bSdan CREATE TABLE t11(a, b); 902146ed78bSdan } 903146ed78bSdan} {1 {database or disk is full}} 904de4996e2Sdan 905153eda0aSdan 906153eda0aSdan#------------------------------------------------------------------------- 907153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and 908153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE". 909153eda0aSdan# 910153eda0aSdan# Each test is specified with 5 variables. As follows: 911153eda0aSdan# 912153eda0aSdan# $tn: Test Number. Used as part of the [do_test] test names. 913153eda0aSdan# $sql: SQL to execute. 914153eda0aSdan# $res: Expected result of executing $sql. 915153eda0aSdan# $js: The expected size of the journal file, in bytes, after executing 916153eda0aSdan# the SQL script. Or -1 if the journal is not expected to exist. 917153eda0aSdan# $ws: The expected size of the WAL file, in bytes, after executing 918153eda0aSdan# the SQL script. Or -1 if the WAL is not expected to exist. 919153eda0aSdan# 920153eda0aSdanfaultsim_delete_and_reopen 921153eda0aSdanforeach {tn sql res js ws} [subst { 922153eda0aSdan 923153eda0aSdan 1 { 924153eda0aSdan CREATE TABLE t1(a, b); 925153eda0aSdan PRAGMA auto_vacuum=OFF; 926153eda0aSdan PRAGMA synchronous=NORMAL; 927153eda0aSdan PRAGMA page_size=1024; 928153eda0aSdan PRAGMA locking_mode=EXCLUSIVE; 929153eda0aSdan PRAGMA journal_mode=TRUNCATE; 930153eda0aSdan INSERT INTO t1 VALUES(1, 2); 931153eda0aSdan } {exclusive truncate} 0 -1 932153eda0aSdan 933153eda0aSdan 2 { 934153eda0aSdan BEGIN IMMEDIATE; 935153eda0aSdan SELECT * FROM t1; 936153eda0aSdan COMMIT; 937153eda0aSdan } {1 2} 0 -1 938153eda0aSdan 939153eda0aSdan 3 { 940153eda0aSdan BEGIN; 941153eda0aSdan SELECT * FROM t1; 942153eda0aSdan COMMIT; 943153eda0aSdan } {1 2} 0 -1 944153eda0aSdan 945153eda0aSdan 4 { PRAGMA journal_mode = WAL } wal -1 -1 946153eda0aSdan 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024] 947153eda0aSdan 6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024] 948153eda0aSdan 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024] 949153eda0aSdan 950153eda0aSdan 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1 951153eda0aSdan 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1 952153eda0aSdan 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1 953153eda0aSdan 954153eda0aSdan}] { 955153eda0aSdan do_execsql_test pager1-7.1.$tn.1 $sql $res 956153eda0aSdan catch { set J -1 ; set J [file size test.db-journal] } 957153eda0aSdan catch { set W -1 ; set W [file size test.db-wal] } 958153eda0aSdan do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws] 959153eda0aSdan} 960153eda0aSdan 9610e986f51Sdanforeach {tn filename} { 9620e986f51Sdan 1 :memory: 9630e986f51Sdan 2 "" 9640e986f51Sdan} { 9650e986f51Sdan do_test pager1-8.$tn.1 { 966153eda0aSdan faultsim_delete_and_reopen 967153eda0aSdan db close 9680e986f51Sdan sqlite3 db $filename 969153eda0aSdan execsql { 970153eda0aSdan CREATE TABLE x1(x); 971153eda0aSdan INSERT INTO x1 VALUES('Charles'); 972153eda0aSdan INSERT INTO x1 VALUES('James'); 973153eda0aSdan INSERT INTO x1 VALUES('Mary'); 974153eda0aSdan SELECT * FROM x1; 975153eda0aSdan } 976153eda0aSdan } {Charles James Mary} 9770e986f51Sdan 9780e986f51Sdan do_test pager1-8.$tn.2 { 9790e986f51Sdan sqlite3 db2 $filename 9800e986f51Sdan catchsql { SELECT * FROM x1 } db2 981153eda0aSdan } {1 {no such table: x1}} 982153eda0aSdan 9830e986f51Sdan do_execsql_test pager1-8.$tn.3 { 9840e986f51Sdan BEGIN; 9850e986f51Sdan INSERT INTO x1 VALUES('William'); 9860e986f51Sdan INSERT INTO x1 VALUES('Anne'); 9870e986f51Sdan ROLLBACK; 9880e986f51Sdan } {} 9890e986f51Sdan} 9900e986f51Sdan 991dca321aeSdan#------------------------------------------------------------------------- 992dca321aeSdan# The next block of tests - pager1-9.* - deal with interactions between 993dca321aeSdan# the pager and the backup API. Test cases: 994dca321aeSdan# 995dca321aeSdan# pager1-9.1.*: Test that a backup completes successfully even if the 996dca321aeSdan# source db is written to during the backup op. 997dca321aeSdan# 998dca321aeSdan# pager1-9.2.*: Test that a backup completes successfully even if the 999dca321aeSdan# source db is written to and then rolled back during a 1000dca321aeSdan# backup operation. 1001dca321aeSdan# 1002dca321aeSdando_test pager1-9.0.1 { 1003dca321aeSdan faultsim_delete_and_reopen 1004dca321aeSdan db func a_string a_string 1005dca321aeSdan execsql { 1006dca321aeSdan PRAGMA cache_size = 10; 1007dca321aeSdan BEGIN; 1008dca321aeSdan CREATE TABLE ab(a, b, UNIQUE(a, b)); 1009dca321aeSdan INSERT INTO ab VALUES( a_string(200), a_string(300) ); 1010dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1011dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1012dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1013dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1014dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1015dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1016dca321aeSdan INSERT INTO ab SELECT a_string(200), a_string(300) FROM ab; 1017dca321aeSdan COMMIT; 1018dca321aeSdan } 1019dca321aeSdan} {} 1020dca321aeSdando_test pager1-9.0.2 { 1021dca321aeSdan sqlite3 db2 test.db2 1022dca321aeSdan db2 eval { PRAGMA cache_size = 10 } 1023dca321aeSdan sqlite3_backup B db2 main db main 1024dca321aeSdan list [B step 10000] [B finish] 1025dca321aeSdan} {SQLITE_DONE SQLITE_OK} 1026dca321aeSdando_test pager1-9.0.3 { 1027dca321aeSdan db one {SELECT md5sum(a, b) FROM ab} 1028dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}] 1029dca321aeSdan 1030dca321aeSdando_test pager1-9.1.1 { 1031dca321aeSdan execsql { UPDATE ab SET a = a_string(201) } 1032dca321aeSdan sqlite3_backup B db2 main db main 1033dca321aeSdan B step 30 1034dca321aeSdan} {SQLITE_OK} 1035dca321aeSdando_test pager1-9.1.2 { 1036dca321aeSdan execsql { UPDATE ab SET b = a_string(301) } 1037dca321aeSdan list [B step 10000] [B finish] 1038dca321aeSdan} {SQLITE_DONE SQLITE_OK} 1039dca321aeSdando_test pager1-9.1.3 { 1040dca321aeSdan db one {SELECT md5sum(a, b) FROM ab} 1041dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}] 1042dca321aeSdando_test pager1-9.1.4 { execsql { SELECT count(*) FROM ab } } {128} 1043dca321aeSdan 1044dca321aeSdando_test pager1-9.2.1 { 1045dca321aeSdan execsql { UPDATE ab SET a = a_string(202) } 1046dca321aeSdan sqlite3_backup B db2 main db main 1047dca321aeSdan B step 30 1048dca321aeSdan} {SQLITE_OK} 1049dca321aeSdando_test pager1-9.2.2 { 1050dca321aeSdan execsql { 1051dca321aeSdan BEGIN; 1052dca321aeSdan UPDATE ab SET b = a_string(301); 1053dca321aeSdan ROLLBACK; 1054dca321aeSdan } 1055dca321aeSdan list [B step 10000] [B finish] 1056dca321aeSdan} {SQLITE_DONE SQLITE_OK} 1057dca321aeSdando_test pager1-9.2.3 { 1058dca321aeSdan db one {SELECT md5sum(a, b) FROM ab} 1059dca321aeSdan} [db2 one {SELECT md5sum(a, b) FROM ab}] 1060dca321aeSdando_test pager1-9.2.4 { execsql { SELECT count(*) FROM ab } } {128} 1061*ec6ffc1aSdandb close 1062*ec6ffc1aSdandb2 close 1063*ec6ffc1aSdan 1064*ec6ffc1aSdan#------------------------------------------------------------------------- 1065*ec6ffc1aSdan# Test that regardless of the value returned by xSectorSize(), the 1066*ec6ffc1aSdan# minimum effective sector-size is 512 and the maximum 65536 bytes. 1067*ec6ffc1aSdan# 1068*ec6ffc1aSdantestvfs tv -default 1 1069*ec6ffc1aSdanforeach sectorsize { 1070*ec6ffc1aSdan 32 64 128 256 512 1024 2048 1071*ec6ffc1aSdan 4096 8192 16384 32768 65536 131072 262144 1072*ec6ffc1aSdan} { 1073*ec6ffc1aSdan tv sectorsize $sectorsize 1074*ec6ffc1aSdan set eff $sectorsize 1075*ec6ffc1aSdan if {$sectorsize < 512} { set eff 512 } 1076*ec6ffc1aSdan if {$sectorsize > 65536} { set eff 65536 } 1077*ec6ffc1aSdan 1078*ec6ffc1aSdan do_test pager1-10.$sectorsize { 1079*ec6ffc1aSdan faultsim_delete_and_reopen 1080*ec6ffc1aSdan execsql { 1081*ec6ffc1aSdan PRAGMA journal_mode = PERSIST; 1082*ec6ffc1aSdan PRAGMA page_size = 1024; 1083*ec6ffc1aSdan CREATE TABLE t1(a, b); 1084*ec6ffc1aSdan } 1085*ec6ffc1aSdan file size test.db-journal 1086*ec6ffc1aSdan } [expr $sectorsize > 65536 ? 65536 : $sectorsize] 1087*ec6ffc1aSdan} 1088*ec6ffc1aSdandb close 1089*ec6ffc1aSdantv delete 1090*ec6ffc1aSdan 1091*ec6ffc1aSdantestvfs tv -default 1 1092*ec6ffc1aSdanfaultsim_delete_and_reopen 1093*ec6ffc1aSdandb func a_string a_string 1094*ec6ffc1aSdando_execsql_test pager1-11.1 { 1095*ec6ffc1aSdan PRAGMA cache_size = 10; 1096*ec6ffc1aSdan BEGIN; 1097*ec6ffc1aSdan CREATE TABLE zz(top PRIMARY KEY); 1098*ec6ffc1aSdan INSERT INTO zz VALUES(a_string(222)); 1099*ec6ffc1aSdan INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; 1100*ec6ffc1aSdan INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; 1101*ec6ffc1aSdan INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; 1102*ec6ffc1aSdan INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; 1103*ec6ffc1aSdan INSERT INTO zz SELECT a_string((SELECT 222+max(rowid) FROM zz)) FROM zz; 1104*ec6ffc1aSdan COMMIT; 1105*ec6ffc1aSdan BEGIN; 1106*ec6ffc1aSdan UPDATE zz SET top = a_string(345); 1107*ec6ffc1aSdan} {} 1108*ec6ffc1aSdan 1109*ec6ffc1aSdanproc lockout {method args} { return SQLITE_IOERR } 1110*ec6ffc1aSdantv script lockout 1111*ec6ffc1aSdantv filter {xWrite xTruncate xSync} 1112*ec6ffc1aSdando_catchsql_test pager1-11.2 { COMMIT } {1 {disk I/O error}} 1113*ec6ffc1aSdan 1114*ec6ffc1aSdantv script {} 1115*ec6ffc1aSdando_test pager1-11.3 { 1116*ec6ffc1aSdan sqlite3 db2 test.db 1117*ec6ffc1aSdan execsql { 1118*ec6ffc1aSdan PRAGMA journal_mode = TRUNCATE; 1119*ec6ffc1aSdan PRAGMA integrity_check; 1120*ec6ffc1aSdan } db2 1121*ec6ffc1aSdan} {truncate ok} 1122*ec6ffc1aSdando_test pager1-11.4 { 1123*ec6ffc1aSdan db2 close 1124*ec6ffc1aSdan file size test.db-journal 1125*ec6ffc1aSdan} {0} 1126*ec6ffc1aSdanbreakpoint 1127*ec6ffc1aSdando_execsql_test pager1-11.5 { SELECT count(*) FROM zz } {32} 1128*ec6ffc1aSdan 1129dca321aeSdan 1130e91a54e1Sdanfinish_test 1131e91a54e1Sdan 1132