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