1e336b001Sdan# 2010 November 19 2e336b001Sdan# 3e336b001Sdan# The author disclaims copyright to this source code. In place of 4e336b001Sdan# a legal notice, here is a blessing: 5e336b001Sdan# 6e336b001Sdan# May you do good and not evil. 7e336b001Sdan# May you find forgiveness for yourself and forgive others. 8e336b001Sdan# May you share freely, never taking more than you give. 9e336b001Sdan# 10e336b001Sdan#*********************************************************************** 11e336b001Sdan# 12e336b001Sdan 13e336b001Sdanset testdir [file dirname $argv0] 14e336b001Sdansource $testdir/tester.tcl 15e336b001Sdansource $testdir/lock_common.tcl 16e336b001Sdan 17e336b001Sdanset testprefix superlock 187bd6b49aSdrhdo_not_use_codec 19e336b001Sdan 20e284a0e9Sdan# Test organization: 21e284a0e9Sdan# 22e284a0e9Sdan# 1.*: Test superlock on a rollback database. Test that once the db is 23e284a0e9Sdan# superlocked, it is not possible for a second client to read from 24e284a0e9Sdan# it. 25e284a0e9Sdan# 26e284a0e9Sdan# 2.*: Test superlock on a WAL database with zero frames in the WAL file. 27e284a0e9Sdan# Test that once the db is superlocked, it is not possible to read, 28e284a0e9Sdan# write or checkpoint the db. 29e284a0e9Sdan# 30e284a0e9Sdan# 3.*: As 2.*, for WAL databases with one or more frames in the WAL. 31e284a0e9Sdan# 32e284a0e9Sdan# 4.*: As 2.*, for WAL databases with one or more checkpointed frames 33e284a0e9Sdan# in the WAL. 34e284a0e9Sdan# 35e284a0e9Sdan# 5.*: Test that a call to sqlite3demo_superlock() uses the busy handler 36e284a0e9Sdan# correctly to wait for existing clients to clear on a WAL database. 37e284a0e9Sdan# And returns SQLITE_BUSY if no busy handler is defined or the busy 38e284a0e9Sdan# handler returns 0 before said clients relinquish their locks. 39e284a0e9Sdan# 40e284a0e9Sdan# 6.*: Test that if a superlocked WAL database is overwritten, existing 41e284a0e9Sdan# clients run the recovery to build the new wal-index after the 42e284a0e9Sdan# superlock is released. 43e284a0e9Sdan# 44e284a0e9Sdan# 45e284a0e9Sdan 46e336b001Sdando_execsql_test 1.1 { 47e336b001Sdan CREATE TABLE t1(a, b); 48e336b001Sdan INSERT INTO t1 VALUES(1, 2); 49e336b001Sdan PRAGMA journal_mode = DELETE; 50e336b001Sdan} {delete} 51e336b001Sdan 525209132aSdanifcapable !wal { 535209132aSdan finish_test 545209132aSdan return 555209132aSdan} 565209132aSdan 57e336b001Sdando_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock} 58e336b001Sdando_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}} 59e336b001Sdando_test 1.4 { unlock } {} 60e336b001Sdan 61e336b001Sdando_execsql_test 2.1 { 62e336b001Sdan INSERT INTO t1 VALUES(3, 4); 63e336b001Sdan PRAGMA journal_mode = WAL; 64e336b001Sdan} {wal} 65e336b001Sdan 66e336b001Sdando_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock} 67e336b001Sdando_catchsql_test 2.3 { SELECT * FROM t1 } {1 {database is locked}} 68e336b001Sdando_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} 69d42892ebSdando_catchsql_test 2.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} 70e336b001Sdando_test 2.6 { unlock } {} 71e336b001Sdan 72e336b001Sdando_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } 73e336b001Sdan 74e336b001Sdando_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock} 75e336b001Sdando_catchsql_test 3.3 { SELECT * FROM t1 } {1 {database is locked}} 76e336b001Sdando_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} 77d42892ebSdando_catchsql_test 3.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} 78e336b001Sdando_test 3.6 { unlock } {} 79e336b001Sdan 800774bb59Sdan# At this point the WAL file consists of a single frame only - written 810774bb59Sdan# by test case 3.1. If the ZERO_DAMAGE flag were not set, it would consist 820774bb59Sdan# of two frames - the frame written by 3.1 and a padding frame. 830774bb59Sdando_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 1 1} 84e336b001Sdan 85e336b001Sdando_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock} 86e336b001Sdando_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}} 87e336b001Sdando_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} 88d42892ebSdando_catchsql_test 4.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}} 89e336b001Sdando_test 4.6 { unlock } {} 90e336b001Sdan 91e336b001Sdando_multiclient_test tn { 92e284a0e9Sdan 93e336b001Sdan proc busyhandler {x} { 94e336b001Sdan switch -- $x { 95e336b001Sdan 1 { sql1 "COMMIT" } 96e336b001Sdan 2 { sql2 "COMMIT" } 97e336b001Sdan 3 { sql3 "COMMIT" } 98e336b001Sdan } 99e336b001Sdan lappend ::busylist $x 100e336b001Sdan return 1 101e336b001Sdan } 102e336b001Sdan set ::busylist [list] 103e336b001Sdan 104e336b001Sdan do_test 5.$tn.1 { 105e336b001Sdan sql1 { 106e336b001Sdan CREATE TABLE t1(a, b); 107e336b001Sdan PRAGMA journal_mode = WAL; 108e336b001Sdan INSERT INTO t1 VALUES(1, 2); 109e336b001Sdan } 110e336b001Sdan } {wal} 111e336b001Sdan 112e336b001Sdan do_test 5.$tn.2 { 113e336b001Sdan sql1 { BEGIN ; SELECT * FROM t1 } 114e336b001Sdan sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) } 115e336b001Sdan sql3 { BEGIN ; SELECT * FROM t1 } 116e336b001Sdan } {1 2} 117e336b001Sdan 118e336b001Sdan do_test 5.$tn.3 { 119e336b001Sdan set ::busylist [list] 120e336b001Sdan sqlite3demo_superlock unlock test.db "" busyhandler 121e336b001Sdan set ::busylist 122e336b001Sdan } {0 1 2 3} 123e336b001Sdan 124e336b001Sdan do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} 125e336b001Sdan do_test 5.$tn.5 { 126e336b001Sdan csql3 { INSERT INTO t1 VALUES(5, 6) } 127e336b001Sdan } {1 {database is locked}} 128d42892ebSdan do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {0 {1 -1 -1}} 129e336b001Sdan 130e336b001Sdan do_test 5.$tn.7 { unlock } {} 131e284a0e9Sdan 132e284a0e9Sdan 133e284a0e9Sdan do_test 5.$tn.8 { 134e284a0e9Sdan sql1 { BEGIN ; SELECT * FROM t1 } 135e284a0e9Sdan sql2 { BEGIN ; INSERT INTO t1 VALUES(5, 6) } 136e284a0e9Sdan sql3 { BEGIN ; SELECT * FROM t1 } 137e284a0e9Sdan } {1 2 3 4} 138e284a0e9Sdan 139e284a0e9Sdan do_test 5.$tn.9 { 140e284a0e9Sdan list [catch {sqlite3demo_superlock unlock test.db} msg] $msg 141e284a0e9Sdan } {1 {database is locked}} 142e284a0e9Sdan do_test 5.$tn.10 { 143e284a0e9Sdan sql1 COMMIT 144e284a0e9Sdan list [catch {sqlite3demo_superlock unlock test.db} msg] $msg 145e284a0e9Sdan } {1 {database is locked}} 146e284a0e9Sdan do_test 5.$tn.11 { 147e284a0e9Sdan sql2 COMMIT 148e284a0e9Sdan list [catch {sqlite3demo_superlock unlock test.db} msg] $msg 149e284a0e9Sdan } {1 {database is locked}} 150e284a0e9Sdan do_test 5.$tn.12 { 151e284a0e9Sdan sql3 COMMIT 152e284a0e9Sdan list [catch {sqlite3demo_superlock unlock test.db} msg] $msg 153e284a0e9Sdan } {0 unlock} 154e284a0e9Sdan unlock 155c216eee7Sdan 156c216eee7Sdan 157c216eee7Sdan do_test 5.$tn.13 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6} 158c216eee7Sdan do_test 5.$tn.14 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} 159c216eee7Sdan do_test 5.$tn.15 { sqlite3demo_superlock unlock test.db } {unlock} 160c216eee7Sdan do_test 5.$tn.16 { unlock } {} 161c216eee7Sdan do_test 5.$tn.17 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} 162c216eee7Sdan do_test 5.$tn.18 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6} 163c216eee7Sdan do_test 5.$tn.19 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} 164e336b001Sdan} 165e336b001Sdan 166e284a0e9Sdanproc read_content {file} { 167e284a0e9Sdan if {[file exists $file]==0} {return ""} 168e284a0e9Sdan set fd [open $file] 169e284a0e9Sdan fconfigure $fd -encoding binary -translation binary 170e284a0e9Sdan set content [read $fd] 171e284a0e9Sdan close $fd 172e284a0e9Sdan return $content 173e284a0e9Sdan} 174e284a0e9Sdan 175e284a0e9Sdanproc write_content {file content} { 176e284a0e9Sdan set fd [open $file w+] 177e284a0e9Sdan fconfigure $fd -encoding binary -translation binary 178e284a0e9Sdan puts -nonewline $fd $content 179e284a0e9Sdan close $fd 180e284a0e9Sdan} 181e284a0e9Sdan 182e284a0e9Sdan# Both $file1 and $file2 are database files. This function takes a 183e284a0e9Sdan# superlock on each, then exchanges the content of the two files (i.e. 184e284a0e9Sdan# overwrites $file1 with the initial contents of $file2, and overwrites 185e284a0e9Sdan# $file2 with the initial contents of $file1). The contents of any WAL 186e284a0e9Sdan# file is also exchanged. 187e284a0e9Sdan# 188e284a0e9Sdanproc db_swap {file1 file2} { 189e284a0e9Sdan sqlite3demo_superlock unlock1 $file1 190e284a0e9Sdan sqlite3demo_superlock unlock2 $file2 191e284a0e9Sdan 192e284a0e9Sdan set db1 [read_content $file1] 193e284a0e9Sdan set db2 [read_content $file2] 194e284a0e9Sdan write_content $file1 $db2 195e284a0e9Sdan write_content $file2 $db1 196e284a0e9Sdan 197e284a0e9Sdan set wal1 [read_content ${file1}-wal] 198e284a0e9Sdan set wal2 [read_content ${file2}-wal] 199e284a0e9Sdan write_content ${file1}-wal $wal2 200e284a0e9Sdan write_content ${file2}-wal $wal1 201e284a0e9Sdan 202e284a0e9Sdan unlock1 203e284a0e9Sdan unlock2 204e284a0e9Sdan} 205e284a0e9Sdan 206e284a0e9Sdanforcedelete test.db 207e284a0e9Sdansqlite3 db test.db 208e284a0e9Sdando_execsql_test 6.1 { 209e284a0e9Sdan ATTACH 'test.db2' AS aux; 210e284a0e9Sdan PRAGMA aux.journal_mode = wal; 211e284a0e9Sdan CREATE TABLE aux.t2(x, y); 212e284a0e9Sdan INSERT INTO aux.t2 VALUES('a', 'b'); 213e284a0e9Sdan PRAGMA schema_version = 450; 214e284a0e9Sdan DETACH aux; 215e284a0e9Sdan 216e284a0e9Sdan PRAGMA main.journal_mode = wal; 217e284a0e9Sdan CREATE TABLE t1(a, b); 218e284a0e9Sdan INSERT INTO t1 VALUES(1, 2); 219e284a0e9Sdan INSERT INTO t1 VALUES(3, 4); 220e284a0e9Sdan SELECT * FROM t1; 221e284a0e9Sdan} {wal wal 1 2 3 4} 222e284a0e9Sdan 223e284a0e9Sdan 224e284a0e9Sdandb_swap test.db2 test.db 225e284a0e9Sdando_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}} 226e284a0e9Sdando_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}} 227e284a0e9Sdan 228e284a0e9Sdandb_swap test.db2 test.db 229e284a0e9Sdando_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}} 230e284a0e9Sdando_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}} 231e284a0e9Sdan 2329c5e3680Sdando_execsql_test 6.6 { PRAGMA wal_checkpoint } {0 0 0} 233e284a0e9Sdan 234e284a0e9Sdandb_swap test.db2 test.db 235e284a0e9Sdando_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}} 236e284a0e9Sdando_catchsql_test 6.8 { SELECT * FROM t2 } {0 {a b}} 237e284a0e9Sdan 238e284a0e9Sdandb_swap test.db2 test.db 239e284a0e9Sdando_catchsql_test 6.9 { SELECT * FROM t1 } {0 {1 2 3 4}} 240e284a0e9Sdando_catchsql_test 6.10 { SELECT * FROM t2 } {1 {no such table: t2}} 241e284a0e9Sdan 242*4a8a6467Sdrhif {[nonzero_reserved_bytes]} { 243*4a8a6467Sdrh # Vacuum with a size change is not allowed with the codec 244*4a8a6467Sdrh do_execsql_test 6.11codec { 245*4a8a6467Sdrh PRAGMA journal_mode = delete; 246*4a8a6467Sdrh VACUUM; 247*4a8a6467Sdrh PRAGMA journal_mode = wal; 248*4a8a6467Sdrh INSERT INTO t1 VALUES(5, 6); 249*4a8a6467Sdrh } {delete wal} 250*4a8a6467Sdrh} else { 251e284a0e9Sdan do_execsql_test 6.11 { 252e284a0e9Sdan PRAGMA journal_mode = delete; 253e284a0e9Sdan PRAGMA page_size = 512; 254e284a0e9Sdan VACUUM; 255e284a0e9Sdan PRAGMA journal_mode = wal; 256e284a0e9Sdan INSERT INTO t1 VALUES(5, 6); 257e284a0e9Sdan } {delete wal} 258*4a8a6467Sdrh} 259e284a0e9Sdan 260e284a0e9Sdandb_swap test.db2 test.db 261e284a0e9Sdando_catchsql_test 6.12 { SELECT * FROM t1 } {1 {no such table: t1}} 262e284a0e9Sdando_catchsql_test 6.13 { SELECT * FROM t2 } {0 {a b}} 263e284a0e9Sdan 264e284a0e9Sdandb_swap test.db2 test.db 265e284a0e9Sdando_catchsql_test 6.14 { SELECT * FROM t1 } {0 {1 2 3 4 5 6}} 266e284a0e9Sdando_catchsql_test 6.15 { SELECT * FROM t2 } {1 {no such table: t2}} 267e336b001Sdan 268e336b001Sdanfinish_test 269