SQLite

Check-in [44f0874a95]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix parsing of %00 in uri handling code.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | uri
Files: files | file ages | folders
SHA1: 44f0874a95408c75a296964a04eef00341abb94a
User & Date: dan 2011-04-23 10:12:30.605
Context
2011-04-23
15:54
Have the ATTACH command do URI interpretation in the same way as sqlite3_open() and sqlite3_open_v2() do. (check-in: 68240e75e8 user: dan tags: uri)
10:12
Fix parsing of %00 in uri handling code. (check-in: 44f0874a95 user: dan tags: uri)
2011-04-22
19:37
Add the start of the "uri-filenames" feature. (check-in: b8a8132e71 user: dan tags: uri)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/main.c.
1799
1800
1801
1802
1803
1804
1805












1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
  const char *zDefaultVfs,        /* VFS to use if no "vfs=xxx" query option */
  const char *zUri,               /* Nul-terminated URI to parse */
  int *pFlags,                    /* IN/OUT: SQLITE_OPEN_XXX flags */
  sqlite3_vfs **ppVfs,            /* OUT: VFS to use */ 
  char **pzFile,                  /* OUT: Filename component of URI */
  char **pzErrMsg                 /* OUT: Error message (if rc!=SQLITE_OK) */
){












  int flags = *pFlags;
  const char *zVfs = zDefaultVfs;
  char *zFile;
  int nUri = sqlite3Strlen30(zUri);

  assert( *pzErrMsg==0 );

  if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri) 
   && nUri>=5 && memcmp(zUri, "file:", 5)==0 
  ){
    char *zOpt = 0;
    int eState;                   /* Parser state when parsing URI */
    int iIn;                      /* Input character index */
    int iOut = 0;                 /* Output character index */
    int nByte = nUri+2;           /* Bytes of space to allocate */
    for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');

    zFile = sqlite3_malloc(nByte);







>
>
>
>
>
>
>
>
>
>
>
>










|







1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
  const char *zDefaultVfs,        /* VFS to use if no "vfs=xxx" query option */
  const char *zUri,               /* Nul-terminated URI to parse */
  int *pFlags,                    /* IN/OUT: SQLITE_OPEN_XXX flags */
  sqlite3_vfs **ppVfs,            /* OUT: VFS to use */ 
  char **pzFile,                  /* OUT: Filename component of URI */
  char **pzErrMsg                 /* OUT: Error message (if rc!=SQLITE_OK) */
){
  struct UriOption {
    const char *zOption;
    int mask;
  } aOpt [] = {
    { "vfs", 0 },
    { "readonly",     SQLITE_OPEN_READONLY },
    { "readwrite",    SQLITE_OPEN_READWRITE },
    { "create",       SQLITE_OPEN_CREATE },
    { "sharedcache",  SQLITE_OPEN_SHAREDCACHE },
    { "privatecache", SQLITE_OPEN_PRIVATECACHE }
  };

  int flags = *pFlags;
  const char *zVfs = zDefaultVfs;
  char *zFile;
  int nUri = sqlite3Strlen30(zUri);

  assert( *pzErrMsg==0 );

  if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri) 
   && nUri>=5 && memcmp(zUri, "file:", 5)==0 
  ){
    char *zOpt;
    int eState;                   /* Parser state when parsing URI */
    int iIn;                      /* Input character index */
    int iOut = 0;                 /* Output character index */
    int nByte = nUri+2;           /* Bytes of space to allocate */
    for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');

    zFile = sqlite3_malloc(nByte);
1848
1849
1850
1851
1852
1853
1854
1855













1856
1857
1858
1859



1860



1861

1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928

1929
1930
1931
1932
1933
1934
1935
       && sqlite3Isxdigit(zUri[iIn]) 
       && sqlite3Isxdigit(zUri[iIn+1]) 
      ){
        int codepoint = (sqlite3HexToInt(zUri[iIn++]) << 4);
        codepoint += sqlite3HexToInt(zUri[iIn++]);

        assert( codepoint>=0 && codepoint<256 );
        if( codepoint==0 ) continue;













        c = codepoint;
      }else if( (eState==0 && c=='?') || (eState==1 && c=='=') ){
        if( eState==0 ){
          zOpt = &zFile[iOut+1];



        }



        eState++;

        c = 0;
      }else if( eState!=0 && c=='&' ){
        if( eState==1 ) zFile[iOut++] = '\0';
        eState = 1;
        c = 0;
      }
      zFile[iOut++] = c;
    }
    if( eState==1 ) zFile[iOut++] = '\0';
    zFile[iOut++] = '\0';
    zFile[iOut++] = '\0';

    /* Check if there were any options specified that should be interpreted 
    ** here. Options that are interpreted here include "vfs" and those that
    ** correspond to flags that may be passed to the sqlite3_open_v2()
    ** method.  */
    if( zOpt ){
      struct Option {
        const char *zOption;
        int mask;
      } aOpt [] = {
        { "vfs", 0 },
        { "readonly",     SQLITE_OPEN_READONLY },
        { "readwrite",    SQLITE_OPEN_READWRITE },
        { "create",       SQLITE_OPEN_CREATE },
        { "sharedcache",  SQLITE_OPEN_SHAREDCACHE },
        { "privatecache", SQLITE_OPEN_PRIVATECACHE }
      };

      while( zOpt[0] ){
        int nOpt = sqlite3Strlen30(zOpt);
        char *zVal = &zOpt[nOpt+1];
        int nVal = sqlite3Strlen30(zVal);
        int i;

        for(i=0; i<ArraySize(aOpt); i++){
          const char *z = aOpt[i].zOption;
          if( nOpt==sqlite3Strlen30(z) && 0==memcmp(zOpt, z, nOpt) ){
            int mask = aOpt[i].mask;
            if( mask==0 ){
              zVfs = zVal;
            }else{
              if( zVal[0]=='\0' || sqlite3GetBoolean(zVal) ){
                flags |= mask;
              }else{
                flags &= ~mask;
              }
            }
          }
        }

        zOpt = &zVal[nVal+1];
      }
    }

  }else{
    zFile = sqlite3_malloc(nUri+2);
    if( !zFile ) return SQLITE_NOMEM;
    memcpy(zFile, zUri, nUri);
    zFile[nUri] = '\0';
    zFile[nUri+1] = '\0';
  }

  *ppVfs = sqlite3_vfs_find(zVfs);
  if( *ppVfs==0 ){
    sqlite3_free(zFile);
    *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs);

    return SQLITE_ERROR;
  }
  *pFlags = flags;
  *pzFile = zFile;
  return SQLITE_OK;
}








