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