From: drh Date: Thu, 1 Dec 2011 02:32:12 +0000 (+0000) Subject: Add a prototype implementation of stdio-like routines for accessing the VFS. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=24076fd97d733ab6627d9907e24fb2563bf8fb44;p=thirdparty%2Fsqlite.git Add a prototype implementation of stdio-like routines for accessing the VFS. This is intended as documentation. The code is untested. There is no guarantee that any of this will ever make it into trunk. Substantial revision is possible prior to reaching trunk, if it ever does. FossilOrigin-Name: 8936542b225836f5f4a789e1ecc1451f6a1e120d --- diff --git a/manifest b/manifest index ef7ae4ea60..76704b4e0d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sunused\sfields\sfrom\sthe\sParse\sobject.\s\sDocumentation\sand\sformatting\nimprovements\son\sdata\sstructure\sdefinitions. -D 2011-11-29T15:40:32.491 +C Add\sa\sprototype\simplementation\sof\sstdio-like\sroutines\sfor\saccessing\sthe\sVFS.\nThis\sis\sintended\sas\sdocumentation.\s\sThe\scode\sis\suntested.\sThere\sis\sno\nguarantee\sthat\sany\sof\sthis\swill\sever\smake\sit\sinto\strunk.\s\sSubstantial\nrevision\sis\spossible\sprior\sto\sreaching\strunk,\sif\sit\sever\sdoes. +D 2011-12-01T02:32:12.196 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -162,7 +162,7 @@ F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579 F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 -F src/os.c 28bbdab2170dfce84d86c45456a18eab1d0f99a9 +F src/os.c c2dcf314db63a0ea06af00fcf087750a1852bbbc F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 @@ -182,7 +182,7 @@ F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c 80f3ac44a8514b1d107b80f5df4a424ae059d2b6 F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8 -F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5 +F src/sqlite.h.in 6ab24133ed28e094dc67981cd9279d7bd8b31cf6 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 F src/sqliteInt.h 6f28b69d77356b1e45c024a6c103a1e0f0ec9f62 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d @@ -976,7 +976,10 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P b10d091ec02e94643e865743129e2a21147b3136 -R 7d0a9d11e01c28eaf1d831b1553005fb +P 431556cac0b2c86d7f6a60412ff1023feeaafedf +R 0398714a93656ee1bfce81727b47b578 +T *branch * vfs-stdio +T *sym-vfs-stdio * +T -sym-trunk * U drh -Z fa710425b85d2a4b5e2c971fb970b087 +Z 536ed89e4d5b4e5e1bbeee8dbaae70fb diff --git a/manifest.uuid b/manifest.uuid index b014af186d..b5e7f2bf4d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -431556cac0b2c86d7f6a60412ff1023feeaafedf \ No newline at end of file +8936542b225836f5f4a789e1ecc1451f6a1e120d \ No newline at end of file diff --git a/src/os.c b/src/os.c index a347ec1d02..e3346e5df8 100644 --- a/src/os.c +++ b/src/os.c @@ -340,3 +340,235 @@ int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ sqlite3_mutex_leave(mutex); return SQLITE_OK; } + +#ifndef SQLITE_OMIT_VFS_STDIO +/***************************************************************************** +** The remainder of this file contains a simplified stdio-like interface +** to the VFS layer. +*/ + +/* +** An instance of the following object records the state of an +** open file. This object is opaque to all users - the internal +** structure is only visible to the functions below. +*/ +struct sqlite3_FILE { + char *zFilename; /* Full pathname of the open file */ + sqlite3_int64 iOfst; /* Current offset into the file */ + sqlite3_vfs *pVfs; /* The VFS used for this file */ + u8 alwaysAppend; /* Always append if true */ + sqlite3_file sFile; /* Open file. MUST BE LAST */ +}; + +/* +** This is a helper routine used to translate a URI into a full pathname +** and a pointer to the appropriate VFS. +*/ +static int getFilename(const char *zURI, sqlite3_vfs **ppVfs, char **pzName){ + int rc; + char *zOpen = 0; + char *zFullname = 0; + unsigned int flags; + char *zErrmsg = 0; + sqlite3_vfs *pVfs = 0; + + rc = sqlite3ParseUri(0, zURI, &flags, &pVfs, &zOpen, &zErrmsg); + sqlite3_free(zErrmsg); + if( rc ) goto getFilename_error; + zFullname = sqlite3_malloc( pVfs->mxPathname+1 ); + if( zFullname==0 ){ rc = SQLITE_NOMEM; goto getFilename_error; } + rc = pVfs->xFullPathname(pVfs, zOpen, pVfs->mxPathname, zFullname); + if( rc ) goto getFilename_error; + sqlite3_free(zOpen); + zOpen = 0; + *pzName = sqlite3_realloc(zFullname, sqlite3Strlen30(zFullname)+1); + if( *pzName==0 ) goto getFilename_error; + zFullname = 0; + *ppVfs = pVfs; + return SQLITE_OK; + +getFilename_error: + sqlite3_free(zOpen); + sqlite3_free(zFullname); + *pzName = 0; + *ppVfs = 0; + return rc; +} + +/* +** Open a file for stdio-like reading and writing. The file is identified +** by the URI in the first parameter. The access mode can be "r", "r+", +** "w", "w+", "a", or "a+" with the usual meanings. +** +** On success, a pointer to a new sqlite3_FILE object is returned. On +** failure, NULL is returned. Unfortunately, there is no way to recover +** detailed error information after a failure. +*/ +sqlite3_FILE *sqlite3_fopen(const char *zURI, const char *zMode){ + char *zFile = 0; + sqlite3_vfs *pVfs = 0; + int rc; + int openFlags; + int doTruncate = 0; + int seekEnd = 0; + int alwaysAppend = 0; + int nToAlloc; + sqlite3_FILE *p; + + if( zMode[0]==0 ) return 0; + if( zMode[0]=='r' ){ + if( zMode[1]=='+' ){ + openFlags = SQLITE_OPEN_READWRITE; + }else{ + openFlags = SQLITE_OPEN_READONLY; + } + }else if( zMode[0]=='w' ){ + openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + doTruncate = 1; + }else if( zMode[0]=='a' ){ + openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + if( zMode[1]=='+' ){ + alwaysAppend = 1; + }else{ + seekEnd = 1; + } + }else{ + return 0; + } + rc = getFilename(zURI, &pVfs, &zFile); + if( rc ) return 0; + nToAlloc = sizeof(*p) + ROUND8(pVfs->szOsFile); + p = sqlite3_malloc( nToAlloc ); + if( p==0 ){ + sqlite3_free(zFile); + return 0; + } + memset(p, 0, nToAlloc); + p->zFilename = zFile; + rc = pVfs->xOpen(pVfs, zFile, &p->sFile, openFlags, &openFlags); + if( rc!=SQLITE_OK ){ + sqlite3_free(zFile); + sqlite3_free(p); + return 0; + } + p->pVfs = pVfs; + p->alwaysAppend = alwaysAppend; + if( seekEnd ) sqlite3_fseek(p, 0, SQLITE_SEEK_END); + if( doTruncate ) sqlite3_ftruncate(p, 0); + return p; +} + +/* +** Close a file perviously opened by sqlite3_fopen(). +*/ +int sqlite3_fclose(sqlite3_FILE *p){ + p->sFile.pMethods->xClose(&p->sFile); + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Read iAmt bytes from the file p into pBuf. +** +** Return 0 on success or an error code if the full amount could +** not be read. +*/ +int sqlite3_fread( + void *pBuf, /* Write content read into this buffer */ + sqlite3_int64 iAmt, /* Number of bytes to read */ + sqlite3_FILE *p /* Read from this file */ +){ + int rc = p->sFile.pMethods->xRead(&p->sFile, pBuf, iAmt, p->iOfst); + if( rc==SQLITE_OK ){ + p->iOfst += iAmt; + } + return rc; +} + +/* +** Write iAmt bytes from buffer pBuf into the file p. +** +** Return 0 on success or an error code if anything goes wrong. +*/ +int sqlite3_fwrite( + const void *pBuf, /* Take content to be written from this buffer */ + sqlite3_int64 iAmt, /* Number of bytes to write */ + sqlite3_FILE *p /* Write into this file */ +){ + int rc; + + if( p->alwaysAppend ) sqlite3_fseek(p, 0, SQLITE_SEEK_END); + rc = p->sFile.pMethods->xWrite(&p->sFile, pBuf, iAmt, p->iOfst); + if( rc==SQLITE_OK ){ + p->iOfst += iAmt; + } + return rc; +} + +/* +** Truncate an open file to newSize bytes. +*/ +int sqlite3_ftruncate(sqlite3_FILE *p, sqlite3_int64 newSize){ + int rc; + rc = p->sFile.pMethods->xTruncate(&p->sFile, newSize); + return rc; +} + +/* +** Return the current position of the file pointer. +*/ +sqlite3_int64 sqlite3_ftell(sqlite3_FILE *p){ + return p->iOfst; +} + +/* +** Move the file pointer to a new position in the file. +*/ +int sqlite3_fseek(sqlite3_FILE *p, sqlite3_int64 ofst, int whence){ + int rc = SQLITE_OK; + if( whence==SQLITE_SEEK_SET ){ + p->iOfst = ofst; + }else if( whence==SQLITE_SEEK_CUR ){ + p->iOfst += ofst; + }else{ + sqlite3_int64 iCur = 0; + rc = p->sFile.pMethods->xFileSize(&p->sFile, &iCur); + if( rc==SQLITE_OK ){ + p->iOfst = iCur + ofst; + } + } + return rc; +} + +/* +** Rewind the file pointer to the beginning of the file. +*/ +int sqlite3_rewind(sqlite3_FILE *p){ + p->iOfst = 0; + return SQLITE_OK; +} + +/* +** Flush the content of OS cache buffers to disk. (fsync()) +*/ +int sqlite3_fflush(sqlite3_FILE *p){ + return p->sFile.pMethods->xSync(&p->sFile, SQLITE_SYNC_NORMAL); +} + +/* +** Delete the file identified by the URI in the first parameter +*/ +int sqlite3_remove(const char *zURI){ + sqlite3_vfs *pVfs = 0; + char *zFilename = 0; + int rc; + + rc = getFilename(zURI, &pVfs, &zFilename); + if( rc==SQLITE_OK ){ + rc = pVfs->xDelete(pVfs, zFilename, 0); + } + sqlite3_free(zFilename); + return rc; +} + +#endif /* SQLITE_OMIT_VFS_STDIO */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index bd5b41f431..3978ad1369 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -572,6 +572,10 @@ int sqlite3_exec( ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing ** I/O operations on the open file. +** +** Do not confuse the low-level sqlite3_file object described here +** and used by the [VFS] with the high-level [sqlite3_FILE object] +** that provides a stdio-like interface for use by application programs. */ typedef struct sqlite3_file sqlite3_file; struct sqlite3_file { @@ -6823,7 +6827,196 @@ int sqlite3_vtab_on_conflict(sqlite3 *); /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 +/* +** CAPI3REF: Simplified Interface To The VFS +** KEYWORDS: *sqlite3_FILE {*sqlite3_FILE object} +** +** An instance of this object represents an open file in a [VFS]. +** This object is used by simplified I/O routines such as +** [sqlite3_fopen()] and [sqlite3_fread()] and [sqlite3_fwrite()]. +** +** The sqlite3_FILE object provides a simple means to access +** the filesystem through an SQLite [VFS]. This allows, for example, +** ordinary file I/O to occur through VFS shims such as the quota or +** multiplexor or vfstrace, or for I/O to occur on specialized +** application-specific VFSes. The sqlite3_FILE object also provides +** a safe way for the application to read directly from an SQLite database +** file, without the risk of cancelling posix advisory locks when the +** connection is closed, as would happen using stdio or direct OS +** I/O interfaces. +** +** The simplified I/O routines a similar to the "fopen()" family +** of I/O routines in the standard C library, though there are some +** minor differences. Note in particular that [sqlite3_fread()] and +** [sqlite3_fwrite()] have 3 instead of 4 parameters and return a +** success code rather than the number of elements read or written. +** Also, standard C library numeric datatypes such as size_t and off_t are +** replaced in this interface with the 64-bit signed integer type +** [sqlite3_int64]. +** +** The sqlite3_FILE object is not intended to be a general-purpose +** replacement for stdio, but it can be very useful in some +** circumstances. +** +** Do not confuse the high-level sqlite3_FILE object described here +** with the low-level [sqlite3_file] object used by the [VFS]. +** +** See also: +** [sqlite3_fopen()], +** [sqlite3_fread()], +** [sqlite3_fwrite()], +** [sqlite3_fseek()], +** [sqlite3_ftell()], +** [sqlite3_rewind()], +** [sqlite3_fflush()], +** [sqlite3_ftruncate()], +** [sqlite3_close()], +** [sqlite3_remove()]. +*/ +typedef struct sqlite3_FILE sqlite3_FILE; + +/* +** CAPI3REF: Open A File Object +** +** The sqlite3_fopen(F,M) routine opens an [sqlite3_FILE object] on the +** the file identified by [URI filename] F with mode M. Return a pointer +** to the [sqlite3_FILE object] created, or a NULL pointer if the open failed. +** +** The mode M can be one of the following: +** +**
+**
"r" +**
Open the file read-only. Fail if the file does not already exist. +**
"r+" +**
Open the file read and writing, but fail if the file does not +** already exist. +**
"w" +**
Open for reading and writing. Create the file if it does not +** already exist. If the file does already exist, truncate it to zero +** bytes in size immediately after opening. +**
"w+" +**
This works the same as "w". +**
"a" +**
Open for reading and writing. Create the file if it does not +** already exist. Set the file cursor to the end of the file so that +** any write operations that occur prior to an [sqlite3_fseek()] or +** [sqlite3_rewind()] will append to the file. +**
"a+" +**
Open for reading and writing. Create the file if it does not +** already exist. The file cursor is positioned at the beginning of +** the file for reading purposes. However, any writes on the file +** cause the file cursor to move to the end of the file prior to the +** write (and thus append to the file). +**
+** +** Any value for the mode M other than those listed above cause the +** sqlite3_fopen() call to fail. +** +** See also: +** [sqlite3_fread()], +** [sqlite3_fwrite()], +** [sqlite3_fseek()], +** [sqlite3_ftell()], +** [sqlite3_rewind()], +** [sqlite3_fflush()], +** [sqlite3_close()], +** [sqlite3_remove()]. +*/ +sqlite3_FILE *sqlite3_fopen(const char *zURI, const char *zMode); + +/* +** CAPI3REF: Read and Write A File Object +** +** The [sqlite3_fread(B,N,P)] routine reads N bytes of content from +** [sqlite3_FILE object] P and into buffer B. +** The [sqlite3_fwrite(B,N,P)] routine writes N bytes of content from +** buffer B into [sqlite3_FILE object] P. Both routines return +** SQLITE_OK on success or an [error code] on failure. +** +** Reading and writing always occur beginning at the file cursor. The file +** cursor is advanced by N if the read or write is successful and unchanged +** if the read is not successful. Not that the file cursor is +** unchanged by a partial read ([SQLITE_IOERR_SHORT_READ]). +** If the mode from [sqlite3_fopen()] was "a+" then the file cursor is +** always moved to the end of the file prior to every write. +** +** Note that these routines differ from stdio fread() and fwrite() +** functions in two significant ways: These routines use 3 parameters +** instead of 4; these routines always assume an element size of one byte. +** And these routines return a success code, not the number of elements +** read or written. With the routines described here, and unlike +** fread() and fwrite(), a partial read or write is considered an error. +*/ +int sqlite3_fread(void*, sqlite3_int64, sqlite3_FILE*); +int sqlite3_fwrite(const void*, sqlite3_int64, sqlite3_FILE*); +/* +** CAPI3REF: Whence Parameter For sqlite3_fseek() +** +** The 3rd parameter to [sqlite3_fseek(P,N,W)] can take on any of the +** following values. +*/ +#define SQLITE_SEEK_SET 0 +#define SQLITE_SEEK_CUR 1 +#define SQLITE_SEEK_END 2 + +/* +** CAPI3REF: Position The Cursor For An sqlite3_FILE object +** +** This routines move the current cursor position in an [sqlite3_FILE object]. +** The sqlite3_fseek(P,N,W) call move the [sqlite3_FILE object] P to a new +** position N relative to W. W is [SQLITE_SEEK_SET] for the beginning of +** the file, [SQLITE_SEEK_CUR] for the cursor position prior to the current +** move, or [SQLITE_SEEK_END] for the end of the file. +** +** The sqlite3_rewind(P) call move the cursor position to the beginning +** of the file. It is equivalent to sqlite3_fseek(P,0,SQLITE_SEEK_SET). +** +** The sqlite3_ftell(P) call returns the current cursor position. +*/ +int sqlite3_fseek(sqlite3_FILE*, sqlite3_int64, int whence); +int sqlite3_rewind(sqlite3_FILE*); +sqlite3_int64 sqlite3_ftell(sqlite3_FILE*); + +/* +** CAPI3REF: Flush OS File Cache Buffers +** +** A call to sqlite3_fflush(P) causes any writes previously made against +** [sqlite3_FILE object] P to be flushed from the operating-system cache +** to nonvolatile storage. +*/ +int sqlite3_fflush(sqlite3_FILE*); + +/* +** CAPI3REF: Truncate An sqlite3_FILE object +** +** The sqlite3_ftruncate(P,N) interface calls the [sqlite3_FILE object] P +** to be truncated to N bytes in size. +** +** It is undefined what happens if N is larger than the current file size. +** Some [VFS] implementations will extend the file. Others will fail. +** Still others will leave the file in an inconsistent state. +*/ +int sqlite3_ftruncate(sqlite3_FILE*, sqlite3_int64); + +/* +** CAPI3REF: Close an sqlite3_FILE object +** +** Every successful call to [sqlite3_fopen()] should be eventually followed +** by a call to this routine to destroy the [sqlite3_FILE object] that +** [sqlite3_fopen()] created. After calling this routine, the +** [sqlite3_FILE object] must not be used again. +*/ +int sqlite3_fclose(sqlite3_FILE*); + +/* +** CAPI3REF: Remove A File +** +** The sqlite3_remove(U) interface attempts to delete the file identified +** by the [URI filename] U. Query parameters on U can specify a particular +** [VFS] to use to do the deletion. +*/ +int sqlite3_remove(const char *zURI); /* ** Undo the hack that converts floating point types to integer for