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 18*b0ac3e3aSdan# 19*b0ac3e3aSdan# pager1-1.*: Test inter-process locking (clients in multiple processes). 20*b0ac3e3aSdan# 21*b0ac3e3aSdan# pager1-2.*: Test intra-process locking (multiple clients in this process). 22*b0ac3e3aSdan# 23*b0ac3e3aSdan# pager1-3.*: Savepoint related tests. 24*b0ac3e3aSdan# 25*b0ac3e3aSdan 26*b0ac3e3aSdanproc do_execsql_test {testname sql result} { 27*b0ac3e3aSdan uplevel do_test $testname [list "execsql {$sql}"] [list $result] 28*b0ac3e3aSdan} 29*b0ac3e3aSdanproc do_catchsql_test {testname sql result} { 30*b0ac3e3aSdan uplevel do_test $testname [list "catchsql {$sql}"] [list $result] 31*b0ac3e3aSdan} 32*b0ac3e3aSdan 33a4a9095eSdando_multiclient_test tn { 34e91a54e1Sdan 35e91a54e1Sdan # Create and populate a database table using connection [db]. Check 36e91a54e1Sdan # that connections [db2] and [db3] can see the schema and content. 37e91a54e1Sdan # 38e91a54e1Sdan do_test pager1-$tn.1 { 39e91a54e1Sdan sql1 { 40e91a54e1Sdan CREATE TABLE t1(a PRIMARY KEY, b); 41e91a54e1Sdan CREATE INDEX i1 ON t1(b); 42e91a54e1Sdan INSERT INTO t1 VALUES(1, 'one'); 43e91a54e1Sdan INSERT INTO t1 VALUES(2, 'two'); 44e91a54e1Sdan } 45e91a54e1Sdan } {} 46e91a54e1Sdan do_test pager1-$tn.2 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 47e91a54e1Sdan do_test pager1-$tn.3 { sql3 { SELECT * FROM t1 } } {1 one 2 two} 48e91a54e1Sdan 49e91a54e1Sdan # Open a transaction and add a row using [db]. This puts [db] in 50e91a54e1Sdan # RESERVED state. Check that connections [db2] and [db3] can still 51e91a54e1Sdan # read the database content as it was before the transaction was 52e91a54e1Sdan # opened. [db] should see the inserted row. 53e91a54e1Sdan # 54e91a54e1Sdan do_test pager1-$tn.4 { 55e91a54e1Sdan sql1 { 56e91a54e1Sdan BEGIN; 57e91a54e1Sdan INSERT INTO t1 VALUES(3, 'three'); 58e91a54e1Sdan } 59e91a54e1Sdan } {} 60e91a54e1Sdan do_test pager1-$tn.5 { sql2 { SELECT * FROM t1 } } {1 one 2 two} 61e91a54e1Sdan do_test pager1-$tn.7 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 62e91a54e1Sdan 63e91a54e1Sdan # [db] still has an open write transaction. Check that this prevents 64e91a54e1Sdan # other connections (specifically [db2]) from writing to the database. 65e91a54e1Sdan # 66e91a54e1Sdan # Even if [db2] opens a transaction first, it may not write to the 67e91a54e1Sdan # database. After the attempt to write the db within a transaction, 68e91a54e1Sdan # [db2] is left with an open transaction, but not a read-lock on 69e91a54e1Sdan # the main database. So it does not prevent [db] from committing. 70e91a54e1Sdan # 71e91a54e1Sdan do_test pager1-$tn.8 { 72e91a54e1Sdan csql2 { UPDATE t1 SET a = a + 10 } 73e91a54e1Sdan } {1 {database is locked}} 74e91a54e1Sdan do_test pager1-$tn.9 { 75e91a54e1Sdan csql2 { 76e91a54e1Sdan BEGIN; 77e91a54e1Sdan UPDATE t1 SET a = a + 10; 78e91a54e1Sdan } 79e91a54e1Sdan } {1 {database is locked}} 80e91a54e1Sdan 81e91a54e1Sdan # Have [db] commit its transactions. Check the other connections can 82e91a54e1Sdan # now see the new database content. 83e91a54e1Sdan # 84e91a54e1Sdan do_test pager1-$tn.10 { sql1 { COMMIT } } {} 85e91a54e1Sdan do_test pager1-$tn.11 { sql1 { SELECT * FROM t1 } } {1 one 2 two 3 three} 86e91a54e1Sdan do_test pager1-$tn.12 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 87e91a54e1Sdan do_test pager1-$tn.13 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 88e91a54e1Sdan 89e91a54e1Sdan # Check that, as noted above, [db2] really did keep an open transaction 90e91a54e1Sdan # after the attempt to write the database failed. 91e91a54e1Sdan # 92e91a54e1Sdan do_test pager1-$tn.14 { 93e91a54e1Sdan csql2 { BEGIN } 94e91a54e1Sdan } {1 {cannot start a transaction within a transaction}} 95e91a54e1Sdan do_test pager1-$tn.15 { sql2 { ROLLBACK } } {} 96e91a54e1Sdan 97e91a54e1Sdan # Have [db2] open a transaction and take a read-lock on the database. 98e91a54e1Sdan # Check that this prevents [db] from writing to the database (outside 99e91a54e1Sdan # of any transaction). After this fails, check that [db3] can read 100e91a54e1Sdan # the db (showing that [db] did not take a PENDING lock etc.) 101e91a54e1Sdan # 102e91a54e1Sdan do_test pager1-$tn.15 { 103e91a54e1Sdan sql2 { BEGIN; SELECT * FROM t1; } 104e91a54e1Sdan } {1 one 2 two 3 three} 105e91a54e1Sdan do_test pager1-$tn.16 { 106e91a54e1Sdan csql1 { UPDATE t1 SET a = a + 10 } 107e91a54e1Sdan } {1 {database is locked}} 108e91a54e1Sdan do_test pager1-$tn.17 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 109e91a54e1Sdan 110e91a54e1Sdan # This time, have [db] open a transaction before writing the database. 111e91a54e1Sdan # This works - [db] gets a RESERVED lock which does not conflict with 112e91a54e1Sdan # the SHARED lock [db2] is holding. 113e91a54e1Sdan # 114e91a54e1Sdan do_test pager1-$tn.18 { 115e91a54e1Sdan sql1 { 116e91a54e1Sdan BEGIN; 117e91a54e1Sdan UPDATE t1 SET a = a + 10; 118e91a54e1Sdan } 119e91a54e1Sdan } {} 120e91a54e1Sdan do_test pager1-$tn-19 { 121e91a54e1Sdan sql1 { PRAGMA lock_status } 122e91a54e1Sdan } {main reserved temp closed} 123e91a54e1Sdan do_test pager1-$tn-20 { 124e91a54e1Sdan sql2 { PRAGMA lock_status } 125e91a54e1Sdan } {main shared temp closed} 126e91a54e1Sdan 127e91a54e1Sdan # Check that all connections can still read the database. Only [db] sees 128e91a54e1Sdan # the updated content (as the transaction has not been committed yet). 129e91a54e1Sdan # 130e91a54e1Sdan do_test pager1-$tn.21 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 131e91a54e1Sdan do_test pager1-$tn.22 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 132e91a54e1Sdan do_test pager1-$tn.23 { sql3 { SELECT * FROM t1 } } {1 one 2 two 3 three} 133e91a54e1Sdan 134e91a54e1Sdan # Because [db2] still has the SHARED lock, [db] is unable to commit the 135e91a54e1Sdan # transaction. If it tries, an error is returned and the connection 136e91a54e1Sdan # upgrades to a PENDING lock. 137e91a54e1Sdan # 138e91a54e1Sdan # Once this happens, [db] can read the database and see the new content, 139e91a54e1Sdan # [db2] (still holding SHARED) can still read the old content, but [db3] 140e91a54e1Sdan # (not holding any lock) is prevented by [db]'s PENDING from reading 141e91a54e1Sdan # the database. 142e91a54e1Sdan # 143e91a54e1Sdan do_test pager1-$tn.24 { csql1 { COMMIT } } {1 {database is locked}} 144e91a54e1Sdan do_test pager1-$tn-25 { 145e91a54e1Sdan sql1 { PRAGMA lock_status } 146e91a54e1Sdan } {main pending temp closed} 147e91a54e1Sdan do_test pager1-$tn.26 { sql1 { SELECT * FROM t1 } } {11 one 12 two 13 three} 148e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {1 one 2 two 3 three} 149e91a54e1Sdan do_test pager1-$tn.28 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 150e91a54e1Sdan 151e91a54e1Sdan # Have [db2] commit its read transaction, releasing the SHARED lock it 152e91a54e1Sdan # is holding. Now, neither [db2] nor [db3] may read the database (as [db] 153e91a54e1Sdan # is still holding a PENDING). 154e91a54e1Sdan # 155e91a54e1Sdan do_test pager1-$tn.29 { sql2 { COMMIT } } {} 156e91a54e1Sdan do_test pager1-$tn.30 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} 157e91a54e1Sdan do_test pager1-$tn.31 { csql3 { SELECT * FROM t1 } } {1 {database is locked}} 158e91a54e1Sdan 159e91a54e1Sdan # [db] is now able to commit the transaction. Once the transaction is 160e91a54e1Sdan # committed, all three connections can read the new content. 161e91a54e1Sdan # 162e91a54e1Sdan do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} 163e91a54e1Sdan do_test pager1-$tn.26 { sql1 { COMMIT } } {} 164e91a54e1Sdan do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} 165e91a54e1Sdan do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} 166e91a54e1Sdan do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} 167e91a54e1Sdan} 168e91a54e1Sdan 169*b0ac3e3aSdando_test pager1-3.1 { 170*b0ac3e3aSdan faultsim_delete_and_reopen 171*b0ac3e3aSdan execsql { 172*b0ac3e3aSdan CREATE TABLE t1(a PRIMARY KEY, b); 173*b0ac3e3aSdan CREATE TABLE counter( 174*b0ac3e3aSdan i CHECK (i<5), 175*b0ac3e3aSdan u CHECK (u<10) 176*b0ac3e3aSdan ); 177*b0ac3e3aSdan INSERT INTO counter VALUES(0, 0); 178*b0ac3e3aSdan CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN 179*b0ac3e3aSdan UPDATE counter SET i = i+1; 180*b0ac3e3aSdan END; 181*b0ac3e3aSdan CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN 182*b0ac3e3aSdan UPDATE counter SET u = u+1; 183*b0ac3e3aSdan END; 184*b0ac3e3aSdan } 185*b0ac3e3aSdan execsql { SELECT * FROM counter } 186*b0ac3e3aSdan} {0 0} 187*b0ac3e3aSdan 188*b0ac3e3aSdando_execsql_test pager1-3.2 { 189*b0ac3e3aSdan BEGIN; 190*b0ac3e3aSdan INSERT INTO t1 VALUES(1, randomblob(1500)); 191*b0ac3e3aSdan INSERT INTO t1 VALUES(2, randomblob(1500)); 192*b0ac3e3aSdan INSERT INTO t1 VALUES(3, randomblob(1500)); 193*b0ac3e3aSdan SELECT * FROM counter; 194*b0ac3e3aSdan} {3 0} 195*b0ac3e3aSdando_catchsql_test pager1-3.3 { 196*b0ac3e3aSdan INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 197*b0ac3e3aSdan} {1 {constraint failed}} 198*b0ac3e3aSdando_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} 199*b0ac3e3aSdando_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} 200*b0ac3e3aSdando_execsql_test pager1-3.6 { COMMIT } {} 201*b0ac3e3aSdan 202e91a54e1Sdanfinish_test 203e91a54e1Sdan 204