]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge the command-line shell enhancements from trunk. Other edits toward
authordrh <drh@noemail.net>
Thu, 25 Oct 2012 15:32:29 +0000 (15:32 +0000)
committerdrh <drh@noemail.net>
Thu, 25 Oct 2012 15:32:29 +0000 (15:32 +0000)
trying to get ssdsim to run.

FossilOrigin-Name: 848f87e22fc6bfbbb7dbc7491c8a20cdd53b7420

1  2 
manifest
manifest.uuid
src/shell.c
src/test_ssdsim.c

diff --cc manifest
index 4603362b799945775be1dc5c060abf0f470cf96e,7a4a586671847433f0f7b49acf13e61428e703a9..6a63d6791d4ffffa90b095eb451d989d4c1dbf0e
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Initial\scheck-in\sof\sa\stest\sVFS\sdesigned\sto\ssimulate\sa\sNAND-flash\sSSD\sfor\sthe\npurpose\sof\smeasuring\sand\ssubsequently\sminimizing\swrite\samplification\scaused\nby\sSQLite.\s\sThe\scode\sin\sthis\scheck-in\scompiles\sbut\sdoes\snot\srun.
- D 2012-10-25T01:50:59.924
 -C Improvements\sto\sthe\scommand-line\sargument\sparsing\sin\sthe\s\ncommand-line\sshell.\s\sCommand-line\soptions\scan\snow\soccur\seither\sbefore\nor\safter\sthe\sdatabase\sname\sand\sfirst\scommand\sand\sare\sstill\saccepted\sand\nprocessed.\s\sCommand-line\soptions\sare\sprocessed\seven\sif\sno\sdatabase\sname\nis\sgiven\s(and\s:memory:\sis\sassumed).
 -D 2012-10-25T15:23:14.369
++C Merge\sthe\scommand-line\sshell\senhancements\sfrom\strunk.\s\sOther\sedits\stoward\ntrying\sto\sget\sssdsim\sto\srun.
++D 2012-10-25T15:32:29.056
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
  F Makefile.in 5f4f26109f9d80829122e0e09f9cda008fa065fb
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@@ -175,7 -175,7 +175,7 @@@ F src/random.c cd4a67b3953b88019f8cd4cc
  F src/resolve.c 7b986a715ac281643309c29257bb58cfae7aa810
  F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
  F src/select.c 9b759521f805e25e97baa2dd64037157fe365817
- F src/shell.c 623242218008315d402d3781c12c26800f4c5edd
 -F src/shell.c d570e6cb3889eb07fdf15808a1b6b136c78e4da5
++F src/shell.c 9e9af864536409a5ceb7e0b432f3e5b0c2cb9249
  F src/sqlite.h.in c7be05ad191d2634292fcc77bdb2bcfa4526eb98
  F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
  F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
@@@ -221,7 -221,6 +221,7 @@@ F src/test_rtree.c aba603c949766c4193f1
  F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
  F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
  F src/test_spellfix.c 76dd8d3111d2f5354c374f71fa23b752bd0b029c
- F src/test_ssdsim.c 8588d7342a0f7860a4f82133739649f68061f19c
++F src/test_ssdsim.c b4b2ff870e31cdecaf5716c4373a7d9ffc1d3762
  F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935
  F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
  F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
@@@ -1022,10 -1021,7 +1022,7 @@@ F tool/vdbe-compress.tcl f12c884766bd14
  F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
  F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
  F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9
--P aaa2d9b0db74d8452d9294de17cff786ab4ec7c8
- R 96b1e07669c40725e91ba8ce05ad794a
- T *branch * ssdsim
- T *sym-ssdsim *
- T -sym-trunk *
 -R 4204a6c0a11b862c569be1046625095c
++P 9e6efcf05402cd0b259c9b2ae57e349800650bd7 317c80cba3688a97ade9cde622cc3bd94cf3436a
++R b62d4d680a3785f5f27895a1b1bebbb9
  U drh
- Z b9dc476f0ad97be89f8c78207cb254e0
 -Z 98d27aa00ef0fe7caf71f35a32885811
++Z cbf54f1147196dcbcbab5648835d5435
diff --cc manifest.uuid
index f8bf09ca638cf52a6cfa156ea92ac96f67905611,00c77f09758311f278a96e2b8b85d1558e6e2fdc..6203b38216569431317f351e8efde83f65ae68d9
@@@ -1,1 -1,1 +1,1 @@@
- 9e6efcf05402cd0b259c9b2ae57e349800650bd7
 -317c80cba3688a97ade9cde622cc3bd94cf3436a
