SQLite

Check-in [68240e75e8]
Login

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

Overview
Comment:Have the ATTACH command do URI interpretation in the same way as sqlite3_open() and sqlite3_open_v2() do.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | uri
Files: files | file ages | folders
SHA1: 68240e75e87a54cde93352b0ec364d34365a8170
User & Date: dan 2011-04-23 15:54:54.370
Context
2011-04-23
19:06
Test that it is now possible to use different VFSs for two databases attached to a single handle. (check-in: 2af51f856c user: dan tags: uri)
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)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/attach.c.
66
67
68
69
70
71
72



73
74

75
76
77
78
79
80
81
  sqlite3_value **argv
){
  int i;
  int rc = 0;
  sqlite3 *db = sqlite3_context_db_handle(context);
  const char *zName;
  const char *zFile;



  Db *aNew;
  char *zErrDyn = 0;


  UNUSED_PARAMETER(NotUsed);

  zFile = (const char *)sqlite3_value_text(argv[0]);
  zName = (const char *)sqlite3_value_text(argv[1]);
  if( zFile==0 ) zFile = "";
  if( zName==0 ) zName = "";







>
>
>


>







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  sqlite3_value **argv
){
  int i;
  int rc = 0;
  sqlite3 *db = sqlite3_context_db_handle(context);
  const char *zName;
  const char *zFile;
  char *zPath = 0;
  char *zErr = 0;
  int flags;
  Db *aNew;
  char *zErrDyn = 0;
  sqlite3_vfs *pVfs;

  UNUSED_PARAMETER(NotUsed);

  zFile = (const char *)sqlite3_value_text(argv[0]);
  zName = (const char *)sqlite3_value_text(argv[1]);
  if( zFile==0 ) zFile = "";
  if( zName==0 ) zName = "";
120
121
122
123
124
125
126




127



128


129
130
131
132
133
134
135
  aNew = &db->aDb[db->nDb];
  memset(aNew, 0, sizeof(*aNew));

  /* Open the database file. If the btree is successfully opened, use
  ** it to obtain the database schema. At this point the schema may
  ** or may not be initialised.
  */




  rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0,



                        db->openFlags | SQLITE_OPEN_MAIN_DB);


  db->nDb++;
  if( rc==SQLITE_CONSTRAINT ){
    rc = SQLITE_ERROR;
    zErrDyn = sqlite3MPrintf(db, "database is already attached");
  }else if( rc==SQLITE_OK ){
    Pager *pPager;
    aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);







