821 lines
24 KiB
C
821 lines
24 KiB
C
/*
|
|
** 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;
|
|
}
|