000001  # 2011 May 06
000002  #
000003  # The author disclaims copyright to this source code.  In place of
000004  # a legal notice, here is a blessing:
000005  #
000006  #    May you do good and not evil.
000007  #    May you find forgiveness for yourself and forgive others.
000008  #    May you share freely, never taking more than you give.
000009  #
000010  #***********************************************************************
000011  #
000012  
000013  set testdir [file dirname $argv0]
000014  source $testdir/tester.tcl
000015  set testprefix e_uri
000016  do_not_use_codec
000017  db close
000018  
000019  proc parse_uri {uri} {
000020    testvfs tvfs2
000021    testvfs tvfs 
000022    tvfs filter xOpen
000023    tvfs script parse_uri_open_cb
000024  
000025    set ::uri_open [list]
000026    set DB [sqlite3_open_v2 $uri {
000027      SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_WAL
000028    } tvfs]
000029    set fileName [sqlite3_db_filename $DB main]
000030    sqlite3_close $DB
000031    forcedelete $fileName
000032    tvfs delete
000033    tvfs2 delete
000034  
000035    set ::uri_open
000036  }
000037  proc parse_uri_open_cb {method file arglist} {
000038    set ::uri_open [list $file $arglist]
000039  }
000040  
000041  proc open_uri_error {uri} {
000042    set flags {SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_WAL}
000043    set DB [sqlite3_open_v2 $uri $flags ""]
000044    set e [sqlite3_errmsg $DB]
000045    sqlite3_close $DB
000046    set e
000047  }
000048  
000049  # EVIDENCE-OF: R-35840-33204 If URI filename interpretation is enabled,
000050  # and the filename argument begins with "file:", then the filename is
000051  # interpreted as a URI.
000052  #
000053  # EVIDENCE-OF: R-24124-56960 URI filename interpretation is enabled if
000054  # the SQLITE_OPEN_URI flag is set in the fourth argument to
000055  # sqlite3_open_v2(), or if it has been enabled globally using the
000056  # SQLITE_CONFIG_URI option with the sqlite3_config() method or by the
000057  # SQLITE_USE_URI compile-time option.
000058  #
000059  if {$tcl_platform(platform) == "unix"} {
000060    set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE]
000061  
000062    # Tests with SQLITE_CONFIG_URI configured to false. URI intepretation is
000063    # only enabled if the SQLITE_OPEN_URI flag is specified.
000064    sqlite3_shutdown
000065    sqlite3_config_uri 0
000066    do_test 1.1 {
000067      forcedelete file:test.db test.db
000068      set DB [sqlite3_open_v2 file:test.db [concat $flags SQLITE_OPEN_URI] ""]
000069      list [file exists file:test.db] [file exists test.db]
000070    } {0 1}
000071    do_test 1.2 {
000072      forcedelete file:test.db2 test.db2
000073      set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
000074      sqlite3_step $STMT
000075      sqlite3_finalize $STMT
000076      list [file exists file:test.db2] [file exists test.db2]
000077    } {0 1}
000078    sqlite3_close $DB
000079    do_test 1.3 {
000080      forcedelete file:test.db test.db
000081      set DB [sqlite3_open_v2 file:test.db [concat $flags] ""]
000082      list [file exists file:test.db] [file exists test.db]
000083    } {1 0}
000084    do_test 1.4 {
000085      forcedelete file:test.db2 test.db2
000086      set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
000087      sqlite3_step $STMT
000088      sqlite3_finalize $STMT
000089      list [file exists file:test.db2] [file exists test.db2]
000090    } {1 0}
000091    sqlite3_close $DB
000092  
000093    # Tests with SQLITE_CONFIG_URI configured to true. URI intepretation is
000094    # enabled with or without SQLITE_OPEN_URI.
000095    #
000096    sqlite3_shutdown
000097    sqlite3_config_uri 1
000098    do_test 1.5 {
000099      forcedelete file:test.db test.db
000100      set DB [sqlite3_open_v2 file:test.db [concat $flags SQLITE_OPEN_URI] ""]
000101      list [file exists file:test.db] [file exists test.db]
000102    } {0 1}
000103    do_test 1.6 {
000104      forcedelete file:test.db2 test.db2
000105      set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
000106      sqlite3_step $STMT
000107      sqlite3_finalize $STMT
000108      list [file exists file:test.db2] [file exists test.db2]
000109    } {0 1}
000110    sqlite3_close $DB
000111    do_test 1.7 {
000112      forcedelete file:test.db test.db
000113      set DB [sqlite3_open_v2 file:test.db [concat $flags] ""]
000114      list [file exists file:test.db] [file exists test.db]
000115    } {0 1}
000116    do_test 1.8 {
000117      forcedelete file:test.db2 test.db2
000118      set STMT [sqlite3_prepare $DB "ATTACH 'file:test.db2' AS aux" -1 dummy]
000119      sqlite3_step $STMT
000120      sqlite3_finalize $STMT
000121      list [file exists file:test.db2] [file exists test.db2]
000122    } {0 1}
000123    sqlite3_close $DB
000124  }
000125  
000126  # ensure uri processing enabled for the rest of the tests
000127  sqlite3_shutdown
000128  sqlite3_config_uri 1
000129  
000130  # EVIDENCE-OF: R-06842-00595 If the URI contains an authority, then it
000131  # must be either an empty string or the string "localhost".
000132  #
000133  # EVIDENCE-OF: R-17482-00398 If the authority is not an empty string or
000134  # "localhost", an error is returned to the caller.
000135  #
000136  if {$tcl_platform(platform) == "unix"} {
000137    set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
000138    foreach {tn uri error} "
000139      1  {file://localhost[test_pwd /]test.db}   {not an error}
000140      2  {file://[test_pwd /]test.db}            {not an error}
000141      3  {file://x[test_pwd /]test.db}           {invalid uri authority: x}
000142      4  {file://invalid[test_pwd /]test.db}     {invalid uri authority: invalid}
000143    " {
000144      do_test 2.$tn {
000145        set DB [sqlite3_open_v2 $uri $flags ""]
000146        set e [sqlite3_errmsg $DB]
000147        sqlite3_close $DB
000148        set e
000149      } $error
000150    }
000151  }
000152  
000153  # EVIDENCE-OF: R-45981-25528 The fragment component of a URI, if
000154  # present, is ignored.
000155  #
000156  #   It is difficult to test that something is ignored correctly. So these tests
000157  #   just show that adding a fragment does not interfere with the pathname or
000158  #   parameters passed through to the VFS xOpen() methods.
000159  #
000160  foreach {tn uri parse} "
000161    1    {file:test.db#abc}      {[test_pwd / {}]test.db {}}
000162    2    {file:test.db?a=b#abc}  {[test_pwd / {}]test.db {a b}}
000163    3    {file:test.db?a=b#?c=d} {[test_pwd / {}]test.db {a b}}
000164  " {
000165    do_filepath_test 3.$tn { parse_uri $uri } $parse
000166  }
000167  
000168  # EVIDENCE-OF: R-62557-09390 SQLite uses the path component of the URI
000169  # as the name of the disk file which contains the database.
000170  #
000171  # EVIDENCE-OF: R-28659-11035 If the path begins with a '/' character,
000172  # then it is interpreted as an absolute path.
000173  #
000174  # EVIDENCE-OF: R-46234-61323 If the path does not begin with a '/'
000175  # (meaning that the authority section is omitted from the URI) then the
000176  # path is interpreted as a relative path.
000177  #
000178  foreach {tn uri parse} "
000179    1    {file:test.db}             {[test_pwd / {}]test.db {}}
000180    2    {file:/test.db}            {/test.db {}}
000181    3    {file:///test.db}          {/test.db {}}
000182    4    {file://localhost/test.db} {/test.db {}}
000183    5    {file:/a/b/c/test.db}      {/a/b/c/test.db {}}
000184  " {
000185    do_filepath_test 4.$tn { parse_uri $uri } $parse
000186  }
000187  
000188  # EVIDENCE-OF: R-01612-30877 The "vfs" parameter may be used to specify
000189  # the name of a VFS object that provides the operating system interface
000190  # that should be used to access the database file on disk.
000191  #
000192  #   The above is tested by cases 1.* below.
000193  #
000194  # EVIDENCE-OF: R-52293-58497 If this option is set to an empty string
000195  # the default VFS object is used.
000196  #
000197  #   The above is tested by cases 2.* below.
000198  #
000199  # EVIDENCE-OF: R-31855-18665 If sqlite3_open_v2() is used and the vfs
000200  # option is present, then the VFS specified by the option takes
000201  # precedence over the value passed as the fourth parameter to
000202  # sqlite3_open_v2().
000203  #
000204  #   The above is tested by cases 3.* below.
000205  #
000206  proc vfs_open_cb {name args} {
000207    set ::vfs $name
000208  }
000209  foreach {name default} {vfs1 0 vfs2 0 vfs3 1} {
000210    testvfs $name -default $default
000211    $name filter xOpen
000212    $name script [list vfs_open_cb $name]
000213  }
000214  foreach {tn uri defvfs vfs} {
000215    1.1    "file:test.db?vfs=vfs1"    ""    vfs1
000216    1.2    "file:test.db?vfs=vfs2"    ""    vfs2
000217  
000218    2.1    "file:test.db"             vfs1  vfs1
000219    2.2    "file:test.db?vfs="        vfs1  vfs3
000220  
000221    3.1    "file:test.db?vfs=vfs1"    vfs2  vfs1
000222    3.2    "file:test.db?vfs=vfs2"    vfs1  vfs2
000223    3.3    "file:test.db?xvfs=vfs1"   vfs2  vfs2
000224    3.4    "file:test.db?xvfs=vfs2"   vfs1  vfs1
000225  } {
000226    do_test 5.$tn {
000227      set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
000228      sqlite3_close [
000229        sqlite3_open_v2 $uri $flags $defvfs
000230      ]
000231      set ::vfs
000232    } $vfs
000233  }
000234  vfs1 delete
000235  vfs2 delete
000236  vfs3 delete
000237  
000238  # EVIDENCE-OF: R-48365-36308 Specifying an unknown VFS is an error.
000239  #
000240  set flags [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
000241  do_test 6.1 {
000242    set DB [sqlite3_open_v2 file:test.db?vfs=nosuchvfs $flags ""]
000243    set errmsg [sqlite3_errmsg $DB]
000244    sqlite3_close $DB
000245    set errmsg
000246  } {no such vfs: nosuchvfs}
000247  
000248  
000249  # EVIDENCE-OF: R-44013-13102 The mode parameter may be set to either
000250  # "ro", "rw", "rwc", or "memory". Attempting to set it to any other
000251  # value is an error
000252  #
000253  sqlite3 db test.db
000254  db close
000255  foreach {tn uri error} "
000256    1    {file:test.db?mode=ro}    {not an error}
000257    2    {file:test.db?mode=rw}    {not an error}
000258    3    {file:test.db?mode=rwc}   {not an error}
000259    4    {file:test.db?mode=Ro}    {no such access mode: Ro}
000260    5    {file:test.db?mode=Rw}    {no such access mode: Rw}
000261    6    {file:test.db?mode=Rwc}   {no such access mode: Rwc}
000262    7    {file:test.db?mode=memory} {not an error}
000263    8    {file:test.db?mode=MEMORY} {no such access mode: MEMORY}
000264  " {
000265    do_test 7.$tn { open_uri_error $uri } $error
000266  }
000267  
000268  
000269  # EVIDENCE-OF: R-43036-46756 If "ro" is specified, then the database is
000270  # opened for read-only access, just as if the SQLITE_OPEN_READONLY flag
000271  # had been set in the third argument to sqlite3_open_v2().
000272  #
000273  # EVIDENCE-OF: R-40137-26050 If the mode option is set to "rw", then the
000274  # database is opened for read-write (but not create) access, as if
000275  # SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had been set.
000276  #
000277  # EVIDENCE-OF: R-26845-32976 Value "rwc" is equivalent to setting both
000278  # SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE.
000279  #
000280  foreach {tn uri read write create} {
000281    1    {file:test.db?mode=ro}     1 0 0
000282    2    {file:test.db?mode=rw}     1 1 0
000283    3    {file:test.db?mode=rwc}    1 1 1
000284  } {
000285    set RES(c,0) {1 {unable to open database file}}
000286    set RES(c,1) {0 {}}
000287    set RES(w,0) {1 {attempt to write a readonly database}}
000288    set RES(w,1) {0 {}}
000289    set RES(r,0) {1 {this never happens}}
000290    set RES(r,1) {0 {a b}}
000291  
000292    # Test CREATE access:
000293    forcedelete test.db
000294    do_test 8.$tn.c { list [catch { sqlite3 db $uri } msg] $msg } $RES(c,$create)
000295    catch { db close }
000296  
000297    sqlite3 db test.db
000298    db eval { CREATE TABLE t1(a, b) ; INSERT INTO t1 VALUES('a', 'b') ;}
000299    db close
000300    
000301    # Test READ access:
000302    do_test 8.$tn.r { 
000303      sqlite3 db $uri
000304      catchsql { SELECT * FROM t1 }
000305    } $RES(r,$read)
000306    
000307    # Test WRITE access:
000308    do_test 8.$tn.w { 
000309      sqlite3 db $uri
000310      catchsql { INSERT INTO t1 VALUES(1, 2) }
000311    } $RES(w,$write)
000312  
000313    catch {db close}
000314  }
000315  
000316  # EVIDENCE-OF: R-20590-08726 It is an error to specify a value for the
000317  # mode parameter that is less restrictive than that specified by the
000318  # flags passed in the third parameter to sqlite3_open_v2().
000319  #
000320  forcedelete test.db
000321  sqlite3 db test.db
000322  db close
000323  foreach {tn uri flags error} {
000324    1   {file:test.db?mode=ro}   ro    {not an error}
000325    2   {file:test.db?mode=ro}   rw    {not an error}
000326    3   {file:test.db?mode=ro}   rwc   {not an error}
000327  
000328    4   {file:test.db?mode=rw}   ro    {access mode not allowed: rw}
000329    5   {file:test.db?mode=rw}   rw    {not an error}
000330    6   {file:test.db?mode=rw}   rwc   {not an error}
000331  
000332    7   {file:test.db?mode=rwc}  ro    {access mode not allowed: rwc}
000333    8   {file:test.db?mode=rwc}  rw    {access mode not allowed: rwc}
000334    9   {file:test.db?mode=rwc}  rwc   {not an error}
000335  } {
000336    set f(ro)  [list SQLITE_OPEN_READONLY SQLITE_OPEN_URI]
000337    set f(rw)  [list SQLITE_OPEN_READWRITE SQLITE_OPEN_URI]
000338    set f(rwc) [list SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI]
000339  
000340    set DB [sqlite3_open_v2 $uri $f($flags) ""]
000341    set e [sqlite3_errmsg $DB]
000342    sqlite3_close $DB
000343  
000344    do_test 9.$tn { set e } $error
000345  }
000346  
000347  # EVIDENCE-OF: R-23182-54295 The cache parameter may be set to either
000348  # "shared" or "private".
000349  sqlite3 db test.db
000350  db close
000351  foreach {tn uri error} "
000352    1    {file:test.db?cache=private}    {not an error}
000353    2    {file:test.db?cache=shared}     {not an error}
000354    3    {file:test.db?cache=yes}        {no such cache mode: yes}
000355    4    {file:test.db?cache=}           {no such cache mode: }
000356  " {
000357    do_test 10.$tn { open_uri_error $uri } $error
000358  }
000359  
000360  # EVIDENCE-OF: R-23027-03515 Setting it to "shared" is equivalent to
000361  # setting the SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed
000362  # to sqlite3_open_v2().
000363  #
000364  # EVIDENCE-OF: R-49793-28525 Setting the cache parameter to "private" is
000365  # equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
000366  #
000367  # EVIDENCE-OF: R-31773-41793 If sqlite3_open_v2() is used and the
000368  # "cache" parameter is present in a URI filename, its value overrides
000369  # any behavior requested by setting SQLITE_OPEN_PRIVATECACHE or
000370  # SQLITE_OPEN_SHAREDCACHE flag.
000371  #
000372  set orig [sqlite3_enable_shared_cache]
000373  foreach {tn uri flags shared_default isshared} {
000374    1.1   "file:test.db"                  ""         0    0
000375    1.2   "file:test.db"                  ""         1    1
000376    1.3   "file:test.db"                  private    0    0
000377    1.4   "file:test.db"                  private    1    0
000378    1.5   "file:test.db"                  shared     0    1
000379    1.6   "file:test.db"                  shared     1    1
000380  
000381    2.1   "file:test.db?cache=private"    ""         0    0
000382    2.2   "file:test.db?cache=private"    ""         1    0
000383    2.3   "file:test.db?cache=private"    private    0    0
000384    2.4   "file:test.db?cache=private"    private    1    0
000385    2.5   "file:test.db?cache=private"    shared     0    0
000386    2.6   "file:test.db?cache=private"    shared     1    0
000387  
000388    3.1   "file:test.db?cache=shared"     ""         0    1
000389    3.2   "file:test.db?cache=shared"     ""         1    1
000390    3.3   "file:test.db?cache=shared"     private    0    1
000391    3.4   "file:test.db?cache=shared"     private    1    1
000392    3.5   "file:test.db?cache=shared"     shared     0    1
000393    3.6   "file:test.db?cache=shared"     shared     1    1
000394  } {
000395    forcedelete test.db
000396    sqlite3_enable_shared_cache 1
000397    sqlite3 db test.db
000398    sqlite3_enable_shared_cache 0
000399  
000400    db eval {
000401      CREATE TABLE t1(x);
000402      INSERT INTO t1 VALUES('ok');
000403    }
000404  
000405    unset -nocomplain f
000406    set f()        {SQLITE_OPEN_READWRITE SQLITE_OPEN_CREATE SQLITE_OPEN_URI}
000407    set f(shared)  [concat $f() SQLITE_OPEN_SHAREDCACHE]
000408    set f(private) [concat $f() SQLITE_OPEN_PRIVATECACHE]
000409  
000410    sqlite3_enable_shared_cache $shared_default
000411    set DB [sqlite3_open_v2 $uri $f($flags) ""]
000412  
000413    set STMT [sqlite3_prepare $DB "SELECT * FROM t1" -1 dummy]
000414  
000415    db eval {
000416      BEGIN;
000417        INSERT INTO t1 VALUES('ko');
000418    }
000419  
000420    sqlite3_step $STMT
000421    sqlite3_finalize $STMT
000422  
000423    set RES(0) {not an error}
000424    set RES(1) {database table is locked: t1}
000425  
000426    do_test 11.$tn { sqlite3_errmsg $DB } $RES($isshared)
000427  
000428    sqlite3_close $DB
000429    db close
000430  }
000431  sqlite3_enable_shared_cache $orig
000432  
000433  # EVIDENCE-OF: R-63472-46769 Specifying an unknown parameter in the
000434  # query component of a URI is not an error.
000435  #
000436  do_filepath_test 12.1 {
000437    parse_uri file://localhost/test.db?an=unknown&parameter=is&ok=
000438  } {/test.db {an unknown parameter is ok {}}}
000439  do_filepath_test 12.2 {
000440    parse_uri file://localhost/test.db?an&unknown&parameter&is&ok
000441  } {/test.db {an {} unknown {} parameter {} is {} ok {}}}
000442  
000443  # EVIDENCE-OF: R-27458-04043 URI hexadecimal escape sequences (%HH) are
000444  # supported within the path and query components of a URI.
000445  #
000446  # EVIDENCE-OF: R-52765-50368 Before the path or query components of a
000447  # URI filename are interpreted, they are encoded using UTF-8 and all
000448  # hexadecimal escape sequences replaced by a single byte containing the
000449  # corresponding octet.
000450  #
000451  #   The second of the two statements above is tested by creating a
000452  #   multi-byte utf-8 character using a sequence of %HH escapes.
000453  #
000454  foreach {tn uri parse} "
000455    1  {file:/test.%64%62}                             {/test.db {}}
000456    2  {file:/test.db?%68%65%6c%6c%6f=%77%6f%72%6c%64} {/test.db {hello world}}
000457    3  {file:/%C3%BF.db}                               {/\xFF.db {}}
000458  " {
000459    do_filepath_test 13.$tn { parse_uri $uri } $parse
000460  }
000461  
000462  finish_test