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