|
>
>
>
>
>
>
>
>
>
>
>
>
>

<
|
|
>
>
>

>
>
>
|
>

|
|

<











<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
<












<

>







1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881

1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896

1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907












1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931

1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943

1944
1945
1946
1947
1948
1949
1950
1951
1952
       && sqlite3Isxdigit(zUri[iIn]) 
       && sqlite3Isxdigit(zUri[iIn+1]) 
      ){
        int codepoint = (sqlite3HexToInt(zUri[iIn++]) << 4);
        codepoint += sqlite3HexToInt(zUri[iIn++]);

        assert( codepoint>=0 && codepoint<256 );
        if( codepoint==0 ){
          /* This branch is taken when "%00" appears within the URI. In this
          ** case we ignore all text in the remainder of the path, name or
          ** value currently being parsed. So ignore the current character
          ** and skip to the next "?", "=" or "&", as appropriate. */
          while( zUri[iIn] && zUri[iIn]!='#' 
              && (eState!=0 || zUri[iIn]!='?')
              && (eState!=1 || (zUri[iIn]!='=' && zUri[iIn]!='&'))
              && (eState!=2 || zUri[iIn]!='&')
          ){
            iIn++;
          }
          continue;
        }
        c = codepoint;

      }else if( eState==1 && (c=='&' || c=='=') ){
        if( zFile[iOut-1]==0 ){
          /* An empty option name. Ignore this option altogether. */
          while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++;
          continue;
        }
        if( c=='&' ){
          zFile[iOut++] = '\0';
        }else{
          eState = 2;
        }
        c = 0;
      }else if( (eState==0 && c=='?') || (eState==2 && c=='&') ){
        c = 0;
        eState = 1;

      }
      zFile[iOut++] = c;
    }
    if( eState==1 ) zFile[iOut++] = '\0';
    zFile[iOut++] = '\0';
    zFile[iOut++] = '\0';

    /* Check if there were any options specified that should be interpreted 
    ** here. Options that are interpreted here include "vfs" and those that
    ** correspond to flags that may be passed to the sqlite3_open_v2()
    ** method.  */












    zOpt = &zFile[sqlite3Strlen30(zFile)+1];
    while( zOpt[0] ){
      int nOpt = sqlite3Strlen30(zOpt);
      char *zVal = &zOpt[nOpt+1];
      int nVal = sqlite3Strlen30(zVal);
      int i;

      for(i=0; i<ArraySize(aOpt); i++){
        const char *z = aOpt[i].zOption;
        if( nOpt==sqlite3Strlen30(z) && 0==memcmp(zOpt, z, nOpt) ){
          int mask = aOpt[i].mask;
          if( mask==0 ){
            zVfs = zVal;
          }else{
            if( zVal[0]=='\0' || sqlite3GetBoolean(zVal) ){
              flags |= mask;
            }else{
              flags &= ~mask;
            }
          }
        }
      }

      zOpt = &zVal[nVal+1];

    }

  }else{
    zFile = sqlite3_malloc(nUri+2);
    if( !zFile ) return SQLITE_NOMEM;
    memcpy(zFile, zUri, nUri);
    zFile[nUri] = '\0';
    zFile[nUri+1] = '\0';
  }

  *ppVfs = sqlite3_vfs_find(zVfs);
  if( *ppVfs==0 ){

    *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs);
    sqlite3_free(zFile);
    return SQLITE_ERROR;
  }
  *pFlags = flags;
  *pzFile = zFile;
  return SQLITE_OK;
}

