summaryrefslogtreecommitdiffstats
path: root/test/walvfs.test
diff options
context:
space:
mode:
Diffstat (limited to 'test/walvfs.test')
-rw-r--r--test/walvfs.test437
1 files changed, 437 insertions, 0 deletions
diff --git a/test/walvfs.test b/test/walvfs.test
new file mode 100644
index 0000000..d32cbd7
--- /dev/null
+++ b/test/walvfs.test
@@ -0,0 +1,437 @@
+# 2018 December 23
+#
+# 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.
+#
+# TESTRUNNER: slow
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+source $testdir/wal_common.tcl
+set testprefix walvfs
+
+ifcapable !wal {finish_test ; return }
+
+db close
+testvfs tvfs
+tvfs script xSync
+tvfs filter xSync
+set ::sync_count 0
+proc xSync {method file args} {
+ if {[file tail $file]=="test.db-wal"} {
+ incr ::sync_count
+ }
+}
+
+
+#-------------------------------------------------------------------------
+# Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
+# disk immediately after it is written.
+#
+sqlite3 db test.db -vfs tvfs
+do_execsql_test 1.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA journal_mode = wal;
+ PRAGMA synchronous = normal;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES(4, 5, 6);
+ INSERT INTO t1 VALUES(7, 8, 9);
+ PRAGMA wal_checkpoint;
+} {wal 0 5 5}
+
+set ::sync_count 0
+do_test 1.1 {
+ execsql { INSERT INTO t1 VALUES(10, 11, 12) }
+ set ::sync_count
+} 1
+
+db close
+tvfs devchar sequential
+sqlite3 db test.db -vfs tvfs
+do_execsql_test 1.2 {
+ PRAGMA synchronous = normal;
+ INSERT INTO t1 VALUES(13, 14, 15);
+ INSERT INTO t1 VALUES(16, 17, 18);
+ PRAGMA wal_checkpoint;
+} {0 4 4}
+
+set ::sync_count 0
+do_test 1.3 {
+ execsql { INSERT INTO t1 VALUES(10, 11, 12) }
+ set ::sync_count
+} 0
+
+#-------------------------------------------------------------------------
+# Test that "PRAGMA journal_size_limit" works in wal mode.
+#
+reset_db
+do_execsql_test 2.0 {
+ PRAGMA journal_size_limit = 10000;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+} {10000 wal}
+do_test 2.1 {
+ expr [file size test.db-wal]>12000
+} {1}
+do_test 2.2 {
+ execsql {
+ PRAGMA wal_checkpoint;
+ INSERT INTO t1 VALUES(randomblob(750));
+ }
+ file size test.db-wal
+} {10000}
+do_test 2.3 {
+ execsql {
+ PRAGMA journal_size_limit = 8000;
+ PRAGMA wal_checkpoint;
+ INSERT INTO t1 VALUES(randomblob(750));
+ }
+ file size test.db-wal
+} {8000}
+
+#-------------------------------------------------------------------------
+# Test that a checkpoint may be interrupted using sqlite3_interrupt().
+# And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
+# an OOM error occurs just before the sqlite3_interrupt() call.
+#
+reset_db
+db close
+sqlite3 db test.db -vfs tvfs
+tvfs filter {}
+
+do_execsql_test 3.0 {
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+} {wal}
+
+tvfs filter xWrite
+tvfs script xWrite
+set ::cnt 2
+proc xWrite {method file args} {
+ if {[file tail $file]=="test.db"} {
+ incr ::cnt -1
+ if {$::cnt==0} {
+ sqlite3_interrupt db
+ }
+ }
+ return SQLITE_OK
+}
+
+do_catchsql_test 3.1 {
+ PRAGMA wal_checkpoint
+} {1 interrupted}
+
+set ::cnt 2
+proc xWrite {method file args} {
+ if {[file tail $file]=="test.db"} {
+ incr ::cnt -1
+ if {$::cnt==0} {
+ sqlite3_memdebug_fail 1 -repeat 0
+ # For this test to pass, the following statement must call malloc() at
+ # least once. Even if the lookaside is enabled.
+ set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }]
+ sqlite3_interrupt db
+ }
+ }
+ return SQLITE_OK
+}
+
+set ::xwrite_stmt_res ""
+do_catchsql_test 3.2 {
+ PRAGMA wal_checkpoint
+} {1 {out of memory}}
+do_test 3.2.2 {
+ set ::xwrite_stmt_res
+} {1 {out of memory}}
+unset ::xwrite_stmt_res
+
+#-------------------------------------------------------------------------
+#
+reset_db
+db close
+do_test 4.0 {
+ sqlite3 db test.db -vfs tvfs
+ execsql {
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+ } db
+} {wal}
+db close
+
+tvfs filter xShmMap
+tvfs script xShmMap
+proc xShmMap {method file args} {
+ return SQLITE_READONLY
+}
+sqlite3 db test.db -vfs tvfs
+do_catchsql_test 4.1 {
+ SELECT count(*) FROM t1
+} {1 {attempt to write a readonly database}}
+
+set ::cnt 5
+tvfs filter {xShmMap xShmLock}
+proc xShmMap {method file name args} {
+ switch -- $method {
+ xShmMap { return SQLITE_READONLY }
+ xShmLock {
+ if {$args == "{0 1 lock shared}"} {
+ incr ::cnt -1
+ if {$::cnt>0} { return SQLITE_BUSY }
+ }
+ }
+ }
+ return SQLITE_OK
+}
+do_catchsql_test 4.2 {
+ SELECT count(*) FROM t1
+} {1 {attempt to write a readonly database}}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+db close
+sqlite3 db test.db -vfs tvfs
+tvfs filter {}
+do_execsql_test 5.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+} {wal}
+
+do_execsql_test 5.1 {
+ SELECT count(*) FROM t1
+} {20}
+
+do_test 5.2 {
+ vfs_set_readmark db main 1 100
+ vfs_set_readmark db main 2 100
+ vfs_set_readmark db main 3 100
+ vfs_set_readmark db main 4 100
+} {100}
+
+do_execsql_test 5.3 {
+ SELECT count(*) FROM t1
+} {20}
+
+do_test 5.3 {
+ list [vfs_set_readmark db main 1] \
+ [vfs_set_readmark db main 2] \
+ [vfs_set_readmark db main 3] \
+ [vfs_set_readmark db main 4]
+} {24 100 100 100}
+
+tvfs script xShmLock
+tvfs filter xShmLock
+set ::cnt 20
+proc xShmLock {args} {
+ incr ::cnt -1
+ if {$::cnt>0} { return SQLITE_BUSY }
+ return SQLITE_OK
+}
+
+do_test 5.4 {
+ vfs_set_readmark db main 1 100
+ execsql { SELECT count(*) FROM t1 }
+} {20}
+
+vfs_set_readmark db main 1 100
+vfs_set_readmark db main 2 100
+vfs_set_readmark db main 3 100
+vfs_set_readmark db main 4 100
+
+tvfs script xShmMapLock
+tvfs filter {xShmLock xShmMap}
+proc xShmMapLock {method args} {
+ if {$method=="xShmMap"} {
+ return "SQLITE_READONLY"
+ }
+ return SQLITE_BUSY
+}
+
+sqlite3 db2 test.db -vfs tvfs
+breakpoint
+do_test 5.5 {
+ list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
+} {1 {attempt to write a readonly database}}
+
+tvfs filter {}
+vfs_set_readmark db main 1 1
+
+do_test 5.6 {
+ list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
+} {0 20}
+db2 close
+db close
+
+#-------------------------------------------------------------------------
+# Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
+#
+reset_db
+tvfs filter {}
+db close
+sqlite3 db test.db -vfs tvfs
+do_execsql_test 6.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+} {wal}
+
+do_test 6.1 {
+ execsql { PRAGMA wal_checkpoint }
+ set {} {}
+} {}
+
+tvfs filter xShmLock
+tvfs script xShmLock
+set ::flag 0
+proc xShmLock {method file handle spec} {
+ if {$::flag && [lrange $spec 2 end]=="lock shared"} {
+ return SQLITE_BUSY
+ }
+ if {$spec=="3 1 unlock shared"} {
+ set ::flag 1
+ }
+ return SQLITE_OK
+}
+
+puts "# WARNING: This next test takes around 12 seconds"
+do_catchsql_test 6.2 {
+ INSERT INTO t1 VALUES(1);
+} {1 {locking protocol}}
+
+#-------------------------------------------------------------------------
+# Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
+#
+reset_db
+tvfs filter {}
+db close
+sqlite3 db test.db -vfs tvfs
+do_execsql_test 7.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS (
+ SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
+ )
+ INSERT INTO t1 SELECT randomblob(750) FROM s;
+} {wal}
+
+tvfs script xShmLock
+tvfs filter xShmLock
+proc xShmLock {method file handle spec} {
+ if {$spec=="1 1 lock exclusive"} {
+ return SQLITE_BUSY
+ }
+ return SQLITE_OK
+}
+
+do_execsql_test 7.1 {
+ PRAGMA wal_checkpoint
+} {1 -1 -1}
+
+#-------------------------------------------------------------------------
+# Check that the page cache is correctly flushed if a checkpointer using
+# a version 2 VFS makes a checkpoint with an out-of-date cache.
+#
+reset_db
+testvfs tvfs2 -iversion 2
+db close
+sqlite3 db test.db -vfs tvfs2
+do_execsql_test 8.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
+ INSERT INTO t1 SELECT randomblob(75) FROM s;
+} {wal}
+
+do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
+
+do_test 8.2 {
+ sqlite3 db2 test.db -vfs tvfs2
+ execsql {
+ INSERT INTO t1 VALUES(randomblob(75));
+ } db2
+ db2 close
+} {}
+
+do_execsql_test 8.3 {
+ PRAGMA wal_checkpoint;
+ SELECT count(*) FROM t1
+} {0 5 5 21}
+db close
+tvfs2 delete
+
+#-------------------------------------------------------------------------
+reset_db
+db close
+sqlite3 db test.db -vfs tvfs
+do_execsql_test 9.0 {
+ PRAGMA auto_vacuum = 0;
+ PRAGMA page_size = 1024;
+ CREATE TABLE t1(x);
+ PRAGMA journal_mode = wal;
+ WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
+ INSERT INTO t1 SELECT randomblob(75) FROM s;
+} {wal}
+
+sqlite3 db2 test.db -vfs tvfs
+tvfs filter {xShmMap xShmLock}
+tvfs script xShmMap
+proc xShmMap {method file handle args} {
+ switch -- $method {
+ xShmMap {
+ return "SQLITE_READONLY_CANTINIT"
+ }
+ xShmLock {
+ if {$args=="{3 1 lock shared}"} {
+ return "SQLITE_IOERR"
+ }
+ }
+ }
+}
+
+do_test 9.1 {
+ catchsql { SELECT count(*) FROM t1 } db2
+} {1 {disk I/O error}}
+
+db close
+db2 close
+tvfs delete
+finish_test