diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 17 | ||||
-rw-r--r-- | src/btree.c | 4 | ||||
-rw-r--r-- | src/btree.h | 1 | ||||
-rw-r--r-- | src/build.c | 2 | ||||
-rw-r--r-- | src/global.c | 5 | ||||
-rw-r--r-- | src/main.c | 260 | ||||
-rw-r--r-- | src/os_win.c | 7 | ||||
-rw-r--r-- | src/pager.c | 14 | ||||
-rw-r--r-- | src/sqlite.h.in | 119 | ||||
-rw-r--r-- | src/sqliteInt.h | 3 | ||||
-rw-r--r-- | src/test1.c | 71 | ||||
-rw-r--r-- | src/test3.c | 2 | ||||
-rw-r--r-- | src/test_malloc.c | 30 | ||||
-rw-r--r-- | src/test_vfs.c | 78 | ||||
-rw-r--r-- | src/util.c | 6 | ||||
-rw-r--r-- | src/vdbe.c | 2 |
16 files changed, 562 insertions, 59 deletions
diff --git a/src/attach.c b/src/attach.c index bda1c8744..7c546a5c1 100644 --- a/src/attach.c +++ b/src/attach.c @@ -70,8 +70,12 @@ static void attachFunc( 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); @@ -124,8 +128,17 @@ static void attachFunc( ** 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); + 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; diff --git a/src/btree.c b/src/btree.c index 103a1f323..d02189324 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1688,13 +1688,13 @@ static int btreeInvokeBusyHandler(void *pArg){ ** 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() */ ){ - 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 */ @@ -1716,6 +1716,7 @@ int sqlite3BtreeOpen( #endif assert( db!=0 ); + assert( pVfs!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ @@ -1734,7 +1735,6 @@ int sqlite3BtreeOpen( 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; diff --git a/src/btree.h b/src/btree.h index c6f6aec5d..9e3a73b3b 100644 --- a/src/btree.h +++ b/src/btree.h @@ -42,6 +42,7 @@ 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 */ diff --git a/src/build.c b/src/build.c index 83a1db840..fa2d53a14 100644 --- a/src/build.c +++ b/src/build.c @@ -3443,7 +3443,7 @@ int sqlite3OpenTempDatabase(Parse *pParse){ SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; - rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags); + 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"); diff --git a/src/global.c b/src/global.c index 0c890684d..f01eaa8f4 100644 --- a/src/global.c +++ b/src/global.c @@ -129,7 +129,9 @@ const unsigned char sqlite3CtypeMap[256] = { }; #endif - +#ifndef SQLITE_USE_URI +# define SQLITE_USE_URI 0 +#endif /* ** The following singleton contains the global configuration for @@ -139,6 +141,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ + SQLITE_USE_URI, /* bOpenUri */ 0x7ffffffe, /* mxStrlen */ 100, /* szLookaside */ 500, /* nLookaside */ diff --git a/src/main.c b/src/main.c index 4aaa61899..adaa6c662 100644 --- a/src/main.c +++ b/src/main.c @@ -426,6 +426,11 @@ int sqlite3_config(int op, ...){ break; } + case SQLITE_CONFIG_URI: { + sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); + break; + } + default: { rc = SQLITE_ERROR; break; @@ -1786,6 +1791,233 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ } /* +** This function is used to parse both URIs and non-URI filenames passed by the +** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database +** URIs specified as part of ATTACH statements. +** +** The first argument to this function is the name of the VFS to use (or +** a NULL to signify the default VFS) if the URI does not contain a "vfs=xxx" +** query parameter. The second argument contains the URI (or non-URI filename) +** itself. When this function is called the *pFlags variable should contain +** the default flags to open the database handle with. The value stored in +** *pFlags may be updated before returning if the URI filename contains +** "cache=xxx" or "mode=xxx" query parameters. +** +** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to +** the VFS that should be used to open the database file. *pzFile is set to +** point to a buffer containing the name of the file to open. It is the +** responsibility of the caller to eventually call sqlite3_free() to release +** this buffer. +** +** If an error occurs, then an SQLite error code is returned and *pzErrMsg +** may be set to point to a buffer containing an English language error +** message. It is the responsibility of the caller to eventually release +** this buffer by calling sqlite3_free(). +*/ +int sqlite3ParseUri( + 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 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; + 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 */ + + /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen + ** method that there may be extra parameters following the file-name. */ + flags |= SQLITE_OPEN_URI; + + for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); + zFile = sqlite3_malloc(nByte); + if( !zFile ) return SQLITE_NOMEM; + + /* Discard the scheme and authority segments of the URI. */ + if( zUri[5]=='/' && zUri[6]=='/' ){ + iIn = 7; + while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; + + if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ + *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", + iIn-7, &zUri[7]); + rc = SQLITE_ERROR; + goto parse_uri_out; + } + }else{ + iIn = 5; + } + + /* Copy the filename and any query parameters into the zFile buffer. + ** Decode %HH escape codes along the way. + ** + ** Within this loop, variable eState may be set to 0, 1 or 2, depending + ** on the parsing context. As follows: + ** + ** 0: Parsing file-name. + ** 1: Parsing name section of a name=value query parameter. + ** 2: Parsing value section of a name=value query parameter. + */ + eState = 0; + while( zUri[iIn] && zUri[iIn]!='#' ){ + char c = zUri[iIn++]; + if( c=='%' + && sqlite3Isxdigit(zUri[iIn]) + && sqlite3Isxdigit(zUri[iIn+1]) + ){ + int octet = (sqlite3HexToInt(zUri[iIn++]) << 4); + octet += sqlite3HexToInt(zUri[iIn++]); + + assert( octet>=0 && octet<256 ); + if( octet==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 = octet; + }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); + + if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ + zVfs = zVal; + }else{ + struct OpenMode { + const char *z; + int mode; + } *aMode = 0; + char *zModeType; + int mask; + int limit; + + if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){ + static struct OpenMode aCacheMode[] = { + { "shared", SQLITE_OPEN_SHAREDCACHE }, + { "private", SQLITE_OPEN_PRIVATECACHE }, + { 0, 0 } + }; + + mask = SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_PRIVATECACHE; + aMode = aCacheMode; + limit = mask; + zModeType = "cache"; + } + if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){ + static struct OpenMode aOpenMode[] = { + { "ro", SQLITE_OPEN_READONLY }, + { "rw", SQLITE_OPEN_READWRITE }, + { "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE }, + { 0, 0 } + }; + + mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; + aMode = aOpenMode; + limit = mask & flags; + zModeType = "access"; + } + + if( aMode ){ + int i; + int mode = 0; + for(i=0; aMode[i].z; i++){ + const char *z = aMode[i].z; + if( nVal==strlen(z) && 0==memcmp(zVal, z, nVal) ){ + mode = aMode[i].mode; + break; + } + } + if( mode==0 ){ + *pzErrMsg = sqlite3_mprintf("no such %s mode: %s", zModeType, zVal); + rc = SQLITE_ERROR; + goto parse_uri_out; + } + if( mode>limit ){ + rc = SQLITE_PERM; + goto parse_uri_out; + } + flags = (flags & ~mask) | mode; + } + } + + 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); + rc = SQLITE_ERROR; + } + parse_uri_out: + if( rc!=SQLITE_OK ){ + sqlite3_free(zFile); + zFile = 0; + } + *pFlags = flags; + *pzFile = zFile; + return rc; +} + + +/* ** This routine does the work of opening a database on behalf of ** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" ** is UTF-8 encoded. @@ -1796,9 +2028,11 @@ static int openDatabase( unsigned flags, /* Operational flags */ const char *zVfs /* Name of the VFS to use */ ){ - sqlite3 *db; - int rc; - int isThreadsafe; + sqlite3 *db; /* Store allocated handle here */ + int rc; /* Return code */ + int isThreadsafe; /* True for threadsafe connections */ + char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ + char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT @@ -1822,7 +2056,7 @@ static int openDatabase( testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ - if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE; + if( ((1<<(flags&7)) & 0x46)==0 ) rc = SQLITE_MISUSE; if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; @@ -1903,13 +2137,6 @@ static int openDatabase( sqlite3HashInit(&db->aModule); #endif - db->pVfs = sqlite3_vfs_find(zVfs); - if( !db->pVfs ){ - rc = SQLITE_ERROR; - sqlite3Error(db, rc, "no such vfs: %s", zVfs); - goto opendb_out; - } - /* Add the default collation sequence BINARY. BINARY works for both UTF-8 ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. @@ -1932,9 +2159,17 @@ static int openDatabase( createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); + /* Parse the filename/URI argument. */ + rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); + if( rc!=SQLITE_OK ){ + sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg); + sqlite3_free(zErrMsg); + goto opendb_out; + } + /* Open the backend database driver */ db->openFlags = flags; - rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0, + 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 ){ @@ -2027,6 +2262,7 @@ static int openDatabase( sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT); opendb_out: + sqlite3_free(zOpen); if( db ){ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); diff --git a/src/os_win.c b/src/os_win.c index 4e91f7ab3..1300b9014 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -2465,6 +2465,13 @@ static int winFullPathname( void *zConverted; char *zOut; + /* If this path name begins with "/X:", where "X" is any alphabetic + ** character, discard the initial "/" from the pathname. + */ + if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){ + zRelative++; + } + /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. This function could fail if, for example, the diff --git a/src/pager.c b/src/pager.c index 94f647dcc..8ad6984f8 100644 --- a/src/pager.c +++ b/src/pager.c @@ -4299,6 +4299,8 @@ int sqlite3PagerOpen( int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ + const char *zUri = 0; /* URI args to copy */ + int nUri = 0; /* Number of bytes of URI args at *zUri */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). This @@ -4329,6 +4331,7 @@ int sqlite3PagerOpen( ** leave both nPathname and zPathname set to 0. */ if( zFilename && zFilename[0] ){ + const char *z; nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ @@ -4337,6 +4340,12 @@ int sqlite3PagerOpen( zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); nPathname = sqlite3Strlen30(zPathname); + z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1]; + while( *z ){ + z += sqlite3Strlen30(z)+1; + z += sqlite3Strlen30(z)+1; + } + nUri = &z[1] - zUri; if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname @@ -4369,7 +4378,7 @@ int sqlite3PagerOpen( ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + /* zFilename */ + nPathname + 1 + nUri + /* zFilename */ nPathname + 8 + 1 /* zJournal */ #ifndef SQLITE_OMIT_WAL + nPathname + 4 + 1 /* zWal */ @@ -4391,8 +4400,9 @@ int sqlite3PagerOpen( /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ assert( nPathname>0 ); - pPager->zJournal = (char*)(pPtr += nPathname + 1); + pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); memcpy(pPager->zFilename, zPathname, nPathname); + memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); #ifndef SQLITE_OMIT_WAL diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ad424dbbf..d78daa767 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -480,6 +480,7 @@ int sqlite3_exec( #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ +#define SQLITE_OPEN_URI 0x00100000 /* Ok for sqlite3_open_v2() */ /* Reserved: 0x00F00000 */ @@ -1427,6 +1428,18 @@ struct sqlite3_mem_methods { ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** +** <dt>SQLITE_CONFIG_URI +** <dd> This option takes a single argument of type int. If non-zero, then +** URI handling is globally enabled. If the parameter is zero, then URI handling +** is globally disabled. If URI handling is globally enabled, all filenames +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or +** specified as part of [ATTACH] commands are interpreted as URIs, regardless +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database +** connection is opened. If it is globally disabled, filenames are +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the +** database connection is opened. By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** [SQLITE_USE_URI] symbol defined. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1445,6 +1458,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2324,7 +2338,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** -** ^These routines open an SQLite database file whose name is given by the +** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually @@ -2387,6 +2401,11 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not ** participate in [shared cache mode] even if it is enabled. ** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** ** ^If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might @@ -2399,10 +2418,100 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. +** ^If URI filename interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** is set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method. +** +** URI filenames are parsed according to RFC 1738. If the URI contains an +** 'authority', then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The 'fragment' component of a URI, if +** present, is always ignored. +** +** ^SQLite uses the 'path' component of the URI as the path to the database file +** to open. ^If the path begins with a '/' character, then it is interpreted as +** an absolute path. ^If it does not begin with a '/', it is interpreted as a +** relative path. ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [sqlite3_vfs | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +** <ul> +** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or +** "rwc". Attempting to set it to any other value is an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_prepare_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is +** used, it is an error to specify a value for the mode parameter that is +** less restrictive than that specified by the flags passed as the third +** parameter. +** +** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behaviour requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +** </ul> +** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. +** +** URI filename examples: +** +** <table border="1" align=center cellpadding=5> +** <tr><th> URI filenames <th> Results +** <tr><td> file:data.db <td> +** Open the file "data.db" in the current directory. +** <tr><td> file:/home/fred/data.db<br> +** file:///home/fred/data.db <br> +** file://localhost/home/fred/data.db <br> <td> +** Open the database file "/home/fred/data.db". +** <tr><td> file://darkstar/home/fred/data.db <td> +** An error. "darkstar" is not a recognized authority. +** <tr><td style="white-space:nowrap"> +** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** <td> Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +** <tr><td> file:data.db?mode=ro&cache=private <td> +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td> +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +** <tr><td> file:data.db?mode=readonly <td> +** An error. "readonly" is not a valid option for the "mode" parameter. +** </table> +** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. ** ** <b>Note to Windows users:</b> The encoding used for the filename argument ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9c1bda7a7..72a96a601 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2423,6 +2423,7 @@ struct Sqlite3Config { int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ + int bOpenUri; /* True to interpret filenames as URIs */ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ @@ -2672,6 +2673,7 @@ void sqlite3AddColumnType(Parse*,Token*); void sqlite3AddDefaultValue(Parse*,ExprSpan*); void sqlite3AddCollateType(Parse*, Token*); void sqlite3EndTable(Parse*,Token*,Token*,Select*); +int sqlite3ParseUri(const char*,const char*,int*,sqlite3_vfs**,char**,char **); Bitvec *sqlite3BitvecCreate(u32); int sqlite3BitvecTest(Bitvec*, u32); @@ -2922,6 +2924,7 @@ char sqlite3ExprAffinity(Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); void sqlite3Error(sqlite3*, int, const char*,...); void *sqlite3HexToBlob(sqlite3*, const char *z, int n); +u8 sqlite3HexToInt(int h); int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); const char *sqlite3ErrStr(int); int sqlite3ReadSchema(Parse *pParse); diff --git a/src/test1.c b/src/test1.c index b79bbd082..8e3012343 100644 --- a/src/test1.c +++ b/src/test1.c @@ -3846,6 +3846,76 @@ static int test_open( } /* +** Usage: sqlite3_open_v2 FILENAME FLAGS VFS +*/ +static int test_open_v2( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *zFilename; + const char *zVfs; + int flags = 0; + sqlite3 *db; + int rc; + char zBuf[100]; + + int nFlag; + Tcl_Obj **apFlag; + int i; + + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "FILENAME FLAGS VFS"); + return TCL_ERROR; + } + zFilename = Tcl_GetString(objv[1]); + zVfs = Tcl_GetString(objv[3]); + if( zVfs[0]==0x00 ) zVfs = 0; + + rc = Tcl_ListObjGetElements(interp, objv[2], &nFlag, &apFlag); + if( rc!=TCL_OK ) return rc; + for(i=0; i<nFlag; i++){ + int iFlag; + struct OpenFlag { + const char *zFlag; + int flag; + } aFlag[] = { + { "SQLITE_OPEN_READONLY", SQLITE_OPEN_READONLY }, + { "SQLITE_OPEN_READWRITE", SQLITE_OPEN_READWRITE }, + { "SQLITE_OPEN_CREATE", SQLITE_OPEN_CREATE }, + { "SQLITE_OPEN_DELETEONCLOSE", SQLITE_OPEN_DELETEONCLOSE }, + { "SQLITE_OPEN_EXCLUSIVE", SQLITE_OPEN_EXCLUSIVE }, + { "SQLITE_OPEN_AUTOPROXY", SQLITE_OPEN_AUTOPROXY }, + { "SQLITE_OPEN_MAIN_DB", SQLITE_OPEN_MAIN_DB }, + { "SQLITE_OPEN_TEMP_DB", SQLITE_OPEN_TEMP_DB }, + { "SQLITE_OPEN_TRANSIENT_DB", SQLITE_OPEN_TRANSIENT_DB }, + { "SQLITE_OPEN_MAIN_JOURNAL", SQLITE_OPEN_MAIN_JOURNAL }, + { "SQLITE_OPEN_TEMP_JOURNAL", SQLITE_OPEN_TEMP_JOURNAL }, + { "SQLITE_OPEN_SUBJOURNAL", SQLITE_OPEN_SUBJOURNAL }, + { "SQLITE_OPEN_MASTER_JOURNAL", SQLITE_OPEN_MASTER_JOURNAL }, + { "SQLITE_OPEN_NOMUTEX", SQLITE_OPEN_NOMUTEX }, + { "SQLITE_OPEN_FULLMUTEX", SQLITE_OPEN_FULLMUTEX }, + { "SQLITE_OPEN_SHAREDCACHE", SQLITE_OPEN_SHAREDCACHE }, + { "SQLITE_OPEN_PRIVATECACHE", SQLITE_OPEN_PRIVATECACHE }, + { "SQLITE_OPEN_WAL", SQLITE_OPEN_WAL }, + { "SQLITE_OPEN_URI", SQLITE_OPEN_URI }, + { 0, 0 } + }; + rc = Tcl_GetIndexFromObjStruct(interp, apFlag[i], aFlag, sizeof(aFlag[0]), + "flag", 0, &iFlag + ); + if( rc!=TCL_OK ) return rc; + flags |= aFlag[iFlag].flag; + } + + rc = sqlite3_open_v2(zFilename, &db, flags, zVfs); + if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; + Tcl_AppendResult(interp, zBuf, 0); + return TCL_OK; +} + +/* ** Usage: sqlite3_open16 filename options */ static int test_open16( @@ -5593,6 +5663,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_errmsg16", test_errmsg16 ,0 }, { "sqlite3_open", test_open ,0 }, { "sqlite3_open16", test_open16 ,0 }, + { "sqlite3_open_v2", test_open_v2 ,0 }, { "sqlite3_complete16", test_complete16 ,0 }, { "sqlite3_prepare", test_prepare ,0 }, diff --git a/src/test3.c b/src/test3.c index ef004ca71..4eabdccfd 100644 --- a/src/test3.c +++ b/src/test3.c @@ -78,7 +78,7 @@ static int btree_open( sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); sqlite3_mutex_enter(sDb.mutex); } - rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0, + 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); diff --git a/src/test_malloc.c b/src/test_malloc.c index c63ded703..5023dca44 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -1175,6 +1175,35 @@ static int test_config_error( } /* +** tclcmd: sqlite3_config_uri BOOLEAN +** +** Invoke sqlite3_config() or sqlite3_db_config() with invalid +** opcodes and verify that they return errors. +*/ +static int test_config_uri( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + int bOpenUri; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); + return TCL_ERROR; + } + if( Tcl_GetBooleanFromObj(interp, objv[1], &bOpenUri) ){ + return TCL_ERROR; + } + + rc = sqlite3_config(SQLITE_CONFIG_URI, bOpenUri); + Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + + return TCL_OK; +} + +/* ** Usage: ** ** sqlite3_dump_memsys3 FILENAME @@ -1422,6 +1451,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, + { "sqlite3_config_uri", test_config_uri ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, diff --git a/src/test_vfs.c b/src/test_vfs.c index 64b3cb574..ba078a0f9 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -114,20 +114,21 @@ struct Testvfs { ** + Simulating IO errors, and ** + Invoking the Tcl callback script. */ -#define TESTVFS_SHMOPEN_MASK 0x00000001 -#define TESTVFS_SHMLOCK_MASK 0x00000010 -#define TESTVFS_SHMMAP_MASK 0x00000020 -#define TESTVFS_SHMBARRIER_MASK 0x00000040 -#define TESTVFS_SHMCLOSE_MASK 0x00000080 - -#define TESTVFS_OPEN_MASK 0x00000100 -#define TESTVFS_SYNC_MASK 0x00000200 -#define TESTVFS_DELETE_MASK 0x00000400 -#define TESTVFS_CLOSE_MASK 0x00000800 -#define TESTVFS_WRITE_MASK 0x00001000 -#define TESTVFS_TRUNCATE_MASK 0x00002000 -#define TESTVFS_ACCESS_MASK 0x00004000 -#define TESTVFS_ALL_MASK 0x00007FFF +#define TESTVFS_SHMOPEN_MASK 0x00000001 +#define TESTVFS_SHMLOCK_MASK 0x00000010 +#define TESTVFS_SHMMAP_MASK 0x00000020 +#define TESTVFS_SHMBARRIER_MASK 0x00000040 +#define TESTVFS_SHMCLOSE_MASK 0x00000080 + +#define TESTVFS_OPEN_MASK 0x00000100 +#define TESTVFS_SYNC_MASK 0x00000200 +#define TESTVFS_DELETE_MASK 0x00000400 +#define TESTVFS_CLOSE_MASK 0x00000800 +#define TESTVFS_WRITE_MASK 0x00001000 +#define TESTVFS_TRUNCATE_MASK 0x00002000 +#define TESTVFS_ACCESS_MASK 0x00004000 +#define TESTVFS_FULLPATHNAME_MASK 0x00008000 +#define TESTVFS_ALL_MASK 0x0001FFFF #define TESTVFS_MAX_PAGES 1024 @@ -545,7 +546,7 @@ static int tvfsOpen( /* Evaluate the Tcl script: ** - ** SCRIPT xOpen FILENAME + ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS ** ** If the script returns an SQLite error code other than SQLITE_OK, an ** error is returned to the caller. If it returns SQLITE_OK, the new @@ -554,7 +555,19 @@ static int tvfsOpen( */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){ - tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); + Tcl_Obj *pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + if( flags&SQLITE_OPEN_MAIN_DB ){ + const char *z = &zName[strlen(zName)+1]; + while( *z ){ + Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); + z += strlen(z) + 1; + Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); + z += strlen(z) + 1; + } + } + tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0); + Tcl_DecrRefCount(pArg); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ @@ -663,6 +676,14 @@ static int tvfsFullPathname( int nOut, char *zOut ){ + Testvfs *p = (Testvfs *)pVfs->pAppData; + if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ + int rc; + tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0); + if( tvfsResultCode(p, &rc) ){ + if( rc!=SQLITE_OK ) return rc; + } + } return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut); } @@ -1028,18 +1049,19 @@ static int testvfs_obj_cmd( char *zName; int mask; } vfsmethod [] = { - { "xShmOpen", TESTVFS_SHMOPEN_MASK }, - { "xShmLock", TESTVFS_SHMLOCK_MASK }, - { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, - { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, - { "xShmMap", TESTVFS_SHMMAP_MASK }, - { "xSync", TESTVFS_SYNC_MASK }, - { "xDelete", TESTVFS_DELETE_MASK }, - { "xWrite", TESTVFS_WRITE_MASK }, - { "xTruncate", TESTVFS_TRUNCATE_MASK }, - { "xOpen", TESTVFS_OPEN_MASK }, - { "xClose", TESTVFS_CLOSE_MASK }, - { "xAccess", TESTVFS_ACCESS_MASK }, + { "xShmOpen", TESTVFS_SHMOPEN_MASK }, + { "xShmLock", TESTVFS_SHMLOCK_MASK }, + { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, + { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, + { "xShmMap", TESTVFS_SHMMAP_MASK }, + { "xSync", TESTVFS_SYNC_MASK }, + { "xDelete", TESTVFS_DELETE_MASK }, + { "xWrite", TESTVFS_WRITE_MASK }, + { "xTruncate", TESTVFS_TRUNCATE_MASK }, + { "xOpen", TESTVFS_OPEN_MASK }, + { "xClose", TESTVFS_CLOSE_MASK }, + { "xAccess", TESTVFS_ACCESS_MASK }, + { "xFullPathname", TESTVFS_FULLPATHNAME_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; diff --git a/src/util.c b/src/util.c index 1c9b401f8..50dc59120 100644 --- a/src/util.c +++ b/src/util.c @@ -983,13 +983,12 @@ void sqlite3Put4byte(unsigned char *p, u32 v){ -#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Translate a single byte of Hex into an integer. ** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ -static u8 hexToInt(int h){ +u8 sqlite3HexToInt(int h){ assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); #ifdef SQLITE_ASCII h += 9*(1&(h>>6)); @@ -999,7 +998,6 @@ static u8 hexToInt(int h){ #endif return (u8)(h & 0xf); } -#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ #if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* @@ -1016,7 +1014,7 @@ void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ n--; if( zBlob ){ for(i=0; i<n; i+=2){ - zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]); + zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]); } zBlob[i/2] = 0; } diff --git a/src/vdbe.c b/src/vdbe.c index 996c649f8..c409fdd83 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3149,7 +3149,7 @@ case OP_OpenEphemeral: { pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; - rc = sqlite3BtreeOpen(0, db, &pCx->pBt, + 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); |