>
>
>
>
|
>
>
>
|
>
>







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  aNew = &db->aDb[db->nDb];
  memset(aNew, 0, sizeof(*aNew));

  /* Open the database file. If the btree is successfully opened, use
  ** it to obtain the database schema. At this point the schema may
  ** or may not be initialised.
  */
  flags = db->openFlags;
  rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
  if( rc!=SQLITE_OK ){
    sqlite3_result_error(context, zErr, -1);
    sqlite3_free(zErr);
    return;
  }
  assert( pVfs );
  flags |= SQLITE_OPEN_MAIN_DB;
  rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags);
  sqlite3_free( zPath );
  db->nDb++;
  if( rc==SQLITE_CONSTRAINT ){
    rc = SQLITE_ERROR;
    zErrDyn = sqlite3MPrintf(db, "database is already attached");
  }else if( rc==SQLITE_OK ){
    Pager *pPager;
    aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
Changes to src/btree.c.
1684
1685
1686
1687
1688
1689
1690

1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an
** SQLITE_CONSTRAINT error.  We cannot allow two or more BtShared
** objects in the same database connection since doing so will lead
** to problems with locking.
*/
int sqlite3BtreeOpen(

  const char *zFilename,  /* Name of the file containing the BTree database */
  sqlite3 *db,            /* Associated database handle */
  Btree **ppBtree,        /* Pointer to new Btree object written here */
  int flags,              /* Options */
  int vfsFlags            /* Flags passed through to sqlite3_vfs.xOpen() */
){
  sqlite3_vfs *pVfs;             /* The VFS to use for this btree */
  BtShared *pBt = 0;             /* Shared part of btree structure */
  Btree *p;                      /* Handle to return */
  sqlite3_mutex *mutexOpen = 0;  /* Prevents a race condition. Ticket #3537 */
  int rc = SQLITE_OK;            /* Result code from this function */
  u8 nReserve;                   /* Byte of unused space on each page */
  unsigned char zDbHeader[100];  /* Database header content */








>






<







1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697

1698
1699
1700
1701
1702
1703
1704
** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an
** SQLITE_CONSTRAINT error.  We cannot allow two or more BtShared
** objects in the same database connection since doing so will lead
** to problems with locking.
*/
int sqlite3BtreeOpen(
  sqlite3_vfs *pVfs,      /* VFS to use for this b-tree */
  const char *zFilename,  /* Name of the file containing the BTree database */
  sqlite3 *db,            /* Associated database handle */
  Btree **ppBtree,        /* Pointer to new Btree object written here */
  int flags,              /* Options */
  int vfsFlags            /* Flags passed through to sqlite3_vfs.xOpen() */
){

  BtShared *pBt = 0;             /* Shared part of btree structure */
  Btree *p;                      /* Handle to return */
  sqlite3_mutex *mutexOpen = 0;  /* Prevents a race condition. Ticket #3537 */
  int rc = SQLITE_OK;            /* Result code from this function */
  u8 nReserve;                   /* Byte of unused space on each page */
  unsigned char zDbHeader[100];  /* Database header content */

1712
1713
1714
1715
1716
1717
1718

1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
  const int isMemdb = 0;
#else
  const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
                       || (isTempDb && sqlite3TempInMemory(db));
#endif

  assert( db!=0 );

  assert( sqlite3_mutex_held(db->mutex) );
  assert( (flags&0xff)==flags );   /* flags fit in 8 bits */

  /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */
  assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 );

  /* A BTREE_SINGLE database is always a temporary and/or ephemeral */
  assert( (flags & BTREE_SINGLE)==0 || isTempDb );

  if( db->flags & SQLITE_NoReadlock ){
    flags |= BTREE_NO_READLOCK;
  }
  if( isMemdb ){
    flags |= BTREE_MEMORY;
  }
  if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
    vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
  }
  pVfs = db->pVfs;
  p = sqlite3MallocZero(sizeof(Btree));
  if( !p ){
    return SQLITE_NOMEM;
  }
  p->inTrans = TRANS_NONE;
  p->db = db;
#ifndef SQLITE_OMIT_SHARED_CACHE







>


















<







1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737

1738
1739
1740
1741
1742
1743
1744
  const int isMemdb = 0;
#else
  const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
                       || (isTempDb && sqlite3TempInMemory(db));
#endif

  assert( db!=0 );
  assert( pVfs!=0 );
  assert( sqlite3_mutex_held(db->mutex) );
  assert( (flags&0xff)==flags );   /* flags fit in 8 bits */

  /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */
  assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 );

  /* A BTREE_SINGLE database is always a temporary and/or ephemeral */
  assert( (flags & BTREE_SINGLE)==0 || isTempDb );

  if( db->flags & SQLITE_NoReadlock ){
    flags |= BTREE_NO_READLOCK;
  }
  if( isMemdb ){
    flags |= BTREE_MEMORY;
  }
  if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
    vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
  }

  p = sqlite3MallocZero(sizeof(Btree));
  if( !p ){
    return SQLITE_NOMEM;
  }
  p->inTrans = TRANS_NONE;
  p->db = db;
#ifndef SQLITE_OMIT_SHARED_CACHE
Changes to src/btree.h.
38
39
40
41
42
43
44

45
46
47
48
49
50
51
*/
typedef struct Btree Btree;
typedef struct BtCursor BtCursor;
typedef struct BtShared BtShared;


