summaryrefslogtreecommitdiffstats
path: root/ext/misc/vfsstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/misc/vfsstat.c')
-rw-r--r--ext/misc/vfsstat.c820
1 files changed, 820 insertions, 0 deletions
diff --git a/ext/misc/vfsstat.c b/ext/misc/vfsstat.c
new file mode 100644
index 0000000..83a7a3d
--- /dev/null
+++ b/ext/misc/vfsstat.c
@@ -0,0 +1,820 @@
+/*
+** 2016-05-27
+**
+** 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 the implementation of an SQLite vfs shim that
+** tracks I/O. Access to the accumulated status counts is provided using
+** an eponymous virtual table.
+*/
+#include <sqlite3ext.h>
+SQLITE_EXTENSION_INIT1
+
+/*
+** This module contains code for a wrapper VFS that cause stats for
+** most VFS calls to be recorded.
+**
+** To use this module, first compile it as a loadable extension. See
+** https://www.sqlite.org/loadext.html#build for compilations instructions.
+**
+** After compliing, load this extension, then open database connections to be
+** measured. Query usages status using the vfsstat virtual table:
+**
+** SELECT * FROM vfsstat;
+**
+** Reset counters using UPDATE statements against vfsstat:
+**
+** UPDATE vfsstat SET count=0;
+**
+** EXAMPLE SCRIPT:
+**
+** .load ./vfsstat
+** .open test.db
+** DROP TABLE IF EXISTS t1;
+** CREATE TABLE t1(x,y);
+** INSERT INTO t1 VALUES(123, randomblob(5000));
+** CREATE INDEX t1x ON t1(x);
+** DROP TABLE t1;
+** VACUUM;
+** SELECT * FROM vfsstat WHERE count>0;
+**
+** LIMITATIONS:
+**
+** This module increments counters without using mutex protection. So if
+** two or more threads try to use this module at the same time, race conditions
+** may occur which mess up the counts. This is harmless, other than giving
+** incorrect statistics.
+*/
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/*
+** File types
+*/
+#define VFSSTAT_MAIN 0 /* Main database file */
+#define VFSSTAT_JOURNAL 1 /* Rollback journal */
+#define VFSSTAT_WAL 2 /* Write-ahead log file */
+#define VFSSTAT_MASTERJRNL 3 /* Master journal */
+#define VFSSTAT_SUBJRNL 4 /* Subjournal */
+#define VFSSTAT_TEMPDB 5 /* TEMP database */
+#define VFSSTAT_TEMPJRNL 6 /* Journal for TEMP database */
+#define VFSSTAT_TRANSIENT 7 /* Transient database */
+#define VFSSTAT_ANY 8 /* Unspecified file type */
+#define VFSSTAT_nFile 9 /* This many file types */
+
+/* Names of the file types. These are allowed values for the
+** first column of the vfsstat virtual table.
+*/
+static const char *azFile[] = {
+ "database", "journal", "wal", "master-journal", "sub-journal",
+ "temp-database", "temp-journal", "transient-db", "*"
+};
+
+/*
+** Stat types
+*/
+#define VFSSTAT_BYTESIN 0 /* Bytes read in */
+#define VFSSTAT_BYTESOUT 1 /* Bytes written out */
+#define VFSSTAT_READ 2 /* Read requests */
+#define VFSSTAT_WRITE 3 /* Write requests */
+#define VFSSTAT_SYNC 4 /* Syncs */
+#define VFSSTAT_OPEN 5 /* File opens */
+#define VFSSTAT_LOCK 6 /* Lock requests */
+#define VFSSTAT_ACCESS 0 /* xAccess calls. filetype==ANY only */
+#define VFSSTAT_DELETE 1 /* xDelete calls. filetype==ANY only */
+#define VFSSTAT_FULLPATH 2 /* xFullPathname calls. ANY only */
+#define VFSSTAT_RANDOM 3 /* xRandomness calls. ANY only */
+#define VFSSTAT_SLEEP 4 /* xSleep calls. ANY only */
+#define VFSSTAT_CURTIME 5 /* xCurrentTime calls. ANY only */
+#define VFSSTAT_nStat 7 /* This many stat types */
+
+
+/* Names for the second column of the vfsstat virtual table for all
+** cases except when the first column is "*" or VFSSTAT_ANY. */
+static const char *azStat[] = {
+ "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock",
+};
+static const char *azStatAny[] = {
+ "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp",
+ "not-used"
+};
+
+/* Total number of counters */
+#define VFSSTAT_MXCNT (VFSSTAT_nStat*VFSSTAT_nFile)
+
+/*
+** Performance stats are collected in an instance of the following
+** global array.
+*/
+static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT];
+
+/*
+** Access to a specific counter
+*/
+#define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)])
+
+/*
+** Forward declaration of objects used by this utility
+*/
+typedef struct VStatVfs VStatVfs;
+typedef struct VStatFile VStatFile;
+
+/* An instance of the VFS */
+struct VStatVfs {
+ sqlite3_vfs base; /* VFS methods */
+ sqlite3_vfs *pVfs; /* Parent VFS */
+};
+
+/* An open file */
+struct VStatFile {
+ sqlite3_file base; /* IO methods */
+ sqlite3_file *pReal; /* Underlying file handle */
+ unsigned char eFiletype; /* What type of file is this */
+};
+
+#define REALVFS(p) (((VStatVfs*)(p))->pVfs)
+
+/*
+** Methods for VStatFile
+*/
+static int vstatClose(sqlite3_file*);
+static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int vstatTruncate(sqlite3_file*, sqlite3_int64 size);
+static int vstatSync(sqlite3_file*, int flags);
+static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int vstatLock(sqlite3_file*, int);
+static int vstatUnlock(sqlite3_file*, int);
+static int vstatCheckReservedLock(sqlite3_file*, int *pResOut);
+static int vstatFileControl(sqlite3_file*, int op, void *pArg);
+static int vstatSectorSize(sqlite3_file*);
+static int vstatDeviceCharacteristics(sqlite3_file*);
+static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
+static int vstatShmLock(sqlite3_file*, int offset, int n, int flags);
+static void vstatShmBarrier(sqlite3_file*);
+static int vstatShmUnmap(sqlite3_file*, int deleteFlag);
+static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
+static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
+
+/*
+** Methods for VStatVfs
+*/
+static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename);
+static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
+static void vstatDlClose(sqlite3_vfs*, void*);
+static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int vstatSleep(sqlite3_vfs*, int microseconds);
+static int vstatCurrentTime(sqlite3_vfs*, double*);
+static int vstatGetLastError(sqlite3_vfs*, int, char *);
+static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static VStatVfs vstat_vfs = {
+ {
+ 2, /* iVersion */
+ 0, /* szOsFile (set by register_vstat()) */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "vfslog", /* zName */
+ 0, /* pAppData */
+ vstatOpen, /* xOpen */
+ vstatDelete, /* xDelete */
+ vstatAccess, /* xAccess */
+ vstatFullPathname, /* xFullPathname */
+ vstatDlOpen, /* xDlOpen */
+ vstatDlError, /* xDlError */
+ vstatDlSym, /* xDlSym */
+ vstatDlClose, /* xDlClose */
+ vstatRandomness, /* xRandomness */
+ vstatSleep, /* xSleep */
+ vstatCurrentTime, /* xCurrentTime */
+ vstatGetLastError, /* xGetLastError */
+ vstatCurrentTimeInt64 /* xCurrentTimeInt64 */
+ },
+ 0
+};
+
+static const sqlite3_io_methods vstat_io_methods = {
+ 3, /* iVersion */
+ vstatClose, /* xClose */
+ vstatRead, /* xRead */
+ vstatWrite, /* xWrite */
+ vstatTruncate, /* xTruncate */
+ vstatSync, /* xSync */
+ vstatFileSize, /* xFileSize */
+ vstatLock, /* xLock */
+ vstatUnlock, /* xUnlock */
+ vstatCheckReservedLock, /* xCheckReservedLock */
+ vstatFileControl, /* xFileControl */
+ vstatSectorSize, /* xSectorSize */
+ vstatDeviceCharacteristics, /* xDeviceCharacteristics */
+ vstatShmMap, /* xShmMap */
+ vstatShmLock, /* xShmLock */
+ vstatShmBarrier, /* xShmBarrier */
+ vstatShmUnmap, /* xShmUnmap */
+ vstatFetch, /* xFetch */
+ vstatUnfetch /* xUnfetch */
+};
+
+
+
+/*
+** Close an vstat-file.
+*/
+static int vstatClose(sqlite3_file *pFile){
+ VStatFile *p = (VStatFile *)pFile;
+ int rc = SQLITE_OK;
+
+ if( p->pReal->pMethods ){
+ rc = p->pReal->pMethods->xClose(p->pReal);
+ }
+ return rc;
+}
+
+
+/*
+** Read data from an vstat-file.
+*/
+static int vstatRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+ STATCNT(p->eFiletype,VFSSTAT_READ)++;
+ if( rc==SQLITE_OK ){
+ STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt;
+ }
+ return rc;
+}
+
+/*
+** Write data to an vstat-file.
+*/
+static int vstatWrite(
+ sqlite3_file *pFile,
+ const void *z,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
+ STATCNT(p->eFiletype,VFSSTAT_WRITE)++;
+ if( rc==SQLITE_OK ){
+ STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt;
+ }
+ return rc;
+}
+
+/*
+** Truncate an vstat-file.
+*/
+static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
+ return rc;
+}
+
+/*
+** Sync an vstat-file.
+*/
+static int vstatSync(sqlite3_file *pFile, int flags){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
+ STATCNT(p->eFiletype,VFSSTAT_SYNC)++;
+ return rc;
+}
+
+/*
+** Return the current file-size of an vstat-file.
+*/
+static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+ return rc;
+}
+
+/*
+** Lock an vstat-file.
+*/
+static int vstatLock(sqlite3_file *pFile, int eLock){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+ return rc;
+}
+
+/*
+** Unlock an vstat-file.
+*/
+static int vstatUnlock(sqlite3_file *pFile, int eLock){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+ return rc;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an vstat-file.
+*/
+static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+ STATCNT(p->eFiletype,VFSSTAT_LOCK)++;
+ return rc;
+}
+
+/*
+** File control method. For custom operations on an vstat-file.
+*/
+static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){
+ VStatFile *p = (VStatFile *)pFile;
+ int rc;
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg);
+ }
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an vstat-file.
+*/
+static int vstatSectorSize(sqlite3_file *pFile){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
+ return rc;
+}
+
+/*
+** Return the device characteristic flags supported by an vstat-file.
+*/
+static int vstatDeviceCharacteristics(sqlite3_file *pFile){
+ int rc;
+ VStatFile *p = (VStatFile *)pFile;
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+ return rc;
+}
+
+/* Create a shared memory file mapping */
+static int vstatShmMap(
+ sqlite3_file *pFile,
+ int iPg,
+ int pgsz,
+ int bExtend,
+ void volatile **pp
+){
+ VStatFile *p = (VStatFile *)pFile;
+ return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp);
+}
+
+/* Perform locking on a shared-memory segment */
+static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){
+ VStatFile *p = (VStatFile *)pFile;
+ return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags);
+}
+
+/* Memory barrier operation on shared memory */
+static void vstatShmBarrier(sqlite3_file *pFile){
+ VStatFile *p = (VStatFile *)pFile;
+ p->pReal->pMethods->xShmBarrier(p->pReal);
+}
+
+/* Unmap a shared memory segment */
+static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){
+ VStatFile *p = (VStatFile *)pFile;
+ return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
+}
+
+/* Fetch a page of a memory-mapped file */
+static int vstatFetch(
+ sqlite3_file *pFile,
+ sqlite3_int64 iOfst,
+ int iAmt,
+ void **pp
+){
+ VStatFile *p = (VStatFile *)pFile;
+ return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp);
+}
+
+/* Release a memory-mapped page */
+static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
+ VStatFile *p = (VStatFile *)pFile;
+ return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage);
+}
+
+/*
+** Open an vstat file handle.
+*/
+static int vstatOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ int rc;
+ VStatFile *p = (VStatFile*)pFile;
+
+ p->pReal = (sqlite3_file*)&p[1];
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ p->eFiletype = VFSSTAT_MAIN;
+ }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
+ p->eFiletype = VFSSTAT_JOURNAL;
+ }else if( flags & SQLITE_OPEN_WAL ){
+ p->eFiletype = VFSSTAT_WAL;
+ }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){
+ p->eFiletype = VFSSTAT_MASTERJRNL;
+ }else if( flags & SQLITE_OPEN_SUBJOURNAL ){
+ p->eFiletype = VFSSTAT_SUBJRNL;
+ }else if( flags & SQLITE_OPEN_TEMP_DB ){
+ p->eFiletype = VFSSTAT_TEMPDB;
+ }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){
+ p->eFiletype = VFSSTAT_TEMPJRNL;
+ }else{
+ p->eFiletype = VFSSTAT_TRANSIENT;
+ }
+ STATCNT(p->eFiletype,VFSSTAT_OPEN)++;
+ pFile->pMethods = rc ? 0 : &vstat_io_methods;
+ return rc;
+}
+
+/*
+** 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 vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc;
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
+ STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++;
+ return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int vstatAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ int rc;
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
+ STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++;
+ return rc;
+}
+
+/*
+** 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 (INST_MAX_PATHNAME+1) bytes.
+*/
+static int vstatFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++;
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *vstatDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
+}
+
+/*
+** 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 vstatDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void vstatDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++;
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){
+ STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++;
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
+}
+
+static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
+}
+static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
+ STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++;
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
+}
+
+/*
+** A virtual table for accessing the stats collected by this VFS shim
+*/
+static int vstattabConnect(sqlite3*, void*, int, const char*const*,
+ sqlite3_vtab**,char**);
+static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*);
+static int vstattabDisconnect(sqlite3_vtab*);
+static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
+static int vstattabClose(sqlite3_vtab_cursor*);
+static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv);
+static int vstattabNext(sqlite3_vtab_cursor*);
+static int vstattabEof(sqlite3_vtab_cursor*);
+static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int);
+static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*);
+static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
+
+/* A cursor for the vfsstat virtual table */
+typedef struct VfsStatCursor {
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
+ int i; /* Pointing to this aVfsCnt[] value */
+} VfsStatCursor;
+
+
+static int vstattabConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ sqlite3_vtab *pNew;
+ int rc;
+
+/* Column numbers */
+#define VSTAT_COLUMN_FILE 0
+#define VSTAT_COLUMN_STAT 1
+#define VSTAT_COLUMN_COUNT 2
+
+ rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)");
+ if( rc==SQLITE_OK ){
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for vstat table object.
+*/
+static int vstattabDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new vstat table cursor object.
+*/
+static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ VfsStatCursor *pCur;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+
+/*
+** Destructor for a VfsStatCursor.
+*/
+static int vstattabClose(sqlite3_vtab_cursor *cur){
+ sqlite3_free(cur);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a VfsStatCursor to its next row of output.
+*/
+static int vstattabNext(sqlite3_vtab_cursor *cur){
+ ((VfsStatCursor*)cur)->i++;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the VfsStatCursor
+** is currently pointing.
+*/
+static int vstattabColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
+ switch( i ){
+ case VSTAT_COLUMN_FILE: {
+ sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC);
+ break;
+ }
+ case VSTAT_COLUMN_STAT: {
+ const char **az;
+ az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat;
+ sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC);
+ break;
+ }
+ case VSTAT_COLUMN_COUNT: {
+ sqlite3_result_int64(ctx, aVfsCnt[pCur->i]);
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row.
+*/
+static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
+ *pRowid = pCur->i;
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int vstattabEof(sqlite3_vtab_cursor *cur){
+ VfsStatCursor *pCur = (VfsStatCursor*)cur;
+ return pCur->i >= VFSSTAT_MXCNT;
+}
+
+/*
+** Only a full table scan is supported. So xFilter simply rewinds to
+** the beginning.
+*/
+static int vstattabFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor;
+ pCur->i = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Only a forwards full table scan is supported. xBestIndex is a no-op.
+*/
+static int vstattabBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ return SQLITE_OK;
+}
+
+/*
+** Any VSTAT_COLUMN_COUNT can be changed to a positive integer.
+** No deletions or insertions are allowed. No changes to other
+** columns are allowed.
+*/
+static int vstattabUpdate(
+ sqlite3_vtab *tab,
+ int argc, sqlite3_value **argv,
+ sqlite3_int64 *pRowid
+){
+ sqlite3_int64 iRowid, x;
+ if( argc==1 ) return SQLITE_ERROR;
+ if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR;
+ iRowid = sqlite3_value_int64(argv[0]);
+ if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR;
+ if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR;
+ if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){
+ return SQLITE_ERROR;
+ }
+ x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]);
+ if( x<0 ) return SQLITE_ERROR;
+ aVfsCnt[iRowid] = x;
+ return SQLITE_OK;
+}
+
+static sqlite3_module VfsStatModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ vstattabConnect, /* xConnect */
+ vstattabBestIndex, /* xBestIndex */
+ vstattabDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ vstattabOpen, /* xOpen - open a cursor */
+ vstattabClose, /* xClose - close a cursor */
+ vstattabFilter, /* xFilter - configure scan constraints */
+ vstattabNext, /* xNext - advance a cursor */
+ vstattabEof, /* xEof - check for end of scan */
+ vstattabColumn, /* xColumn - read data */
+ vstattabRowid, /* xRowid - read data */
+ vstattabUpdate, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+};
+
+/*
+** This routine is an sqlite3_auto_extension() callback, invoked to register
+** the vfsstat virtual table for all new database connections.
+*/
+static int vstatRegister(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pThunk
+){
+ return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+/*
+** This routine is called when the extension is loaded.
+**
+** Register the new VFS. Make arrangement to register the virtual table
+** for each new database connection.
+*/
+int sqlite3_vfsstat_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ vstat_vfs.pVfs = sqlite3_vfs_find(0);
+ if( vstat_vfs.pVfs==0 ) return SQLITE_ERROR;
+ vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile;
+ rc = sqlite3_vfs_register(&vstat_vfs.base, 1);
+ if( rc==SQLITE_OK ){
+ rc = vstatRegister(db, pzErrMsg, pApi);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_auto_extension((void(*)(void))vstatRegister);
+ }
+ }
+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
+ return rc;
+}