/ Check-in [af3b72d9]
Login

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

Overview
Comment:Add file test_demovfs.c, containing a simple VFS implementation that demonstrates how writes to the journal file may be safely buffered by the VFS layer to improve performance on some embedded systems.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:af3b72d94a1b6513f02402af3ada5fb5dd390151
User & Date: dan 2010-04-07 07:57:38
Context
2010-04-07
20:29
When rolling back a savepoint to the beginning of the transaction, make sure to initialize the database size in the btree layer correctly even if the database size field of the header is zeroed. check-in: a3540c6a user: drh tags: trunk
07:57
Add file test_demovfs.c, containing a simple VFS implementation that demonstrates how writes to the journal file may be safely buffered by the VFS layer to improve performance on some embedded systems. check-in: af3b72d9 user: dan tags: trunk
2010-04-05
15:11
Minor comment changes to the OP_OpenEphemeral header. No changes to code. check-in: 8e1d7ef4 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to main.mk.

   227    227     $(TOP)/src/test8.c \
   228    228     $(TOP)/src/test9.c \
   229    229     $(TOP)/src/test_autoext.c \
   230    230     $(TOP)/src/test_async.c \
   231    231     $(TOP)/src/test_backup.c \
   232    232     $(TOP)/src/test_btree.c \
   233    233     $(TOP)/src/test_config.c \
          234  +  $(TOP)/src/test_demovfs.c \
   234    235     $(TOP)/src/test_devsym.c \
   235    236     $(TOP)/src/test_func.c \
   236    237     $(TOP)/src/test_hexio.c \
   237    238     $(TOP)/src/test_init.c \
   238    239     $(TOP)/src/test_intarray.c \
   239    240     $(TOP)/src/test_journal.c \
   240    241     $(TOP)/src/test_malloc.c \

Changes to src/tclsqlite.c.

  3473   3473       extern int Sqlitetest5_Init(Tcl_Interp*);
  3474   3474       extern int Sqlitetest6_Init(Tcl_Interp*);
  3475   3475       extern int Sqlitetest7_Init(Tcl_Interp*);
  3476   3476       extern int Sqlitetest8_Init(Tcl_Interp*);
  3477   3477       extern int Sqlitetest9_Init(Tcl_Interp*);
  3478   3478       extern int Sqlitetestasync_Init(Tcl_Interp*);
  3479   3479       extern int Sqlitetest_autoext_Init(Tcl_Interp*);
         3480  +    extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
  3480   3481       extern int Sqlitetest_func_Init(Tcl_Interp*);
  3481   3482       extern int Sqlitetest_hexio_Init(Tcl_Interp*);
  3482   3483       extern int Sqlitetest_init_Init(Tcl_Interp*);
  3483   3484       extern int Sqlitetest_malloc_Init(Tcl_Interp*);
  3484   3485       extern int Sqlitetest_mutex_Init(Tcl_Interp*);
  3485   3486       extern int Sqlitetestschema_Init(Tcl_Interp*);
  3486   3487       extern int Sqlitetestsse_Init(Tcl_Interp*);
................................................................................
  3499   3500       Sqlitetest5_Init(interp);
  3500   3501       Sqlitetest6_Init(interp);
  3501   3502       Sqlitetest7_Init(interp);
  3502   3503       Sqlitetest8_Init(interp);
  3503   3504       Sqlitetest9_Init(interp);
  3504   3505       Sqlitetestasync_Init(interp);
  3505   3506       Sqlitetest_autoext_Init(interp);
         3507  +    Sqlitetest_demovfs_Init(interp);
  3506   3508       Sqlitetest_func_Init(interp);
  3507   3509       Sqlitetest_hexio_Init(interp);
  3508   3510       Sqlitetest_init_Init(interp);
  3509   3511       Sqlitetest_malloc_Init(interp);
  3510   3512       Sqlitetest_mutex_Init(interp);
  3511   3513       Sqlitetestschema_Init(interp);
  3512   3514       Sqlitetesttclvar_Init(interp);

