1# 2018 December 23 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# TESTRUNNER: slow 16 17set testdir [file dirname $argv0] 18source $testdir/tester.tcl 19source $testdir/lock_common.tcl 20source $testdir/malloc_common.tcl 21source $testdir/wal_common.tcl 22set testprefix walvfs 23 24ifcapable !wal {finish_test ; return } 25 26db close 27testvfs tvfs 28tvfs script xSync 29tvfs filter xSync 30set ::sync_count 0 31proc xSync {method file args} { 32 if {[file tail $file]=="test.db-wal"} { 33 incr ::sync_count 34 } 35} 36 37 38#------------------------------------------------------------------------- 39# Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to 40# disk immediately after it is written. 41# 42sqlite3 db test.db -vfs tvfs 43do_execsql_test 1.0 { 44 PRAGMA auto_vacuum = 0; 45 PRAGMA journal_mode = wal; 46 PRAGMA synchronous = normal; 47 CREATE TABLE t1(a, b, c); 48 INSERT INTO t1 VALUES(1, 2, 3); 49 INSERT INTO t1 VALUES(4, 5, 6); 50 INSERT INTO t1 VALUES(7, 8, 9); 51 PRAGMA wal_checkpoint; 52} {wal 0 5 5} 53 54set ::sync_count 0 55do_test 1.1 { 56 execsql { INSERT INTO t1 VALUES(10, 11, 12) } 57 set ::sync_count 58} 1 59 60db close 61tvfs devchar sequential 62sqlite3 db test.db -vfs tvfs 63do_execsql_test 1.2 { 64 PRAGMA synchronous = normal; 65 INSERT INTO t1 VALUES(13, 14, 15); 66 INSERT INTO t1 VALUES(16, 17, 18); 67 PRAGMA wal_checkpoint; 68} {0 4 4} 69 70set ::sync_count 0 71do_test 1.3 { 72 execsql { INSERT INTO t1 VALUES(10, 11, 12) } 73 set ::sync_count 74} 0 75 76#------------------------------------------------------------------------- 77# Test that "PRAGMA journal_size_limit" works in wal mode. 78# 79reset_db 80do_execsql_test 2.0 { 81 PRAGMA journal_size_limit = 10000; 82 CREATE TABLE t1(x); 83 PRAGMA journal_mode = wal; 84 WITH s(i) AS ( 85 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 86 ) 87 INSERT INTO t1 SELECT randomblob(750) FROM s; 88} {10000 wal} 89do_test 2.1 { 90 expr [file size test.db-wal]>12000 91} {1} 92do_test 2.2 { 93 execsql { 94 PRAGMA wal_checkpoint; 95 INSERT INTO t1 VALUES(randomblob(750)); 96 } 97 file size test.db-wal 98} {10000} 99do_test 2.3 { 100 execsql { 101 PRAGMA journal_size_limit = 8000; 102 PRAGMA wal_checkpoint; 103 INSERT INTO t1 VALUES(randomblob(750)); 104 } 105 file size test.db-wal 106} {8000} 107 108#------------------------------------------------------------------------- 109# Test that a checkpoint may be interrupted using sqlite3_interrupt(). 110# And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if 111# an OOM error occurs just before the sqlite3_interrupt() call. 112# 113reset_db 114db close 115sqlite3 db test.db -vfs tvfs 116tvfs filter {} 117 118do_execsql_test 3.0 { 119 CREATE TABLE t1(x); 120 PRAGMA journal_mode = wal; 121 WITH s(i) AS ( 122 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 123 ) 124 INSERT INTO t1 SELECT randomblob(750) FROM s; 125} {wal} 126 127tvfs filter xWrite 128tvfs script xWrite 129set ::cnt 2 130proc xWrite {method file args} { 131 if {[file tail $file]=="test.db"} { 132 incr ::cnt -1 133 if {$::cnt==0} { 134 sqlite3_interrupt db 135 } 136 } 137 return SQLITE_OK 138} 139 140do_catchsql_test 3.1 { 141 PRAGMA wal_checkpoint 142} {1 interrupted} 143 144set ::cnt 2 145proc xWrite {method file args} { 146 if {[file tail $file]=="test.db"} { 147 incr ::cnt -1 148 if {$::cnt==0} { 149 sqlite3_memdebug_fail 1 -repeat 0 150 # For this test to pass, the following statement must call malloc() at 151 # least once. Even if the lookaside is enabled. 152 set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }] 153 sqlite3_interrupt db 154 } 155 } 156 return SQLITE_OK 157} 158 159set ::xwrite_stmt_res "" 160do_catchsql_test 3.2 { 161 PRAGMA wal_checkpoint 162} {1 {out of memory}} 163do_test 3.2.2 { 164 set ::xwrite_stmt_res 165} {1 {out of memory}} 166unset ::xwrite_stmt_res 167 168#------------------------------------------------------------------------- 169# 170reset_db 171db close 172do_test 4.0 { 173 sqlite3 db test.db -vfs tvfs 174 execsql { 175 CREATE TABLE t1(x); 176 PRAGMA journal_mode = wal; 177 WITH s(i) AS ( 178 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 179 ) 180 INSERT INTO t1 SELECT randomblob(750) FROM s; 181 } db 182} {wal} 183db close 184 185tvfs filter xShmMap 186tvfs script xShmMap 187proc xShmMap {method file args} { 188 return SQLITE_READONLY 189} 190sqlite3 db test.db -vfs tvfs 191do_catchsql_test 4.1 { 192 SELECT count(*) FROM t1 193} {1 {attempt to write a readonly database}} 194 195set ::cnt 5 196tvfs filter {xShmMap xShmLock} 197proc xShmMap {method file name args} { 198 switch -- $method { 199 xShmMap { return SQLITE_READONLY } 200 xShmLock { 201 if {$args == "{0 1 lock shared}"} { 202 incr ::cnt -1 203 if {$::cnt>0} { return SQLITE_BUSY } 204 } 205 } 206 } 207 return SQLITE_OK 208} 209do_catchsql_test 4.2 { 210 SELECT count(*) FROM t1 211} {1 {attempt to write a readonly database}} 212 213#------------------------------------------------------------------------- 214# 215reset_db 216db close 217sqlite3 db test.db -vfs tvfs 218tvfs filter {} 219do_execsql_test 5.0 { 220 PRAGMA auto_vacuum = 0; 221 PRAGMA page_size = 1024; 222 CREATE TABLE t1(x); 223 PRAGMA journal_mode = wal; 224 WITH s(i) AS ( 225 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 226 ) 227 INSERT INTO t1 SELECT randomblob(750) FROM s; 228} {wal} 229 230do_execsql_test 5.1 { 231 SELECT count(*) FROM t1 232} {20} 233 234do_test 5.2 { 235 vfs_set_readmark db main 1 100 236 vfs_set_readmark db main 2 100 237 vfs_set_readmark db main 3 100 238 vfs_set_readmark db main 4 100 239} {100} 240 241do_execsql_test 5.3 { 242 SELECT count(*) FROM t1 243} {20} 244 245do_test 5.3 { 246 list [vfs_set_readmark db main 1] \ 247 [vfs_set_readmark db main 2] \ 248 [vfs_set_readmark db main 3] \ 249 [vfs_set_readmark db main 4] 250} {24 100 100 100} 251 252tvfs script xShmLock 253tvfs filter xShmLock 254set ::cnt 20 255proc xShmLock {args} { 256 incr ::cnt -1 257 if {$::cnt>0} { return SQLITE_BUSY } 258 return SQLITE_OK 259} 260 261do_test 5.4 { 262 vfs_set_readmark db main 1 100 263 execsql { SELECT count(*) FROM t1 } 264} {20} 265 266vfs_set_readmark db main 1 100 267vfs_set_readmark db main 2 100 268vfs_set_readmark db main 3 100 269vfs_set_readmark db main 4 100 270 271tvfs script xShmMapLock 272tvfs filter {xShmLock xShmMap} 273proc xShmMapLock {method args} { 274 if {$method=="xShmMap"} { 275 return "SQLITE_READONLY" 276 } 277 return SQLITE_BUSY 278} 279 280sqlite3 db2 test.db -vfs tvfs 281breakpoint 282do_test 5.5 { 283 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg 284} {1 {attempt to write a readonly database}} 285 286tvfs filter {} 287vfs_set_readmark db main 1 1 288 289do_test 5.6 { 290 list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg 291} {0 20} 292db2 close 293db close 294 295#------------------------------------------------------------------------- 296# Cause an SQLITE_PROTOCOL while attempting to restart the wal file. 297# 298reset_db 299tvfs filter {} 300db close 301sqlite3 db test.db -vfs tvfs 302do_execsql_test 6.0 { 303 PRAGMA auto_vacuum = 0; 304 PRAGMA page_size = 1024; 305 CREATE TABLE t1(x); 306 PRAGMA journal_mode = wal; 307 WITH s(i) AS ( 308 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 309 ) 310 INSERT INTO t1 SELECT randomblob(750) FROM s; 311} {wal} 312 313do_test 6.1 { 314 execsql { PRAGMA wal_checkpoint } 315 set {} {} 316} {} 317 318tvfs filter xShmLock 319tvfs script xShmLock 320set ::flag 0 321proc xShmLock {method file handle spec} { 322 if {$::flag && [lrange $spec 2 end]=="lock shared"} { 323 return SQLITE_BUSY 324 } 325 if {$spec=="3 1 unlock shared"} { 326 set ::flag 1 327 } 328 return SQLITE_OK 329} 330 331puts "# WARNING: This next test takes around 12 seconds" 332do_catchsql_test 6.2 { 333 INSERT INTO t1 VALUES(1); 334} {1 {locking protocol}} 335 336#------------------------------------------------------------------------- 337# Check that a checkpoint fails if it cannot get the CHECKPOINTER lock 338# 339reset_db 340tvfs filter {} 341db close 342sqlite3 db test.db -vfs tvfs 343do_execsql_test 7.0 { 344 PRAGMA auto_vacuum = 0; 345 PRAGMA page_size = 1024; 346 CREATE TABLE t1(x); 347 PRAGMA journal_mode = wal; 348 WITH s(i) AS ( 349 SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 350 ) 351 INSERT INTO t1 SELECT randomblob(750) FROM s; 352} {wal} 353 354tvfs script xShmLock 355tvfs filter xShmLock 356proc xShmLock {method file handle spec} { 357 if {$spec=="1 1 lock exclusive"} { 358 return SQLITE_BUSY 359 } 360 return SQLITE_OK 361} 362 363do_execsql_test 7.1 { 364 PRAGMA wal_checkpoint 365} {1 -1 -1} 366 367#------------------------------------------------------------------------- 368# Check that the page cache is correctly flushed if a checkpointer using 369# a version 2 VFS makes a checkpoint with an out-of-date cache. 370# 371reset_db 372testvfs tvfs2 -iversion 2 373db close 374sqlite3 db test.db -vfs tvfs2 375do_execsql_test 8.0 { 376 PRAGMA auto_vacuum = 0; 377 PRAGMA page_size = 1024; 378 CREATE TABLE t1(x); 379 PRAGMA journal_mode = wal; 380 WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 ) 381 INSERT INTO t1 SELECT randomblob(75) FROM s; 382} {wal} 383 384do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20} 385 386do_test 8.2 { 387 sqlite3 db2 test.db -vfs tvfs2 388 execsql { 389 INSERT INTO t1 VALUES(randomblob(75)); 390 } db2 391 db2 close 392} {} 393 394do_execsql_test 8.3 { 395 PRAGMA wal_checkpoint; 396 SELECT count(*) FROM t1 397} {0 5 5 21} 398db close 399tvfs2 delete 400 401#------------------------------------------------------------------------- 402reset_db 403db close 404sqlite3 db test.db -vfs tvfs 405do_execsql_test 9.0 { 406 PRAGMA auto_vacuum = 0; 407 PRAGMA page_size = 1024; 408 CREATE TABLE t1(x); 409 PRAGMA journal_mode = wal; 410 WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 ) 411 INSERT INTO t1 SELECT randomblob(75) FROM s; 412} {wal} 413 414sqlite3 db2 test.db -vfs tvfs 415tvfs filter {xShmMap xShmLock} 416tvfs script xShmMap 417proc xShmMap {method file handle args} { 418 switch -- $method { 419 xShmMap { 420 return "SQLITE_READONLY_CANTINIT" 421 } 422 xShmLock { 423 if {$args=="{3 1 lock shared}"} { 424 return "SQLITE_IOERR" 425 } 426 } 427 } 428} 429 430do_test 9.1 { 431 catchsql { SELECT count(*) FROM t1 } db2 432} {1 {disk I/O error}} 433 434db close 435db2 close 436tvfs delete 437finish_test 438