14edc6bf3Sdan# 2011 May 09 24edc6bf3Sdan# 34edc6bf3Sdan# The author disclaims copyright to this source code. In place of 44edc6bf3Sdan# a legal notice, here is a blessing: 54edc6bf3Sdan# 64edc6bf3Sdan# May you do good and not evil. 74edc6bf3Sdan# May you find forgiveness for yourself and forgive others. 84edc6bf3Sdan# May you share freely, never taking more than you give. 94edc6bf3Sdan# 104edc6bf3Sdan#*********************************************************************** 114edc6bf3Sdan# 124edc6bf3Sdan# This file contains tests for using WAL databases in read-only mode. 134edc6bf3Sdan# 144edc6bf3Sdan 154edc6bf3Sdanset testdir [file dirname $argv0] 164edc6bf3Sdansource $testdir/tester.tcl 174edc6bf3Sdansource $testdir/lock_common.tcl 184edc6bf3Sdanset ::testprefix walro 194edc6bf3Sdan 204edc6bf3Sdan# These tests are only going to work on unix. 214edc6bf3Sdan# 2205fd9aabSshanehif {$::tcl_platform(platform) != "unix"} { 2305fd9aabSshaneh finish_test 2405fd9aabSshaneh return 2505fd9aabSshaneh} 264edc6bf3Sdan 275209132aSdan# And only if the build is WAL-capable. 285209132aSdan# 295209132aSdanifcapable !wal { 305209132aSdan finish_test 315209132aSdan return 325209132aSdan} 335209132aSdan 3405fd9aabSshanehdo_multiclient_test tn { 354edc6bf3Sdan 364edc6bf3Sdan # Close all connections and delete the database. 374edc6bf3Sdan # 384edc6bf3Sdan code1 { db close } 394edc6bf3Sdan code2 { db2 close } 404edc6bf3Sdan code3 { db3 close } 414edc6bf3Sdan forcedelete test.db 424edc6bf3Sdan forcedelete walro 434edc6bf3Sdan 4498c91905Sdan # Do not run tests with the connections in the same process. 4598c91905Sdan # 4698c91905Sdan if {$tn==2} continue 4798c91905Sdan 48b6d2f9c5Sdan foreach c {code1 code2 code3} { 49b6d2f9c5Sdan $c { 50b6d2f9c5Sdan sqlite3_shutdown 51b6d2f9c5Sdan sqlite3_config_uri 1 52b6d2f9c5Sdan } 53b6d2f9c5Sdan } 54b6d2f9c5Sdan 554edc6bf3Sdan file mkdir walro 564edc6bf3Sdan 574edc6bf3Sdan do_test 1.1.1 { 584edc6bf3Sdan code2 { sqlite3 db2 test.db } 594edc6bf3Sdan sql2 { 6081f0740eSdan PRAGMA auto_vacuum = 0; 614edc6bf3Sdan PRAGMA journal_mode = WAL; 624edc6bf3Sdan CREATE TABLE t1(x, y); 634edc6bf3Sdan INSERT INTO t1 VALUES('a', 'b'); 644edc6bf3Sdan } 654edc6bf3Sdan file exists test.db-shm 664edc6bf3Sdan } {1} 674edc6bf3Sdan 684edc6bf3Sdan do_test 1.1.2 { 694edc6bf3Sdan file attributes test.db-shm -permissions r--r--r-- 70b6d2f9c5Sdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 714edc6bf3Sdan } {} 724edc6bf3Sdan 734edc6bf3Sdan do_test 1.1.3 { sql1 "SELECT * FROM t1" } {a b} 744edc6bf3Sdan do_test 1.1.4 { sql2 "INSERT INTO t1 VALUES('c', 'd')" } {} 754edc6bf3Sdan do_test 1.1.5 { sql1 "SELECT * FROM t1" } {a b c d} 764edc6bf3Sdan 774edc6bf3Sdan # Check that the read-only connection cannot write or checkpoint the db. 784edc6bf3Sdan # 794edc6bf3Sdan do_test 1.1.6 { 804edc6bf3Sdan csql1 "INSERT INTO t1 VALUES('e', 'f')" 814edc6bf3Sdan } {1 {attempt to write a readonly database}} 824edc6bf3Sdan do_test 1.1.7 { 834edc6bf3Sdan csql1 "PRAGMA wal_checkpoint" 844edc6bf3Sdan } {1 {attempt to write a readonly database}} 854edc6bf3Sdan 864edc6bf3Sdan do_test 1.1.9 { sql2 "INSERT INTO t1 VALUES('e', 'f')" } {} 874edc6bf3Sdan do_test 1.1.10 { sql1 "SELECT * FROM t1" } {a b c d e f} 884edc6bf3Sdan 894edc6bf3Sdan do_test 1.1.11 { 904edc6bf3Sdan sql2 { 914edc6bf3Sdan INSERT INTO t1 VALUES('g', 'h'); 924edc6bf3Sdan PRAGMA wal_checkpoint; 934edc6bf3Sdan } 944edc6bf3Sdan set {} {} 954edc6bf3Sdan } {} 964edc6bf3Sdan do_test 1.1.12 { sql1 "SELECT * FROM t1" } {a b c d e f g h} 974edc6bf3Sdan do_test 1.1.13 { sql2 "INSERT INTO t1 VALUES('i', 'j')" } {} 984edc6bf3Sdan 994edc6bf3Sdan do_test 1.2.1 { 1004edc6bf3Sdan code2 { db2 close } 1014edc6bf3Sdan code1 { db close } 1024edc6bf3Sdan list [file exists test.db-wal] [file exists test.db-shm] 1034edc6bf3Sdan } {1 1} 104ab04eff8Sdan 1054edc6bf3Sdan do_test 1.2.2 { 106b6d2f9c5Sdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 107ab04eff8Sdan list [catch { sql1 { SELECT * FROM t1 } } msg] $msg 10811caf4f4Sdan } {0 {a b c d e f g h i j}} 1094edc6bf3Sdan 1104edc6bf3Sdan do_test 1.2.3 { 1114edc6bf3Sdan code1 { db close } 1124edc6bf3Sdan file attributes test.db-shm -permissions rw-r--r-- 1134edc6bf3Sdan hexio_write test.db-shm 0 01020304 1144edc6bf3Sdan file attributes test.db-shm -permissions r--r--r-- 115b6d2f9c5Sdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 1164edc6bf3Sdan csql1 { SELECT * FROM t1 } 11711caf4f4Sdan } {0 {a b c d e f g h i j}} 1184edc6bf3Sdan do_test 1.2.4 { 1194edc6bf3Sdan code1 { sqlite3_extended_errcode db } 12011caf4f4Sdan } {SQLITE_OK} 1214edc6bf3Sdan 1224edc6bf3Sdan do_test 1.2.5 { 1234edc6bf3Sdan file attributes test.db-shm -permissions rw-r--r-- 1244edc6bf3Sdan code2 { sqlite3 db2 test.db } 1254edc6bf3Sdan sql2 "SELECT * FROM t1" 1264edc6bf3Sdan } {a b c d e f g h i j} 1274edc6bf3Sdan file attributes test.db-shm -permissions r--r--r-- 1284edc6bf3Sdan do_test 1.2.6 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j} 1294edc6bf3Sdan 1304edc6bf3Sdan do_test 1.2.7 { 1314edc6bf3Sdan sql2 { 1324edc6bf3Sdan PRAGMA wal_checkpoint; 1334edc6bf3Sdan INSERT INTO t1 VALUES('k', 'l'); 1344edc6bf3Sdan } 1354edc6bf3Sdan set {} {} 1364edc6bf3Sdan } {} 1374edc6bf3Sdan do_test 1.2.8 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j k l} 1383640db58Sdan 1393640db58Sdan # Now check that if the readonly_shm option is not supplied, or if it 1403640db58Sdan # is set to zero, it is not possible to connect to the database without 1413640db58Sdan # read-write access to the shm. 142*f12ba66cSdan # 143*f12ba66cSdan # UPDATE: os_unix.c now opens the *-shm file in readonly mode 144*f12ba66cSdan # automatically. 145*f12ba66cSdan # 1463640db58Sdan do_test 1.3.1 { 1473640db58Sdan code1 { db close } 1483640db58Sdan code1 { sqlite3 db test.db } 1493640db58Sdan csql1 { SELECT * FROM t1 } 150*f12ba66cSdan } {0 {a b c d e f g h i j k l}} 1513640db58Sdan 1523640db58Sdan # Also test that if the -shm file can be opened for read/write access, 1533ec4a0c1Sdrh # it is not if readonly_shm=1 is present in the URI. 1543640db58Sdan do_test 1.3.2.1 { 1553640db58Sdan code1 { db close } 1563640db58Sdan code2 { db2 close } 1573640db58Sdan file exists test.db-shm 1583640db58Sdan } {0} 1593640db58Sdan do_test 1.3.2.2 { 1603640db58Sdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 1613ec4a0c1Sdrh csql1 { SELECT * FROM sqlite_master } 1623ec4a0c1Sdrh } {1 {unable to open database file}} 1633640db58Sdan do_test 1.3.2.3 { 1643640db58Sdan code1 { db close } 1653640db58Sdan close [open test.db-shm w] 1663640db58Sdan file attributes test.db-shm -permissions r--r--r-- 1673640db58Sdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 1683640db58Sdan csql1 { SELECT * FROM t1 } 16911caf4f4Sdan } {0 {a b c d e f g h i j k l}} 1703640db58Sdan do_test 1.3.2.4 { 1713640db58Sdan code1 { sqlite3_extended_errcode db } 17211caf4f4Sdan } {SQLITE_OK} 1735373b76bSdan 1745373b76bSdan #----------------------------------------------------------------------- 1755373b76bSdan # Test cases 1.4.* check that checkpoints and log wraps don't prevent 1765373b76bSdan # read-only connections from reading the database. 1775373b76bSdan do_test 1.4.1 { 1785373b76bSdan code1 { db close } 1795373b76bSdan forcedelete test.db-shm 1805373b76bSdan file exists test.db-shm 1815373b76bSdan } {0} 1825373b76bSdan 1835373b76bSdan # Open one read-only and one read-write connection. Write some data 1845373b76bSdan # and then run a checkpoint using the read-write connection. Then 1855373b76bSdan # check the read-only connection can still read. 1865373b76bSdan do_test 1.4.2 { 1875373b76bSdan code1 { sqlite3 db file:test.db?readonly_shm=1 } 1885373b76bSdan code2 { sqlite3 db2 test.db } 1895373b76bSdan csql2 { 1905373b76bSdan INSERT INTO t1 VALUES(1, 2); 1915373b76bSdan INSERT INTO t1 VALUES(3, 4); 1925373b76bSdan INSERT INTO t1 VALUES(5, 6); 1935373b76bSdan PRAGMA wal_checkpoint; 1945373b76bSdan } 1955373b76bSdan } {0 {0 3 3}} 1965373b76bSdan do_test 1.4.3 { 1975373b76bSdan csql1 { SELECT * FROM t1 } 1985373b76bSdan } {0 {a b c d e f g h i j k l 1 2 3 4 5 6}} 1995373b76bSdan 2005373b76bSdan # Using the read-write connection, open a transaction and write lots 2015373b76bSdan # of data - causing a cache spill and a log wrap. Then check that the 2025373b76bSdan # read-only connection can still read the database. 2035373b76bSdan do_test 1.4.4.1 { 2045373b76bSdan csql2 { 2055373b76bSdan PRAGMA cache_size = 10; 2065373b76bSdan BEGIN; 2075373b76bSdan CREATE TABLE t2(x, y); 2085373b76bSdan INSERT INTO t2 VALUES('abc', 'xyz'); 2095373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2105373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2115373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2125373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2135373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2145373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2155373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2165373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2175373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2185373b76bSdan } 2195373b76bSdan file size test.db-wal 2207da56b4fSdrh } [expr {[nonzero_reserved_bytes]?148848:147800}] 2215373b76bSdan do_test 1.4.4.2 { 2225373b76bSdan csql1 { SELECT * FROM t1 } 2235373b76bSdan } {0 {a b c d e f g h i j k l 1 2 3 4 5 6}} 2245373b76bSdan do_test 1.4.4.3 { 2255373b76bSdan csql2 COMMIT 2265373b76bSdan csql1 { SELECT count(*) FROM t2 } 2275373b76bSdan } {0 512} 2285373b76bSdan do_test 1.4.5 { 2295373b76bSdan code2 { db2 close } 2305373b76bSdan code1 { db close } 2315373b76bSdan } {} 2325373b76bSdan} 2335373b76bSdan 2345373b76bSdanforcedelete test.db 2355373b76bSdan 2365373b76bSdan#----------------------------------------------------------------------- 2375373b76bSdan# Test cases 2.* check that a read-only connection may read the 2385373b76bSdan# database file while a checkpoint operation is ongoing. 2395373b76bSdan# 2405373b76bSdando_multiclient_test tn { 2415373b76bSdan 2425373b76bSdan # Close all connections and delete the database. 2435373b76bSdan # 2445373b76bSdan code1 { db close } 2455373b76bSdan code2 { db2 close } 2465373b76bSdan code3 { db3 close } 2475373b76bSdan forcedelete test.db 2485373b76bSdan forcedelete walro 2495373b76bSdan 25098c91905Sdan # Do not run tests with the connections in the same process. 25198c91905Sdan # 25298c91905Sdan if {$tn==2} continue 25398c91905Sdan 2545373b76bSdan foreach c {code1 code2 code3} { 2555373b76bSdan $c { 2565373b76bSdan sqlite3_shutdown 2575373b76bSdan sqlite3_config_uri 1 2585373b76bSdan } 2595373b76bSdan } 2605373b76bSdan 2615373b76bSdan proc tv_hook {x file args} { 2625373b76bSdan if {[file tail $file]=="test.db-wal"} { 2635373b76bSdan do_test 2.1.2 { 2645373b76bSdan code2 { sqlite3 db2 file:test.db?readonly_shm=1 } 2655373b76bSdan csql2 { SELECT count(*) FROM t2 } 2665373b76bSdan } {0 4} 2675373b76bSdan do_test 2.1.3 { 2685373b76bSdan code2 { db2 close } 2695373b76bSdan } {} 2705373b76bSdan } 2715373b76bSdan } 2725373b76bSdan 2735373b76bSdan do_test 2.1.1 { 2745373b76bSdan testvfs tv -default 1 -fullshm 1 2755373b76bSdan tv script tv_hook 2765373b76bSdan tv filter {} 2775373b76bSdan code1 { sqlite3 db test.db } 2785373b76bSdan csql1 { 27981f0740eSdan PRAGMA auto_vacuum = 0; 2805373b76bSdan PRAGMA journal_mode = WAL; 2815373b76bSdan BEGIN; 2825373b76bSdan CREATE TABLE t2(x, y); 2835373b76bSdan INSERT INTO t2 VALUES('abc', 'xyz'); 2845373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2855373b76bSdan INSERT INTO t2 SELECT x||y, y||x FROM t2; 2865373b76bSdan COMMIT; 2875373b76bSdan } 2885373b76bSdan } {0 wal} 2895373b76bSdan 2905373b76bSdan tv filter xSync 2915373b76bSdan set res [csql1 { PRAGMA wal_checkpoint }] 2925373b76bSdan do_test 2.1.4 { set res } {0 {0 2 2}} 2935373b76bSdan 2945373b76bSdan do_test 2.1.5 { 2955373b76bSdan code1 { db close } 2965373b76bSdan code1 { tv delete } 2975373b76bSdan } {} 2984edc6bf3Sdan} 2994edc6bf3Sdan 3004edc6bf3Sdanfinish_test 301