1# 2010 April 13 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# This file implements regression tests for SQLite library. The 12# focus of this file is testing the operation of the library in 13# "PRAGMA journal_mode=WAL" mode. 14# 15 16set testdir [file dirname $argv0] 17source $testdir/tester.tcl 18source $testdir/lock_common.tcl 19source $testdir/wal_common.tcl 20source $testdir/malloc_common.tcl 21ifcapable !wal {finish_test ; return } 22 23set a_string_counter 1 24proc a_string {n} { 25 global a_string_counter 26 incr a_string_counter 27 string range [string repeat "${a_string_counter}." $n] 1 $n 28} 29db func a_string a_string 30 31#------------------------------------------------------------------------- 32# When a rollback or savepoint rollback occurs, the client may remove 33# elements from one of the hash tables in the wal-index. This block 34# of test cases tests that nothing appears to go wrong when this is 35# done. 36# 37do_test wal3-1.0 { 38 execsql { 39 PRAGMA cache_size = 2000; 40 PRAGMA page_size = 1024; 41 PRAGMA auto_vacuum = off; 42 PRAGMA synchronous = normal; 43 PRAGMA journal_mode = WAL; 44 PRAGMA wal_autocheckpoint = 0; 45 BEGIN; 46 CREATE TABLE t1(x); 47 INSERT INTO t1 VALUES( a_string(800) ); /* 1 */ 48 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */ 49 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */ 50 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */ 51 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */ 52 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */ 53 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 64 */ 54 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 128*/ 55 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 256 */ 56 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 512 */ 57 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 1024 */ 58 INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2048 */ 59 INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 2000; /* 4048 */ 60 COMMIT; 61 PRAGMA cache_size = 10; 62 } 63 wal_frame_count test.db-wal 1024 64} 4086 65 66for {set i 1} {$i < 20} {incr i} { 67 68 do_test wal3-1.$i.1 { 69 set str [a_string 800] 70 execsql { UPDATE t1 SET x = $str WHERE rowid = $i } 71 lappend L [wal_frame_count test.db-wal 1024] 72 execsql { 73 BEGIN; 74 INSERT INTO t1 SELECT a_string(800) FROM t1 LIMIT 100; 75 ROLLBACK; 76 PRAGMA integrity_check; 77 } 78 } {ok} 79 80 # Check that everything looks OK from the point of view of an 81 # external connection. 82 # 83 sqlite3 db2 test.db 84 do_test wal3-1.$i.2 { 85 execsql { SELECT count(*) FROM t1 } db2 86 } 4048 87 do_test wal3-1.$i.3 { 88 execsql { SELECT x FROM t1 WHERE rowid = $i } 89 } $str 90 do_test wal3-1.$i.4 { 91 execsql { PRAGMA integrity_check } db2 92 } {ok} 93 db2 close 94 95 # Check that the file-system in its current state can be recovered. 96 # 97 file copy -force test.db test2.db 98 file copy -force test.db-wal test2.db-wal 99 file delete -force test2.db-journal 100 sqlite3 db2 test2.db 101 do_test wal3-1.$i.5 { 102 execsql { SELECT count(*) FROM t1 } db2 103 } 4048 104 do_test wal3-1.$i.6 { 105 execsql { SELECT x FROM t1 WHERE rowid = $i } 106 } $str 107 do_test wal3-1.$i.7 { 108 execsql { PRAGMA integrity_check } db2 109 } {ok} 110 db2 close 111} 112 113db close 114foreach code [list { 115 proc code2 {tcl} { uplevel #0 $tcl } 116 proc code3 {tcl} { uplevel #0 $tcl } 117 set tn singleproc 118} { 119 set ::code2_chan [launch_testfixture] 120 set ::code3_chan [launch_testfixture] 121 proc code2 {tcl} { testfixture $::code2_chan $tcl } 122 proc code3 {tcl} { testfixture $::code3_chan $tcl } 123 set tn multiproc 124}] { 125 file delete -force test.db test.db-wal test.db-journal 126 sqlite3 db test.db 127 eval $code 128 129 # Open connections [db2] and [db3]. Depending on which iteration this 130 # is, the connections may be created in this interpreter, or in 131 # interpreters running in other OS processes. As such, the [db2] and [db3] 132 # commands should only be accessed within [code2] and [code3] blocks, 133 # respectively. 134 # 135 code2 { sqlite3 db2 test.db ; db2 eval { PRAGMA journal_mode = WAL } } 136 code3 { sqlite3 db3 test.db ; db3 eval { PRAGMA journal_mode = WAL } } 137 138 # Shorthand commands. Execute SQL using database connection [db], [db2] 139 # or [db3]. Return the results. 140 # 141 proc sql {sql} { db eval $sql } 142 proc sql2 {sql} { code2 [list db2 eval $sql] } 143 proc sql3 {sql} { code3 [list db3 eval $sql] } 144 145 do_test wal3-2.$tn.1 { 146 sql { 147 PRAGMA page_size = 1024; 148 PRAGMA auto_vacuum = OFF; 149 PRAGMA journal_mode = WAL; 150 } 151 sql { 152 CREATE TABLE t1(a, b); 153 INSERT INTO t1 VALUES(1, 'one'); 154 BEGIN; 155 SELECT * FROM t1; 156 } 157 } {1 one} 158 do_test wal3-2.$tn.2 { 159 sql2 { 160 CREATE TABLE t2(a, b); 161 INSERT INTO t2 VALUES(2, 'two'); 162 BEGIN; 163 SELECT * FROM t2; 164 } 165 } {2 two} 166 do_test wal3-2.$tn.3 { 167 sql3 { 168 CREATE TABLE t3(a, b); 169 INSERT INTO t3 VALUES(3, 'three'); 170 BEGIN; 171 SELECT * FROM t3; 172 } 173 } {3 three} 174 175 # Try to checkpoint the database using [db]. It should be possible to 176 # checkpoint everything except the table added by [db3] (checkpointing 177 # these frames would clobber the snapshot currently being used by [db2]). 178 # 179 # After [db2] has committed, a checkpoint can copy the entire log to the 180 # database file. Checkpointing after [db3] has committed is therefore a 181 # no-op, as the entire log has already been backfilled. 182 # 183 do_test wal3-2.$tn.4 { 184 sql { 185 COMMIT; 186 PRAGMA wal_checkpoint; 187 } 188 file size test.db 189 } [expr $AUTOVACUUM ? 4*1024 : 3*1024] 190 do_test wal3-2.$tn.5 { 191 sql2 { 192 COMMIT; 193 PRAGMA wal_checkpoint; 194 } 195 file size test.db 196 } [expr $AUTOVACUUM ? 5*1024 : 4*1024] 197 do_test wal3-2.$tn.6 { 198 sql3 { 199 COMMIT; 200 PRAGMA wal_checkpoint; 201 } 202 file size test.db 203 } [expr $AUTOVACUUM ? 5*1024 : 4*1024] 204 205 catch { db close } 206 catch { code2 { db2 close } } 207 catch { code3 { db3 close } } 208 catch { close $::code2_chan } 209 catch { close $::code3_chan } 210} 211catch {db close} 212 213#------------------------------------------------------------------------- 214# Test that that for the simple test: 215# 216# CREATE TABLE x(y); 217# INSERT INTO x VALUES('z'); 218# PRAGMA wal_checkpoint; 219# 220# in WAL mode the xSync method is invoked as expected for each of 221# synchronous=off, synchronous=normal and synchronous=full. 222# 223foreach {tn syncmode synccount} { 224 1 off 225 {} 226 2 normal 227 {test.db-wal normal test.db normal} 228 3 full 229 {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal} 230} { 231 232 proc sync_counter {args} { 233 foreach {method filename id flags} $args break 234 lappend ::syncs [file tail $filename] $flags 235 } 236 do_test wal3-3.$tn { 237 file delete -force test.db test.db-wal test.db-journal 238 239 testvfs T 240 T filter {} 241 T script sync_counter 242 sqlite3 db test.db -vfs T 243 244 execsql "PRAGMA synchronous = $syncmode" 245 execsql { PRAGMA journal_mode = WAL } 246 247 set ::syncs [list] 248 T filter xSync 249 execsql { 250 CREATE TABLE x(y); 251 INSERT INTO x VALUES('z'); 252 PRAGMA wal_checkpoint; 253 } 254 T filter {} 255 set ::syncs 256 } $synccount 257 258 db close 259 T delete 260} 261 262#------------------------------------------------------------------------- 263# When recovering the contents of a WAL file, a process obtains the WRITER 264# lock, then locks all other bytes before commencing recovery. If it fails 265# to lock all other bytes (because some other process is holding a read 266# lock) it should retry up to 100 times. Then return SQLITE_PROTOCOL to the 267# caller. Test this (test case wal3-4.3). 268# 269# Also test the effect of hitting an SQLITE_BUSY while attempting to obtain 270# the WRITER lock (should be the same). Test case wal3-4.4. 271# 272proc lock_callback {method filename handle lock} { 273 lappend ::locks $lock 274} 275do_test wal3-4.1 { 276 testvfs T 277 T filter xShmLock 278 T script lock_callback 279 set ::locks [list] 280 sqlite3 db test.db -vfs T 281 execsql { SELECT * FROM x } 282 lrange $::locks 0 3 283} [list {0 1 lock exclusive} {1 7 lock exclusive} \ 284 {1 7 unlock exclusive} {0 1 unlock exclusive} \ 285] 286do_test wal3-4.2 { 287 db close 288 set ::locks [list] 289 sqlite3 db test.db -vfs T 290 execsql { SELECT * FROM x } 291 lrange $::locks 0 3 292} [list {0 1 lock exclusive} {1 7 lock exclusive} \ 293 {1 7 unlock exclusive} {0 1 unlock exclusive} \ 294] 295proc lock_callback {method filename handle lock} { 296 if {$lock == "1 7 lock exclusive"} { return SQLITE_BUSY } 297 return SQLITE_OK 298} 299puts " Warning: This next test case causes SQLite to call xSleep(1) 100 times." 300puts " Normally this equates to a 100ms delay, but if SQLite is built on unix" 301puts " without HAVE_USLEEP defined, it may be 100 seconds." 302do_test wal3-4.3 { 303 db close 304 set ::locks [list] 305 sqlite3 db test.db -vfs T 306 catchsql { SELECT * FROM x } 307} {1 {locking protocol}} 308 309puts " Warning: Same again!" 310proc lock_callback {method filename handle lock} { 311 if {$lock == "0 1 lock exclusive"} { return SQLITE_BUSY } 312 return SQLITE_OK 313} 314do_test wal3-4.4 { 315 db close 316 set ::locks [list] 317 sqlite3 db test.db -vfs T 318 catchsql { SELECT * FROM x } 319} {1 {locking protocol}} 320db close 321T delete 322 323 324#------------------------------------------------------------------------- 325# Only one client may run recovery at a time. Test this mechanism. 326# 327# When client-2 tries to open a read transaction while client-1 is 328# running recovery, it fails to obtain a lock on an aReadMark[] slot 329# (because they are all locked by recovery). It then tries to obtain 330# a shared lock on the RECOVER lock to see if there really is a 331# recovery running or not. 332# 333# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR 334# being returned when client-2 attempts a shared lock on the RECOVER byte. 335# 336# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An 337# SQLITE_IOERR should be returned to the caller. 338# 339do_test wal3-5.1 { 340 faultsim_delete_and_reopen 341 execsql { 342 PRAGMA journal_mode = WAL; 343 CREATE TABLE t1(a, b); 344 INSERT INTO t1 VALUES(1, 2); 345 INSERT INTO t1 VALUES(3, 4); 346 } 347 faultsim_save_and_close 348} {} 349 350testvfs T -default 1 351T script method_callback 352 353proc method_callback {method args} { 354 if {$method == "xShmBarrier"} { 355 incr ::barrier_count 356 if {$::barrier_count == 1} { 357 # This code is executed within the xShmBarrier() callback invoked 358 # by the client running recovery as part of writing the recovered 359 # wal-index header. If a second client attempts to access the 360 # database now, it reads a corrupt (partially written) wal-index 361 # header. But it cannot even get that far, as the first client 362 # is still holding all the locks (recovery takes an exclusive lock 363 # on *all* db locks, preventing access by any other client). 364 # 365 # If global variable ::wal3_do_lockfailure is non-zero, then set 366 # things up so that an IO error occurs within an xShmLock() callback 367 # made by the second client (aka [db2]). 368 # 369 sqlite3 db2 test.db 370 if { $::wal3_do_lockfailure } { T filter xShmLock } 371 set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ] 372 T filter {} 373 db2 close 374 } 375 } 376 377 if {$method == "xShmLock"} { 378 foreach {file handle spec} $args break 379 if { $spec == "2 1 lock shared" } { 380 return SQLITE_IOERR 381 } 382 } 383 384 return SQLITE_OK 385} 386 387# Test a normal SQLITE_BUSY return. 388# 389T filter xShmBarrier 390set testrc "" 391set testmsg "" 392set barrier_count 0 393set wal3_do_lockfailure 0 394do_test wal3-5.2 { 395 faultsim_restore_and_reopen 396 execsql { SELECT * FROM t1 } 397} {1 2 3 4} 398do_test wal3-5.3 { 399 list $::testrc $::testmsg 400} {1 {database is locked}} 401db close 402 403# Test an SQLITE_IOERR return. 404# 405T filter xShmBarrier 406set barrier_count 0 407set wal3_do_lockfailure 1 408set testrc "" 409set testmsg "" 410do_test wal3-5.4 { 411 faultsim_restore_and_reopen 412 execsql { SELECT * FROM t1 } 413} {1 2 3 4} 414do_test wal3-5.5 { 415 list $::testrc $::testmsg 416} {1 {disk I/O error}} 417 418db close 419T delete 420 421#------------------------------------------------------------------------- 422# When opening a read-transaction on a database, if the entire log has 423# already been copied to the database file, the reader grabs a special 424# kind of read lock (on aReadMark[0]). This set of test cases tests the 425# outcome of the following: 426# 427# + The reader discovering that between the time when it determined 428# that the log had been completely backfilled and the lock is obtained 429# that a writer has written to the log. In this case the reader should 430# acquire a different read-lock (not aReadMark[0]) and read the new 431# snapshot. 432# 433# + The attempt to obtain the lock on aReadMark[0] fails with SQLITE_BUSY. 434# This can happen if a checkpoint is ongoing. In this case also simply 435# obtain a different read-lock. 436# 437catch {db close} 438testvfs T -default 1 439do_test wal3-6.1.1 { 440 file delete -force test.db test.db-journal test.db wal 441 sqlite3 db test.db 442 execsql { PRAGMA journal_mode = WAL } 443 execsql { 444 CREATE TABLE t1(a, b); 445 INSERT INTO t1 VALUES('o', 't'); 446 INSERT INTO t1 VALUES('t', 'f'); 447 } 448} {} 449do_test wal3-6.1.2 { 450 sqlite3 db2 test.db 451 sqlite3 db3 test.db 452 execsql { BEGIN ; SELECT * FROM t1 } db3 453} {o t t f} 454do_test wal3-6.1.3 { 455 execsql { PRAGMA wal_checkpoint } db2 456} {} 457 458# At this point the log file has been fully checkpointed. However, 459# connection [db3] holds a lock that prevents the log from being wrapped. 460# Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But 461# as it is obtaining the lock, [db2] appends to the log file. 462# 463T filter xShmLock 464T script lock_callback 465proc lock_callback {method file handle spec} { 466 if {$spec == "3 1 lock shared"} { 467 # This is the callback for [db] to obtain the read lock on aReadMark[0]. 468 # Disable future callbacks using [T filter {}] and write to the log 469 # file using [db2]. [db3] is preventing [db2] from wrapping the log 470 # here, so this is an append. 471 T filter {} 472 db2 eval { INSERT INTO t1 VALUES('f', 's') } 473 } 474 return SQLITE_OK 475} 476do_test wal3-6.1.4 { 477 execsql { 478 BEGIN; 479 SELECT * FROM t1; 480 } 481} {o t t f f s} 482 483# [db] should be left holding a read-lock on some slot other than 484# aReadMark[0]. Test this by demonstrating that the read-lock is preventing 485# the log from being wrapped. 486# 487do_test wal3-6.1.5 { 488 db3 eval COMMIT 489 db2 eval { PRAGMA wal_checkpoint } 490 set sz1 [file size test.db-wal] 491 db2 eval { INSERT INTO t1 VALUES('s', 'e') } 492 set sz2 [file size test.db-wal] 493 expr {$sz2>$sz1} 494} {1} 495 496# Test that if [db2] had not interfered when [db] was trying to grab 497# aReadMark[0], it would have been possible to wrap the log in 3.6.1.5. 498# 499do_test wal3-6.1.6 { 500 execsql { COMMIT } 501 execsql { PRAGMA wal_checkpoint } db2 502 execsql { 503 BEGIN; 504 SELECT * FROM t1; 505 } 506} {o t t f f s s e} 507do_test wal3-6.1.7 { 508 db2 eval { PRAGMA wal_checkpoint } 509 set sz1 [file size test.db-wal] 510 db2 eval { INSERT INTO t1 VALUES('n', 't') } 511 set sz2 [file size test.db-wal] 512 expr {$sz2==$sz1} 513} {1} 514 515db3 close 516db2 close 517db close 518 519do_test wal3-6.2.1 { 520 file delete -force test.db test.db-journal test.db wal 521 sqlite3 db test.db 522 sqlite3 db2 test.db 523 execsql { PRAGMA journal_mode = WAL } 524 execsql { 525 CREATE TABLE t1(a, b); 526 INSERT INTO t1 VALUES('h', 'h'); 527 INSERT INTO t1 VALUES('l', 'b'); 528 } 529} {} 530 531T filter xShmLock 532T script lock_callback 533proc lock_callback {method file handle spec} { 534 if {$spec == "3 1 unlock exclusive"} { 535 T filter {} 536 set ::R [db2 eval { 537 BEGIN; 538 SELECT * FROM t1; 539 }] 540 } 541} 542do_test wal3-6.2.2 { 543 execsql { PRAGMA wal_checkpoint } 544} {} 545do_test wal3-6.2.3 { 546 set ::R 547} {h h l b} 548do_test wal3-6.2.4 { 549 set sz1 [file size test.db-wal] 550 execsql { INSERT INTO t1 VALUES('b', 'c'); } 551 set sz2 [file size test.db-wal] 552 expr {$sz2 > $sz1} 553} {1} 554do_test wal3-6.2.5 { 555 db2 eval { COMMIT } 556 execsql { PRAGMA wal_checkpoint } 557 set sz1 [file size test.db-wal] 558 execsql { INSERT INTO t1 VALUES('n', 'o'); } 559 set sz2 [file size test.db-wal] 560 expr {$sz2 == $sz1} 561} {1} 562 563db2 close 564db close 565T delete 566 567#------------------------------------------------------------------------- 568# When opening a read-transaction on a database, if the entire log has 569# not yet been copied to the database file, the reader grabs a read 570# lock on aReadMark[x], where x>0. The following test cases experiment 571# with the outcome of the following: 572# 573# + The reader discovering that between the time when it read the 574# wal-index header and the lock was obtained that a writer has 575# written to the log. In this case the reader should re-read the 576# wal-index header and lock a snapshot corresponding to the new 577# header. 578# 579# + The value in the aReadMark[x] slot has been modified since it was 580# read. 581# 582catch {db close} 583testvfs T -default 1 584do_test wal3-7.1.1 { 585 file delete -force test.db test.db-journal test.db wal 586 sqlite3 db test.db 587 execsql { 588 PRAGMA journal_mode = WAL; 589 CREATE TABLE blue(red PRIMARY KEY, green); 590 } 591} {wal} 592 593T script method_callback 594T filter xOpen 595proc method_callback {method args} { 596 if {$method == "xOpen"} { return "reader" } 597} 598do_test wal3-7.1.2 { 599 sqlite3 db2 test.db 600 execsql { SELECT * FROM blue } db2 601} {} 602 603T filter xShmLock 604set ::locks [list] 605proc method_callback {method file handle spec} { 606 if {$handle != "reader" } { return } 607 if {$method == "xShmLock"} { 608 catch { execsql { INSERT INTO blue VALUES(1, 2) } } 609 catch { execsql { INSERT INTO blue VALUES(3, 4) } } 610 } 611 lappend ::locks $spec 612} 613do_test wal3-7.1.3 { 614 execsql { SELECT * FROM blue } db2 615} {1 2 3 4} 616do_test wal3-7.1.4 { 617 set ::locks 618} {{4 1 lock shared} {4 1 unlock shared} {5 1 lock shared} {5 1 unlock shared}} 619 620set ::locks [list] 621proc method_callback {method file handle spec} { 622 if {$handle != "reader" } { return } 623 if {$method == "xShmLock"} { 624 catch { execsql { INSERT INTO blue VALUES(5, 6) } } 625 } 626 lappend ::locks $spec 627} 628do_test wal3-7.2.1 { 629 execsql { SELECT * FROM blue } db2 630} {1 2 3 4 5 6} 631do_test wal3-7.2.2 { 632 set ::locks 633} {{5 1 lock shared} {5 1 unlock shared} {4 1 lock shared} {4 1 unlock shared}} 634 635db close 636db2 close 637T delete 638 639#------------------------------------------------------------------------- 640# 641do_test wal3-8.1 { 642 file delete -force test.db test.db-journal test.db wal 643 sqlite3 db test.db 644 sqlite3 db2 test.db 645 execsql { 646 PRAGMA journal_mode = WAL; 647 CREATE TABLE b(c); 648 INSERT INTO b VALUES('Tehran'); 649 INSERT INTO b VALUES('Qom'); 650 INSERT INTO b VALUES('Markazi'); 651 PRAGMA wal_checkpoint; 652 } 653} {wal} 654do_test wal3-8.2 { 655 execsql { SELECT * FROM b } 656} {Tehran Qom Markazi} 657do_test wal3-8.3 { 658 db eval { SELECT * FROM b } { 659 db eval { INSERT INTO b VALUES('Qazvin') } 660 set r [db2 eval { SELECT * FROM b }] 661 break 662 } 663 set r 664} {Tehran Qom Markazi Qazvin} 665do_test wal3-8.4 { 666 execsql { 667 INSERT INTO b VALUES('Gilan'); 668 INSERT INTO b VALUES('Ardabil'); 669 } 670} {} 671db2 close 672 673faultsim_save_and_close 674testvfs T -default 1 675faultsim_restore_and_reopen 676T filter xShmLock 677T script lock_callback 678 679proc lock_callback {method file handle spec} { 680 if {$spec == "4 1 unlock exclusive"} { 681 T filter {} 682 set ::r [catchsql { SELECT * FROM b } db2] 683 } 684} 685sqlite3 db test.db 686sqlite3 db2 test.db 687do_test wal3-8.5 { 688 execsql { SELECT * FROM b } 689} {Tehran Qom Markazi Qazvin Gilan Ardabil} 690do_test wal3-8.6 { 691 set ::r 692} {1 {locking protocol}} 693 694db close 695db2 close 696 697faultsim_restore_and_reopen 698sqlite3 db2 test.db 699T filter xShmLock 700T script lock_callback 701proc lock_callback {method file handle spec} { 702 if {$spec == "1 7 unlock exclusive"} { 703 T filter {} 704 set ::r [catchsql { SELECT * FROM b } db2] 705 } 706} 707unset ::r 708do_test wal3-8.5 { 709 execsql { SELECT * FROM b } 710} {Tehran Qom Markazi Qazvin Gilan Ardabil} 711do_test wal3-8.6 { 712 set ::r 713} {1 {locking protocol}} 714 715db close 716db2 close 717T delete 718 719#------------------------------------------------------------------------- 720# When a connection opens a read-lock on the database, it searches for 721# an aReadMark[] slot that is already set to the mxFrame value for the 722# new transaction. If it cannot find one, it attempts to obtain an 723# exclusive lock on an aReadMark[] slot for the purposes of modifying 724# the value, then drops back to a shared-lock for the duration of the 725# transaction. 726# 727# This test case verifies that if an exclusive lock cannot be obtained 728# on any aReadMark[] slot (because there are already several readers), 729# the client takes a shared-lock on a slot without modifying the value 730# and continues. 731# 732do_test wal3-9.0 { 733 file delete -force test.db test.db-journal test.db wal 734 sqlite3 db test.db 735 execsql { 736 PRAGMA journal_mode = WAL; 737 CREATE TABLE whoami(x); 738 INSERT INTO whoami VALUES('nobody'); 739 } 740} {wal} 741for {set i 0} {$i < 50} {incr i} { 742 set c db$i 743 do_test wal3-9.1.$i { 744 sqlite3 $c test.db 745 execsql { UPDATE whoami SET x = $c } 746 execsql { 747 BEGIN; 748 SELECT * FROM whoami 749 } $c 750 } $c 751} 752for {set i 0} {$i < 50} {incr i} { 753 set c db$i 754 do_test wal3-9.2.$i { 755 execsql { SELECT * FROM whoami } $c 756 } $c 757} 758do_test wal3-9.3 { 759 for {set i 0} {$i < 49} {incr i} { db$i close } 760 execsql { PRAGMA wal_checkpoint } 761 set sz1 [file size test.db] 762 db49 close 763 execsql { PRAGMA wal_checkpoint } 764 set sz2 [file size test.db] 765 expr {$sz2 > $sz1} 766} {1} 767 768db close 769 770finish_test 771 772