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 18*68928b6cSdan 19*68928b6cSdan# Do not use a codec for tests in this file, as the database file is 20*68928b6cSdan# manipulated directly using tcl scripts (using the [hexio_write] command). 21*68928b6cSdan# 22*68928b6cSdando_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# 41b0ac3e3aSdan 4253f04f3bSdanset a_string_counter 1 4353f04f3bSdanproc a_string {n} { 4453f04f3bSdan global a_string_counter 4553f04f3bSdan incr a_string_counter 4653f04f3bSdan string range [string repeat "${a_string_counter}." $n] 1 $n 4753f04f3bSdan} 4853f04f3bSdandb func a_string a_string 4953f04f3bSdan 50a4a9095eSdando_multiclient_test tn { 51e91a54e1Sdan 52e91a54e1Sdan # Create and populate a database table using connection [db]. Check 53e91a54e1Sdan # that connections [db2] and [db3] can see the schema and content. 54e91a54e1Sdan # 55e91a54e1Sdan do_test pager1-$tn.1 { 56e91a54e1Sdan sql1 { 57e91a54e1Sdan CREATE TABLE t1(a PRIMARY KEY, b); 58e91a54e1Sdan CREATE INDEX i1 ON t1(b); 59e91a54e1Sdan INSERT INTO t1 VALUES(1, 'one'); 60e91a54e1Sdan INSERT INTO t1 VALUES(2, 'two'); 61e91a54e1Sdan } 62e91a54e1Sdan } {} 63e91a54e1Sdan do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 64e91a54e1Sdan do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two} 65e91a54e1Sdan 66e91a54e1Sdan # Open a transaction and add a row using [db]. This puts [db] in 67e91a54e1Sdan # RESERVED state. Check that connections [db2] and [db3] can still 68e91a54e1Sdan # read the database content as it was before the transaction was 69e91a54e1Sdan # opened. [db] should see the inserted row. 70e91a54e1Sdan # 71e91a54e1Sdan do_test pager1-$tn.4 { 72e91a54e1Sdan sql1 { 73e91a54e1Sdan BEGIN; 74e91a54e1Sdan INSERT INTO t1 VALUES(3, 'three'); 75e91a54e1Sdan } 76e91a54e1Sdan } {} 77e91a54e1Sdan do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 78e91a54e1Sdan do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 79e91a54e1Sdan 80e91a54e1Sdan # [db] still has an open write transaction. Check that this prevents 81e91a54e1Sdan # other connections (specifically [db2]) from writing to the database. 82e91a54e1Sdan # 83e91a54e1Sdan # Even if [db2] opens a transaction first, it may not write to the 84e91a54e1Sdan # database. After the attempt to write the db within a transaction, 85e91a54e1Sdan # [db2] is left with an open transaction, but not a read-lock on 86e91a54e1Sdan # the main database. So it does not prevent [db] from committing. 87e91a54e1Sdan # 88e91a54e1Sdan do_test pager1-$tn.8 { 89e91a54e1Sdan csql2 { UPDATE t1 SET a = a + 10 } 90e91a54e1Sdan } {1 {database is locked}} 91e91a54e1Sdan do_test pager1-$tn.9 { 92e91a54e1Sdan csql2 { 93e91a54e1Sdan BEGIN; 94e91a54e1Sdan UPDATE t1 SET a = a + 10; 95e91a54e1Sdan } 96e91a54e1Sdan } {1 {database is locked}} 97e91a54e1Sdan 98e91a54e1Sdan # Have [db] commit its transactions. Check the other connections can 99e91a54e1Sdan # now see the new database content. 100e91a54e1Sdan # 101e91a54e1Sdan do_test pager1-$tn.10 { sql1 { COMMIT } } {} 102e91a54e1Sdan do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 103e91a54e1Sdan do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 104e91a54e1Sdan do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 105e91a54e1Sdan 106e91a54e1Sdan # Check that, as noted above, [db2] really did keep an open transaction 107e91a54e1Sdan # after the attempt to write the database failed. 108e91a54e1Sdan # 109e91a54e1Sdan do_test pager1-$tn.14 { 110e91a54e1Sdan csql2 { BEGIN } 111e91a54e1Sdan } {1 {cannot start a transaction within a transaction}} 112e91a54e1Sdan do_test pager1-$tn.15 { sql2 { ROLLBACK } } {} 113e91a54e1Sdan 114e91a54e1Sdan # Have [db2] open a transaction and take a read-lock on the database. 115e91a54e1Sdan # Check that this prevents [db] from writing to the database (outside 116e91a54e1Sdan # of any transaction). After this fails, check that [db3] can read 117e91a54e1Sdan # the db (showing that [db] did not take a PENDING lock etc.) 118e91a54e1Sdan # 119e91a54e1Sdan do_test pager1-$tn.15 { 120e91a54e1Sdan sql2 { BEGIN; SELECT * FROM t1; } 121e91a54e1Sdan } {1 one 2 two 3 three} 122e91a54e1Sdan do_test pager1-$tn.16 { 123e91a54e1Sdan csql1 { UPDATE t1 SET a = a + 10 } 124e91a54e1Sdan } {1 {database is locked}} 125e91a54e1Sdan do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 126e91a54e1Sdan 127e91a54e1Sdan # This time, have [db] open a transaction before writing the database. 128e91a54e1Sdan # This works - [db] gets a RESERVED lock which does not conflict with 129e91a54e1Sdan # the SHARED lock [db2] is holding. 130e91a54e1Sdan # 131e91a54e1Sdan do_test pager1-$tn.18 { 132e91a54e1Sdan sql1 { 133e91a54e1Sdan BEGIN; 134e91a54e1Sdan UPDATE t1 SET a = a + 10; 135e91a54e1Sdan } 136e91a54e1Sdan } {} 137e91a54e1Sdan do_test pager1-$tn-19 { 138e91a54e1Sdan sql1 { PRAGMA lock_status } 139e91a54e1Sdan } {main reserved temp closed} 140e91a54e1Sdan do_test pager1-$tn-20 { 141e91a54e1Sdan sql2 { PRAGMA lock_status } 142e91a54e1Sdan } {main shared temp closed} 143e91a54e1Sdan 144e91a54e1Sdan # Check that all connections can still read the database. Only [db] sees 145e91a54e1Sdan # the updated content (as the transaction has not been committed yet). 146e91a54e1Sdan # 147e91a54e1Sdan do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 148e91a54e1Sdan do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 149e91a54e1Sdan do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 150e91a54e1Sdan 151e91a54e1Sdan # Because [db2] still has the SHARED lock, [db] is unable to commit the 152e91a54e1Sdan # transaction. If it tries, an error is returned and the connection 153e91a54e1Sdan # upgrades to a PENDING lock. 154e91a54e1Sdan # 155e91a54e1Sdan # Once this happens, [db] can read the database and see the new content, 156e91a54e1Sdan # [db2] (still holding SHARED) can still read the old content, but [db3] 157e91a54e1Sdan # (not holding any lock) is prevented by [db]'s PENDING from reading 158e91a54e1Sdan # the database. 159e91a54e1Sdan # 160e91a54e1Sdan do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}} 161e91a54e1Sdan do_test pager1-$tn-25 { 162e91a54e1Sdan sql1 { PRAGMA lock_status } 163e91a54e1Sdan } {main pending temp closed} 164e91a54e1Sdan do_test pager1-$tn.26 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 165e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 166e91a54e1Sdan do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 167e91a54e1Sdan 168e91a54e1Sdan # Have [db2] commit its read transaction, releasing the SHARED lock it 169e91a54e1Sdan # is holding. Now, neither [db2] nor [db3] may read the database (as [db] 170e91a54e1Sdan # is still holding a PENDING). 171e91a54e1Sdan # 172e91a54e1Sdan do_test pager1-$tn.29 { sql2 { COMMIT } } {} 173e91a54e1Sdan do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} 174e91a54e1Sdan do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 175e91a54e1Sdan 176e91a54e1Sdan # [db] is now able to commit the transaction. Once the transaction is 177e91a54e1Sdan # committed, all three connections can read the new content. 178e91a54e1Sdan # 179e91a54e1Sdan do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} 180e91a54e1Sdan do_test pager1-$tn.26 { sql1 { COMMIT } } {} 181e91a54e1Sdan do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} 182e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} 183e91a54e1Sdan do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} 184e91a54e1Sdan} 185e91a54e1Sdan 18653f04f3bSdan#------------------------------------------------------------------------- 18753f04f3bSdan# Savepoint related test cases. 18853f04f3bSdan# 1890e986f51Sdando_test pager1-3.1.1 { 190b0ac3e3aSdan faultsim_delete_and_reopen 191b0ac3e3aSdan execsql { 192b0ac3e3aSdan CREATE TABLE t1(a PRIMARY KEY, b); 193b0ac3e3aSdan CREATE TABLE counter( 194b0ac3e3aSdan i CHECK (i<5), 195b0ac3e3aSdan u CHECK (u<10) 196b0ac3e3aSdan ); 197b0ac3e3aSdan INSERT INTO counter VALUES(0, 0); 198b0ac3e3aSdan CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN 199b0ac3e3aSdan UPDATE counter SET i = i+1; 200b0ac3e3aSdan END; 201b0ac3e3aSdan CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN 202b0ac3e3aSdan UPDATE counter SET u = u+1; 203b0ac3e3aSdan END; 204b0ac3e3aSdan } 205b0ac3e3aSdan execsql { SELECT * FROM counter } 206b0ac3e3aSdan} {0 0} 207b0ac3e3aSdan 2080e986f51Sdando_execsql_test pager1-3.1.2 { 209b0ac3e3aSdan BEGIN; 210b0ac3e3aSdan INSERT INTO t1 VALUES(1, randomblob(1500)); 211b0ac3e3aSdan INSERT INTO t1 VALUES(2, randomblob(1500)); 212b0ac3e3aSdan INSERT INTO t1 VALUES(3, randomblob(1500)); 213b0ac3e3aSdan SELECT * FROM counter; 214b0ac3e3aSdan} {3 0} 2150e986f51Sdando_catchsql_test pager1-3.1.3 { 216b0ac3e3aSdan INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 217b0ac3e3aSdan} {1 {constraint failed}} 218b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} 219b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} 220b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {} 221b0ac3e3aSdan 22253f04f3bSdan#------------------------------------------------------------------------- 22353f04f3bSdan# Hot journal rollback related test cases. 22453f04f3bSdan# 22553f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid 22653f04f3bSdan# journal files. 22753f04f3bSdan# 22853f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a 22953f04f3bSdan# hot-journal file appears to be corrupt (checksum does not 23053f04f3bSdan# compute) the associated journal is rolled back (and no 23153f04f3bSdan# xAccess() call to check for the presence of any master 23253f04f3bSdan# journal file is made). 23353f04f3bSdan# 234de4996e2Sdan# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the 235de4996e2Sdan# page-size or sector-size in the journal header appear to 236de4996e2Sdan# be invalid (too large, too small or not a power of 2). 237de4996e2Sdan# 238e08341c6Sdan# pager1.4.4.*: Test hot-journal rollback of journal file with a master 239e08341c6Sdan# journal pointer generated in various "PRAGMA synchronous" 240e08341c6Sdan# modes. 241e08341c6Sdan# 242e08341c6Sdan# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a 243e08341c6Sdan# journal-record for which the checksum fails. 244e08341c6Sdan# 24553f04f3bSdando_test pager1.4.1.1 { 24653f04f3bSdan faultsim_delete_and_reopen 24753f04f3bSdan execsql { 24853f04f3bSdan CREATE TABLE x(y, z); 24953f04f3bSdan INSERT INTO x VALUES(1, 2); 25053f04f3bSdan } 25153f04f3bSdan set fd [open test.db-journal w] 25253f04f3bSdan puts -nonewline $fd "helloworld" 25353f04f3bSdan close $fd 25453f04f3bSdan file exists test.db-journal 25553f04f3bSdan} {1} 25653f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2} 25753f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0} 25853f04f3bSdan 25953f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite 26053f04f3bSdan# deletes the master-journal to commit a multi-file transaction. 26153f04f3bSdan# 26253f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets 26353f04f3bSdan# up the file system to contain two databases, two hot-journal files and 26453f04f3bSdan# a master-journal. 26553f04f3bSdan# 26653f04f3bSdando_test pager1.4.2.1 { 26753f04f3bSdan testvfs tstvfs -default 1 26853f04f3bSdan tstvfs filter xDelete 26953f04f3bSdan tstvfs script xDeleteCallback 27053f04f3bSdan proc xDeleteCallback {method file args} { 27153f04f3bSdan set file [file tail $file] 27253f04f3bSdan if { [string match *mj* $file] } { faultsim_save } 27353f04f3bSdan } 27453f04f3bSdan faultsim_delete_and_reopen 27553f04f3bSdan db func a_string a_string 27653f04f3bSdan execsql { 27753f04f3bSdan ATTACH 'test.db2' AS aux; 27853f04f3bSdan PRAGMA journal_mode = DELETE; 27953f04f3bSdan PRAGMA main.cache_size = 10; 28053f04f3bSdan PRAGMA aux.cache_size = 10; 28153f04f3bSdan CREATE TABLE t1(a UNIQUE, b UNIQUE); 28253f04f3bSdan CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); 28353f04f3bSdan INSERT INTO t1 VALUES(a_string(200), a_string(300)); 28453f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 28553f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 28653f04f3bSdan INSERT INTO t2 SELECT * FROM t1; 28753f04f3bSdan BEGIN; 28853f04f3bSdan INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; 28953f04f3bSdan INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; 29053f04f3bSdan INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; 29153f04f3bSdan INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; 29253f04f3bSdan REPLACE INTO t2 SELECT * FROM t1; 29353f04f3bSdan COMMIT; 29453f04f3bSdan } 29553f04f3bSdan db close 29653f04f3bSdan tstvfs delete 29753f04f3bSdan} {} 29853f04f3bSdando_test pager1.4.2.2 { 29953f04f3bSdan faultsim_restore_and_reopen 30053f04f3bSdan execsql { 30153f04f3bSdan SELECT count(*) FROM t1; 30253f04f3bSdan PRAGMA integrity_check; 30353f04f3bSdan } 30453f04f3bSdan} {4 ok} 30553f04f3bSdando_test pager1.4.2.3 { 30653f04f3bSdan faultsim_restore_and_reopen 30753f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 30853f04f3bSdan execsql { 30953f04f3bSdan SELECT count(*) FROM t1; 31053f04f3bSdan PRAGMA integrity_check; 31153f04f3bSdan } 31253f04f3bSdan} {64 ok} 31353f04f3bSdando_test pager1.4.2.4 { 31453f04f3bSdan faultsim_restore_and_reopen 31553f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 31653f04f3bSdan execsql { 31753f04f3bSdan SELECT count(*) FROM t1; 31853f04f3bSdan PRAGMA integrity_check; 31953f04f3bSdan } 32053f04f3bSdan} {4 ok} 32153f04f3bSdando_test pager1.4.2.5 { 32253f04f3bSdan faultsim_restore_and_reopen 32353f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 32453f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 32553f04f3bSdan execsql { 32653f04f3bSdan SELECT count(*) FROM t1; 32753f04f3bSdan PRAGMA integrity_check; 32853f04f3bSdan } 32953f04f3bSdan} {4 ok} 33053f04f3bSdan 331de4996e2Sdando_test pager1.4.3.1 { 332de4996e2Sdan testvfs tstvfs -default 1 333de4996e2Sdan tstvfs filter xSync 334de4996e2Sdan tstvfs script xSyncCallback 335de4996e2Sdan proc xSyncCallback {method file args} { 336de4996e2Sdan set file [file tail $file] 337de4996e2Sdan if { 0==[string match *journal $file] } { faultsim_save } 338de4996e2Sdan } 339de4996e2Sdan faultsim_delete_and_reopen 340de4996e2Sdan execsql { 341de4996e2Sdan PRAGMA journal_mode = DELETE; 342de4996e2Sdan CREATE TABLE t1(a, b); 343de4996e2Sdan INSERT INTO t1 VALUES(1, 2); 344de4996e2Sdan INSERT INTO t1 VALUES(3, 4); 345de4996e2Sdan } 346de4996e2Sdan db close 347de4996e2Sdan tstvfs delete 348de4996e2Sdan} {} 349de4996e2Sdan 350de4996e2Sdanforeach {tn ofst value result} { 351de4996e2Sdan 2 20 31 {1 2 3 4} 352de4996e2Sdan 3 20 32 {1 2 3 4} 353de4996e2Sdan 4 20 33 {1 2 3 4} 354de4996e2Sdan 5 20 65536 {1 2 3 4} 355de4996e2Sdan 6 20 131072 {1 2 3 4} 356de4996e2Sdan 357de4996e2Sdan 7 24 511 {1 2 3 4} 358de4996e2Sdan 8 24 513 {1 2 3 4} 359de4996e2Sdan 9 24 65536 {1 2 3 4} 360de4996e2Sdan 361de4996e2Sdan 10 32 65536 {1 2} 362de4996e2Sdan} { 363de4996e2Sdan do_test pager1.4.3.$tn { 364de4996e2Sdan faultsim_restore_and_reopen 365de4996e2Sdan hexio_write test.db-journal $ofst [format %.8x $value] 366de4996e2Sdan execsql { SELECT * FROM t1 } 367de4996e2Sdan } $result 368de4996e2Sdan} 369de4996e2Sdandb close 370de4996e2Sdan 371e08341c6Sdan# Set up a VFS that snapshots the file-system just before a master journal 372e08341c6Sdan# file is deleted to commit a multi-file transaction. Specifically, the 373e08341c6Sdan# file-system is saved just before the xDelete() call to remove the 374e08341c6Sdan# master journal file from the file-system. 375e08341c6Sdan# 376e08341c6Sdantestvfs tv -default 1 377e08341c6Sdantv script copy_on_mj_delete 378e08341c6Sdanset ::mj_filename_length 0 379e08341c6Sdanproc copy_on_mj_delete {method filename args} { 380e08341c6Sdan if {[string match *mj* [file tail $filename]]} { 381e08341c6Sdan set ::mj_filename_length [string length $filename] 382e08341c6Sdan faultsim_save 383e08341c6Sdan } 384e08341c6Sdan return SQLITE_OK 385e08341c6Sdan} 386e08341c6Sdan 387e08341c6Sdanset pwd [pwd] 388e08341c6Sdanforeach {tn1 tcl} { 389e08341c6Sdan 1 { set prefix "test.db" } 390e08341c6Sdan 2 { 391e08341c6Sdan # This test depends on the underlying VFS being able to open paths 392e08341c6Sdan # 512 bytes in length. The idea is to create a hot-journal file that 393e08341c6Sdan # contains a master-journal pointer so large that it could contain 394e08341c6Sdan # a valid page record (if the file page-size is 512 bytes). So as to 395e08341c6Sdan # make sure SQLite doesn't get confused by this. 396e08341c6Sdan # 397e08341c6Sdan set nPadding [expr 511 - $::mj_filename_length] 398e08341c6Sdan 399e08341c6Sdan # We cannot just create a really long database file name to open, as 400e08341c6Sdan # Linux limits a single component of a path to 255 bytes by default 401e08341c6Sdan # (and presumably other systems have limits too). So create a directory 402e08341c6Sdan # hierarchy to work in. 403e08341c6Sdan # 404e08341c6Sdan set dirname "d123456789012345678901234567890/" 405e08341c6Sdan set nDir [expr $nPadding / 32] 406e08341c6Sdan if { $nDir } { 407e08341c6Sdan set p [string repeat $dirname $nDir] 408e08341c6Sdan file mkdir $p 409e08341c6Sdan cd $p 410e08341c6Sdan } 411e08341c6Sdan 412e08341c6Sdan set padding [string repeat x [expr $nPadding %32]] 413e08341c6Sdan set prefix "test.db${padding}" 414e08341c6Sdan } 415e08341c6Sdan} { 416e08341c6Sdan eval $tcl 417e08341c6Sdan foreach {tn2 sql} { 418e08341c6Sdan o { 419e08341c6Sdan PRAGMA main.synchronous=OFF; 420e08341c6Sdan PRAGMA aux.synchronous=OFF; 421e08341c6Sdan } 422e08341c6Sdan o512 { 423e08341c6Sdan PRAGMA main.synchronous=OFF; 424e08341c6Sdan PRAGMA aux.synchronous=OFF; 425e08341c6Sdan PRAGMA main.page_size = 512; 426e08341c6Sdan PRAGMA aux.page_size = 512; 427e08341c6Sdan } 428e08341c6Sdan n { 429e08341c6Sdan PRAGMA main.synchronous=NORMAL; 430e08341c6Sdan PRAGMA aux.synchronous=NORMAL; 431e08341c6Sdan } 432e08341c6Sdan f { 433e08341c6Sdan PRAGMA main.synchronous=FULL; 434e08341c6Sdan PRAGMA aux.synchronous=FULL; 435e08341c6Sdan } 436e08341c6Sdan } { 437e08341c6Sdan 438e08341c6Sdan set tn "${tn1}.${tn2}" 439e08341c6Sdan 440e08341c6Sdan # Set up a connection to have two databases, test.db (main) and 441e08341c6Sdan # test.db2 (aux). Then run a multi-file transaction on them. The 442e08341c6Sdan # VFS will snapshot the file-system just before the master-journal 443e08341c6Sdan # file is deleted to commit the transaction. 444e08341c6Sdan # 445e08341c6Sdan tv filter xDelete 446e08341c6Sdan do_test pager1-4.4.$tn.1 { 447e08341c6Sdan faultsim_delete_and_reopen $prefix 448e08341c6Sdan execsql " 449e08341c6Sdan ATTACH '${prefix}2' AS aux; 450e08341c6Sdan $sql 451e08341c6Sdan CREATE TABLE a(x); 452e08341c6Sdan CREATE TABLE aux.b(x); 453e08341c6Sdan INSERT INTO a VALUES('double-you'); 454e08341c6Sdan INSERT INTO a VALUES('why'); 455e08341c6Sdan INSERT INTO a VALUES('zed'); 456e08341c6Sdan INSERT INTO b VALUES('won'); 457e08341c6Sdan INSERT INTO b VALUES('too'); 458e08341c6Sdan INSERT INTO b VALUES('free'); 459e08341c6Sdan " 460e08341c6Sdan execsql { 461e08341c6Sdan BEGIN; 462e08341c6Sdan INSERT INTO a SELECT * FROM b WHERE rowid<=3; 463e08341c6Sdan INSERT INTO b SELECT * FROM a WHERE rowid<=3; 464e08341c6Sdan COMMIT; 465e08341c6Sdan } 466e08341c6Sdan } {} 467e08341c6Sdan tv filter {} 468e08341c6Sdan 469e08341c6Sdan # Check that the transaction was committed successfully. 470e08341c6Sdan # 471e08341c6Sdan do_execsql_test pager1-4.4.$tn.2 { 472e08341c6Sdan SELECT * FROM a 473e08341c6Sdan } {double-you why zed won too free} 474e08341c6Sdan do_execsql_test pager1-4.4.$tn.3 { 475e08341c6Sdan SELECT * FROM b 476e08341c6Sdan } {won too free double-you why zed} 477e08341c6Sdan 478e08341c6Sdan # Restore the file-system and reopen the databases. Check that it now 479e08341c6Sdan # appears that the transaction was not committed (because the file-system 480e08341c6Sdan # was restored to the state where it had not been). 481e08341c6Sdan # 482e08341c6Sdan do_test pager1-4.4.$tn.4 { 483e08341c6Sdan faultsim_restore_and_reopen $prefix 484e08341c6Sdan execsql "ATTACH '${prefix}2' AS aux" 485e08341c6Sdan } {} 486e08341c6Sdan do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed} 487e08341c6Sdan do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free} 488e08341c6Sdan 489e08341c6Sdan # Restore the file-system again. This time, before reopening the databases, 490e08341c6Sdan # delete the master-journal file from the file-system. It now appears that 491e08341c6Sdan # the transaction was committed (no master-journal file == no rollback). 492e08341c6Sdan # 493e08341c6Sdan do_test pager1-4.4.$tn.7 { 494e08341c6Sdan faultsim_restore_and_reopen $prefix 495e08341c6Sdan foreach f [glob ${prefix}-mj*] { file delete -force $f } 496e08341c6Sdan execsql "ATTACH '${prefix}2' AS aux" 497e08341c6Sdan } {} 498e08341c6Sdan do_execsql_test pager1-4.4.$tn.8 { 499e08341c6Sdan SELECT * FROM a 500e08341c6Sdan } {double-you why zed won too free} 501e08341c6Sdan do_execsql_test pager1-4.4.$tn.9 { 502e08341c6Sdan SELECT * FROM b 503e08341c6Sdan } {won too free double-you why zed} 504e08341c6Sdan } 505e08341c6Sdan 506e08341c6Sdan cd $pwd 507e08341c6Sdan} 508e08341c6Sdandb close 509e08341c6Sdantv delete 5100e986f51Sdanfile delete -force $dirname 5110e986f51Sdan 5120e986f51Sdan 5130e986f51Sdan# Set up a VFS to make a copy of the file-system just before deleting a 5140e986f51Sdan# journal file to commit a transaction. The transaction modifies exactly 5150e986f51Sdan# two database pages (and page 1 - the change counter). 5160e986f51Sdan# 5170e986f51Sdantestvfs tv -default 1 5180e986f51Sdantv sectorsize 512 5190e986f51Sdantv script copy_on_journal_delete 5200e986f51Sdantv filter xDelete 5210e986f51Sdanset ::mj_filename_length 0 5220e986f51Sdanproc copy_on_journal_delete {method filename args} { 5230e986f51Sdan if {[string match *journal $filename]} faultsim_save 5240e986f51Sdan return SQLITE_OK 5250e986f51Sdan} 5260e986f51Sdanfaultsim_delete_and_reopen 5270e986f51Sdando_execsql_test pager1.4.5.1 { 5280e986f51Sdan PRAGMA page_size = 1024; 5290e986f51Sdan CREATE TABLE t1(a, b); 5300e986f51Sdan CREATE TABLE t2(a, b); 5310e986f51Sdan INSERT INTO t1 VALUES('I', 'II'); 5320e986f51Sdan INSERT INTO t2 VALUES('III', 'IV'); 5330e986f51Sdan BEGIN; 5340e986f51Sdan INSERT INTO t1 VALUES(1, 2); 5350e986f51Sdan INSERT INTO t2 VALUES(3, 4); 5360e986f51Sdan COMMIT; 5370e986f51Sdan} {} 5380e986f51Sdantv filter {} 5390e986f51Sdan 5400e986f51Sdan# Check the transaction was committed: 5410e986f51Sdan# 5420e986f51Sdando_execsql_test pager1.4.5.2 { 5430e986f51Sdan SELECT * FROM t1; 5440e986f51Sdan SELECT * FROM t2; 5450e986f51Sdan} {I II 1 2 III IV 3 4} 5460e986f51Sdan 5470e986f51Sdan# Now try three tests: 5480e986f51Sdan# 5490e986f51Sdan# pager1-4.5.3: Restore the file-system. Check that the whole transaction 5500e986f51Sdan# is rolled back. 5510e986f51Sdan# 5520e986f51Sdan# pager1-4.5.4: Restore the file-system. Corrupt the first record in the 5530e986f51Sdan# journal. Check the transaction is not rolled back. 5540e986f51Sdan# 5550e986f51Sdan# pager1-4.5.5: Restore the file-system. Corrupt the second record in the 5560e986f51Sdan# journal. Check that the first record in the transaction is 5570e986f51Sdan# played back, but not the second. 5580e986f51Sdan# 5590e986f51Sdanfaultsim_restore_and_reopen 5600e986f51Sdando_execsql_test pager1.4.5.3 { 5610e986f51Sdan SELECT * FROM t1; 5620e986f51Sdan SELECT * FROM t2; 5630e986f51Sdan} {I II III IV} 5640e986f51Sdanfaultsim_restore_and_reopen 5650e986f51Sdanhexio_write test.db-journal [expr 512+4+1024 - 202] 0123456789ABCDEF 5660e986f51Sdando_execsql_test pager1.4.5.4 { 5670e986f51Sdan SELECT * FROM t1; 5680e986f51Sdan SELECT * FROM t2; 5690e986f51Sdan} {I II 1 2 III IV 3 4} 5700e986f51Sdanfaultsim_restore_and_reopen 5710e986f51Sdanhexio_write test.db-journal [expr 512+4+1024+4+4+1024 - 202] 0123456789ABCDEF 5720e986f51Sdando_execsql_test pager1.4.5.5 { 5730e986f51Sdan SELECT * FROM t1; 5740e986f51Sdan SELECT * FROM t2; 5750e986f51Sdan} {I II III IV 3 4} 5760e986f51Sdan 5770e986f51Sdandb close 5780e986f51Sdantv delete 579e08341c6Sdan 580de4996e2Sdan#------------------------------------------------------------------------- 581146ed78bSdan# The following tests deal with multi-file commits. 582de4996e2Sdan# 583146ed78bSdan# pager1-5.1.*: The case where a multi-file cannot be committed because 584146ed78bSdan# another connection is holding a SHARED lock on one of the 585146ed78bSdan# files. After the SHARED lock is removed, the COMMIT succeeds. 586146ed78bSdan# 587146ed78bSdan# pager1-5.2.*: Multi-file commits with journal_mode=memory. 588146ed78bSdan# 589146ed78bSdan# pager1-5.3.*: Multi-file commits with journal_mode=memory. 590146ed78bSdan# 591146ed78bSdan# pager1-5.4.*: Check that with synchronous=normal, the master-journal file 592146ed78bSdan# name is added to a journal file immediately after the last 593146ed78bSdan# journal record. But with synchronous=full, extra unused space 594146ed78bSdan# is allocated between the last journal record and the 595146ed78bSdan# master-journal file name so that the master-journal file 596146ed78bSdan# name does not lie on the same sector as the last journal file 597146ed78bSdan# record. 598146ed78bSdan# 599e08341c6Sdan# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is 600e08341c6Sdan# truncated to zero bytes when a multi-file transaction is 601e08341c6Sdan# committed (instead of the first couple of bytes being zeroed). 602e08341c6Sdan# 603146ed78bSdan# 604146ed78bSdando_test pager1-5.1.1 { 605146ed78bSdan faultsim_delete_and_reopen 606146ed78bSdan execsql { 607146ed78bSdan ATTACH 'test.db2' AS aux; 608146ed78bSdan CREATE TABLE t1(a, b); 609146ed78bSdan CREATE TABLE aux.t2(a, b); 610146ed78bSdan INSERT INTO t1 VALUES(17, 'Lenin'); 611146ed78bSdan INSERT INTO t1 VALUES(22, 'Stalin'); 612146ed78bSdan INSERT INTO t1 VALUES(53, 'Khrushchev'); 613146ed78bSdan } 614146ed78bSdan} {} 615146ed78bSdando_test pager1-5.1.2 { 616146ed78bSdan execsql { 617146ed78bSdan BEGIN; 618146ed78bSdan INSERT INTO t1 VALUES(64, 'Brezhnev'); 619146ed78bSdan INSERT INTO t2 SELECT * FROM t1; 620146ed78bSdan } 621146ed78bSdan sqlite3 db2 test.db2 622146ed78bSdan execsql { 623146ed78bSdan BEGIN; 624146ed78bSdan SELECT * FROM t2; 625146ed78bSdan } db2 626146ed78bSdan} {} 627146ed78bSdando_test pager1-5.1.3 { 628146ed78bSdan catchsql COMMIT 629146ed78bSdan} {1 {database is locked}} 630146ed78bSdando_test pager1-5.1.4 { 631146ed78bSdan execsql COMMIT db2 632146ed78bSdan execsql COMMIT 633146ed78bSdan execsql { SELECT * FROM t2 } db2 634146ed78bSdan} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev} 635146ed78bSdando_test pager1-5.1.5 { 636146ed78bSdan db2 close 637146ed78bSdan} {} 638146ed78bSdan 639146ed78bSdando_test pager1-5.2.1 { 640146ed78bSdan execsql { 641146ed78bSdan PRAGMA journal_mode = memory; 642146ed78bSdan BEGIN; 643146ed78bSdan INSERT INTO t1 VALUES(84, 'Andropov'); 644146ed78bSdan INSERT INTO t2 VALUES(84, 'Andropov'); 645146ed78bSdan COMMIT; 646146ed78bSdan } 647146ed78bSdan} {memory} 648146ed78bSdando_test pager1-5.3.1 { 649146ed78bSdan execsql { 650146ed78bSdan PRAGMA journal_mode = off; 651146ed78bSdan BEGIN; 652146ed78bSdan INSERT INTO t1 VALUES(85, 'Gorbachev'); 653146ed78bSdan INSERT INTO t2 VALUES(85, 'Gorbachev'); 654146ed78bSdan COMMIT; 655146ed78bSdan } 656146ed78bSdan} {off} 657146ed78bSdan 658146ed78bSdando_test pager1-5.4.1 { 659146ed78bSdan db close 660146ed78bSdan testvfs tv 661146ed78bSdan sqlite3 db test.db -vfs tv 662146ed78bSdan execsql { ATTACH 'test.db2' AS aux } 663146ed78bSdan 664146ed78bSdan tv filter xDelete 665146ed78bSdan tv script max_journal_size 666146ed78bSdan tv sectorsize 512 667146ed78bSdan set ::max_journal 0 668146ed78bSdan proc max_journal_size {method args} { 669146ed78bSdan set sz 0 670146ed78bSdan catch { set sz [file size test.db-journal] } 671146ed78bSdan if {$sz > $::max_journal} { 672146ed78bSdan set ::max_journal $sz 673146ed78bSdan } 674146ed78bSdan return SQLITE_OK 675146ed78bSdan } 676146ed78bSdan execsql { 677146ed78bSdan PRAGMA journal_mode = DELETE; 678146ed78bSdan PRAGMA synchronous = NORMAL; 679146ed78bSdan BEGIN; 680146ed78bSdan INSERT INTO t1 VALUES(85, 'Gorbachev'); 681146ed78bSdan INSERT INTO t2 VALUES(85, 'Gorbachev'); 682146ed78bSdan COMMIT; 683146ed78bSdan } 684146ed78bSdan set ::max_journal 685146ed78bSdan} [expr 2615+[string length [pwd]]] 686146ed78bSdando_test pager1-5.4.2 { 687146ed78bSdan set ::max_journal 0 688146ed78bSdan execsql { 689146ed78bSdan PRAGMA synchronous = full; 690146ed78bSdan BEGIN; 691146ed78bSdan DELETE FROM t1 WHERE b = 'Lenin'; 692146ed78bSdan DELETE FROM t2 WHERE b = 'Lenin'; 693146ed78bSdan COMMIT; 694146ed78bSdan } 695146ed78bSdan set ::max_journal 696146ed78bSdan} [expr 3111+[string length [pwd]]] 697146ed78bSdandb close 698146ed78bSdantv delete 699146ed78bSdan 700146ed78bSdando_test pager1-5.5.1 { 701146ed78bSdan sqlite3 db test.db 702146ed78bSdan execsql { 703146ed78bSdan ATTACH 'test.db2' AS aux; 704146ed78bSdan PRAGMA journal_mode = PERSIST; 705146ed78bSdan CREATE TABLE t3(a, b); 706146ed78bSdan INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1; 707146ed78bSdan UPDATE t3 SET b = randomblob(1500); 708146ed78bSdan } 709146ed78bSdan expr [file size test.db-journal] > 15000 710146ed78bSdan} {1} 711146ed78bSdando_test pager1-5.5.2 { 712146ed78bSdan execsql { 713146ed78bSdan PRAGMA synchronous = full; 714146ed78bSdan BEGIN; 715146ed78bSdan DELETE FROM t1 WHERE b = 'Stalin'; 716146ed78bSdan DELETE FROM t2 WHERE b = 'Stalin'; 717146ed78bSdan COMMIT; 718146ed78bSdan } 719146ed78bSdan file size test.db-journal 720146ed78bSdan} {0} 721146ed78bSdan 722146ed78bSdan 723146ed78bSdan#------------------------------------------------------------------------- 724146ed78bSdan# The following tests work with "PRAGMA max_page_count" 725146ed78bSdan# 726146ed78bSdando_test pager1-6.1 { 727146ed78bSdan faultsim_delete_and_reopen 728146ed78bSdan execsql { 729146ed78bSdan PRAGMA max_page_count = 10; 730146ed78bSdan CREATE TABLE t2(a, b); 731146ed78bSdan CREATE TABLE t3(a, b); 732146ed78bSdan CREATE TABLE t4(a, b); 733146ed78bSdan CREATE TABLE t5(a, b); 734146ed78bSdan CREATE TABLE t6(a, b); 735146ed78bSdan CREATE TABLE t7(a, b); 736146ed78bSdan CREATE TABLE t8(a, b); 737146ed78bSdan CREATE TABLE t9(a, b); 738146ed78bSdan CREATE TABLE t10(a, b); 739146ed78bSdan } 740146ed78bSdan} {10} 741146ed78bSdando_test pager1-6.2 { 742146ed78bSdan catchsql { 743146ed78bSdan CREATE TABLE t11(a, b); 744146ed78bSdan } 745146ed78bSdan} {1 {database or disk is full}} 746de4996e2Sdan 747153eda0aSdan 748153eda0aSdan#------------------------------------------------------------------------- 749153eda0aSdan# The following tests work with "PRAGMA journal_mode=TRUNCATE" and 750153eda0aSdan# "PRAGMA locking_mode=EXCLUSIVE". 751153eda0aSdan# 752153eda0aSdan# Each test is specified with 5 variables. As follows: 753153eda0aSdan# 754153eda0aSdan# $tn: Test Number. Used as part of the [do_test] test names. 755153eda0aSdan# $sql: SQL to execute. 756153eda0aSdan# $res: Expected result of executing $sql. 757153eda0aSdan# $js: The expected size of the journal file, in bytes, after executing 758153eda0aSdan# the SQL script. Or -1 if the journal is not expected to exist. 759153eda0aSdan# $ws: The expected size of the WAL file, in bytes, after executing 760153eda0aSdan# the SQL script. Or -1 if the WAL is not expected to exist. 761153eda0aSdan# 762153eda0aSdanfaultsim_delete_and_reopen 763153eda0aSdanforeach {tn sql res js ws} [subst { 764153eda0aSdan 765153eda0aSdan 1 { 766153eda0aSdan CREATE TABLE t1(a, b); 767153eda0aSdan PRAGMA auto_vacuum=OFF; 768153eda0aSdan PRAGMA synchronous=NORMAL; 769153eda0aSdan PRAGMA page_size=1024; 770153eda0aSdan PRAGMA locking_mode=EXCLUSIVE; 771153eda0aSdan PRAGMA journal_mode=TRUNCATE; 772153eda0aSdan INSERT INTO t1 VALUES(1, 2); 773153eda0aSdan } {exclusive truncate} 0 -1 774153eda0aSdan 775153eda0aSdan 2 { 776153eda0aSdan BEGIN IMMEDIATE; 777153eda0aSdan SELECT * FROM t1; 778153eda0aSdan COMMIT; 779153eda0aSdan } {1 2} 0 -1 780153eda0aSdan 781153eda0aSdan 3 { 782153eda0aSdan BEGIN; 783153eda0aSdan SELECT * FROM t1; 784153eda0aSdan COMMIT; 785153eda0aSdan } {1 2} 0 -1 786153eda0aSdan 787153eda0aSdan 4 { PRAGMA journal_mode = WAL } wal -1 -1 788153eda0aSdan 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024] 789153eda0aSdan 6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024] 790153eda0aSdan 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024] 791153eda0aSdan 792153eda0aSdan 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1 793153eda0aSdan 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1 794153eda0aSdan 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1 795153eda0aSdan 796153eda0aSdan}] { 797153eda0aSdan do_execsql_test pager1-7.1.$tn.1 $sql $res 798153eda0aSdan catch { set J -1 ; set J [file size test.db-journal] } 799153eda0aSdan catch { set W -1 ; set W [file size test.db-wal] } 800153eda0aSdan do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws] 801153eda0aSdan} 802153eda0aSdan 8030e986f51Sdanforeach {tn filename} { 8040e986f51Sdan 1 :memory: 8050e986f51Sdan 2 "" 8060e986f51Sdan} { 8070e986f51Sdan do_test pager1-8.$tn.1 { 808153eda0aSdan faultsim_delete_and_reopen 809153eda0aSdan db close 8100e986f51Sdan sqlite3 db $filename 811153eda0aSdan execsql { 812153eda0aSdan CREATE TABLE x1(x); 813153eda0aSdan INSERT INTO x1 VALUES('Charles'); 814153eda0aSdan INSERT INTO x1 VALUES('James'); 815153eda0aSdan INSERT INTO x1 VALUES('Mary'); 816153eda0aSdan SELECT * FROM x1; 817153eda0aSdan } 818153eda0aSdan } {Charles James Mary} 8190e986f51Sdan 8200e986f51Sdan do_test pager1-8.$tn.2 { 8210e986f51Sdan sqlite3 db2 $filename 8220e986f51Sdan catchsql { SELECT * FROM x1 } db2 823153eda0aSdan } {1 {no such table: x1}} 824153eda0aSdan 8250e986f51Sdan do_execsql_test pager1-8.$tn.3 { 8260e986f51Sdan BEGIN; 8270e986f51Sdan INSERT INTO x1 VALUES('William'); 8280e986f51Sdan INSERT INTO x1 VALUES('Anne'); 8290e986f51Sdan ROLLBACK; 8300e986f51Sdan } {} 8310e986f51Sdan} 8320e986f51Sdan 833e91a54e1Sdanfinish_test 834e91a54e1Sdan 835