int sqlite3BtreeOpen(

  const char *zFilename,   /* Name of database file to open */
  sqlite3 *db,             /* Associated database connection */
  Btree **ppBtree,         /* Return open Btree* here */
  int flags,               /* Flags */
  int vfsFlags             /* Flags passed through to VFS open */
);








>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
*/
typedef struct Btree Btree;
typedef struct BtCursor BtCursor;
typedef struct BtShared BtShared;


int sqlite3BtreeOpen(
  sqlite3_vfs *pVfs,       /* VFS to use with this b-tree */
  const char *zFilename,   /* Name of database file to open */
  sqlite3 *db,             /* Associated database connection */
  Btree **ppBtree,         /* Return open Btree* here */
  int flags,               /* Flags */
  int vfsFlags             /* Flags passed through to VFS open */
);

Changes to src/build.c.
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
    static const int flags = 
          SQLITE_OPEN_READWRITE |
          SQLITE_OPEN_CREATE |
          SQLITE_OPEN_EXCLUSIVE |
          SQLITE_OPEN_DELETEONCLOSE |
          SQLITE_OPEN_TEMP_DB;

    rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags);
    if( rc!=SQLITE_OK ){
      sqlite3ErrorMsg(pParse, "unable to open a temporary database "
        "file for storing temporary tables");
      pParse->rc = rc;
      return 1;
    }
    db->aDb[1].pBt = pBt;







|







3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
    static const int flags = 
          SQLITE_OPEN_READWRITE |
          SQLITE_OPEN_CREATE |
          SQLITE_OPEN_EXCLUSIVE |
          SQLITE_OPEN_DELETEONCLOSE |
          SQLITE_OPEN_TEMP_DB;

    rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags);
    if( rc!=SQLITE_OK ){
      sqlite3ErrorMsg(pParse, "unable to open a temporary database "
        "file for storing temporary tables");
      pParse->rc = rc;
      return 1;
    }
    db->aDb[1].pBt = pBt;
Changes to src/date.c.
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  sqlite3_int64 iT;
  char zBuf[20];

  UNUSED_PARAMETER(argc);
  UNUSED_PARAMETER(argv);

  db = sqlite3_context_db_handle(context);
  sqlite3OsCurrentTimeInt64(db->pVfs, &iT);
  t = iT/1000 - 10000*(sqlite3_int64)21086676;
#ifdef HAVE_GMTIME_R
  {
    struct tm sNow;
    gmtime_r(&t, &sNow);
    strftime(zBuf, 20, zFormat, &sNow);
  }







|







1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  sqlite3_int64 iT;
  char zBuf[20];

  UNUSED_PARAMETER(argc);
  UNUSED_PARAMETER(argv);

  db = sqlite3_context_db_handle(context);
  sqlite3OsCurrentTimeInt64(db->pVfs &iT);
  t = iT/1000 - 10000*(sqlite3_int64)21086676;
#ifdef HAVE_GMTIME_R
  {
    struct tm sNow;
    gmtime_r(&t, &sNow);
    strftime(zBuf, 20, zFormat, &sNow);
  }
Changes to src/main.c.
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
  if( ((1<<(flags&7)) & 0x46)==0 ){
    rc = SQLITE_MISUSE;
    goto opendb_out;
  }

  /* Open the backend database driver */
  db->openFlags = flags;
  rc = sqlite3BtreeOpen(zOpen, db, &db->aDb[0].pBt, 0,
                        flags | SQLITE_OPEN_MAIN_DB);
  if( rc!=SQLITE_OK ){
    if( rc==SQLITE_IOERR_NOMEM ){
      rc = SQLITE_NOMEM;
    }
    sqlite3Error(db, rc, 0);
    goto opendb_out;







|







2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
  if( ((1<<(flags&7)) & 0x46)==0 ){
    rc = SQLITE_MISUSE;
    goto opendb_out;
  }

  /* Open the backend database driver */
  db->openFlags = flags;
  rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
                        flags | SQLITE_OPEN_MAIN_DB);
  if( rc!=SQLITE_OK ){
    if( rc==SQLITE_IOERR_NOMEM ){
      rc = SQLITE_NOMEM;
    }
    sqlite3Error(db, rc, 0);
    goto opendb_out;
Changes to src/test3.c.
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
  nRefSqlite3++;
  if( nRefSqlite3==1 ){
    sDb.pVfs = sqlite3_vfs_find(0);
    sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
    sqlite3_mutex_enter(sDb.mutex);
  }
  rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0, 
     SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  sqlite3BtreeSetCacheSize(pBt, nCache);
  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);







