/* ** 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 */ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ 0 /* xIntegrity */ }; /* ** 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; }