Changes to test/fts3atoken.test.
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181
182
183
184
  append output "1 tokens tokens "
  append output "2 then then "
  append output "3 [string tolower $longtoken] $longtoken"

  do_icu_test fts3token-4.6 MiddleOfTheOcean  $input $output
  do_icu_test fts3token-4.7 th_TH  $input $output
  do_icu_test fts3token-4.8 en_US  $input $output
}

do_execsql_test 5.1 {
  CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
  insert into x1 (name) values (NULL);
  insert into x1 (name) values (NULL);
  delete from x1;
}



do_test fts3token-internal {
  execsql { SELECT fts3_tokenizer_internal_test() }
} {ok}


finish_test







|
<
|
|
|
|
|
|
>
>







163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  append output "1 tokens tokens "
  append output "2 then then "
  append output "3 [string tolower $longtoken] $longtoken"

  do_icu_test fts3token-4.6 MiddleOfTheOcean  $input $output
  do_icu_test fts3token-4.7 th_TH  $input $output
  do_icu_test fts3token-4.8 en_US  $input $output


  do_execsql_test 5.1 {
    CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
    insert into x1 (name) values (NULL);
    insert into x1 (name) values (NULL);
    delete from x1;
  }
}


do_test fts3token-internal {
  execsql { SELECT fts3_tokenizer_internal_test() }
} {ok}


finish_test
Changes to test/pager1.test.
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
#-------------------------------------------------------------------------
# Check that it is not possible to open a database file if the full path
# to the associated journal file will be longer than sqlite3_vfs.mxPathname.
#
testvfs tv -default 1
tv script xOpenCb
tv filter xOpen
proc xOpenCb {method filename} {
  set ::file_len [string length $filename]
}
sqlite3 db test.db
db close
tv delete

