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 17e91a54e1Sdan 18b0ac3e3aSdan# 19b0ac3e3aSdan# pager1-1.*: Test inter-process locking (clients in multiple processes). 20b0ac3e3aSdan# 21b0ac3e3aSdan# pager1-2.*: Test intra-process locking (multiple clients in this process). 22b0ac3e3aSdan# 23b0ac3e3aSdan# pager1-3.*: Savepoint related tests. 24b0ac3e3aSdan# 25b0ac3e3aSdan 26b0ac3e3aSdanproc do_execsql_test {testname sql result} { 27b0ac3e3aSdan uplevel do_test $testname [list "execsql {$sql}"] [list $result] 28b0ac3e3aSdan} 29b0ac3e3aSdanproc do_catchsql_test {testname sql result} { 30b0ac3e3aSdan uplevel do_test $testname [list "catchsql {$sql}"] [list $result] 31b0ac3e3aSdan} 32b0ac3e3aSdan 33*53f04f3bSdanset a_string_counter 1 34*53f04f3bSdanproc a_string {n} { 35*53f04f3bSdan global a_string_counter 36*53f04f3bSdan incr a_string_counter 37*53f04f3bSdan string range [string repeat "${a_string_counter}." $n] 1 $n 38*53f04f3bSdan} 39*53f04f3bSdandb func a_string a_string 40*53f04f3bSdan 41a4a9095eSdando_multiclient_test tn { 42e91a54e1Sdan 43e91a54e1Sdan # Create and populate a database table using connection [db]. Check 44e91a54e1Sdan # that connections [db2] and [db3] can see the schema and content. 45e91a54e1Sdan # 46e91a54e1Sdan do_test pager1-$tn.1 { 47e91a54e1Sdan sql1 { 48e91a54e1Sdan CREATE TABLE t1(a PRIMARY KEY, b); 49e91a54e1Sdan CREATE INDEX i1 ON t1(b); 50e91a54e1Sdan INSERT INTO t1 VALUES(1, 'one'); 51e91a54e1Sdan INSERT INTO t1 VALUES(2, 'two'); 52e91a54e1Sdan } 53e91a54e1Sdan } {} 54e91a54e1Sdan do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 55e91a54e1Sdan do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two} 56e91a54e1Sdan 57e91a54e1Sdan # Open a transaction and add a row using [db]. This puts [db] in 58e91a54e1Sdan # RESERVED state. Check that connections [db2] and [db3] can still 59e91a54e1Sdan # read the database content as it was before the transaction was 60e91a54e1Sdan # opened. [db] should see the inserted row. 61e91a54e1Sdan # 62e91a54e1Sdan do_test pager1-$tn.4 { 63e91a54e1Sdan sql1 { 64e91a54e1Sdan BEGIN; 65e91a54e1Sdan INSERT INTO t1 VALUES(3, 'three'); 66e91a54e1Sdan } 67e91a54e1Sdan } {} 68e91a54e1Sdan do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 69e91a54e1Sdan do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 70e91a54e1Sdan 71e91a54e1Sdan # [db] still has an open write transaction. Check that this prevents 72e91a54e1Sdan # other connections (specifically [db2]) from writing to the database. 73e91a54e1Sdan # 74e91a54e1Sdan # Even if [db2] opens a transaction first, it may not write to the 75e91a54e1Sdan # database. After the attempt to write the db within a transaction, 76e91a54e1Sdan # [db2] is left with an open transaction, but not a read-lock on 77e91a54e1Sdan # the main database. So it does not prevent [db] from committing. 78e91a54e1Sdan # 79e91a54e1Sdan do_test pager1-$tn.8 { 80e91a54e1Sdan csql2 { UPDATE t1 SET a = a + 10 } 81e91a54e1Sdan } {1 {database is locked}} 82e91a54e1Sdan do_test pager1-$tn.9 { 83e91a54e1Sdan csql2 { 84e91a54e1Sdan BEGIN; 85e91a54e1Sdan UPDATE t1 SET a = a + 10; 86e91a54e1Sdan } 87e91a54e1Sdan } {1 {database is locked}} 88e91a54e1Sdan 89e91a54e1Sdan # Have [db] commit its transactions. Check the other connections can 90e91a54e1Sdan # now see the new database content. 91e91a54e1Sdan # 92e91a54e1Sdan do_test pager1-$tn.10 { sql1 { COMMIT } } {} 93e91a54e1Sdan do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 94e91a54e1Sdan do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 95e91a54e1Sdan do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 96e91a54e1Sdan 97e91a54e1Sdan # Check that, as noted above, [db2] really did keep an open transaction 98e91a54e1Sdan # after the attempt to write the database failed. 99e91a54e1Sdan # 100e91a54e1Sdan do_test pager1-$tn.14 { 101e91a54e1Sdan csql2 { BEGIN } 102e91a54e1Sdan } {1 {cannot start a transaction within a transaction}} 103e91a54e1Sdan do_test pager1-$tn.15 { sql2 { ROLLBACK } } {} 104e91a54e1Sdan 105e91a54e1Sdan # Have [db2] open a transaction and take a read-lock on the database. 106e91a54e1Sdan # Check that this prevents [db] from writing to the database (outside 107e91a54e1Sdan # of any transaction). After this fails, check that [db3] can read 108e91a54e1Sdan # the db (showing that [db] did not take a PENDING lock etc.) 109e91a54e1Sdan # 110e91a54e1Sdan do_test pager1-$tn.15 { 111e91a54e1Sdan sql2 { BEGIN; SELECT * FROM t1; } 112e91a54e1Sdan } {1 one 2 two 3 three} 113e91a54e1Sdan do_test pager1-$tn.16 { 114e91a54e1Sdan csql1 { UPDATE t1 SET a = a + 10 } 115e91a54e1Sdan } {1 {database is locked}} 116e91a54e1Sdan do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 117e91a54e1Sdan 118e91a54e1Sdan # This time, have [db] open a transaction before writing the database. 119e91a54e1Sdan # This works - [db] gets a RESERVED lock which does not conflict with 120e91a54e1Sdan # the SHARED lock [db2] is holding. 121e91a54e1Sdan # 122e91a54e1Sdan do_test pager1-$tn.18 { 123e91a54e1Sdan sql1 { 124e91a54e1Sdan BEGIN; 125e91a54e1Sdan UPDATE t1 SET a = a + 10; 126e91a54e1Sdan } 127e91a54e1Sdan } {} 128e91a54e1Sdan do_test pager1-$tn-19 { 129e91a54e1Sdan sql1 { PRAGMA lock_status } 130e91a54e1Sdan } {main reserved temp closed} 131e91a54e1Sdan do_test pager1-$tn-20 { 132e91a54e1Sdan sql2 { PRAGMA lock_status } 133e91a54e1Sdan } {main shared temp closed} 134e91a54e1Sdan 135e91a54e1Sdan # Check that all connections can still read the database. Only [db] sees 136e91a54e1Sdan # the updated content (as the transaction has not been committed yet). 137e91a54e1Sdan # 138e91a54e1Sdan do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 139e91a54e1Sdan do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 140e91a54e1Sdan do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 141e91a54e1Sdan 142e91a54e1Sdan # Because [db2] still has the SHARED lock, [db] is unable to commit the 143e91a54e1Sdan # transaction. If it tries, an error is returned and the connection 144e91a54e1Sdan # upgrades to a PENDING lock. 145e91a54e1Sdan # 146e91a54e1Sdan # Once this happens, [db] can read the database and see the new content, 147e91a54e1Sdan # [db2] (still holding SHARED) can still read the old content, but [db3] 148e91a54e1Sdan # (not holding any lock) is prevented by [db]'s PENDING from reading 149e91a54e1Sdan # the database. 150e91a54e1Sdan # 151e91a54e1Sdan do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}} 152e91a54e1Sdan do_test pager1-$tn-25 { 153e91a54e1Sdan sql1 { PRAGMA lock_status } 154e91a54e1Sdan } {main pending temp closed} 155e91a54e1Sdan do_test pager1-$tn.26 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 156e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 157e91a54e1Sdan do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 158e91a54e1Sdan 159e91a54e1Sdan # Have [db2] commit its read transaction, releasing the SHARED lock it 160e91a54e1Sdan # is holding. Now, neither [db2] nor [db3] may read the database (as [db] 161e91a54e1Sdan # is still holding a PENDING). 162e91a54e1Sdan # 163e91a54e1Sdan do_test pager1-$tn.29 { sql2 { COMMIT } } {} 164e91a54e1Sdan do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} 165e91a54e1Sdan do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 166e91a54e1Sdan 167e91a54e1Sdan # [db] is now able to commit the transaction. Once the transaction is 168e91a54e1Sdan # committed, all three connections can read the new content. 169e91a54e1Sdan # 170e91a54e1Sdan do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} 171e91a54e1Sdan do_test pager1-$tn.26 { sql1 { COMMIT } } {} 172e91a54e1Sdan do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} 173e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} 174e91a54e1Sdan do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} 175e91a54e1Sdan} 176e91a54e1Sdan 177*53f04f3bSdan#------------------------------------------------------------------------- 178*53f04f3bSdan# Savepoint related test cases. 179*53f04f3bSdan# 180b0ac3e3aSdando_test pager1-3.1 { 181b0ac3e3aSdan faultsim_delete_and_reopen 182b0ac3e3aSdan execsql { 183b0ac3e3aSdan CREATE TABLE t1(a PRIMARY KEY, b); 184b0ac3e3aSdan CREATE TABLE counter( 185b0ac3e3aSdan i CHECK (i<5), 186b0ac3e3aSdan u CHECK (u<10) 187b0ac3e3aSdan ); 188b0ac3e3aSdan INSERT INTO counter VALUES(0, 0); 189b0ac3e3aSdan CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN 190b0ac3e3aSdan UPDATE counter SET i = i+1; 191b0ac3e3aSdan END; 192b0ac3e3aSdan CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN 193b0ac3e3aSdan UPDATE counter SET u = u+1; 194b0ac3e3aSdan END; 195b0ac3e3aSdan } 196b0ac3e3aSdan execsql { SELECT * FROM counter } 197b0ac3e3aSdan} {0 0} 198b0ac3e3aSdan 199b0ac3e3aSdando_execsql_test pager1-3.2 { 200b0ac3e3aSdan BEGIN; 201b0ac3e3aSdan INSERT INTO t1 VALUES(1, randomblob(1500)); 202b0ac3e3aSdan INSERT INTO t1 VALUES(2, randomblob(1500)); 203b0ac3e3aSdan INSERT INTO t1 VALUES(3, randomblob(1500)); 204b0ac3e3aSdan SELECT * FROM counter; 205b0ac3e3aSdan} {3 0} 206b0ac3e3aSdando_catchsql_test pager1-3.3 { 207b0ac3e3aSdan INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 208b0ac3e3aSdan} {1 {constraint failed}} 209b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} 210b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} 211b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {} 212b0ac3e3aSdan 213*53f04f3bSdan#------------------------------------------------------------------------- 214*53f04f3bSdan# Hot journal rollback related test cases. 215*53f04f3bSdan# 216*53f04f3bSdan# pager1.4.1.*: Test that the pager module deletes very small invalid 217*53f04f3bSdan# journal files. 218*53f04f3bSdan# 219*53f04f3bSdan# pager1.4.2.*: Test that if the master journal pointer at the end of a 220*53f04f3bSdan# hot-journal file appears to be corrupt (checksum does not 221*53f04f3bSdan# compute) the associated journal is rolled back (and no 222*53f04f3bSdan# xAccess() call to check for the presence of any master 223*53f04f3bSdan# journal file is made). 224*53f04f3bSdan# 225*53f04f3bSdando_test pager1.4.1.1 { 226*53f04f3bSdan faultsim_delete_and_reopen 227*53f04f3bSdan execsql { 228*53f04f3bSdan CREATE TABLE x(y, z); 229*53f04f3bSdan INSERT INTO x VALUES(1, 2); 230*53f04f3bSdan } 231*53f04f3bSdan set fd [open test.db-journal w] 232*53f04f3bSdan puts -nonewline $fd "helloworld" 233*53f04f3bSdan close $fd 234*53f04f3bSdan file exists test.db-journal 235*53f04f3bSdan} {1} 236*53f04f3bSdando_test pager1.4.1.2 { execsql { SELECT * FROM x } } {1 2} 237*53f04f3bSdando_test pager1.4.1.3 { file exists test.db-journal } {0} 238*53f04f3bSdan 239*53f04f3bSdan# Set up a [testvfs] to snapshot the file-system just before SQLite 240*53f04f3bSdan# deletes the master-journal to commit a multi-file transaction. 241*53f04f3bSdan# 242*53f04f3bSdan# In subsequent test cases, invoking [faultsim_restore_and_reopen] sets 243*53f04f3bSdan# up the file system to contain two databases, two hot-journal files and 244*53f04f3bSdan# a master-journal. 245*53f04f3bSdan# 246*53f04f3bSdando_test pager1.4.2.1 { 247*53f04f3bSdan testvfs tstvfs -default 1 248*53f04f3bSdan tstvfs filter xDelete 249*53f04f3bSdan tstvfs script xDeleteCallback 250*53f04f3bSdan proc xDeleteCallback {method file args} { 251*53f04f3bSdan set file [file tail $file] 252*53f04f3bSdan if { [string match *mj* $file] } { faultsim_save } 253*53f04f3bSdan } 254*53f04f3bSdan faultsim_delete_and_reopen 255*53f04f3bSdan db func a_string a_string 256*53f04f3bSdan execsql { 257*53f04f3bSdan ATTACH 'test.db2' AS aux; 258*53f04f3bSdan PRAGMA journal_mode = DELETE; 259*53f04f3bSdan PRAGMA main.cache_size = 10; 260*53f04f3bSdan PRAGMA aux.cache_size = 10; 261*53f04f3bSdan CREATE TABLE t1(a UNIQUE, b UNIQUE); 262*53f04f3bSdan CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); 263*53f04f3bSdan INSERT INTO t1 VALUES(a_string(200), a_string(300)); 264*53f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 265*53f04f3bSdan INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; 266*53f04f3bSdan INSERT INTO t2 SELECT * FROM t1; 267*53f04f3bSdan BEGIN; 268*53f04f3bSdan INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; 269*53f04f3bSdan INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; 270*53f04f3bSdan INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; 271*53f04f3bSdan INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; 272*53f04f3bSdan REPLACE INTO t2 SELECT * FROM t1; 273*53f04f3bSdan COMMIT; 274*53f04f3bSdan } 275*53f04f3bSdan db close 276*53f04f3bSdan tstvfs delete 277*53f04f3bSdan} {} 278*53f04f3bSdando_test pager1.4.2.2 { 279*53f04f3bSdan faultsim_restore_and_reopen 280*53f04f3bSdan execsql { 281*53f04f3bSdan SELECT count(*) FROM t1; 282*53f04f3bSdan PRAGMA integrity_check; 283*53f04f3bSdan } 284*53f04f3bSdan} {4 ok} 285*53f04f3bSdando_test pager1.4.2.3 { 286*53f04f3bSdan faultsim_restore_and_reopen 287*53f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 288*53f04f3bSdan execsql { 289*53f04f3bSdan SELECT count(*) FROM t1; 290*53f04f3bSdan PRAGMA integrity_check; 291*53f04f3bSdan } 292*53f04f3bSdan} {64 ok} 293*53f04f3bSdando_test pager1.4.2.4 { 294*53f04f3bSdan faultsim_restore_and_reopen 295*53f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 296*53f04f3bSdan execsql { 297*53f04f3bSdan SELECT count(*) FROM t1; 298*53f04f3bSdan PRAGMA integrity_check; 299*53f04f3bSdan } 300*53f04f3bSdan} {4 ok} 301*53f04f3bSdando_test pager1.4.2.5 { 302*53f04f3bSdan faultsim_restore_and_reopen 303*53f04f3bSdan hexio_write test.db-journal [expr [file size test.db-journal]-20] 123456 304*53f04f3bSdan foreach f [glob test.db-mj*] { file delete -force $f } 305*53f04f3bSdan execsql { 306*53f04f3bSdan SELECT count(*) FROM t1; 307*53f04f3bSdan PRAGMA integrity_check; 308*53f04f3bSdan } 309*53f04f3bSdan} {4 ok} 310*53f04f3bSdan 311e91a54e1Sdanfinish_test 312e91a54e1Sdan 313