|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR;
  nRefSqlite3++;
  if( nRefSqlite3==1 ){
    sDb.pVfs = sqlite3_vfs_find(0);
    sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
    sqlite3_mutex_enter(sDb.mutex);
  }
  rc = sqlite3BtreeOpen(sDb.pVfs, argv[1], &sDb, &pBt, 0, 
     SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  sqlite3BtreeSetCacheSize(pBt, nCache);
  sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt);
Changes to src/vdbe.c.
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
      SQLITE_OPEN_DELETEONCLOSE |
      SQLITE_OPEN_TRANSIENT_DB;

  assert( pOp->p1>=0 );
  pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
  if( pCx==0 ) goto no_mem;
  pCx->nullRow = 1;
  rc = sqlite3BtreeOpen(0, db, &pCx->pBt, 
                        BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
  if( rc==SQLITE_OK ){
    rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
  }
  if( rc==SQLITE_OK ){
    /* If a transient index is required, create it by calling
    ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before







|







3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
      SQLITE_OPEN_DELETEONCLOSE |
      SQLITE_OPEN_TRANSIENT_DB;

  assert( pOp->p1>=0 );
  pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
  if( pCx==0 ) goto no_mem;
  pCx->nullRow = 1;
  rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt, 
                        BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
  if( rc==SQLITE_OK ){
    rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
  }
  if( rc==SQLITE_OK ){
    /* If a transient index is required, create it by calling
    ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
Changes to test/uri.test.
43
44
45
46
47
48
49







50
51
52
53
54
55
56
  set file [string map [list PWD [pwd]] $file]

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







}


#-------------------------------------------------------------------------
# Test that URI query parameters are passed through to the VFS layer
# correctly.
#







>
>
>
>
>
>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  set file [string map [list PWD [pwd]] $file]

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

  do_test 1.$tn.3 { file exists $file } 0
  sqlite3 db xxx.db
  execsql { ATTACH $uri AS aux }
  do_test 1.$tn.4 { file exists $file } 1
  db close
}


#-------------------------------------------------------------------------
# Test that URI query parameters are passed through to the VFS layer
# correctly.
#
69
70
71
72
73
74
75


76
77
78
79
80






81
82
83
84
85
86
87
88
89























90
91
92
  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








>
>



|

>
>
>
>
>
>









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



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  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}
  13     test.db?&&&&&&&&hello=world&&&&&&&           {}
  14     http:test.db?hello&world                     {}
} {
  set ::arglist ""
  set DB [sqlite3_open $uri]
  do_test 2.$tn.1 { set ::arglist } $kvlist
  sqlite3_close $DB

  sqlite3 db xxx.db
  set ::arglist ""
  execsql { ATTACH $uri AS aux }
  do_test 2.$tn.2 { set ::arglist } $kvlist
  db close
}
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}}

#-------------------------------------------------------------------------
# Test the "readonly" URI option.
#
do_test 4.0 {
  sqlite3 db test.db
  db eval {CREATE TABLE t1(a, b)}
  db close
} {}
foreach {tn uri ro} {
  1    "file:test.db"                                 0
  2    "file:test.db?readonly=0"                      0
  3    "file:test.db?readonly=1&readwrite=0&create=0" 1
} {
  set RES(0) {0 {}}
  set RES(1) {1 {attempt to write a readonly database}}

  do_test 4.$tn {
    sqlite3 db $uri
    catchsql { INSERT INTO t1 VALUES(1, 2) }
  } $RES($ro)
  db close
}

finish_test