for {set ii [expr $::file_len-5]} {$ii < [expr $::file_len+20]} {incr ii} {







|







1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
#-------------------------------------------------------------------------
# Check that it is not possible to open a database file if the full path
# to the associated journal file will be longer than sqlite3_vfs.mxPathname.
#
testvfs tv -default 1
tv script xOpenCb
tv filter xOpen
proc xOpenCb {method filename args} {
  set ::file_len [string length $filename]
}
sqlite3 db test.db
db close
tv delete

for {set ii [expr $::file_len-5]} {$ii < [expr $::file_len+20]} {incr ii} {
Changes to test/uri.test.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38



39
40
41
42
43
44
45
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

set testprefix uri
db close

sqlite3_shutdown
sqlite3_config_uri 1

#-------------------------------------------------------------------------
# Test that file names are correctly extracted from URIs.
#
foreach {tn uri file} {
  1      test.db                            test.db
  2      file:test.db                       test.db
  3      file://an-authorityPWD/test.db     test.db
  4      file:PWD/test.db                   test.db
  5      file:test.db?mork=1                test.db
  6      file:test.db?mork=1&tonglor=2      test.db
  7      file:test.db?mork=1#boris          test.db
  8      file:test.db#boris                 test.db
  9      test.db#boris                      test.db#boris
  10     test.db?mork=1#boris               test.db?mork=1#boris
  11     file:test%2Edb                     test.db
  12     file                               file
  13     http:test.db                       http:test.db



} {
  set uri  [string map [list PWD [pwd]] $uri]
  set file [string map [list PWD [pwd]] $file]

  forcedelete $file
  do_test 1.$tn.1 { file exists $file } 0
  set DB [sqlite3_open $uri]







<




















>
>
>







11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

set testprefix uri
db close

sqlite3_shutdown
sqlite3_config_uri 1

#-------------------------------------------------------------------------
# Test that file names are correctly extracted from URIs.
#
foreach {tn uri file} {
  1      test.db                            test.db
  2      file:test.db                       test.db
  3      file://an-authorityPWD/test.db     test.db
  4      file:PWD/test.db                   test.db
  5      file:test.db?mork=1                test.db
  6      file:test.db?mork=1&tonglor=2      test.db
  7      file:test.db?mork=1#boris          test.db
  8      file:test.db#boris                 test.db
  9      test.db#boris                      test.db#boris
  10     test.db?mork=1#boris               test.db?mork=1#boris
  11     file:test%2Edb                     test.db
  12     file                               file
  13     http:test.db                       http:test.db
  14     file://xyzPWD/test.db%3Fhello      test.db?hello
  15     file:test.db%00extra               test.db
  16     file:test%00.db%00extra            test
} {
  set uri  [string map [list PWD [pwd]] $uri]
  set file [string map [list PWD [pwd]] $file]

  forcedelete $file
  do_test 1.$tn.1 { file exists $file } 0
  set DB [sqlite3_open $uri]
54
55
56
57
58
59
60
61
62
63
64
65
66








67
68
69
70
71
72








73
74
75
#
testvfs tvfs -default 1
tvfs filter xOpen
tvfs script open_method
proc open_method {method file arglist} {
  set ::arglist $arglist
}
breakpoint
foreach {tn uri kvlist} {
  1      file:test.db?hello=world              {hello world}
  2      file:test.db?hello&world              {hello {} world {}}
  3      file:test.db?hello=1&world=2&vfs=tvfs {hello 1 world 2 vfs tvfs}
  4      file:test.db?hello=1&world=2&vfs=unix {}








} {
  set ::arglist ""
  set DB [sqlite3_open $uri]
  do_test 2.$tn { set ::arglist } $kvlist
  sqlite3_close $DB
}









finish_test








<

|
|
|
|
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>



56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#
testvfs tvfs -default 1
tvfs filter xOpen
tvfs script open_method
proc open_method {method file arglist} {
  set ::arglist $arglist
}

foreach {tn uri kvlist} {
  1      file:test.db?hello=world                     {hello world}
  2      file:test.db?hello&world                     {hello {} world {}}
  3      file:test.db?hello=1&world=2&vfs=tvfs        {hello 1 world 2 vfs tvfs}
  4      file:test.db?hello=1&world=2&vfs=unix        {}
  5      file:test.db?%68%65%6C%6C%6F=%77%6F%72%6C%64 {hello world}
  6      file:test%00.db?hello%00extra=world%00ex     {hello world}
  7      file:test%00.db?hello%00=world%00            {hello world}
  8      file:test%00.db?=world&xyz=abc               {xyz abc}
  9      file:test.db?%00hello=world&xyz=abc          {xyz abc}
  10     file:test.db?hello=%00world&xyz=             {hello {} xyz {}}
  11     file:test.db?=#ravada                        {}
  12     file:test.db?&&&&&&&&hello=world&&&&&&&      {hello world}
} {
  set ::arglist ""
  set DB [sqlite3_open $uri]
  do_test 2.$tn { set ::arglist } $kvlist
  sqlite3_close $DB
}
tvfs delete

#-------------------------------------------------------------------------
# Test that specifying a non-existent VFS raises an error.
#
do_test 3.1 {
  list [catch { sqlite3 db "file:test.db?vfs=nosuchvfs" } msg] $msg
} {1 {no such vfs: nosuchvfs}}

finish_test