Added src/test_demovfs.c.

            1  +/*
            2  +** 2010 April 7
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +**
           13  +** An example of a simple VFS implementation that omits complex features
           14  +** often not required or not possible on embedded platforms. Also includes
           15  +** code to buffer writes to the journal file, which can be a significant
           16  +** performance improvement on some embedded platforms.
           17  +**
           18  +*/
           19  +
           20  +/*
           21  +** OVERVIEW
           22  +**
           23  +**   The code in this file implements a minimal SQLite VFS that can be 
           24  +**   used on Linux and other posix-like operating systems. The following 
           25  +**   system calls are used:
           26  +**
           27  +**    File-system: access(), unlink(), getcwd()
           28  +**    File IO:     open(), read(), write(), fsync(), close(), fstat()
           29  +**    Other:       sleep(), usleep(), time()
           30  +**
           31  +**   The following VFS features are omitted:
           32  +**
           33  +**     1. File locking. The user must ensure that there is at most one
           34  +**        connection to each database when using this VFS. Multiple
           35  +**        connections to a single shared-cache count as a single connection
           36  +**        for the purposes of the previous statement.
           37  +**
           38  +**     2. The loading of dynamic extensions (shared libraries).
           39  +**
           40  +**     3. Temporary files. The user must configure SQLite to use in-memory
           41  +**        temp files when using this VFS. The easiest way to do this is to
           42  +**        compile with:
           43  +**
           44  +**          -DSQLITE_TEMP_STORE=3
           45  +**
           46  +**     4. File truncation. As of version 3.6.24, SQLite may run without
           47  +**        a working xTruncate() call, providing the user does not configure
           48  +**        SQLite to use "journal_mode=truncate", or use both
           49  +**        "journal_mode=persist" and ATTACHed databases.
           50  +**
           51  +**   It is assumed that the system uses UNIX-like path-names. Specifically,
           52  +**   that '/' characters are used to separate path components and that
           53  +**   a path-name is a relative path unless it begins with a '/'. And that
           54  +**   no UTF-8 encoded paths are greater than 512 bytes in length.
           55  +**
           56  +** JOURNAL WRITE-BUFFERING
           57  +**
           58  +**   To commit a transaction to the database, SQLite first writes rollback
           59  +**   information into the journal file. This usually consists of 4 steps:
           60  +**
           61  +**     1. The rollback information is sequentially written into the journal
           62  +**        file, starting at the start of the file.
           63  +**     2. The journal file is synced to disk.
           64  +**     3. A modification is made to the first few bytes of the journal file.
           65  +**     4. The journal file is synced to disk again.
           66  +**
           67  +**   Most of the data is written in step 1 using a series of calls to the
           68  +**   VFS xWrite() method. The buffers passed to the xWrite() calls are of
           69  +**   various sizes. For example, as of version 3.6.24, when committing a 
           70  +**   transaction that modifies 3 pages of a database file that uses 4096 
           71  +**   byte pages residing on a media with 512 byte sectors, SQLite makes 
           72  +**   eleven calls to the xWrite() method to create the rollback journal, 
           73  +**   as follows:
           74  +**
           75  +**             Write offset | Bytes written
           76  +**             ----------------------------
           77  +**                        0            512
           78  +**                      512              4
           79  +**                      516           4096
           80  +**                     4612              4
           81  +**                     4616              4
           82  +**                     4620           4096
           83  +**                     8716              4
           84  +**                     8720              4
           85  +**                     8724           4096
           86  +**                    12820              4
           87  +**             ++++++++++++SYNC+++++++++++
           88  +**                        0             12
           89  +**             ++++++++++++SYNC+++++++++++
           90  +**
           91  +**   On many operating systems, this is an efficient way to write to a file.
           92  +**   However, on some embedded systems that do not cache writes in OS 
           93  +**   buffers it is much more efficient to write data in blocks that are
           94  +**   an integer multiple of the sector-size in size and aligned at the
           95  +**   start of a sector.
           96  +**
           97  +**   To work around this, the code in this file allocates a fixed size
           98  +**   buffer of SQLITE_DEMOVFS_BUFFERSZ using sqlite3_malloc() whenever a 
           99  +**   journal file is opened. It uses the buffer to coalesce sequential
          100  +**   writes into aligned SQLITE_DEMOVFS_BUFFERSZ blocks. When SQLite
          101  +**   invokes the xSync() method to sync the contents of the file to disk,
          102  +**   all accumulated data is written out, even if it does not constitute
          103  +**   a complete block. This means the actual IO to create the rollback 
          104  +**   journal for the example transaction above is this:
          105  +**
          106  +**             Write offset | Bytes written
          107  +**             ----------------------------
          108  +**                        0           8192
          109  +**                     8192           4632
          110  +**             ++++++++++++SYNC+++++++++++
          111  +**                        0             12
          112  +**             ++++++++++++SYNC+++++++++++
          113  +**
          114  +**   Much more efficient if the underlying OS is not caching write 
          115  +**   operations.
          116  +*/
          117  +
          118  +#if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX)
          119  +
          120  +#include <sqlite3.h>
          121  +
          122  +#include <assert.h>
          123  +#include <string.h>
          124  +#include <sys/types.h>
          125  +#include <sys/stat.h>
          126  +#include <sys/file.h>
          127  +#include <sys/param.h>
          128  +#include <unistd.h>
          129  +#include <time.h>
          130  +
          131  +/*
          132  +** Size of the write buffer used by journal files in bytes.
          133  +*/
          134  +#ifndef SQLITE_DEMOVFS_BUFFERSZ
          135  +# define SQLITE_DEMOVFS_BUFFERSZ 8192
          136  +#endif
          137  +
          138  +/*
          139  +** When using this VFS, the sqlite3_file* handles that SQLite uses are
          140  +** actually pointers to instances of type DemoFile.
          141  +*/
          142  +typedef struct DemoFile DemoFile;
          143  +struct DemoFile {
          144  +  sqlite3_file base;              /* Base class. Must be first. */
          145  +  int fd;                         /* File descriptor */
          146  +
          147  +  char *aBuffer;                  /* Pointer to malloc'd buffer */
          148  +  int nBuffer;                    /* Valid bytes of data in zBuffer */
          149  +  sqlite3_int64 iBufferOfst;      /* Offset in file of zBuffer[0] */
          150  +};
          151  +
          152  +/*
          153  +** Write directly to the file passed as the first argument. Even if the
          154  +** file has a write-buffer (DemoFile.aBuffer), ignore it.
          155  +*/
          156  +static int demoDirectWrite(
          157  +  DemoFile *p,                    /* File handle */
          158  +  const void *zBuf,               /* Buffer containing data to write */
          159  +  int iAmt,                       /* Size of data to write in bytes */
          160  +  sqlite_int64 iOfst              /* File offset to write to */
          161  +){
          162  +  off_t ofst;                     /* Return value from lseek() */
          163  +  size_t nWrite;                  /* Return value from write() */
          164  +
          165  +  ofst = lseek(p->fd, iOfst, SEEK_SET);
          166  +  if( ofst!=iOfst ){
          167  +    return SQLITE_IOERR_WRITE;
          168  +  }
          169  +
          170  +  nWrite = write(p->fd, zBuf, iAmt);
          171  +  if( nWrite!=iAmt ){
          172  +    return SQLITE_IOERR_WRITE;
          173  +  }
          174  +
          175  +  return SQLITE_OK;
          176  +}
          177  +
          178  +/*
          179  +** Flush the contents of the DemoFile.aBuffer buffer to disk. This is a
          180  +** no-op if this particular file does not have a buffer (i.e. it is not
          181  +** a journal file) or if the buffer is currently empty.
          182  +*/
          183  +static int demoFlushBuffer(DemoFile *p){
          184  +  int rc = SQLITE_OK;
          185  +  if( p->nBuffer ){
          186  +    rc = demoDirectWrite(p, p->aBuffer, p->nBuffer, p->iBufferOfst);
          187  +    p->nBuffer = 0;
          188  +  }
          189  +  return rc;
          190  +}
          191  +
          192  +/*
          193  +** Close a file.
          194  +*/
          195  +static int demoClose(sqlite3_file *pFile){
          196  +  int rc;
          197  +  DemoFile *p = (DemoFile*)pFile;
          198  +  rc = demoFlushBuffer(p);
          199  +  sqlite3_free(p->aBuffer);
          200  +  close(p->fd);
          201  +  return rc;
          202  +}
          203  +
          204  +/*
          205  +** Read data from a file.
          206  +*/
          207  +static int demoRead(
          208  +  sqlite3_file *pFile, 
          209  +  void *zBuf, 
          210  +  int iAmt, 
          211  +  sqlite_int64 iOfst
          212  +){
          213  +  DemoFile *p = (DemoFile*)pFile;
          214  +  off_t ofst;                     /* Return value from lseek() */
          215  +  int nRead;                      /* Return value from read() */
          216  +  int rc;                         /* Return code from demoFlushBuffer() */
          217  +
          218  +  /* Flush any data in the write buffer to disk in case this operation
          219  +  ** is trying to read data the file-region currently cached in the buffer.
          220  +  ** It would be possible to detect this case and possibly save an 
          221  +  ** unnecessary write here, but in practice SQLite will rarely read from
          222  +  ** a journal file when there is data cached in the write-buffer.
          223  +  */
          224  +  rc = demoFlushBuffer(p);
          225  +  if( rc!=SQLITE_OK ){
          226  +    return rc;
          227  +  }
          228  +
          229  +  ofst = lseek(p->fd, iOfst, SEEK_SET);
          230  +  if( ofst!=iOfst ){
          231  +    return SQLITE_IOERR_READ;
          232  +  }
          233  +  nRead = read(p->fd, zBuf, iAmt);
          234  +
          235  +  if( nRead==iAmt ){
          236  +    return SQLITE_OK;
          237  +  }else if( nRead>=0 ){
          238  +    return SQLITE_IOERR_SHORT_READ;
          239  +  }
          240  +
          241  +  return SQLITE_IOERR_READ;
          242  +}
          243  +
          244  +/*
          245  +** Write data to a crash-file.
          246  +*/
          247  +static int demoWrite(
          248  +  sqlite3_file *pFile, 
          249  +  const void *zBuf, 
          250  +  int iAmt, 
          251  +  sqlite_int64 iOfst
          252  +){
          253  +  DemoFile *p = (DemoFile*)pFile;
          254  +  
          255  +  if( p->aBuffer ){
          256  +    char *z = (char *)zBuf;       /* Pointer to remaining data to write */
          257  +    int n = iAmt;                 /* Number of bytes at z */
          258  +    sqlite3_int64 i = iOfst;      /* File offset to write to */
          259  +
          260  +    while( n>0 ){
          261  +      int nCopy;                  /* Number of bytes to copy into buffer */
          262  +
          263  +      /* If the buffer is full, or if this data is not being written directly
          264  +      ** following the data already buffered, flush the buffer. Flushing
          265  +      ** the buffer is a no-op if it is empty.  
          266  +      */
          267  +      if( p->nBuffer==SQLITE_DEMOVFS_BUFFERSZ || p->iBufferOfst+p->nBuffer!=i ){
          268  +        int rc = demoFlushBuffer(p);
          269  +        if( rc!=SQLITE_OK ){
          270  +          return rc;
          271  +        }
          272  +      }
          273  +      assert( p->nBuffer==0 || p->iBufferOfst+p->nBuffer==i );
          274  +      p->iBufferOfst = i - p->nBuffer;
          275  +
          276  +      /* Copy as much data as possible into the buffer. */
          277  +      nCopy = SQLITE_DEMOVFS_BUFFERSZ - p->nBuffer;
          278  +      if( nCopy>n ){
          279  +        nCopy = n;
          280  +      }
          281  +      memcpy(&p->aBuffer[p->nBuffer], z, nCopy);
          282  +      p->nBuffer += nCopy;
          283  +
          284  +      n -= nCopy;
          285  +      i += nCopy;
          286  +      z += nCopy;
          287  +    }
          288  +  }else{
          289  +    return demoDirectWrite(p, zBuf, iAmt, iOfst);
          290  +  }
          291  +
          292  +  return SQLITE_OK;
          293  +}
          294  +
          295  +/*
          296  +** Truncate a file. This is a no-op for this VFS (see header comments at
          297  +** the top of the file).
          298  +*/
          299  +static int demoTruncate(sqlite3_file *pFile, sqlite_int64 size){
          300  +#if 0
          301  +  if( ftruncate(((DemoFile *)pFile)->fd, size) ) return SQLITE_IOERR_TRUNCATE;
          302  +#endif
          303  +  return SQLITE_OK;
          304  +}
          305  +
          306  +/*
          307  +** Sync the contents of the file to the persistent media.
          308  +*/
          309  +static int demoSync(sqlite3_file *pFile, int flags){
          310  +  DemoFile *p = (DemoFile*)pFile;
          311  +  int rc;
          312  +
          313  +  rc = demoFlushBuffer(p);
          314  +  if( rc!=SQLITE_OK ){
          315  +    return rc;
          316  +  }
          317  +
          318  +  rc = fsync(p->fd);
          319  +  return (rc==0 ? SQLITE_OK : SQLITE_IOERR_FSYNC);
          320  +}
          321  +
          322  +/*
          323  +** Write the size of the file in bytes to *pSize.
          324  +*/
          325  +static int demoFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
          326  +  DemoFile *p = (DemoFile*)pFile;
          327  +  int rc;                         /* Return code from fstat() call */
          328  +  struct stat sStat;              /* Output of fstat() call */
          329  +
          330  +  /* Flush the contents of the buffer to disk. As with the flush in the
          331  +  ** demoRead() method, it would be possible to avoid this and save a write
          332  +  ** here and there. But in practice this comes up so infrequently it is
          333  +  ** not worth the trouble.
          334  +  */
          335  +  rc = demoFlushBuffer(p);
          336  +  if( rc!=SQLITE_OK ){
          337  +    return rc;
          338  +  }
          339  +
          340  +  rc = fstat(p->fd, &sStat);
          341  +  if( rc!=0 ) return SQLITE_IOERR_FSTAT;
          342  +  *pSize = sStat.st_size;
          343  +  return SQLITE_OK;
          344  +}
          345  +
          346  +/*
          347  +** Locking functions. The xLock() and xUnlock() methods are both no-ops.
          348  +** The xCheckReservedLock() always indicates that no other process holds
          349  +** a reserved lock on the database file. This ensures that if a hot-journal
          350  +** file is found in the file-system it is rolled back.
          351  +*/
          352  +static int demoLock(sqlite3_file *pFile, int eLock){
          353  +  return SQLITE_OK;
          354  +}
          355  +static int demoUnlock(sqlite3_file *pFile, int eLock){
          356  +  return SQLITE_OK;
          357  +}
          358  +static int demoCheckReservedLock(sqlite3_file *pFile, int *pResOut){
          359  +  *pResOut = 0;
          360  +  return SQLITE_OK;
          361  +}
          362  +
          363  +/*
          364  +** No xFileControl() verbs are implemented by this VFS.
          365  +*/
          366  +static int demoFileControl(sqlite3_file *pFile, int op, void *pArg){
          367  +  return SQLITE_OK;
          368  +}
          369  +
          370  +/*
          371  +** The xSectorSize() and xDeviceCharacteristics() methods. These two
          372  +** may return special values allowing SQLite to optimize file-system 
          373  +** access to some extent. But it is also safe to simply return 0.
          374  +*/
          375  +static int demoSectorSize(sqlite3_file *pFile){
          376  +  return 0;
          377  +}
          378  +static int demoDeviceCharacteristics(sqlite3_file *pFile){
          379  +  return 0;
          380  +}
          381  +
          382  +/*
          383  +** Open a file handle.
          384  +*/
          385  +static int demoOpen(
          386  +  sqlite3_vfs *pVfs,              /* VFS */
          387  +  const char *zName,              /* File to open, or 0 for a temp file */
          388  +  sqlite3_file *pFile,            /* Pointer to DemoFile struct to populate */
          389  +  int flags,                      /* Input SQLITE_OPEN_XXX flags */
          390  +  int *pOutFlags                  /* Output SQLITE_OPEN_XXX flags (or NULL) */
          391  +){
          392  +  static const sqlite3_io_methods demoio = {
          393  +    1,                            /* iVersion */
          394  +    demoClose,                    /* xClose */
          395  +    demoRead,                     /* xRead */
          396  +    demoWrite,                    /* xWrite */
          397  +    demoTruncate,                 /* xTruncate */
          398  +    demoSync,                     /* xSync */
          399  +    demoFileSize,                 /* xFileSize */
          400  +    demoLock,                     /* xLock */
          401  +    demoUnlock,                   /* xUnlock */
          402  +    demoCheckReservedLock,        /* xCheckReservedLock */
          403  +    demoFileControl,              /* xFileControl */
          404  +    demoSectorSize,               /* xSectorSize */
          405  +    demoDeviceCharacteristics     /* xDeviceCharacteristics */
          406  +  };
          407  +
          408  +  DemoFile *p = (DemoFile*)pFile; /* Populate this structure */
          409  +  int oflags = 0;                 /* flags to pass to open() call */
          410  +  char *aBuf = 0;
          411  +
          412  +  if( zName==0 ){
          413  +    return SQLITE_IOERR;
          414  +  }
          415  +
          416  +  if( flags&SQLITE_OPEN_MAIN_JOURNAL ){
          417  +    aBuf = (char *)sqlite3_malloc(SQLITE_DEMOVFS_BUFFERSZ);
          418  +    if( !aBuf ){
          419  +      return SQLITE_NOMEM;
          420  +    }
          421  +  }
          422  +
          423  +  if( flags&SQLITE_OPEN_EXCLUSIVE ) oflags |= O_EXCL;
          424  +  if( flags&SQLITE_OPEN_CREATE )    oflags |= O_CREAT;
          425  +  if( flags&SQLITE_OPEN_READONLY )  oflags |= O_RDONLY;
          426  +  if( flags&SQLITE_OPEN_READWRITE ) oflags |= O_RDWR;
          427  +
          428  +  memset(p, 0, sizeof(DemoFile));
          429  +  p->fd = open(zName, oflags, 0600);
          430  +  if( p->fd<0 ){
          431  +    sqlite3_free(aBuf);
          432  +    return SQLITE_CANTOPEN;
          433  +  }
          434  +  p->aBuffer = aBuf;
          435  +
          436  +  if( pOutFlags ){
          437  +    *pOutFlags = flags;
          438  +  }
          439  +  p->base.pMethods = &demoio;
          440  +  return SQLITE_OK;
          441  +}
          442  +
          443  +/*
          444  +** Delete the file identified by argument zPath. If the dirSync parameter
          445  +** is non-zero, then ensure the file-system modification to delete the
          446  +** file has been synced to disk before returning.
          447  +*/
          448  +static int demoDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
          449  +  int rc;
          450  +  rc = unlink(zPath);
          451  +  if( rc==0 && dirSync ){
          452  +    int dfd;                      /* File descriptor open on directory */
          453  +    int i;                        /* Iterator variable */
          454  +    char zDir[pVfs->mxPathname+1];/* Name of directory containing file zPath */
          455  +
          456  +    /* Figure out the directory name from the path of the file deleted. */
          457  +    sqlite3_snprintf(pVfs->mxPathname, zDir, "%s", zPath);
          458  +    zDir[pVfs->mxPathname] = '\0';
          459  +    for(i=strlen(zDir); i>1 && zDir[i]!='/'; i++);
          460  +    zDir[i] = '\0';
          461  +
          462  +    /* Open a file-descriptor on the directory. Sync. Close. */
          463  +    dfd = open(zDir, O_RDONLY, 0);
          464  +    if( dfd<0 ){
          465  +      rc = -1;
          466  +    }else{
          467  +      rc = fsync(dfd);
          468  +      close(dfd);
          469  +    }
          470  +  }
          471  +  return (rc==0 ? SQLITE_OK : SQLITE_IOERR_DELETE);
          472  +}
          473  +
          474  +/*
          475  +** Query the file-system to see if the named file exists, is readable or
          476  +** is both readable and writable.
          477  +*/
          478  +static int demoAccess(
          479  +  sqlite3_vfs *pVfs, 
          480  +  const char *zPath, 
          481  +  int flags, 
          482  +  int *pResOut
          483  +){
          484  +  int rc;                         /* access() return code */
          485  +  int eAccess = F_OK;             /* Second argument to access() */
          486  +
          487  +  assert( flags==SQLITE_ACCESS_EXISTS       /* access(zPath, F_OK) */
          488  +       || flags==SQLITE_ACCESS_READ         /* access(zPath, R_OK) */
          489  +       || flags==SQLITE_ACCESS_READWRITE    /* access(zPath, R_OK|W_OK) */
          490  +  );
          491  +
          492  +  if( flags==SQLITE_ACCESS_READWRITE ) eAccess = R_OK|W_OK;
          493  +  if( flags==SQLITE_ACCESS_READ )      eAccess = R_OK;
          494  +
          495  +  rc = access(zPath, eAccess);
          496  +  *pResOut = (rc==0);
          497  +  return SQLITE_OK;
          498  +}
          499  +
          500  +/*
          501  +** Argument zPath points to a nul-terminated string containing a file path.
          502  +** If zPath is an absolute path, then it is copied as is into the output 
          503  +** buffer. Otherwise, if it is a relative path, then the equivalent full
          504  +** path is written to the output buffer.
          505  +**
          506  +** This function assumes that paths are UNIX style. Specifically, that:
          507  +**
          508  +**   1. Path components are separated by a '/'. and 
          509  +**   2. Full paths begin with a '/' character.
          510  +*/
          511  +static int demoFullPathname(
          512  +  sqlite3_vfs *pVfs,              /* VFS */
          513  +  const char *zPath,              /* Input path (possibly a relative path) */
          514  +  int nPathOut,                   /* Size of output buffer in bytes */
          515  +  char *zPathOut                  /* Pointer to output buffer */
          516  +){
          517  +  char zDir[pVfs->mxPathname+1];
          518  +  if( zPath[0]=='/' ){
          519  +    zDir[0] = '\0';
          520  +  }else{
          521  +    getcwd(zDir, sizeof(zDir));
          522  +  }
          523  +  zDir[pVfs->mxPathname] = '\0';
          524  +
          525  +  sqlite3_snprintf(nPathOut, zPathOut, "%s/%s", zDir, zPath);
          526  +  zPathOut[nPathOut-1] = '\0';
          527  +
          528  +  return SQLITE_OK;
          529  +}
          530  +
          531  +/*
          532  +** The following four VFS methods:
          533  +**
          534  +**   xDlOpen
          535  +**   xDlError
          536  +**   xDlSym
          537  +**   xDlClose
          538  +**
          539  +** are supposed to implement the functionality needed by SQLite to load
          540  +** extensions compiled as shared objects. This simple VFS does not support
          541  +** this functionality, so the following functions are no-ops.
          542  +*/
          543  +static void *demoDlOpen(sqlite3_vfs *pVfs, const char *zPath){
          544  +  return 0;
          545  +}
          546  +static void demoDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
          547  +  sqlite3_snprintf(nByte, zErrMsg, "Loadable extensions are not supported");
          548  +  zErrMsg[nByte-1] = '\0';
          549  +}
          550  +static void (*demoDlSym(sqlite3_vfs *pVfs, void *pH, const char *z))(void){
          551  +  return 0;
          552  +}
          553  +static void demoDlClose(sqlite3_vfs *pVfs, void *pHandle){
          554  +  return;
          555  +}
          556  +
          557  +/*
          558  +** Parameter zByte points to a buffer nByte bytes in size. Populate this
          559  +** buffer with pseudo-random data.
          560  +*/
          561  +static int demoRandomness(sqlite3_vfs *pVfs, int nByte, char *zByte){
          562  +  return SQLITE_OK;
          563  +}
          564  +
          565  +/*
          566  +** Sleep for at least nMicro microseconds. Return the (approximate) number 
          567  +** of microseconds slept for.
          568  +*/
          569  +static int demoSleep(sqlite3_vfs *pVfs, int nMicro){
          570  +  sleep(nMicro / 1000000);
          571  +  usleep(nMicro % 1000000);
          572  +  return nMicro;
          573  +}
          574  +
          575  +/*
          576  +** Set *pTime to the current UTC time expressed as a Julian day. Return
          577  +** SQLITE_OK if successful, or an error code otherwise.
          578  +**
          579  +**   http://en.wikipedia.org/wiki/Julian_day
          580  +**
          581  +** This implementation is not very good. The current time is rounded to
          582  +** an integer number of seconds. Also, assuming time_t is a signed 32-bit 
          583  +** value, it will stop working some time in the year 2038 AD (the so-called
          584  +** "year 2038" problem that afflicts systems that store time this way). 
          585  +*/
          586  +static int demoCurrentTime(sqlite3_vfs *pVfs, double *pTime){
          587  +  time_t t = time(0);
          588  +  *pTime = t/86400.0 + 2440587.5; 
          589  +  return SQLITE_OK;
          590  +}
          591  +
          592  +/*
          593  +** This function returns a pointer to the VFS implemented in this file.
          594  +** To make the VFS available to SQLite:
          595  +**
          596  +**   sqlite3_vfs_register(sqlite3_demovfs(), 0);
          597  +*/
          598  +sqlite3_vfs *sqlite3_demovfs(void){
          599  +  static sqlite3_vfs demovfs = {
          600  +    1,                            /* iVersion */
          601  +    sizeof(DemoFile),             /* szOsFile */
          602  +    512,                          /* mxPathname */
          603  +    0,                            /* pNext */
          604  +    "demo",                       /* zName */
          605  +    0,                            /* pAppData */
          606  +    demoOpen,                     /* xOpen */
          607  +    demoDelete,                   /* xDelete */
          608  +    demoAccess,                   /* xAccess */
          609  +    demoFullPathname,             /* xFullPathname */
          610  +    demoDlOpen,                   /* xDlOpen */
          611  +    demoDlError,                  /* xDlError */
          612  +    demoDlSym,                    /* xDlSym */
          613  +    demoDlClose,                  /* xDlClose */
          614  +    demoRandomness,               /* xRandomness */
          615  +    demoSleep,                    /* xSleep */
          616  +    demoCurrentTime               /* xCurrentTime */
          617  +  };
          618  +  return &demovfs;
          619  +}
          620  +
          621  +#endif /* !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX) */
          622  +
          623  +
          624  +#ifdef SQLITE_TEST
          625  +
          626  +#include <tcl.h>
          627  +
          628  +#ifdef SQLITE_OS_UNIX
          629  +static int register_demovfs(
          630  +  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
          631  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          632  +  int objc,              /* Number of arguments */
          633  +  Tcl_Obj *CONST objv[]  /* Command arguments */
          634  +){
          635  +  sqlite3_vfs_register(sqlite3_demovfs(), 1);
          636  +  return TCL_OK;
          637  +}
          638  +static int unregister_demovfs(
          639  +  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
          640  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          641  +  int objc,              /* Number of arguments */
          642  +  Tcl_Obj *CONST objv[]  /* Command arguments */
          643  +){
          644  +  sqlite3_vfs_unregister(sqlite3_demovfs());
          645  +  return TCL_OK;
          646  +}
          647  +
          648  +/*
          649  +** Register commands with the TCL interpreter.
          650  +*/
          651  +int Sqlitetest_demovfs_Init(Tcl_Interp *interp){
          652  +  Tcl_CreateObjCommand(interp, "register_demovfs", register_demovfs, 0, 0);
          653  +  Tcl_CreateObjCommand(interp, "unregister_demovfs", unregister_demovfs, 0, 0);
          654  +  return TCL_OK;
          655  +}
          656  +
          657  +#else
          658  +int Sqlitetest_demovfs_Init(Tcl_Interp *interp){ return TCL_OK; }
          659  +#endif
          660  +
          661  +#endif /* SQLITE_TEST */

Changes to test/permutations.test.

   737    737     ioerr.test
   738    738     corrupt4.test 
   739    739     io.test 
   740    740     crash8.test 
   741    741     async4.test 
   742    742     bigfile.test
   743    743   }
          744  +
          745  +if {[info commands register_demovfs] != ""} {
          746  +  run_tests "demovfs" -description {
          747  +    Check that pages are synced before being written (test_journal.c).
          748  +  } -initialize {
          749  +    register_demovfs
          750  +  } -shutdown {
          751  +    unregister_demovfs
          752  +  } -include {
          753  +    insert.test   insert2.test  insert3.test rollback.test 
          754  +    select1.test  select2.test  select3.test
          755  +  }
          756  +}
   744    757   
   745    758   # End of tests
   746    759   #############################################################################
   747    760   
   748    761   if {$::perm::testmode eq "targets"} { puts "" ; exit }
   749    762   
   750    763   # Restore the [sqlite3] command.