++848f87e22fc6bfbbb7dbc7491c8a20cdd53b7420
diff --cc src/shell.c
index 5e1b6ffb397634cb30939a4ef21927799588143a,d7443b135ad5d600175d3eedf13d64f656a1da86..a810f052de62098bed4990f23d22c7d287035df7
@@@ -2960,14 -2980,9 +2987,14 @@@ int main(int argc, char **argv)
      }else if( strcmp(z,"-multiplex")==0 ){
        extern int sqlite3_multiple_initialize(const char*,int);
        sqlite3_multiplex_initialize(0, 1);
-       ssdsim_register(0, argv[++i], 1);
 +#endif
 +#ifdef SQLITE_ENABLE_SSDSIM
 +    }else if( strcmp(z, "-ssdsim")==0 ){
 +      extern int ssdsim_register(const char*,const char*,int);
++      ssdsim_register(0, cmdline_option_value(argc,argv,++i), 1);
  #endif
      }else if( strcmp(z,"-vfs")==0 ){
-       sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
+       sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
        if( pVfs ){
          sqlite3_vfs_register(pVfs, 1);
        }else{
index a259ac2440277b4b08cd5b3874ecb98f2d2b6679,0000000000000000000000000000000000000000..33dbc3e69c0221a522bffa293abf5de43c96b8f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,912 -1,0 +1,928 @@@
- ** Initialize the ssdsim system.
 +/*
 +** 2012 October 23
 +**
 +** The author disclaims copyright to this source code.  In place of
 +** a legal notice, here is a blessing:
 +**
 +**    May you do good and not evil.
 +**    May you find forgiveness for yourself and forgive others.
 +**    May you share freely, never taking more than you give.
 +**
 +******************************************************************************
 +**
 +** This file contains code implements a VFS shim that attempts to simulate
 +** a NAND-flash SSD in order to estimate the Write Amplification Factor
 +** (WAF) for a typical SQLite workload.
 +**
 +** This simulator is single-threaded, for simplicity.
 +**
 +** USAGE:
 +**
 +** This source file exports a single symbol which is the name of a
 +** function:
 +**
 +**   int ssdsim_register(
 +**     const char *zBaseVfsName,     // Name of the underlying real VFS
 +**     int makeDefault               // Make the new VFS the default
 +**   );
 +*/
 +#include <stdlib.h>
 +#include <string.h>
 +#include <stdio.h>
 +#include <assert.h>
 +#include "sqlite3.h"
 +
 +/* Forward declaration of objects */
 +typedef struct ssdsim_state ssdsim_state;
 +typedef struct ssdsim_inode ssdsim_inode;
 +typedef struct ssdsim_file ssdsim_file;
 +
 +/*
 +** Each file on disk
 +*/
 +struct ssdsim_inode {
 +  ssdsim_inode *pNext;      /* Next inode in a list of them all */
 +  char *zPath;              /* Full pathname of the file */
 +  sqlite3_int64 len;        /* Size of the file in bytes */
 +  int *aiPage;               /* Array of logical page numbers */
 +  ssdsim_file *pFiles;      /* List of open file descriptors */
 +  int inodeFlags;           /* SSDSIM_* flags */
 +  int nShmRegion;           /* Number of allocated shared memory regions */
 +  int szShmRegion;          /* Size of each shared-memory region */
 +  char **apShm;             /* Shared memory regions */
 +};
 +
 +#define SSDSIM_DELETEONCLOSE   0x0001
 +
 +/*
 +** Each open file
 +*/
 +struct ssdsim_file {
 +  sqlite3_file base;            /* Base class.  Must be first */
 +  ssdsim_file *pNext;           /* Next opening of the same inode */
 +  ssdsim_inode *pInode;         /* The file */
 +  signed char eLock;            /* Lock state for this connection */
 +  unsigned char shmOpen;        /* True if SHM is open */
 +  unsigned short shmReadLock;   /* Shared locks held by the shared memory */
 +  unsigned short shmWriteLock;  /* Exclusive locks held by the shared memory */
 +  int openFlags;                /* xOpen() flags used to open this connection */
 +};
 +
 +/*
 +** Page status values
 +*/
 +#define SSDSIM_FREE      0
 +#define SSDSIM_WRITTEN   1
 +#define SSDSIM_OBSOLETE  2
 +
 +/*
 +** Global state of the SSD simulator
 +*/
 +struct ssdsim_state {
 +  int szPage;               /* Size of each page in bytes */
 +  int szEBlock;             /* Size of an erase block in bytes */
 +  sqlite3_int64 szDisk;     /* Total disk space in bytes */
 +  int nPage;                /* Number of slots allocated in apPage[] */
 +  int nEBlock;              /* Nubmer of erase blocks */
 +  int nDealloc;             /* Number of reusable logical page numbers */
 +  int mxAlloc;              /* Maximum allocated logical page number */
 +  unsigned char **apPage;   /* Memory to hold physical pages */
 +  int *aDealloc;            /* Array of reuseable logical page numbers */
 +  int *pageMap;             /* Mapping from logical to physical pages */
 +  unsigned char *eStat;     /* Status of each page */
 +  unsigned int *nErase;     /* Number of erasures for each erase block */
 +  ssdsim_inode *pInode;     /* List of all inodes */
 +  int traceFlag;            /* True to trace operation */
 +  int nHostWrite;           /* Number of pages written by the application */
 +  int nNANDWrite;           /* Number of pages written to NAND-flash */
 +  sqlite3_vfs *pBase;       /* True underlying VFS */
 +};
 +static ssdsim_state g;
 +
 +/*
 +** Method declarations for ssdsim_file.
 +*/
 +static int ssdsimClose(sqlite3_file*);
 +static int ssdsimRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
 +static int ssdsimWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
 +static int ssdsimTruncate(sqlite3_file*, sqlite3_int64 size);
 +static int ssdsimSync(sqlite3_file*, int flags);
 +static int ssdsimFileSize(sqlite3_file*, sqlite3_int64 *pSize);
 +static int ssdsimLock(sqlite3_file*, int);
 +static int ssdsimUnlock(sqlite3_file*, int);
 +static int ssdsimCheckReservedLock(sqlite3_file*, int *);
 +static int ssdsimFileControl(sqlite3_file*, int op, void *pArg);
 +static int ssdsimSectorSize(sqlite3_file*);
 +static int ssdsimDeviceCharacteristics(sqlite3_file*);
 +static int ssdsimShmLock(sqlite3_file*,int,int,int);
 +static int ssdsimShmMap(sqlite3_file*,int,int,int, void volatile **);
 +static void ssdsimShmBarrier(sqlite3_file*);
 +static int ssdsimShmUnmap(sqlite3_file*,int);
 +
 +/*
 +** Method declarations for ssdsim_vfs.
 +*/
 +static int ssdsimOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
 +static int ssdsimDelete(sqlite3_vfs*, const char *zName, int syncDir);
 +static int ssdsimAccess(sqlite3_vfs*, const char *zName, int flags, int *);
 +static int ssdsimFullPathname(sqlite3_vfs*, const char *zName, int, char *);
 +static void *ssdsimDlOpen(sqlite3_vfs*, const char *zFilename);
 +static void ssdsimDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
 +static void (*ssdsimDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
 +static void ssdsimDlClose(sqlite3_vfs*, void*);
 +static int ssdsimRandomness(sqlite3_vfs*, int nByte, char *zOut);
 +static int ssdsimSleep(sqlite3_vfs*, int microseconds);
 +static int ssdsimCurrentTime(sqlite3_vfs*, double*);
 +static int ssdsimGetLastError(sqlite3_vfs*, int, char*);
 +static int ssdsimCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
 +static int ssdsimSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr);
 +static sqlite3_syscall_ptr ssdsimGetSystemCall(sqlite3_vfs*, const char *);
 +static const char *ssdsimNextSystemCall(sqlite3_vfs*, const char *zName);
 +
 +/*
 +** Trace operation.
 +*/
 +static void ssdsimTrace(const char *zFormat, ...){
 +  if( g.traceFlag ){
 +    va_list ap;
 +    char *zMsg;
 +    va_start(ap, zFormat);
 +    vprintf(zFormat, ap);
 +    va_end(ap);
 +  }
 +}
 +
 +/*
 +** Clear all memory associated with the ssd simulator
 +*/
 +static void ssdsimShutdown(void){
 +  int i;
 +  for(i=0; i<g.nPage; i++) sqlite3_free(g.apPage[i]);
 +  sqlite3_free(g.apPage);
 +  g.apPage = 0;
 +  sqlite3_free(g.aDealloc);
 +  g.aDealloc = 0;
 +  g.nDealloc = 0;
 +  g.mxAlloc = 0;
 +  g.nPage = 0;
 +}
 +
 +/*
- static void ssdsimInit(void){
++** Initialize the ssdsim system.  Return non-zero if there is an error.
 +*/
-   if( g.nPage ) return;
++static int ssdsimInit(void){
 +  int nPage;
-   if( g.apPage==0 ){ ssdsimShutdown(); return; }
-   memset(g.apPage, 0, sizeof(g.apPage[0])*nPage);
++  if( g.nPage ) return 0;
 +  if( g.szPage==0 ) g.szPage = 4096;
 +  if( g.szEBlock==0 ) g.szEBlock = 262144;
 +  if( g.szDisk==0 ) g.szDisk = 67108864;
 +  g.nPage = g.szDisk/g.szPage;
 +  g.nEBlock = g.szDisk/g.szEBlock;
 +  sqlite3_free(g.apPage);
 +  g.apPage = sqlite3_malloc( sizeof(g.apPage[0])*g.nPage );
-   if( g.aDealloc==0 ){ ssdsimShutdown(); return; }
++  if( g.apPage==0 ){ ssdsimShutdown(); return 1; }
++  memset(g.apPage, 0, sizeof(g.apPage[0])*g.nPage);
 +  g.aDealloc = sqlite3_malloc( sizeof(g.aDealloc[0])*g.nPage );
-   /* iVersion               */ 2,
++  if( g.aDealloc==0 ){ ssdsimShutdown(); return 1; }
 +  g.nDealloc = 0;
 +  g.mxAlloc = 0;
 +  g.nHostWrite = 0;
 +  g.nNANDWrite = 0;
++  return 0;
 +}
 +
 +/*
 +** Allocate a new, unused logical page number
 +*/
 +static int ssdsimCoreLpnAlloc(void){
 +  if( g.nDealloc ){
 +    return g.aDealloc[--g.nDealloc];
 +  }else if( g.mxAlloc>=g.nPage ){
 +    return -1;
 +  }else{
 +    return g.mxAlloc++;
 +  }
 +}
 +
 +/*
 +** Indicate that the content of a logical page will never again be
 +** read.
 +*/
 +static void ssdsimCoreTrim(int lpn){
 +}
 +
 +/*
 +** Deallocate a logical page number, indicating that it is no longer
 +** in use.
 +*/
 +static int ssdsimCoreLpnDealloc(int lpn){
 +  g.aDealloc[g.nDealloc++] = lpn;
 +}
 +
 +/*
 +** Translate a logical page number into a physical page number.
 +*/
 +static int ssdsimCoreLpnToPpn(int lpn, int writeFlag){
 +  int ppn = lpn;
 +  if( g.apPage[ppn]==0 ){
 +    if( writeFlag ){
 +      g.apPage[ppn] = sqlite3_malloc( g.szPage );
 +    }
 +    if( g.apPage[ppn]==0 ) ppn = -1;
 +  }
 +  return ppn;
 +}
 +
 +/*
 +** Indicate that a transaction boundary has occurred
 +*/
 +static int ssdsimCoreSync(void){
 +}
 +
 +
 +/*
 +** Truncate an inode
 +*/
 +static void ssdsimTruncateInode(ssdsim_inode *pInode, sqlite3_int64 size){
 +  if( pInode->len > size ){
 +    int nOld = pInode->len/g.szPage;
 +    int nNew = size/g.szPage;
 +    int i;
 +    for(i=nOld; i>nNew; i--){
 +      ssdsimCoreLpnDealloc(pInode->aiPage[i]);
 +    }
 +    pInode->len = size;
 +  }
 +}
 +
 +/*
 +** Delete an inode
 +*/
 +static void ssdsimDeleteInode(ssdsim_inode *pInode){
 +  if( pInode->pFiles ){
 +    pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
 +    return;
 +  }
 +  ssdsimTruncateInode(pInode, 0);
 +  sqlite3_free(pInode->apShm);
 +  sqlite3_free(pInode->aiPage);
 +  if( g.pInode==pInode ){
 +    g.pInode = pInode->pNext;
 +  }else{
 +    ssdsim_inode *pX;
 +    for(pX=g.pInode; pX && pX->pNext!=pInode; pX=pX->pNext){}
 +    if( pX ) pX->pNext = pInode->pNext;
 +  }
 +  sqlite3_free(pInode);
 +}
 +
 +
 +/*
 +** Close an ssdsim-file.
 +*/
 +static int ssdsimClose(sqlite3_file *pFile){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  int rc;
 +  ssdsim_inode *pInode = p->pInode;
 +  if( p==pInode->pFiles ){
 +    pInode->pFiles = p->pNext;
 +    if( (pInode->inodeFlags & SSDSIM_DELETEONCLOSE)!=0 ){
 +      ssdsimDeleteInode(pInode);
 +    }
 +  }else{
 +    ssdsim_file *pX;
 +    for(pX = pInode->pFiles; pX && pX->pNext!=p; pX=pX->pNext){}
 +    if( pX ) pX->pNext = p->pNext;
 +  }
 +  memset(p, 0, sizeof(*p));
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Read data from an ssdsim-file.
 +*/
 +static int ssdsimRead(
 +  sqlite3_file *pFile, 
 +  void *zBuf, 
 +  int iAmt, 
 +  sqlite_int64 iOfst
 +){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  int rc = SQLITE_OK;
 +  int lpn, ppn, n;
 +  unsigned char *pOut = (unsigned char*)zBuf;
 +  unsigned char *pContent;
 +  while( iAmt>0 ){
 +    if( iAmt+iOfst>pInode->len ){
 +      rc = SQLITE_IOERR_SHORT_READ;
 +      iAmt = pInode->len - iOfst;
 +      if( iAmt<=0 ) break;
 +    }
 +    lpn = pInode->aiPage[iOfst/g.szPage];
 +    ppn = ssdsimCoreLpnToPpn(lpn, 0);
 +    n = iAmt;
 +    if( (iOfst+n-1)*g.szPage > lpn ){
 +      n = (lpn+1)*g.szPage - iOfst;
 +    }
 +    if( ppn>=0 && ppn<g.nPage && (pContent = g.apPage[ppn])!=0 ){
 +      memcpy(pOut, &pContent[iOfst%g.szPage], n);
 +    }else{
 +      memset(pOut, 0, n);
 +    }
 +    iOfst += n;
 +    iAmt -= n;
 +    pOut += n;
 +  }
 +  return rc;
 +}
 +
 +/*
 +** Write data to an ssdsim-file.
 +*/
 +static int ssdsimWrite(
 +  sqlite3_file *pFile, 
 +  const void *zBuf, 
 +  int iAmt, 
 +  sqlite_int64 iOfst
 +){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  int rc = SQLITE_OK;
 +  int pn, lpn, ppn;
 +  sqlite3_int64 lenNew;
 +  const unsigned char *pIn = (const unsigned char*)zBuf;
 +  unsigned char *pDest;
 +
 +  lenNew = iOfst+iAmt;
 +  if( lenNew <= pInode->len ){
 +    lenNew = pInode->len;
 +  }else{
 +    int nOld, nNew;
 +    int *aiPage;
 +    nOld = pInode->len/g.szPage;
 +    nNew = (iOfst+iAmt)/g.szPage;
 +    if( nOld<nNew ){
 +      aiPage = sqlite3_realloc(pInode->aiPage, nNew*sizeof(int));
 +      if( aiPage==0 ) return SQLITE_NOMEM;
 +      memset(aiPage+nOld, 0xff, sizeof(int)*(nNew - nOld));
 +      pInode->aiPage = aiPage;
 +    }
 +  }
 +  while( iAmt>0 ){
 +    int n;
 +    lpn = pInode->aiPage[iOfst/g.szPage];
 +    if( lpn<0 ){
 +      lpn = ssdsimCoreLpnAlloc();
 +      if( lpn<0 ) return SQLITE_FULL;
 +      pInode->aiPage[iOfst/g.szPage];
 +    }
 +    ppn = ssdsimCoreLpnToPpn(lpn, 1);
 +    if( ppn<0 ) return SQLITE_NOMEM;
 +    n = iAmt;
 +    if( (iOfst+n-1)*g.szPage > lpn ){
 +      n = (lpn+1)*g.szPage - iOfst;
 +    }
 +    pDest = g.apPage[ppn];
 +    memcpy(pDest, pIn, n);
 +    iOfst += n;
 +    iAmt -= n;
 +    pIn += n;
 +  }
 +  pInode->len = lenNew;
 +  return rc;
 +}
 +
 +/*
 +** Truncate an ssdsim-file.
 +*/
 +static int ssdsimTruncate(sqlite3_file *pFile, sqlite_int64 size){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  ssdsimTruncateInode(pInode, size);
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Sync an ssdsim-file.
 +*/
 +static int ssdsimSync(sqlite3_file *pFile, int flags){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  ssdsimCoreSync();
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Return the current file-size of an ssdsim-file.
 +*/
 +static int ssdsimFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  *pSize = pInode->len;
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Return the name of a lock.
 +*/
 +static const char *lockName(int eLock){
 +  const char *azLockNames[] = {
 +     "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE"
 +  };
 +  if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){
 +    return "???";
 +  }else{
 +    return azLockNames[eLock];
 +  }
 +}
 +
 +/*
 +** Lock an ssdsim-file.
 +*/
 +static int ssdsimLock(sqlite3_file *pFile, int eLock){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  ssdsim_file *pF;
 +  int rc = SQLITE_OK;
 +  if( eLock==SQLITE_LOCK_SHARED ){
 +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +      if( pF!=p && pF->eLock>=SQLITE_LOCK_PENDING ) return SQLITE_BUSY;
 +    }
 +  }else if( eLock>=SQLITE_LOCK_RESERVED ){
 +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +      if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ) return SQLITE_BUSY;
 +    }
 +  }else if( eLock==SQLITE_LOCK_EXCLUSIVE ){
 +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +      if( pF!=p && pF->eLock>=SQLITE_LOCK_SHARED ){
 +        eLock = SQLITE_LOCK_PENDING;
 +        rc = SQLITE_BUSY;
 +      }
 +    }
 +  }
 +  p->eLock = eLock;
 +  return rc;
 +}
 +
 +/*
 +** Unlock an ssdsim-file.
 +*/
 +static int ssdsimUnlock(sqlite3_file *pFile, int eLock){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  if( p->eLock>eLock ) p->eLock = eLock;
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Check if another file-handle holds a RESERVED lock on an ssdsim-file.
 +*/
 +static int ssdsimCheckReservedLock(sqlite3_file *pFile, int *pResOut){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  ssdsim_file *pF;
 +  int rc = 0;
 +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +    if( pF!=p && pF->eLock>=SQLITE_LOCK_RESERVED ){
 +      rc = 1;
 +      break;
 +    }
 +  }
 +  *pResOut = rc;
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** File control method. For custom operations on an ssdsim-file.
 +*/
 +static int ssdsimFileControl(sqlite3_file *pFile, int op, void *pArg){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  switch( op ){
 +    case SQLITE_FCNTL_LOCKSTATE: {
 +      *(int*)pArg = p->eLock;
 +      return SQLITE_OK;
 +    }
 +    case SQLITE_FCNTL_VFSNAME: {
 +      *(char**)pArg = sqlite3_mprintf("ssdsim");
 +      return SQLITE_OK;
 +    }
 +    case SQLITE_FCNTL_PRAGMA: {
 +#if 0
 +      const char *const* a = (const char*const*)pArg;
 +      sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
 +      zOp = zBuf;
 +#endif
 +      break;
 +    }
 +    default: {
 +      break;
 +    }
 +  }
 +  return SQLITE_NOTFOUND;
 +}
 +
 +/*
 +** Return the sector-size in bytes for an ssdsim-file.
 +*/
 +static int ssdsimSectorSize(sqlite3_file *pFile){
 +  return g.szPage;
 +}
 +
 +/*
 +** Return the device characteristic flags supported by an ssdsim-file.
 +*/
 +static int ssdsimDeviceCharacteristics(sqlite3_file *pFile){
 +  return 
 +     SQLITE_IOCAP_ATOMIC |
 +     SQLITE_IOCAP_POWERSAFE_OVERWRITE |
 +     SQLITE_IOCAP_SAFE_APPEND |
 +     SQLITE_IOCAP_SEQUENTIAL |
 +     SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
 +     0;
 +}
 +
 +/*
 +** Shared-memory operations.
 +*/
 +static int ssdsimShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  ssdsim_file *pF;
 +  unsigned int lockMask = 0;
 +
 +  /* Constraints on the SQLite core: */
 +  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
 +  assert( n>=1 );
 +  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
 +       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
 +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
 +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
 +  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
 +
 +  /* Mask of bits involved in this lock */
 +  lockMask = (1<<(ofst+n)) - (1<<ofst);
 +
 +  /* The unlock case */
 +  if( flags & SQLITE_SHM_UNLOCK ){
 +    p->shmWriteLock &= ~lockMask;
 +    p->shmReadLock &= ~lockMask;
 +    return SQLITE_OK;
 +  }
 +
 +  /* The shared-lock case */
 +  if( flags & SQLITE_SHM_SHARED ){
 +    /* Disallow if any sibling (including ourself) holds an exclusive lock */
 +    for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +      if( pF->shmWriteLock & lockMask ){
 +        return SQLITE_BUSY;
 +      }
 +    }
 +    p->shmReadLock |= lockMask;
 +    return SQLITE_OK;
 +  }
 +
 +  /* The rest of this procedure is the exclusive lock case */
 +  assert( flags & SQLITE_SHM_EXCLUSIVE );
 +
 +  /* Disallow an exclusive if any kind of lock is held by any other */
 +  for(pF=pInode->pFiles; pF; pF=pF->pNext){
 +    if( pF==p ) continue;
 +    if( (pF->shmWriteLock & lockMask)!=0 ){
 +      return SQLITE_BUSY;
 +    }
 +    if( (pF->shmReadLock & lockMask)!=0 ){
 +      return SQLITE_BUSY;
 +    }
 +  }
 +  p->shmWriteLock |= lockMask;
 +  return SQLITE_OK;
 +}
 +static int ssdsimShmMap(
 +  sqlite3_file *pFile, 
 +  int iRegion, 
 +  int szRegion, 
 +  int isWrite, 
 +  void volatile **pp
 +){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  char **apShm;
 +  int i;
 +  if( p->shmOpen==0 ){
 +    p->shmOpen = 1;
 +    p->shmReadLock = 0;
 +    p->shmWriteLock = 0;
 +  }
 +  if( pInode->nShmRegion<=iRegion ){
 +    if( isWrite==0 ){
 +      *pp = 0;
 +      return SQLITE_OK;
 +    }
 +    apShm = sqlite3_realloc(pInode->apShm, 
 +                            (iRegion+1)*sizeof(pInode->apShm[0]));
 +    if( apShm==0 ) return SQLITE_NOMEM;
 +    pInode->apShm = apShm;
 +    for(i=pInode->nShmRegion; i<=iRegion; i++){
 +      apShm[i] = sqlite3_malloc(szRegion);
 +      if( apShm[i]==0 ) return SQLITE_NOMEM;
 +      memset(apShm[i], 0, szRegion);
 +      pInode->nShmRegion = i+1;
 +    }
 +    pInode->szShmRegion = szRegion;
 +  }
 +  *pp = pInode->apShm[iRegion];
 +  return SQLITE_OK;
 +}
 +static void ssdsimShmBarrier(sqlite3_file *pFile){
 +  /* noop */
 +}
 +static int ssdsimShmUnmap(sqlite3_file *pFile, int delFlag){
 +  ssdsim_file *p = (ssdsim_file *)pFile;
 +  ssdsim_inode *pInode = p->pInode;
 +  if( p->shmOpen ){
 +    ssdsim_file *pF;
 +    unsigned char shmOpen = 0;
 +    p->shmOpen = 0;
 +    for(pF=pInode->pFiles; pF; pF=pF->pNext) shmOpen |= pF->shmOpen;
 +    if( !shmOpen ){
 +      int i;
 +      for(i=0; i<pInode->nShmRegion; i++) sqlite3_free(pInode->apShm[i]);
 +      sqlite3_free(pInode->apShm);
 +      pInode->apShm = 0;
 +      pInode->nShmRegion = 0;
 +    }
 +  }
 +  return SQLITE_OK;
 +}
 +
 +static const sqlite3_io_methods ssdsim_io_methods = {
-   ssdsim_inode *pInode = ssdsimFindInode(zName);
++  /* iVersion               */ 3,
 +  /* xClose                 */ ssdsimClose,
 +  /* xRead                  */ ssdsimRead,
 +  /* xWrite                 */ ssdsimWrite,
 +  /* xTruncate              */ ssdsimTruncate,
 +  /* xSync                  */ ssdsimSync,
 +  /* xFileSize              */ ssdsimFileSize,
 +  /* xLock                  */ ssdsimLock,
 +  /* xUnlock                */ ssdsimUnlock,
 +  /* xCheckReservedLock     */ ssdsimCheckReservedLock,
 +  /* xFileControl           */ ssdsimFileControl,
 +  /* xSectorSize            */ ssdsimSectorSize,
 +  /* xDeviceCharacteristics */ ssdsimDeviceCharacteristics,
 +  /* xShmMap                */ ssdsimShmMap,
 +  /* xShmLock               */ ssdsimShmLock,
 +  /* xShmBarrier            */ ssdsimShmBarrier,
 +  /* xShmUnmap              */ ssdsimShmUnmap
 +};
 +
 +/*
 +** Find an inode given its name.
 +*/
 +static ssdsim_inode *ssdsimFindInode(const char *zName){
 +  ssdsim_inode *pInode;
 +  for(pInode=g.pInode; pInode; pInode=pInode->pNext){
 +    if( strcmp(pInode->zPath, zName)==0 ) break;
 +  }
 +  return pInode;
 +}
 +
 +/*
 +** Open an ssdsim file handle.
 +*/
 +static int ssdsimOpen(
 +  sqlite3_vfs *pVfs,
 +  const char *zName,
 +  sqlite3_file *pFile,
 +  int flags,
 +  int *pOutFlags
 +){
 +  int rc;
 +  ssdsim_file *p = (ssdsim_file *)pFile;
-   ssdsim_inode *pInode = ssdsimFindInode(zPath);
++  ssdsim_inode *pInode;
++  char zTempname[100];
++
++  if( ssdsimInit() ) return SQLITE_CANTOPEN;
++  if( zName==0 ){
++    sqlite3_uint64 r;
++    sqlite3_randomness(sizeof(r), &r);
++    sqlite3_snprintf(sizeof(zTempname), zTempname, "/tmp%llx", r);
++    zName = zTempname;
++  }
++  pInode = ssdsimFindInode(zName);
 +  if( pInode==0 ){
 +    int n = (int)strlen(zName);
 +    pInode = sqlite3_malloc( sizeof(*pInode) + n + 1 );
 +    if( pInode==0 ) return SQLITE_NOMEM;
 +    memset(pInode, 0, sizeof(*pInode));
 +    pInode->pNext = g.pInode;
 +    g.pInode = pInode;
 +    pInode->zPath = (char*)&pInode[1];
 +    strcpy(pInode->zPath, zName);
 +    pInode->len = 0;
 +    pInode->aiPage = 0;
 +    pInode->pFiles = 0;
 +    pInode->inodeFlags = 0;
 +    pInode->nShmRegion = 0;
 +    pInode->szShmRegion = 0;
 +    pInode->apShm = 0;
 +    if( flags & SQLITE_OPEN_DELETEONCLOSE ){
 +      pInode->inodeFlags |= SSDSIM_DELETEONCLOSE;
 +    }
 +  }
 +  p->pInode = pInode;
 +  p->pNext = pInode->pFiles;
 +  pInode->pFiles = p;
 +  p->eLock = 0;
 +  p->shmOpen = 0;
 +  p->shmReadLock = 0;
 +  p->shmWriteLock = 0;
 +  p->openFlags = flags;
 +  p->base.pMethods = &ssdsim_io_methods;
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Delete the file located at zPath. If the dirSync argument is true,
 +** ensure the file-system modifications are synced to disk before
 +** returning.
 +*/
 +static int ssdsimDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
-   ssdsim_inode *pInode = ssdsimFindInode(zPath);
++  ssdsim_inode *pInode;
++
++  if( ssdsimInit() ) return SQLITE_CANTOPEN;
++  pInode = ssdsimFindInode(zPath);
 +  if( pInode==0 ) return SQLITE_NOTFOUND;
 +  ssdsimDeleteInode(pInode);
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Test for access permissions. Return true if the requested permission
 +** is available, or false otherwise.
 +*/
 +static int ssdsimAccess(
 +  sqlite3_vfs *pVfs, 
 +  const char *zPath, 
 +  int flags, 
 +  int *pResOut
 +){
++  ssdsim_inode *pInode;
++  if( ssdsimInit() ) return SQLITE_CANTOPEN;
++  pInode = ssdsimFindInode(zPath);
 +  *pResOut = pInode!=0;
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Populate buffer zOut with the full canonical pathname corresponding
 +** to the pathname in zPath. zOut is guaranteed to point to a buffer
 +** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
 +*/
 +static int ssdsimFullPathname(
 +  sqlite3_vfs *pVfs, 
 +  const char *zPath, 
 +  int nOut, 
 +  char *zOut
 +){
 +  while( zPath[0]=='/' ) zPath++;
 +  sqlite3_snprintf(nOut, zOut, "/%s", zPath);
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Open the dynamic library located at zPath and return a handle.
 +*/
 +static void *ssdsimDlOpen(sqlite3_vfs *pVfs, const char *zPath){
 +  return 0;
 +}
 +
 +/*
 +** Populate the buffer zErrMsg (size nByte bytes) with a human readable
 +** utf-8 string describing the most recent error encountered associated 
 +** with dynamic libraries.
 +*/
 +static void ssdsimDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
 +  sqlite3_snprintf(nByte, zErrMsg, "not supported by this VFS");
 +}
 +
 +/*
 +** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
 +*/
 +static void (*ssdsimDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){
 +  return 0;
 +}
 +
 +/*
 +** Close the dynamic library handle pHandle.
 +*/
 +static void ssdsimDlClose(sqlite3_vfs *pVfs, void *pHandle){
 +}
 +
 +/*
 +** Populate the buffer pointed to by zBufOut with nByte bytes of 
 +** random data.
 +*/
 +static int ssdsimRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
 +  return g.pBase->xRandomness(g.pBase, nByte, zBufOut);
 +}
 +
 +/*
 +** Sleep for nMicro microseconds. Return the number of microseconds 
 +** actually slept.
 +*/
 +static int ssdsimSleep(sqlite3_vfs *pVfs, int nMicro){
 +  return g.pBase->xSleep(g.pBase, nMicro);
 +}
 +
 +/*
 +** Return the current time as a Julian Day number in *pTimeOut.
 +*/
 +static int ssdsimCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
 +  return g.pBase->xCurrentTime(g.pBase, pTimeOut);
 +}
 +static int ssdsimCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
 +  return g.pBase->xCurrentTimeInt64(g.pBase, pTimeOut);
 +}
 +
 +/*
 +** Return th3 emost recent error code and message
 +*/
 +static int ssdsimGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
 +  return SQLITE_OK;
 +}
 +
 +/*
 +** Override system calls.
 +*/
 +static int ssdsimSetSystemCall(
 +  sqlite3_vfs *pVfs,
 +  const char *zName,
 +  sqlite3_syscall_ptr pFunc
 +){
 +  return SQLITE_NOTFOUND;
 +}
 +static sqlite3_syscall_ptr ssdsimGetSystemCall(
 +  sqlite3_vfs *pVfs,
 +  const char *zName
 +){
 +  return 0;
 +}
 +static const char *ssdsimNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
 +  return 0;
 +}
 +
 +static sqlite3_vfs ssdsim_vfs = {
 +  /* iVersion          */ 3,
 +  /* szOsFile          */ sizeof(ssdsim_file),
 +  /* mxPathname        */ 1024,
 +  /* pNext             */ 0,
 +  /* zName             */ "ssdsim",
 +  /* pAppData          */ 0,
 +  /* xOpen             */ ssdsimOpen,
 +  /* xDelete           */ ssdsimDelete,
 +  /* xAccess           */ ssdsimAccess,
 +  /* xFullPathname     */ ssdsimFullPathname,
 +  /* xDlOpen           */ ssdsimDlOpen,
 +  /* xDlError          */ ssdsimDlError,
 +  /* xDlSym            */ ssdsimDlSym,
 +  /* xDlClose          */ ssdsimDlClose,
 +  /* xRandomness       */ ssdsimRandomness,
 +  /* xSleep            */ ssdsimSleep,
 +  /* xCurrentTime      */ ssdsimCurrentTime,
 +  /* xGetLastError     */ ssdsimGetLastError,
 +  /* xCurrentTimeInt64 */ ssdsimCurrentTimeInt64,
 +  /* xSetSystemCall    */ ssdsimSetSystemCall,
 +  /* xGetSystemCall    */ ssdsimGetSystemCall,
 +  /* xNextSystemCall   */ ssdsimNextSystemCall
 +};
 +
 +/*
 +** Clients invoke this routine to register the SSD simulator
 +*/
 +int ssdsim_register(
 +   const char *zBaseName,          /* Name of the underlying VFS */
 +   const char *zParams,            /* Configuration parameter */
 +   int makeDefault                 /* True to make the new VFS the default */
 +){
 +  sqlite3_vfs *pNew;
 +  sqlite3_vfs *pRoot;
 +
 +  if( g.pBase ) return SQLITE_ERROR;
 +  g.pBase = sqlite3_vfs_find(zBaseName);
 +  if( g.pBase==0 ) return SQLITE_NOTFOUND;
 +  return sqlite3_vfs_register(&ssdsim_vfs, makeDefault);
 +}
 +
 +/*
 +** Clients invoke this routine to get SSD simulator write-amplification
 +** statistics.
 +*/
 +void ssdsim_report(FILE *pOut, int reportNum){
 +  fprintf(pOut, "host page writes...... %9d\n", g.nHostWrite);
 +  fprintf(pOut, "NAND page writes...... %9d\n", g.nNANDWrite);
 +  if( g.nHostWrite>0 ){
 +    fprintf(pOut, "write amplification... %11.2f\n",
 +            (double)g.nNANDWrite/(double)g.nHostWrite);
 +  }
 +}