diff options
Diffstat (limited to 'ext/misc/vfsstat.c')
-rw-r--r-- | ext/misc/vfsstat.c | 820 |
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; +} |