| # 2010 May 03 |
| # |
| # The author disclaims copyright to this source code. In place of |
| # a legal notice, here is a blessing: |
| # |
| # May you do good and not evil. |
| # May you find forgiveness for yourself and forgive others. |
| # May you share freely, never taking more than you give. |
| # |
| #*********************************************************************** |
| # This file implements regression tests for SQLite library. The |
| # focus of this file is testing the operation of the library in |
| # "PRAGMA journal_mode=WAL" mode. |
| # |
| |
| set testdir [file dirname $argv0] |
| source $testdir/tester.tcl |
| source $testdir/malloc_common.tcl |
| |
| ifcapable !wal {finish_test ; return } |
| |
| do_malloc_test walfault-oom-1 -sqlbody { |
| PRAGMA journal_mode = WAL; |
| CREATE TABLE t1(a, b); |
| INSERT INTO t1 VALUES(1, 2); |
| PRAGMA wal_checkpoint; |
| } |
| |
| do_malloc_test walfault-oom-2 -tclprep { |
| execsql { |
| PRAGMA journal_mode = WAL; |
| BEGIN; |
| CREATE TABLE x(y, z, UNIQUE(y, z)); |
| INSERT INTO x VALUES(randomblob(100), randomblob(100)); |
| COMMIT; |
| PRAGMA wal_checkpoint; |
| |
| INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; |
| INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; |
| INSERT INTO x SELECT randomblob(100), randomblob(100) FROM x; |
| } |
| file copy -force test.db testX.db |
| file copy -force test.db-wal testX.db-wal |
| db close |
| file rename -force testX.db test.db |
| file rename -force testX.db-wal test.db-wal |
| |
| sqlite3 db test.db |
| sqlite3_extended_result_codes db 1 |
| sqlite3_db_config_lookaside db 0 0 0 |
| } -sqlbody { |
| SELECT count(*) FROM x; |
| } |
| |
| do_ioerr_test walfault-ioerr-1 -sqlprep { |
| PRAGMA auto_vacuum = 1; |
| PRAGMA journal_mode = WAL; |
| CREATE TABLE abc(a PRIMARY KEY); |
| INSERT INTO abc VALUES(randomblob(1500)); |
| } -sqlbody { |
| DELETE FROM abc; |
| PRAGMA wal_checkpoint; |
| } |
| catch {db close} |
| |
| # A [testvfs] callback for the VFS created by [do_shmfault_test]. This |
| # callback injects SQLITE_IOERR faults into methods for which an entry |
| # in array ::shmfault_ioerr_methods is defined. For example, to enable |
| # errors in xShmOpen: |
| # |
| # set ::shmfault_ioerr_methods(xShmOpen) 1 |
| # |
| # Faults are not injected into xShmRelease, xShmClose or xShmLock method |
| # calls. The global tcl variables used are: |
| # |
| # $::shmfault_ioerr_countdown |
| # $::shmfault_ioerr_persist |
| # $::shmfault_ioerr_methods |
| # |
| proc shmfault_vfs_cb {method args} { |
| |
| # If ::shmfault_ioerr_countdown is not set, always return SQLITE_OK. |
| # |
| if {[info exists ::shmfault_ioerr_countdown]==0} { return SQLITE_OK } |
| |
| if {[info exists ::shmfault_ioerr_methods($method)]} { |
| incr ::shmfault_ioerr_countdown -1 |
| if { ($::shmfault_ioerr_countdown==0) |
| || ($::shmfault_ioerr_countdown<=0 && $::shmfault_ioerr_persist) |
| } { |
| return SQLITE_IOERR |
| } |
| } |
| return SQLITE_OK |
| } |
| |
| # Options are: |
| # |
| # -tclprep TCL |
| # -sqlprep SQL |
| # -sqlbody SQL |
| # |
| proc do_shmfault_test {name args} { |
| |
| set A(-tclprep) "sqlite3 db test.db -vfs shmfault" |
| set A(-sqlprep) "" |
| set A(-sqlbody) "" |
| set A(-methods) [list xShmGet xShmOpen xShmSize] |
| set A(-coverageonly) 0 |
| array set A $args |
| |
| # Create a VFS to use: |
| testvfs shmfault shmfault_vfs_cb |
| |
| unset -nocomplain ::shmfault_ioerr_methods |
| foreach m $A(-methods) { set ::shmfault_ioerr_methods($m) 1 } |
| |
| foreach mode {transient persistent} { |
| set ::shmfault_ioerr_persist [expr {$mode == "persistent"}] |
| |
| for {set nDelay 1} {$nDelay < 10000} {incr nDelay} { |
| |
| file delete -force test.db test.db-wal test.db-journal |
| |
| eval $A(-tclprep) |
| db eval $A(-sqlprep) |
| |
| set ::shmfault_ioerr_countdown $nDelay |
| set rc [catch { db eval $A(-sqlbody) } msg] |
| set hit_error [expr {$::shmfault_ioerr_countdown<=0}] |
| unset ::shmfault_ioerr_countdown |
| |
| catch { db close } |
| |
| if {$A(-coverageonly)} { set rc $hit_error } |
| do_test $name-$mode.$nDelay.1 [list set {} $hit_error] $rc |
| |
| if {$hit_error==0} break |
| } |
| } |
| |
| shmfault delete |
| } |
| |
| do_shmfault_test walfault-shm-1 -sqlbody { |
| PRAGMA journal_mode = WAL; |
| CREATE TABLE t1(a PRIMARY KEY, b); |
| INSERT INTO t1 VALUES('a', 'b'); |
| PRAGMA wal_checkpoint; |
| } |
| |
| do_shmfault_test walfault-shm-2 -methods xShmSize -sqlprep { |
| PRAGMA page_size = 512; |
| PRAGMA journal_mode = WAL; |
| PRAGMA wal_autocheckpoint = 0; |
| } -sqlbody { |
| CREATE TABLE t1(x); |
| BEGIN; |
| INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 32 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 64 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 128 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 256 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 512 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 1024 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2048 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4096 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8192 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ |
| COMMIT; |
| } |
| |
| do_shmfault_test walfault-shm-3 -methods xShmSize -tclprep { |
| sqlite3 db test.db -vfs shmfault |
| unset -nocomplain ::shmfault_ioerr_countdown |
| db eval { |
| PRAGMA page_size = 512; |
| PRAGMA journal_mode = WAL; |
| PRAGMA wal_autocheckpoint = 0; |
| CREATE TABLE t1(x); |
| BEGIN; |
| INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 32 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 64 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 128 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 256 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 512 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 1024 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2048 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4096 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 8192 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ |
| COMMIT; |
| } |
| |
| set ::shmfault_ioerr_countdown 1 |
| set ::shmfault_ioerr_methods(xShmGet) 1 |
| db close |
| unset ::shmfault_ioerr_methods(xShmGet) |
| if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"} |
| |
| sqlite3 db test.db -vfs shmfault |
| } -sqlbody { |
| SELECT count(*) FROM t1; |
| } |
| |
| do_shmfault_test walfault-shm-4 -tclprep { |
| sqlite3 db test.db -vfs shmfault |
| unset -nocomplain ::shmfault_ioerr_countdown |
| db eval { |
| PRAGMA page_size = 512; |
| PRAGMA journal_mode = WAL; |
| PRAGMA wal_autocheckpoint = 0; |
| CREATE TABLE t1(x); |
| BEGIN; |
| INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ |
| INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ |
| COMMIT; |
| } |
| |
| set ::shmfault_ioerr_countdown 1 |
| set ::shmfault_ioerr_methods(xShmGet) 1 |
| db close |
| unset ::shmfault_ioerr_methods(xShmGet) |
| if {[file exists test.db-wal]==0} {error "Failed to create WAL file!"} |
| sqlite3 db test.db -vfs shmfault |
| } -sqlbody { |
| SELECT count(*) FROM t1; |
| } |
| |
| do_shmfault_test walfault-shm-5 -coverageonly 1 -sqlprep { |
| PRAGMA cache_size = 10; |
| PRAGMA journal_mode = WAL; |
| CREATE TABLE abc(a PRIMARY KEY); |
| INSERT INTO abc VALUES(randomblob(900)); |
| } -sqlbody { |
| BEGIN; |
| INSERT INTO abc SELECT randomblob(900) FROM abc; /* 1 */ |
| INSERT INTO abc SELECT randomblob(900) FROM abc; /* 2 */ |
| INSERT INTO abc SELECT randomblob(900) FROM abc; /* 4 */ |
| INSERT INTO abc SELECT randomblob(900) FROM abc; /* 8 */ |
| ROLLBACK; |
| } |
| |
| #------------------------------------------------------------------------- |
| # When a database is checkpointed, SQLite does the following: |
| # |
| # 1. xShmLock(CHECKPOINT) to lock the WAL. |
| # 2. xShmGet(-1) to get a mapping to read the wal-index header. |
| # 3. If the mapping obtained in (2) is not large enough to cover the |
| # entire wal-index, call xShmGet(nReq) to get a larger mapping. |
| # 4. Do the checkpoint. |
| # 5. Release the lock and mapping. |
| # |
| # This test case tests the outcome of an IO error in step 2. |
| # |
| proc shmfault_vfs_cb_6 {method args} { |
| switch -- $::shm_state { |
| 0 { return SQLITE_OK } |
| 1 { |
| if {$method == "xShmGet"} { |
| set ::wal_index [tvfs shm [lindex $args 0]] |
| tvfs shm [lindex $args 0] [string range $::wal_index 0 65535] |
| set ::shm_state 2 |
| } |
| } |
| 2 { |
| if {$method == "xShmGet"} { |
| tvfs shm [lindex $args 0] $::wal_index |
| return SQLITE_IOERR |
| } |
| } |
| } |
| return SQLITE_OK |
| } |
| do_test walfault-shm-6.1 { |
| set ::shm_state 0 |
| testvfs tvfs shmfault_vfs_cb_6 |
| |
| sqlite3 db test.db -vfs tvfs |
| sqlite3 db2 test.db -vfs tvfs |
| |
| execsql { |
| PRAGMA journal_mode = WAL; |
| PRAGMA wal_autocheckpoint = 0; |
| CREATE TABLE t1(x); |
| INSERT INTO t1 VALUES(randomblob(900)); |
| } |
| } {wal 0} |
| do_test walfault-shm-6.2 { |
| execsql { |
| PRAGMA wal_autocheckpoint = 0; |
| BEGIN; |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ |
| INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ |
| COMMIT; |
| } db2 |
| } {0} |
| do_test walfault-shm-6.3 { |
| set ::shm_state 1 |
| catchsql { PRAGMA wal_checkpoint } db2 |
| } {1 {disk I/O error}} |
| set ::shm_state 0 |
| db close |
| db2 close |
| tvfs delete |
| unset -nocomplain ::wal_index ::shm_state |
| |
| finish_test |
| |