1db9d981aSdan# 2010 April 13 2db9d981aSdan# 3db9d981aSdan# The author disclaims copyright to this source code. In place of 4db9d981aSdan# a legal notice, here is a blessing: 5db9d981aSdan# 6db9d981aSdan# May you do good and not evil. 7db9d981aSdan# May you find forgiveness for yourself and forgive others. 8db9d981aSdan# May you share freely, never taking more than you give. 9db9d981aSdan# 10db9d981aSdan#*********************************************************************** 11db9d981aSdan# This file implements regression tests for SQLite library. The 12db9d981aSdan# focus of this file is testing the operation of the library in 13db9d981aSdan# "PRAGMA journal_mode=WAL" mode. 14db9d981aSdan# 15*cf2ad7aeSdan# TESTRUNNER: slow 16db9d981aSdan 17db9d981aSdanset testdir [file dirname $argv0] 18db9d981aSdansource $testdir/tester.tcl 19db9d981aSdansource $testdir/lock_common.tcl 20db9d981aSdansource $testdir/wal_common.tcl 21db9d981aSdansource $testdir/malloc_common.tcl 22db9d981aSdanifcapable !wal {finish_test ; return } 23db9d981aSdan 24db9d981aSdanset a_string_counter 1 25db9d981aSdanproc a_string {n} { 26db9d981aSdan global a_string_counter 27db9d981aSdan incr a_string_counter 28db9d981aSdan string range [string repeat "${a_string_counter}." $n] 1 $n 29db9d981aSdan} 30db9d981aSdandb func a_string a_string 31db9d981aSdan 32db9d981aSdan#------------------------------------------------------------------------- 33db9d981aSdan# When a rollback or savepoint rollback occurs, the client may remove 34db9d981aSdan# elements from one of the hash tables in the wal-index. This block 35db9d981aSdan# of test cases tests that nothing appears to go wrong when this is 36db9d981aSdan# done. 37db9d981aSdan# 38db9d981aSdando_test wal3-1.0 { 39db9d981aSdan execsql { 40c60f10a2Sdan PRAGMA cache_size = 2000; 41db9d981aSdan PRAGMA page_size = 1024; 42db9d981aSdan PRAGMA auto_vacuum = off; 43db9d981aSdan PRAGMA synchronous = normal; 44db9d981aSdan PRAGMA journal_mode = WAL; 45db9d981aSdan PRAGMA wal_autocheckpoint = 0; 46db9d981aSdan BEGIN; 47db9d981aSdan CREATE TABLE t1(x); 48db9d981aSdan INSERT INTO t1 VALUES( a_string(800) ); /* 1 */ 49db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */ 50db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */ 51db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */ 52db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */ 53db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */ 54db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 64 */ 55db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 128*/ 56db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 256 */ 57db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 512 */ 58db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 1024 */ 59db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2048 */ 603a3803b6Sdan INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 1970; /* 4018 */ 61db9d981aSdan COMMIT; 62db9d981aSdan PRAGMA cache_size = 10; 63db9d981aSdan } 64bec9d65dSdrh set x [wal_frame_count test.db-wal 1024] 65be7721d1Sdan if {[permutation]=="memsubsys1"} { 66bec9d65dSdrh if {$x==4251 || $x==4290} {set x 4056} 67bec9d65dSdrh } 68bec9d65dSdrh set x 69bec9d65dSdrh} 4056 70db9d981aSdan 713a3803b6Sdanfor {set i 1} {$i < 50} {incr i} { 72db9d981aSdan 73db9d981aSdan do_test wal3-1.$i.1 { 74db9d981aSdan set str [a_string 800] 75db9d981aSdan execsql { UPDATE t1 SET x = $str WHERE rowid = $i } 76db9d981aSdan lappend L [wal_frame_count test.db-wal 1024] 77db9d981aSdan execsql { 78db9d981aSdan BEGIN; 79db9d981aSdan INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100; 80db9d981aSdan ROLLBACK; 81db9d981aSdan PRAGMA integrity_check; 82db9d981aSdan } 83db9d981aSdan } {ok} 84db9d981aSdan 85db9d981aSdan # Check that everything looks OK from the point of view of an 86db9d981aSdan # external connection. 87db9d981aSdan # 88db9d981aSdan sqlite3 db2 test.db 89db9d981aSdan do_test wal3-1.$i.2 { 90db9d981aSdan execsql { SELECT count(*) FROM t1 } db2 913a3803b6Sdan } 4018 92db9d981aSdan do_test wal3-1.$i.3 { 93db9d981aSdan execsql { SELECT x FROM t1 WHERE rowid = $i } 94db9d981aSdan } $str 95db9d981aSdan do_test wal3-1.$i.4 { 96db9d981aSdan execsql { PRAGMA integrity_check } db2 97db9d981aSdan } {ok} 98db9d981aSdan db2 close 99db9d981aSdan 100db9d981aSdan # Check that the file-system in its current state can be recovered. 101db9d981aSdan # 102fda06befSmistachkin forcecopy test.db test2.db 103fda06befSmistachkin forcecopy test.db-wal test2.db-wal 104fda06befSmistachkin forcedelete test2.db-journal 105db9d981aSdan sqlite3 db2 test2.db 106db9d981aSdan do_test wal3-1.$i.5 { 107db9d981aSdan execsql { SELECT count(*) FROM t1 } db2 1083a3803b6Sdan } 4018 109db9d981aSdan do_test wal3-1.$i.6 { 110db9d981aSdan execsql { SELECT x FROM t1 WHERE rowid = $i } 111db9d981aSdan } $str 112db9d981aSdan do_test wal3-1.$i.7 { 113db9d981aSdan execsql { PRAGMA integrity_check } db2 114db9d981aSdan } {ok} 115db9d981aSdan db2 close 116db9d981aSdan} 117db9d981aSdan 118bfcaec72Sdanproc byte_is_zero {file offset} { 119bfcaec72Sdan if {[file size test.db] <= $offset} { return 1 } 120bfcaec72Sdan expr { [hexio_read $file $offset 1] == "00" } 121bfcaec72Sdan} 122bfcaec72Sdan 123a4a9095eSdando_multiclient_test i { 12483f42d1bSdan 125a4a9095eSdan set testname(1) multiproc 126a4a9095eSdan set testname(2) singleproc 127a4a9095eSdan set tn $testname($i) 12883f42d1bSdan 12983f42d1bSdan do_test wal3-2.$tn.1 { 130a4a9095eSdan sql1 { 13183f42d1bSdan PRAGMA page_size = 1024; 13283f42d1bSdan PRAGMA journal_mode = WAL; 13383f42d1bSdan } 134a4a9095eSdan sql1 { 13583f42d1bSdan CREATE TABLE t1(a, b); 13683f42d1bSdan INSERT INTO t1 VALUES(1, 'one'); 13783f42d1bSdan BEGIN; 13883f42d1bSdan SELECT * FROM t1; 13983f42d1bSdan } 14083f42d1bSdan } {1 one} 14183f42d1bSdan do_test wal3-2.$tn.2 { 14283f42d1bSdan sql2 { 14383f42d1bSdan CREATE TABLE t2(a, b); 14483f42d1bSdan INSERT INTO t2 VALUES(2, 'two'); 14583f42d1bSdan BEGIN; 14683f42d1bSdan SELECT * FROM t2; 14783f42d1bSdan } 14883f42d1bSdan } {2 two} 14983f42d1bSdan do_test wal3-2.$tn.3 { 15083f42d1bSdan sql3 { 15183f42d1bSdan CREATE TABLE t3(a, b); 15283f42d1bSdan INSERT INTO t3 VALUES(3, 'three'); 15383f42d1bSdan BEGIN; 15483f42d1bSdan SELECT * FROM t3; 15583f42d1bSdan } 15683f42d1bSdan } {3 three} 15783f42d1bSdan 15883f42d1bSdan # Try to checkpoint the database using [db]. It should be possible to 159d764c7deSdan # checkpoint everything except the table added by [db3] (checkpointing 16083f42d1bSdan # these frames would clobber the snapshot currently being used by [db2]). 16183f42d1bSdan # 162d764c7deSdan # After [db2] has committed, a checkpoint can copy the entire log to the 163d764c7deSdan # database file. Checkpointing after [db3] has committed is therefore a 164d764c7deSdan # no-op, as the entire log has already been backfilled. 165d764c7deSdan # 16683f42d1bSdan do_test wal3-2.$tn.4 { 167a4a9095eSdan sql1 { 16883f42d1bSdan COMMIT; 16983f42d1bSdan PRAGMA wal_checkpoint; 17083f42d1bSdan } 171bfcaec72Sdan byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024] 172bfcaec72Sdan } {1} 173d764c7deSdan do_test wal3-2.$tn.5 { 174d764c7deSdan sql2 { 175d764c7deSdan COMMIT; 176d764c7deSdan PRAGMA wal_checkpoint; 177d764c7deSdan } 178bfcaec72Sdan list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]] \ 179bfcaec72Sdan [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]] 180bfcaec72Sdan } {0 1} 181d764c7deSdan do_test wal3-2.$tn.6 { 182d764c7deSdan sql3 { 183d764c7deSdan COMMIT; 184d764c7deSdan PRAGMA wal_checkpoint; 185d764c7deSdan } 186bfcaec72Sdan list [byte_is_zero test.db [expr $AUTOVACUUM ? 4*1024 : 3*1024]] \ 187bfcaec72Sdan [byte_is_zero test.db [expr $AUTOVACUUM ? 5*1024 : 4*1024]] 188bfcaec72Sdan } {0 1} 18983f42d1bSdan} 190d764c7deSdancatch {db close} 191d764c7deSdan 192d764c7deSdan#------------------------------------------------------------------------- 193d764c7deSdan# Test that that for the simple test: 194d764c7deSdan# 195d764c7deSdan# CREATE TABLE x(y); 196d764c7deSdan# INSERT INTO x VALUES('z'); 197d764c7deSdan# PRAGMA wal_checkpoint; 198d764c7deSdan# 199d764c7deSdan# in WAL mode the xSync method is invoked as expected for each of 200d764c7deSdan# synchronous=off, synchronous=normal and synchronous=full. 201d764c7deSdan# 202d764c7deSdanforeach {tn syncmode synccount} { 203d764c7deSdan 1 off 204d764c7deSdan {} 205d764c7deSdan 2 normal 206d764c7deSdan {test.db-wal normal test.db normal} 207d764c7deSdan 3 full 208d764c7deSdan {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal} 209d764c7deSdan} { 210d764c7deSdan 211d764c7deSdan proc sync_counter {args} { 212d764c7deSdan foreach {method filename id flags} $args break 213d764c7deSdan lappend ::syncs [file tail $filename] $flags 214d764c7deSdan } 215d764c7deSdan do_test wal3-3.$tn { 216fda06befSmistachkin forcedelete test.db test.db-wal test.db-journal 217d764c7deSdan 218d764c7deSdan testvfs T 219d764c7deSdan T filter {} 220d764c7deSdan T script sync_counter 221d764c7deSdan sqlite3 db test.db -vfs T 222d764c7deSdan 223d764c7deSdan execsql "PRAGMA synchronous = $syncmode" 224108e5a9aSdrh execsql "PRAGMA checkpoint_fullfsync = 0" 225d764c7deSdan execsql { PRAGMA journal_mode = WAL } 2264eb02a45Sdrh execsql { CREATE TABLE filler(a,b,c); } 227d764c7deSdan 228d764c7deSdan set ::syncs [list] 229d764c7deSdan T filter xSync 230d764c7deSdan execsql { 231d764c7deSdan CREATE TABLE x(y); 232d764c7deSdan INSERT INTO x VALUES('z'); 233d764c7deSdan PRAGMA wal_checkpoint; 234d764c7deSdan } 235d764c7deSdan T filter {} 236d764c7deSdan set ::syncs 237d764c7deSdan } $synccount 238d764c7deSdan 239d764c7deSdan db close 240d764c7deSdan T delete 241d764c7deSdan} 242d764c7deSdan 243ef4ee8f2Sdan 244ef4ee8f2Sdan#------------------------------------------------------------------------- 245ef4ee8f2Sdan# Only one client may run recovery at a time. Test this mechanism. 246ef4ee8f2Sdan# 247ef4ee8f2Sdan# When client-2 tries to open a read transaction while client-1 is 248ef4ee8f2Sdan# running recovery, it fails to obtain a lock on an aReadMark[] slot 249ef4ee8f2Sdan# (because they are all locked by recovery). It then tries to obtain 250ef4ee8f2Sdan# a shared lock on the RECOVER lock to see if there really is a 251ef4ee8f2Sdan# recovery running or not. 252ef4ee8f2Sdan# 253ef4ee8f2Sdan# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR 254ef4ee8f2Sdan# being returned when client-2 attempts a shared lock on the RECOVER byte. 255ef4ee8f2Sdan# 256ef4ee8f2Sdan# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An 257ef4ee8f2Sdan# SQLITE_IOERR should be returned to the caller. 258ef4ee8f2Sdan# 259ef4ee8f2Sdando_test wal3-5.1 { 260ef4ee8f2Sdan faultsim_delete_and_reopen 261ef4ee8f2Sdan execsql { 262ef4ee8f2Sdan PRAGMA journal_mode = WAL; 263ef4ee8f2Sdan CREATE TABLE t1(a, b); 264ef4ee8f2Sdan INSERT INTO t1 VALUES(1, 2); 265ef4ee8f2Sdan INSERT INTO t1 VALUES(3, 4); 266ef4ee8f2Sdan } 267ef4ee8f2Sdan faultsim_save_and_close 268ef4ee8f2Sdan} {} 269ef4ee8f2Sdan 270ef4ee8f2Sdantestvfs T -default 1 271ef4ee8f2SdanT script method_callback 272ef4ee8f2Sdan 273ef4ee8f2Sdanproc method_callback {method args} { 274ef4ee8f2Sdan if {$method == "xShmBarrier"} { 275ef4ee8f2Sdan incr ::barrier_count 27613a3cb82Sdan if {$::barrier_count == 2} { 277ef4ee8f2Sdan # This code is executed within the xShmBarrier() callback invoked 278ef4ee8f2Sdan # by the client running recovery as part of writing the recovered 279ef4ee8f2Sdan # wal-index header. If a second client attempts to access the 280ef4ee8f2Sdan # database now, it reads a corrupt (partially written) wal-index 281ef4ee8f2Sdan # header. But it cannot even get that far, as the first client 282ef4ee8f2Sdan # is still holding all the locks (recovery takes an exclusive lock 283ef4ee8f2Sdan # on *all* db locks, preventing access by any other client). 284ef4ee8f2Sdan # 285ef4ee8f2Sdan # If global variable ::wal3_do_lockfailure is non-zero, then set 286ef4ee8f2Sdan # things up so that an IO error occurs within an xShmLock() callback 287ef4ee8f2Sdan # made by the second client (aka [db2]). 288ef4ee8f2Sdan # 289ef4ee8f2Sdan sqlite3 db2 test.db 290ef4ee8f2Sdan if { $::wal3_do_lockfailure } { T filter xShmLock } 291ef4ee8f2Sdan set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ] 292ef4ee8f2Sdan T filter {} 293ef4ee8f2Sdan db2 close 294ef4ee8f2Sdan } 295ef4ee8f2Sdan } 296ef4ee8f2Sdan 297ef4ee8f2Sdan if {$method == "xShmLock"} { 298ef4ee8f2Sdan foreach {file handle spec} $args break 299ef4ee8f2Sdan if { $spec == "2 1 lock shared" } { 300ef4ee8f2Sdan return SQLITE_IOERR 301ef4ee8f2Sdan } 302ef4ee8f2Sdan } 303ef4ee8f2Sdan 304ef4ee8f2Sdan return SQLITE_OK 305ef4ee8f2Sdan} 306ef4ee8f2Sdan 307ef4ee8f2Sdan# Test a normal SQLITE_BUSY return. 308ef4ee8f2Sdan# 309ef4ee8f2SdanT filter xShmBarrier 310ef4ee8f2Sdanset testrc "" 311ef4ee8f2Sdanset testmsg "" 312ef4ee8f2Sdanset barrier_count 0 313ef4ee8f2Sdanset wal3_do_lockfailure 0 314ef4ee8f2Sdando_test wal3-5.2 { 315ef4ee8f2Sdan faultsim_restore_and_reopen 316ef4ee8f2Sdan execsql { SELECT * FROM t1 } 317ef4ee8f2Sdan} {1 2 3 4} 318ef4ee8f2Sdando_test wal3-5.3 { 319ef4ee8f2Sdan list $::testrc $::testmsg 320ef4ee8f2Sdan} {1 {database is locked}} 321ef4ee8f2Sdandb close 322ef4ee8f2Sdan 323ef4ee8f2Sdan# Test an SQLITE_IOERR return. 324ef4ee8f2Sdan# 325ef4ee8f2SdanT filter xShmBarrier 326ef4ee8f2Sdanset barrier_count 0 327ef4ee8f2Sdanset wal3_do_lockfailure 1 328ef4ee8f2Sdanset testrc "" 329ef4ee8f2Sdanset testmsg "" 330ef4ee8f2Sdando_test wal3-5.4 { 331ef4ee8f2Sdan faultsim_restore_and_reopen 332ef4ee8f2Sdan execsql { SELECT * FROM t1 } 333ef4ee8f2Sdan} {1 2 3 4} 334ef4ee8f2Sdando_test wal3-5.5 { 335ef4ee8f2Sdan list $::testrc $::testmsg 336ef4ee8f2Sdan} {1 {disk I/O error}} 3370153a9bcSdan 3380dc7b74fSdandb close 3390dc7b74fSdanT delete 3400dc7b74fSdan 341ef4ee8f2Sdan#------------------------------------------------------------------------- 342493cc590Sdan# When opening a read-transaction on a database, if the entire log has 343493cc590Sdan# already been copied to the database file, the reader grabs a special 344640aac48Sdan# kind of read lock (on aReadMark[0]). This set of test cases tests the 345640aac48Sdan# outcome of the following: 346ef4ee8f2Sdan# 347493cc590Sdan# + The reader discovering that between the time when it determined 348493cc590Sdan# that the log had been completely backfilled and the lock is obtained 349493cc590Sdan# that a writer has written to the log. In this case the reader should 350493cc590Sdan# acquire a different read-lock (not aReadMark[0]) and read the new 351493cc590Sdan# snapshot. 352493cc590Sdan# 353493cc590Sdan# + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY. 354493cc590Sdan# This can happen if a checkpoint is ongoing. In this case also simply 355493cc590Sdan# obtain a different read-lock. 356493cc590Sdan# 357493cc590Sdancatch {db close} 358493cc590Sdantestvfs T -default 1 359493cc590Sdando_test wal3-6.1.1 { 360fda06befSmistachkin forcedelete test.db test.db-journal test.db wal 361493cc590Sdan sqlite3 db test.db 3627fa65fbfSdan execsql { PRAGMA auto_vacuum = off } 363493cc590Sdan execsql { PRAGMA journal_mode = WAL } 364493cc590Sdan execsql { 365493cc590Sdan CREATE TABLE t1(a, b); 366493cc590Sdan INSERT INTO t1 VALUES('o', 't'); 367493cc590Sdan INSERT INTO t1 VALUES('t', 'f'); 368493cc590Sdan } 369493cc590Sdan} {} 370493cc590Sdando_test wal3-6.1.2 { 371493cc590Sdan sqlite3 db2 test.db 372493cc590Sdan sqlite3 db3 test.db 373493cc590Sdan execsql { BEGIN ; SELECT * FROM t1 } db3 374493cc590Sdan} {o t t f} 375493cc590Sdando_test wal3-6.1.3 { 376493cc590Sdan execsql { PRAGMA wal_checkpoint } db2 3770774bb59Sdan} {0 4 4} 378493cc590Sdan 379493cc590Sdan# At this point the log file has been fully checkpointed. However, 380493cc590Sdan# connection [db3] holds a lock that prevents the log from being wrapped. 381493cc590Sdan# Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But 382493cc590Sdan# as it is obtaining the lock, [db2] appends to the log file. 383493cc590Sdan# 384493cc590SdanT filter xShmLock 385493cc590SdanT script lock_callback 386493cc590Sdanproc lock_callback {method file handle spec} { 387493cc590Sdan if {$spec == "3 1 lock shared"} { 388493cc590Sdan # This is the callback for [db] to obtain the read lock on aReadMark[0]. 389493cc590Sdan # Disable future callbacks using [T filter {}] and write to the log 390493cc590Sdan # file using [db2]. [db3] is preventing [db2] from wrapping the log 391493cc590Sdan # here, so this is an append. 392493cc590Sdan T filter {} 393493cc590Sdan db2 eval { INSERT INTO t1 VALUES('f', 's') } 394493cc590Sdan } 395493cc590Sdan return SQLITE_OK 396493cc590Sdan} 397493cc590Sdando_test wal3-6.1.4 { 398493cc590Sdan execsql { 399493cc590Sdan BEGIN; 400493cc590Sdan SELECT * FROM t1; 401493cc590Sdan } 402493cc590Sdan} {o t t f f s} 403493cc590Sdan 404493cc590Sdan# [db] should be left holding a read-lock on some slot other than 405493cc590Sdan# aReadMark[0]. Test this by demonstrating that the read-lock is preventing 406493cc590Sdan# the log from being wrapped. 407493cc590Sdan# 408493cc590Sdando_test wal3-6.1.5 { 409493cc590Sdan db3 eval COMMIT 410493cc590Sdan db2 eval { PRAGMA wal_checkpoint } 411493cc590Sdan set sz1 [file size test.db-wal] 412493cc590Sdan db2 eval { INSERT INTO t1 VALUES('s', 'e') } 413493cc590Sdan set sz2 [file size test.db-wal] 414493cc590Sdan expr {$sz2>$sz1} 415493cc590Sdan} {1} 416493cc590Sdan 417493cc590Sdan# Test that if [db2] had not interfered when [db] was trying to grab 418493cc590Sdan# aReadMark[0], it would have been possible to wrap the log in 3.6.1.5. 419493cc590Sdan# 420493cc590Sdando_test wal3-6.1.6 { 421493cc590Sdan execsql { COMMIT } 422493cc590Sdan execsql { PRAGMA wal_checkpoint } db2 423493cc590Sdan execsql { 424493cc590Sdan BEGIN; 425493cc590Sdan SELECT * FROM t1; 426493cc590Sdan } 427493cc590Sdan} {o t t f f s s e} 428493cc590Sdando_test wal3-6.1.7 { 429493cc590Sdan db2 eval { PRAGMA wal_checkpoint } 430493cc590Sdan set sz1 [file size test.db-wal] 431493cc590Sdan db2 eval { INSERT INTO t1 VALUES('n', 't') } 432493cc590Sdan set sz2 [file size test.db-wal] 433493cc590Sdan expr {$sz2==$sz1} 434493cc590Sdan} {1} 435493cc590Sdan 436493cc590Sdandb3 close 437493cc590Sdandb2 close 438493cc590Sdandb close 439493cc590Sdan 440493cc590Sdando_test wal3-6.2.1 { 441fda06befSmistachkin forcedelete test.db test.db-journal test.db wal 442493cc590Sdan sqlite3 db test.db 443493cc590Sdan sqlite3 db2 test.db 4447fa65fbfSdan execsql { PRAGMA auto_vacuum = off } 445493cc590Sdan execsql { PRAGMA journal_mode = WAL } 446493cc590Sdan execsql { 447493cc590Sdan CREATE TABLE t1(a, b); 448493cc590Sdan INSERT INTO t1 VALUES('h', 'h'); 449493cc590Sdan INSERT INTO t1 VALUES('l', 'b'); 450493cc590Sdan } 451493cc590Sdan} {} 452493cc590Sdan 453493cc590SdanT filter xShmLock 454493cc590SdanT script lock_callback 455493cc590Sdanproc lock_callback {method file handle spec} { 456493cc590Sdan if {$spec == "3 1 unlock exclusive"} { 457493cc590Sdan T filter {} 458493cc590Sdan set ::R [db2 eval { 459493cc590Sdan BEGIN; 460493cc590Sdan SELECT * FROM t1; 461493cc590Sdan }] 462493cc590Sdan } 463493cc590Sdan} 464493cc590Sdando_test wal3-6.2.2 { 465493cc590Sdan execsql { PRAGMA wal_checkpoint } 4660774bb59Sdan} {0 4 4} 467493cc590Sdando_test wal3-6.2.3 { 468493cc590Sdan set ::R 469493cc590Sdan} {h h l b} 470493cc590Sdando_test wal3-6.2.4 { 471493cc590Sdan set sz1 [file size test.db-wal] 472493cc590Sdan execsql { INSERT INTO t1 VALUES('b', 'c'); } 473493cc590Sdan set sz2 [file size test.db-wal] 474493cc590Sdan expr {$sz2 > $sz1} 475493cc590Sdan} {1} 476493cc590Sdando_test wal3-6.2.5 { 477493cc590Sdan db2 eval { COMMIT } 478493cc590Sdan execsql { PRAGMA wal_checkpoint } 479493cc590Sdan set sz1 [file size test.db-wal] 480493cc590Sdan execsql { INSERT INTO t1 VALUES('n', 'o'); } 481493cc590Sdan set sz2 [file size test.db-wal] 482493cc590Sdan expr {$sz2 == $sz1} 483493cc590Sdan} {1} 484493cc590Sdan 485493cc590Sdandb2 close 486493cc590Sdandb close 487493cc590SdanT delete 488493cc590Sdan 489640aac48Sdan#------------------------------------------------------------------------- 490640aac48Sdan# When opening a read-transaction on a database, if the entire log has 491640aac48Sdan# not yet been copied to the database file, the reader grabs a read 492640aac48Sdan# lock on aReadMark[x], where x>0. The following test cases experiment 493640aac48Sdan# with the outcome of the following: 494640aac48Sdan# 495640aac48Sdan# + The reader discovering that between the time when it read the 496640aac48Sdan# wal-index header and the lock was obtained that a writer has 497640aac48Sdan# written to the log. In this case the reader should re-read the 498640aac48Sdan# wal-index header and lock a snapshot corresponding to the new 499640aac48Sdan# header. 500640aac48Sdan# 501640aac48Sdan# + The value in the aReadMark[x] slot has been modified since it was 502640aac48Sdan# read. 503640aac48Sdan# 504640aac48Sdancatch {db close} 505640aac48Sdantestvfs T -default 1 506640aac48Sdando_test wal3-7.1.1 { 507fda06befSmistachkin forcedelete test.db test.db-journal test.db wal 508640aac48Sdan sqlite3 db test.db 509640aac48Sdan execsql { 510640aac48Sdan PRAGMA journal_mode = WAL; 511640aac48Sdan CREATE TABLE blue(red PRIMARY KEY, green); 512640aac48Sdan } 513640aac48Sdan} {wal} 514640aac48Sdan 515640aac48SdanT script method_callback 516640aac48SdanT filter xOpen 517640aac48Sdanproc method_callback {method args} { 518640aac48Sdan if {$method == "xOpen"} { return "reader" } 519640aac48Sdan} 520640aac48Sdando_test wal3-7.1.2 { 521640aac48Sdan sqlite3 db2 test.db 522640aac48Sdan execsql { SELECT * FROM blue } db2 523640aac48Sdan} {} 524640aac48Sdan 525640aac48SdanT filter xShmLock 526640aac48Sdanset ::locks [list] 527640aac48Sdanproc method_callback {method file handle spec} { 528640aac48Sdan if {$handle != "reader" } { return } 529640aac48Sdan if {$method == "xShmLock"} { 530640aac48Sdan catch { execsql { INSERT INTO blue VALUES(1, 2) } } 531640aac48Sdan catch { execsql { INSERT INTO blue VALUES(3, 4) } } 532640aac48Sdan } 533640aac48Sdan lappend ::locks $spec 534640aac48Sdan} 535640aac48Sdando_test wal3-7.1.3 { 536640aac48Sdan execsql { SELECT * FROM blue } db2 537640aac48Sdan} {1 2 3 4} 538640aac48Sdando_test wal3-7.1.4 { 539640aac48Sdan set ::locks 540640aac48Sdan} {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}} 541640aac48Sdan 542640aac48Sdanset ::locks [list] 543640aac48Sdanproc method_callback {method file handle spec} { 544640aac48Sdan if {$handle != "reader" } { return } 545640aac48Sdan if {$method == "xShmLock"} { 546640aac48Sdan catch { execsql { INSERT INTO blue VALUES(5, 6) } } 547640aac48Sdan } 548640aac48Sdan lappend ::locks $spec 549640aac48Sdan} 550640aac48Sdando_test wal3-7.2.1 { 551640aac48Sdan execsql { SELECT * FROM blue } db2 552640aac48Sdan} {1 2 3 4 5 6} 553640aac48Sdando_test wal3-7.2.2 { 554640aac48Sdan set ::locks 555640aac48Sdan} {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}} 556640aac48Sdan 557640aac48Sdandb close 558640aac48Sdandb2 close 559640aac48SdanT delete 560db9d981aSdan 56123f71920Sdan 56223f71920Sdan#------------------------------------------------------------------------- 56323f71920Sdan# When a connection opens a read-lock on the database, it searches for 56423f71920Sdan# an aReadMark[] slot that is already set to the mxFrame value for the 56523f71920Sdan# new transaction. If it cannot find one, it attempts to obtain an 56623f71920Sdan# exclusive lock on an aReadMark[] slot for the purposes of modifying 56723f71920Sdan# the value, then drops back to a shared-lock for the duration of the 56823f71920Sdan# transaction. 56923f71920Sdan# 57023f71920Sdan# This test case verifies that if an exclusive lock cannot be obtained 57123f71920Sdan# on any aReadMark[] slot (because there are already several readers), 57223f71920Sdan# the client takes a shared-lock on a slot without modifying the value 57323f71920Sdan# and continues. 57423f71920Sdan# 575675f85c4Sdanset nConn 50 576fe349965Sdrhif { [string match *BSD $tcl_platform(os)] } { set nConn 25 } 57723f71920Sdando_test wal3-9.0 { 578fda06befSmistachkin forcedelete test.db test.db-journal test.db wal 57923f71920Sdan sqlite3 db test.db 58023f71920Sdan execsql { 581bfcaec72Sdan PRAGMA page_size = 1024; 58223f71920Sdan PRAGMA journal_mode = WAL; 58323f71920Sdan CREATE TABLE whoami(x); 58423f71920Sdan INSERT INTO whoami VALUES('nobody'); 58523f71920Sdan } 58623f71920Sdan} {wal} 587675f85c4Sdanfor {set i 0} {$i < $nConn} {incr i} { 58823f71920Sdan set c db$i 58923f71920Sdan do_test wal3-9.1.$i { 59023f71920Sdan sqlite3 $c test.db 59123f71920Sdan execsql { UPDATE whoami SET x = $c } 59223f71920Sdan execsql { 59323f71920Sdan BEGIN; 59423f71920Sdan SELECT * FROM whoami 59523f71920Sdan } $c 59623f71920Sdan } $c 59723f71920Sdan} 598675f85c4Sdanfor {set i 0} {$i < $nConn} {incr i} { 59923f71920Sdan set c db$i 60023f71920Sdan do_test wal3-9.2.$i { 60123f71920Sdan execsql { SELECT * FROM whoami } $c 60223f71920Sdan } $c 60323f71920Sdan} 604bfcaec72Sdan 605bfcaec72Sdanset sz [expr 1024 * (2+$AUTOVACUUM)] 60623f71920Sdando_test wal3-9.3 { 607675f85c4Sdan for {set i 0} {$i < ($nConn-1)} {incr i} { db$i close } 60823f71920Sdan execsql { PRAGMA wal_checkpoint } 609bfcaec72Sdan byte_is_zero test.db [expr $sz-1024] 610bfcaec72Sdan} {1} 611bfcaec72Sdando_test wal3-9.4 { 612675f85c4Sdan db[expr $nConn-1] close 61323f71920Sdan execsql { PRAGMA wal_checkpoint } 61423f71920Sdan set sz2 [file size test.db] 615bfcaec72Sdan byte_is_zero test.db [expr $sz-1024] 616bfcaec72Sdan} {0} 61723f71920Sdan 618aac1bf94Sdando_multiclient_test tn { 619aac1bf94Sdan do_test wal3-10.$tn.1 { 620aac1bf94Sdan sql1 { 621aac1bf94Sdan PRAGMA page_size = 1024; 622aac1bf94Sdan CREATE TABLE t1(x); 623aac1bf94Sdan PRAGMA journal_mode = WAL; 624aac1bf94Sdan PRAGMA wal_autocheckpoint = 100000; 625aac1bf94Sdan BEGIN; 626aac1bf94Sdan INSERT INTO t1 VALUES(randomblob(800)); 627aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2 628aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4 629aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8 630aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 16 631aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 32 632aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 64 633aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 128 634aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 256 635aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 512 636aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 1024 637aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2048 638aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4096 639aac1bf94Sdan INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8192 640aac1bf94Sdan COMMIT; 641aac1bf94Sdan CREATE INDEX i1 ON t1(x); 642aac1bf94Sdan } 643aac1bf94Sdan 644aac1bf94Sdan expr {[file size test.db-wal] > [expr 1032*9000]} 645aac1bf94Sdan } 1 646aac1bf94Sdan 647aac1bf94Sdan do_test wal3-10.$tn.2 { 648aac1bf94Sdan sql2 {PRAGMA integrity_check} 649aac1bf94Sdan } {ok} 650aac1bf94Sdan} 65123f71920Sdan 652ef4ee8f2Sdanfinish_test 653