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