1cd74b611Sdan# 2011 April 22 2cd74b611Sdan# 3cd74b611Sdan# The author disclaims copyright to this source code. In place of 4cd74b611Sdan# a legal notice, here is a blessing: 5cd74b611Sdan# 6cd74b611Sdan# May you do good and not evil. 7cd74b611Sdan# May you find forgiveness for yourself and forgive others. 8cd74b611Sdan# May you share freely, never taking more than you give. 9cd74b611Sdan# 10cd74b611Sdan#*********************************************************************** 11cd74b611Sdan# 12cd74b611Sdan 13cd74b611Sdanset testdir [file dirname $argv0] 14cd74b611Sdansource $testdir/tester.tcl 15cd74b611Sdan 168e98037cSdan# Test organization: 178e98037cSdan# 188e98037cSdan# 1.*: That file names are correctly extracted from URIs. 198e98037cSdan# 2.*: That URI options (query parameters) are correctly extracted from URIs. 208e98037cSdan# 3.*: That specifying an unknown VFS causes an error. 218e98037cSdan# 4.*: Tests for specifying other options (other than "vfs"). 228e98037cSdan# 5.*: Test using a different VFS with an attached database. 23286ab7c2Sdan# 6.*: Test that authorities other than "" and localhost cause errors. 2419432996Sdan# 7.*: Test that a read-write db can be attached to a read-only connection. 258e98037cSdan# 268e98037cSdan 27cd74b611Sdanset testprefix uri 28cd74b611Sdandb close 29cd74b611Sdansqlite3_shutdown 30cd74b611Sdansqlite3_config_uri 1 31cd74b611Sdan 32cd74b611Sdan#------------------------------------------------------------------------- 33cd74b611Sdan# Test that file names are correctly extracted from URIs. 34cd74b611Sdan# 35cd74b611Sdanforeach {tn uri file} { 36cd74b611Sdan 1 test.db test.db 37cd74b611Sdan 2 file:test.db test.db 383b18a9a3Sdan 3 file://PWD/test.db test.db 39cd74b611Sdan 4 file:PWD/test.db test.db 40cd74b611Sdan 5 file:test.db?mork=1 test.db 41cd74b611Sdan 6 file:test.db?mork=1&tonglor=2 test.db 42cd74b611Sdan 7 file:test.db?mork=1#boris test.db 43cd74b611Sdan 8 file:test.db#boris test.db 44cd74b611Sdan 9 test.db#boris test.db#boris 454d7a4461Sdan 10 file:test%2Edb test.db 464d7a4461Sdan 11 file file 474d7a4461Sdan 12 http:test.db http:test.db 484d7a4461Sdan 13 file:test.db%00extra test.db 49812d6088Sdrh 14 file:testdb%00.db%00extra testdb 504d7a4461Sdan 514d7a4461Sdan 15 test.db?mork=1#boris test.db?mork=1#boris 524d7a4461Sdan 16 file://localhostPWD/test.db%3Fhello test.db?hello 53cd74b611Sdan} { 544d7a4461Sdan 554a41f345Smistachkin 564a41f345Smistachkin ifcapable !curdir { if {$tn==3} break } 574a41f345Smistachkin 585c35e903Sdan ifcapable uri_00_error { 595c35e903Sdan if {[string first %00 $uri]>=0} continue 605c35e903Sdan } 615c35e903Sdan 624d7a4461Sdan if {$tcl_platform(platform)=="windows"} { 63776f5c96Smistachkin # 64776f5c96Smistachkin # NOTE: Due to limits on legal characters for file names imposed by 65776f5c96Smistachkin # Windows, we must skip the final two tests here (i.e. the 66776f5c96Smistachkin # question mark is illegal in a file name on Windows). 67776f5c96Smistachkin # 684d7a4461Sdan if {$tn>14} break 69cd74b611Sdan 70776f5c96Smistachkin # 716bd7456eSmistachkin # NOTE: When running on Tcl 8.6 (or higher?) on Windows, a colon within 726bd7456eSmistachkin # the file name no longer tries to access an alternate data stream 736bd7456eSmistachkin # (ADS) named "test.db" for the "http" file, causing some spurious 746bd7456eSmistachkin # failures of this test. 756bd7456eSmistachkin # 766418ffabSmistachkin if {$tn==12 && $::tcl_version>=8.6} continue 776bd7456eSmistachkin 786bd7456eSmistachkin # 79776f5c96Smistachkin # NOTE: On Windows, we need to account for the fact that the current 80776f5c96Smistachkin # directory does not start with a forward slash. 81776f5c96Smistachkin # 82776f5c96Smistachkin set uri [string map [list PWD/ /[test_pwd /]] $uri] 83cd74b611Sdan } else { 844a41f345Smistachkin set uri [string map [list PWD/ [test_pwd /]] $uri] 85cd74b611Sdan } 864a41f345Smistachkin 87812d6088Sdrh if {[file isdir $file]} {error "$file is a directory"} 88cd74b611Sdan forcedelete $file 89cd74b611Sdan do_test 1.$tn.1 { file exists $file } 0 90cd74b611Sdan set DB [sqlite3_open $uri] 91cd74b611Sdan do_test 1.$tn.2 { file exists $file } 1 92cd74b611Sdan sqlite3_close $DB 933a6d8aecSdan forcedelete $file 943a6d8aecSdan 953a6d8aecSdan do_test 1.$tn.3 { file exists $file } 0 963a6d8aecSdan sqlite3 db xxx.db 974d7a4461Sdan catchsql { ATTACH $uri AS aux } 983a6d8aecSdan do_test 1.$tn.4 { file exists $file } 1 993a6d8aecSdan db close 100cd74b611Sdan} 101cd74b611Sdan 102cd74b611Sdan#------------------------------------------------------------------------- 103cd74b611Sdan# Test that URI query parameters are passed through to the VFS layer 104cd74b611Sdan# correctly. 105cd74b611Sdan# 1064d7a4461Sdantestvfs tvfs2 107cd74b611Sdantestvfs tvfs -default 1 108cd74b611Sdantvfs filter xOpen 109cd74b611Sdantvfs script open_method 110cd74b611Sdanproc open_method {method file arglist} { 111cd74b611Sdan set ::arglist $arglist 112cd74b611Sdan} 113cd74b611Sdanforeach {tn uri kvlist} { 114cd74b611Sdan 1 file:test.db?hello=world {hello world} 115cd74b611Sdan 2 file:test.db?hello&world {hello {} world {}} 116cd74b611Sdan 3 file:test.db?hello=1&world=2&vfs=tvfs {hello 1 world 2 vfs tvfs} 1174d7a4461Sdan 4 file:test.db?hello=1&world=2&vfs=tvfs2 {} 1185de15374Sdan 5 file:test.db?%68%65%6C%6C%6F=%77%6F%72%6C%64 {hello world} 119812d6088Sdrh 6 file:testdb%00.db?hello%00extra=world%00ex {hello world} 120812d6088Sdrh 7 file:testdb%00.db?hello%00=world%00 {hello world} 121812d6088Sdrh 8 file:testdb%00.db?=world&xyz=abc {xyz abc} 1225de15374Sdan 9 file:test.db?%00hello=world&xyz=abc {xyz abc} 1235de15374Sdan 10 file:test.db?hello=%00world&xyz= {hello {} xyz {}} 1245de15374Sdan 11 file:test.db?=#ravada {} 1255de15374Sdan 12 file:test.db?&&&&&&&&hello=world&&&&&&& {hello world} 1264d7a4461Sdan 1273a6d8aecSdan 13 test.db?&&&&&&&&hello=world&&&&&&& {} 1283a6d8aecSdan 14 http:test.db?hello&world {} 129cd74b611Sdan} { 1304d7a4461Sdan 1315c35e903Sdan ifcapable uri_00_error { 1325c35e903Sdan if {[string first %00 $uri]>=0} continue 1335c35e903Sdan } 1345c35e903Sdan 1354d7a4461Sdan if {$tcl_platform(platform) == "windows" && $tn>12} { 1364d7a4461Sdan continue 1374d7a4461Sdan } 1384d7a4461Sdan 139cd74b611Sdan set ::arglist "" 140cd74b611Sdan set DB [sqlite3_open $uri] 1413a6d8aecSdan do_test 2.$tn.1 { set ::arglist } $kvlist 142cd74b611Sdan sqlite3_close $DB 1433a6d8aecSdan 1443a6d8aecSdan sqlite3 db xxx.db 1453a6d8aecSdan set ::arglist "" 1463a6d8aecSdan execsql { ATTACH $uri AS aux } 1473a6d8aecSdan do_test 2.$tn.2 { set ::arglist } $kvlist 1483a6d8aecSdan db close 149cd74b611Sdan} 1505de15374Sdantvfs delete 1514d7a4461Sdantvfs2 delete 1525de15374Sdan 1535de15374Sdan#------------------------------------------------------------------------- 1545de15374Sdan# Test that specifying a non-existent VFS raises an error. 1555de15374Sdan# 1565de15374Sdando_test 3.1 { 1575de15374Sdan list [catch { sqlite3 db "file:test.db?vfs=nosuchvfs" } msg] $msg 1585de15374Sdan} {1 {no such vfs: nosuchvfs}} 159cd74b611Sdan 1603a6d8aecSdan#------------------------------------------------------------------------- 1618e98037cSdan# Test some of the other options (other than "vfs"). 1628e98037cSdan# 16378e9dd2bSdanforeach {tn mode create_ok write_ok readonly_ok} { 16478e9dd2bSdan 1 ro 0 0 1 16578e9dd2bSdan 2 rw 0 1 0 16678e9dd2bSdan 3 rwc 1 1 0 16778e9dd2bSdan} { 16878e9dd2bSdan catch { db close } 16978e9dd2bSdan forcedelete test.db 17078e9dd2bSdan 17178e9dd2bSdan set A(1) {0 {}} 17278e9dd2bSdan set A(0) {1 {unable to open database file}} 17378e9dd2bSdan do_test 4.1.$tn.1 { 17478e9dd2bSdan list [catch {sqlite3 db "file:test.db?mode=$mode"} msg] $msg 17578e9dd2bSdan } $A($create_ok) 17678e9dd2bSdan 17778e9dd2bSdan catch { db close } 17878e9dd2bSdan forcedelete test.db 1793a6d8aecSdan sqlite3 db test.db 1803a6d8aecSdan db eval { CREATE TABLE t1(a, b) } 1813a6d8aecSdan db close 1823a6d8aecSdan 18378e9dd2bSdan set A(1) {0 {}} 18478e9dd2bSdan set A(0) {1 {attempt to write a readonly database}} 18578e9dd2bSdan do_test 4.1.$tn.2 { 18678e9dd2bSdan sqlite3 db "file:test.db?mode=$mode" 1873a6d8aecSdan catchsql { INSERT INTO t1 VALUES(1, 2) } 18878e9dd2bSdan } $A($write_ok) 18978e9dd2bSdan 19078e9dd2bSdan set A(1) {0 {}} 191eaadd59aSdan set A(0) [list 1 "access mode not allowed: $mode"] 19278e9dd2bSdan do_test 4.1.$tn.3 { 19378e9dd2bSdan list [catch {sqlite3 db "file:test.db?mode=$mode" -readonly 1} msg] $msg 19478e9dd2bSdan } $A($readonly_ok) 1953a6d8aecSdan} 1963a6d8aecSdan 197*9c5e1e40Sdrhifcapable shared_cache { 19878e9dd2bSdanset orig [sqlite3_enable_shared_cache] 19978e9dd2bSdanforeach {tn options sc_default is_shared} { 20078e9dd2bSdan 1 "" 1 1 20178e9dd2bSdan 2 "cache=private" 1 0 20278e9dd2bSdan 3 "cache=shared" 1 1 20378e9dd2bSdan 4 "" 0 0 20478e9dd2bSdan 5 "cache=private" 0 0 20578e9dd2bSdan 6 "cache=shared" 0 1 20678e9dd2bSdan} { 20778e9dd2bSdan catch { db close } 20878e9dd2bSdan forcedelete test.db 20978e9dd2bSdan 21078e9dd2bSdan sqlite3_enable_shared_cache 1 21178e9dd2bSdan sqlite3 db2 test.db 21278e9dd2bSdan db2 eval {CREATE TABLE t1(a, b)} 21378e9dd2bSdan 21478e9dd2bSdan sqlite3_enable_shared_cache $sc_default 21578e9dd2bSdan sqlite3 db "file:test.db?$options" 21678e9dd2bSdan db eval {SELECT * FROM t1} 21778e9dd2bSdan 21878e9dd2bSdan set A(1) {1 {database table is locked: t1}} 21978e9dd2bSdan set A(0) {0 {}} 22078e9dd2bSdan do_test 4.2.$tn { 22178e9dd2bSdan db2 eval {BEGIN; INSERT INTO t1 VALUES(1, 2);} 22278e9dd2bSdan catchsql { SELECT * FROM t1 } 22378e9dd2bSdan } $A($is_shared) 22478e9dd2bSdan 22578e9dd2bSdan db2 close 22678e9dd2bSdan} 227*9c5e1e40Sdrh} ;# end ifcapable shared_cache 22878e9dd2bSdan 22978e9dd2bSdando_test 4.3.1 { 23078e9dd2bSdan list [catch {sqlite3 db "file:test.db?mode=rc"} msg] $msg 23178e9dd2bSdan} {1 {no such access mode: rc}} 23278e9dd2bSdando_test 4.3.2 { 23378e9dd2bSdan list [catch {sqlite3 db "file:test.db?cache=public"} msg] $msg 23478e9dd2bSdan} {1 {no such cache mode: public}} 23578e9dd2bSdan 2368e98037cSdan#------------------------------------------------------------------------- 2378e98037cSdan# Test that things work if an ATTACHed database uses a different VFS than 2388e98037cSdan# the main database. The important point is that for all operations 2398e98037cSdan# involving the ATTACHed database, the correct versions of the following 2408e98037cSdan# VFS are used for all operations involving the attached database. 2418e98037cSdan# 2428e98037cSdan# xOpen 2438e98037cSdan# xDelete 2448e98037cSdan# xAccess 2458e98037cSdan# xFullPathname 2468e98037cSdan# 2478e98037cSdan 2488e98037cSdan# This block of code creates two VFS - "tvfs1" and "tvfs2". Each time one 2498e98037cSdan# of the above methods is called using "tvfs1", global variable ::T1(X) is 2508e98037cSdan# set, where X is the file-name the method is called on. Calls to the above 2518e98037cSdan# methods using "tvfs2" set entries in the global T2 array. 2528e98037cSdan# 2535209132aSdanifcapable wal { 2548e98037cSdan testvfs tvfs1 2558e98037cSdan tvfs1 filter {xOpen xDelete xAccess xFullPathname} 2568e98037cSdan tvfs1 script tvfs1_callback 2578e98037cSdan proc tvfs1_callback {method filename args} { 2588e98037cSdan set ::T1([file tail $filename]) 1 25988794818Smistachkin return SQLITE_OK 2608e98037cSdan } 2618e98037cSdan testvfs tvfs2 2628e98037cSdan tvfs2 filter {xOpen xDelete xAccess xFullPathname} 2638e98037cSdan tvfs2 script tvfs2_callback 2648e98037cSdan proc tvfs2_callback {method filename args} { 2658e98037cSdan set ::T2([file tail $filename]) 1 26688794818Smistachkin return SQLITE_OK 2678e98037cSdan } 2688e98037cSdan 2694d7a4461Sdan catch {db close} 2708e98037cSdan eval forcedelete [glob test.db*] 2718e98037cSdan do_test 5.1.1 { 2728e98037cSdan sqlite3 db file:test.db1?vfs=tvfs1 2738e98037cSdan execsql { 2748e98037cSdan ATTACH 'file:test.db2?vfs=tvfs2' AS aux; 2758e98037cSdan PRAGMA main.journal_mode = PERSIST; 2768e98037cSdan PRAGMA aux.journal_mode = PERSIST; 2778e98037cSdan CREATE TABLE t1(a, b); 2788e98037cSdan CREATE TABLE aux.t2(a, b); 2798e98037cSdan PRAGMA main.journal_mode = WAL; 28078e9dd2bSdan PRAGMA aux.journal_mode = WAL; 2818e98037cSdan INSERT INTO t1 VALUES('x', 'y'); 2828e98037cSdan INSERT INTO t2 VALUES('x', 'y'); 2838e98037cSdan } 2848e98037cSdan lsort [array names ::T1] 2858e98037cSdan } {test.db1 test.db1-journal test.db1-wal} 2868e98037cSdan 2878e98037cSdan do_test 5.1.2 { 2888e98037cSdan lsort [array names ::T2] 2898e98037cSdan } {test.db2 test.db2-journal test.db2-wal} 2908e98037cSdan db close 2915209132aSdan 2928e98037cSdan tvfs1 delete 2938e98037cSdan tvfs2 delete 2945209132aSdan} 2958e98037cSdan 2963b18a9a3Sdan#------------------------------------------------------------------------- 2973b18a9a3Sdan# Check that only "" and "localhost" are acceptable as authorities. 2983b18a9a3Sdan# 2993b18a9a3Sdancatch {db close} 3003b18a9a3Sdanforeach {tn uri res} { 3013b18a9a3Sdan 1 "file://localhost/PWD/test.db" {not an error} 3023b18a9a3Sdan 2 "file:///PWD/test.db" {not an error} 3033b18a9a3Sdan 3 "file:/PWD/test.db" {not an error} 3043b18a9a3Sdan 4 "file://l%6Fcalhost/PWD/test.db" {invalid uri authority: l%6Fcalhost} 3053b18a9a3Sdan 5 "file://lbcalhost/PWD/test.db" {invalid uri authority: lbcalhost} 3063b18a9a3Sdan 6 "file://x/PWD/test.db" {invalid uri authority: x} 3073b18a9a3Sdan} { 3084d7a4461Sdan 3094d7a4461Sdan if {$tcl_platform(platform)=="windows"} { 310f8a78464Smistachkin set uri [string map [list PWD [string range [get_pwd] 3 end]] $uri] 3114d7a4461Sdan } else { 312f8a78464Smistachkin set uri [string map [list PWD [string range [get_pwd] 1 end]] $uri] 3134d7a4461Sdan } 3144d7a4461Sdan 3153b18a9a3Sdan do_test 6.$tn { 3163b18a9a3Sdan set DB [sqlite3_open $uri] 3173b18a9a3Sdan sqlite3_errmsg $DB 3183b18a9a3Sdan } $res 3193b18a9a3Sdan catch { sqlite3_close $DB } 3203b18a9a3Sdan} 3213b18a9a3Sdan 32219432996Sdanforcedelete test.db test.db2 32319432996Sdando_test 7.1 { 32419432996Sdan sqlite3 db test.db 32519432996Sdan execsql { 32619432996Sdan CREATE TABLE t1(a, b); 32719432996Sdan INSERT INTO t1 VALUES(1, 2); 32819432996Sdan ATTACH 'test.db2' AS aux; 32919432996Sdan CREATE TABLE aux.t2(a, b); 33019432996Sdan INSERT INTO t1 VALUES('a', 'b'); 33119432996Sdan } 33219432996Sdan db close 33319432996Sdan} {} 33419432996Sdando_test 7.2 { 33519432996Sdan sqlite3 db file:test.db?mode=ro 33619432996Sdan execsql { ATTACH 'file:test.db2?mode=rw' AS aux } 33719432996Sdan} {} 33819432996Sdando_execsql_test 7.3 { 33919432996Sdan INSERT INTO t2 VALUES('c', 'd') 34019432996Sdan} {} 34119432996Sdando_catchsql_test 7.4 { 34219432996Sdan INSERT INTO t1 VALUES(3, 4) 34319432996Sdan} {1 {attempt to write a readonly database}} 344cd74b611Sdan 34519432996Sdanfinish_test 346