1 /* 2 ** 2016-05-27 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ****************************************************************************** 12 ** 13 ** This file contains the implementation of an SQLite vfs shim that 14 ** tracks I/O. Access to the accumulated status counts is provided using 15 ** an eponymous virtual table. 16 */ 17 #include <sqlite3ext.h> 18 SQLITE_EXTENSION_INIT1 19 20 /* 21 ** This module contains code for a wrapper VFS that cause stats for 22 ** most VFS calls to be recorded. 23 ** 24 ** To use this module, first compile it as a loadable extension. See 25 ** https://www.sqlite.org/loadext.html#build for compilations instructions. 26 ** 27 ** After compliing, load this extension, then open database connections to be 28 ** measured. Query usages status using the vfsstat virtual table: 29 ** 30 ** SELECT * FROM vfsstat; 31 ** 32 ** Reset counters using UPDATE statements against vfsstat: 33 ** 34 ** UPDATE vfsstat SET count=0; 35 ** 36 ** EXAMPLE SCRIPT: 37 ** 38 ** .load ./vfsstat 39 ** .open test.db 40 ** DROP TABLE IF EXISTS t1; 41 ** CREATE TABLE t1(x,y); 42 ** INSERT INTO t1 VALUES(123, randomblob(5000)); 43 ** CREATE INDEX t1x ON t1(x); 44 ** DROP TABLE t1; 45 ** VACUUM; 46 ** SELECT * FROM vfsstat WHERE count>0; 47 ** 48 ** LIMITATIONS: 49 ** 50 ** This module increments counters without using mutex protection. So if 51 ** two or more threads try to use this module at the same time, race conditions 52 ** may occur which mess up the counts. This is harmless, other than giving 53 ** incorrect statistics. 54 */ 55 #include <string.h> 56 #include <stdlib.h> 57 #include <assert.h> 58 59 /* 60 ** File types 61 */ 62 #define VFSSTAT_MAIN 0 /* Main database file */ 63 #define VFSSTAT_JOURNAL 1 /* Rollback journal */ 64 #define VFSSTAT_WAL 2 /* Write-ahead log file */ 65 #define VFSSTAT_MASTERJRNL 3 /* Master journal */ 66 #define VFSSTAT_SUBJRNL 4 /* Subjournal */ 67 #define VFSSTAT_TEMPDB 5 /* TEMP database */ 68 #define VFSSTAT_TEMPJRNL 6 /* Journal for TEMP database */ 69 #define VFSSTAT_TRANSIENT 7 /* Transient database */ 70 #define VFSSTAT_ANY 8 /* Unspecified file type */ 71 #define VFSSTAT_nFile 9 /* This many file types */ 72 73 /* Names of the file types. These are allowed values for the 74 ** first column of the vfsstat virtual table. 75 */ 76 static const char *azFile[] = { 77 "database", "journal", "wal", "master-journal", "sub-journal", 78 "temp-database", "temp-journal", "transient-db", "*" 79 }; 80 81 /* 82 ** Stat types 83 */ 84 #define VFSSTAT_BYTESIN 0 /* Bytes read in */ 85 #define VFSSTAT_BYTESOUT 1 /* Bytes written out */ 86 #define VFSSTAT_READ 2 /* Read requests */ 87 #define VFSSTAT_WRITE 3 /* Write requests */ 88 #define VFSSTAT_SYNC 4 /* Syncs */ 89 #define VFSSTAT_OPEN 5 /* File opens */ 90 #define VFSSTAT_LOCK 6 /* Lock requests */ 91 #define VFSSTAT_ACCESS 0 /* xAccess calls. filetype==ANY only */ 92 #define VFSSTAT_DELETE 1 /* xDelete calls. filetype==ANY only */ 93 #define VFSSTAT_FULLPATH 2 /* xFullPathname calls. ANY only */ 94 #define VFSSTAT_RANDOM 3 /* xRandomness calls. ANY only */ 95 #define VFSSTAT_SLEEP 4 /* xSleep calls. ANY only */ 96 #define VFSSTAT_CURTIME 5 /* xCurrentTime calls. ANY only */ 97 #define VFSSTAT_nStat 7 /* This many stat types */ 98 99 100 /* Names for the second column of the vfsstat virtual table for all 101 ** cases except when the first column is "*" or VFSSTAT_ANY. */ 102 static const char *azStat[] = { 103 "bytes-in", "bytes-out", "read", "write", "sync", "open", "lock", 104 }; 105 static const char *azStatAny[] = { 106 "access", "delete", "fullpathname", "randomness", "sleep", "currenttimestamp", 107 "not-used" 108 }; 109 110 /* Total number of counters */ 111 #define VFSSTAT_MXCNT (VFSSTAT_nStat*VFSSTAT_nFile) 112 113 /* 114 ** Performance stats are collected in an instance of the following 115 ** global array. 116 */ 117 static sqlite3_uint64 aVfsCnt[VFSSTAT_MXCNT]; 118 119 /* 120 ** Access to a specific counter 121 */ 122 #define STATCNT(filetype,stat) (aVfsCnt[(filetype)*VFSSTAT_nStat+(stat)]) 123 124 /* 125 ** Forward declaration of objects used by this utility 126 */ 127 typedef struct VStatVfs VStatVfs; 128 typedef struct VStatFile VStatFile; 129 130 /* An instance of the VFS */ 131 struct VStatVfs { 132 sqlite3_vfs base; /* VFS methods */ 133 sqlite3_vfs *pVfs; /* Parent VFS */ 134 }; 135 136 /* An open file */ 137 struct VStatFile { 138 sqlite3_file base; /* IO methods */ 139 sqlite3_file *pReal; /* Underlying file handle */ 140 unsigned char eFiletype; /* What type of file is this */ 141 }; 142 143 #define REALVFS(p) (((VStatVfs*)(p))->pVfs) 144 145 /* 146 ** Methods for VStatFile 147 */ 148 static int vstatClose(sqlite3_file*); 149 static int vstatRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); 150 static int vstatWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); 151 static int vstatTruncate(sqlite3_file*, sqlite3_int64 size); 152 static int vstatSync(sqlite3_file*, int flags); 153 static int vstatFileSize(sqlite3_file*, sqlite3_int64 *pSize); 154 static int vstatLock(sqlite3_file*, int); 155 static int vstatUnlock(sqlite3_file*, int); 156 static int vstatCheckReservedLock(sqlite3_file*, int *pResOut); 157 static int vstatFileControl(sqlite3_file*, int op, void *pArg); 158 static int vstatSectorSize(sqlite3_file*); 159 static int vstatDeviceCharacteristics(sqlite3_file*); 160 static int vstatShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); 161 static int vstatShmLock(sqlite3_file*, int offset, int n, int flags); 162 static void vstatShmBarrier(sqlite3_file*); 163 static int vstatShmUnmap(sqlite3_file*, int deleteFlag); 164 static int vstatFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); 165 static int vstatUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); 166 167 /* 168 ** Methods for VStatVfs 169 */ 170 static int vstatOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); 171 static int vstatDelete(sqlite3_vfs*, const char *zName, int syncDir); 172 static int vstatAccess(sqlite3_vfs*, const char *zName, int flags, int *); 173 static int vstatFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); 174 static void *vstatDlOpen(sqlite3_vfs*, const char *zFilename); 175 static void vstatDlError(sqlite3_vfs*, int nByte, char *zErrMsg); 176 static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); 177 static void vstatDlClose(sqlite3_vfs*, void*); 178 static int vstatRandomness(sqlite3_vfs*, int nByte, char *zOut); 179 static int vstatSleep(sqlite3_vfs*, int microseconds); 180 static int vstatCurrentTime(sqlite3_vfs*, double*); 181 static int vstatGetLastError(sqlite3_vfs*, int, char *); 182 static int vstatCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); 183 184 static VStatVfs vstat_vfs = { 185 { 186 2, /* iVersion */ 187 0, /* szOsFile (set by register_vstat()) */ 188 1024, /* mxPathname */ 189 0, /* pNext */ 190 "vfslog", /* zName */ 191 0, /* pAppData */ 192 vstatOpen, /* xOpen */ 193 vstatDelete, /* xDelete */ 194 vstatAccess, /* xAccess */ 195 vstatFullPathname, /* xFullPathname */ 196 vstatDlOpen, /* xDlOpen */ 197 vstatDlError, /* xDlError */ 198 vstatDlSym, /* xDlSym */ 199 vstatDlClose, /* xDlClose */ 200 vstatRandomness, /* xRandomness */ 201 vstatSleep, /* xSleep */ 202 vstatCurrentTime, /* xCurrentTime */ 203 vstatGetLastError, /* xGetLastError */ 204 vstatCurrentTimeInt64 /* xCurrentTimeInt64 */ 205 }, 206 0 207 }; 208 209 static const sqlite3_io_methods vstat_io_methods = { 210 3, /* iVersion */ 211 vstatClose, /* xClose */ 212 vstatRead, /* xRead */ 213 vstatWrite, /* xWrite */ 214 vstatTruncate, /* xTruncate */ 215 vstatSync, /* xSync */ 216 vstatFileSize, /* xFileSize */ 217 vstatLock, /* xLock */ 218 vstatUnlock, /* xUnlock */ 219 vstatCheckReservedLock, /* xCheckReservedLock */ 220 vstatFileControl, /* xFileControl */ 221 vstatSectorSize, /* xSectorSize */ 222 vstatDeviceCharacteristics, /* xDeviceCharacteristics */ 223 vstatShmMap, /* xShmMap */ 224 vstatShmLock, /* xShmLock */ 225 vstatShmBarrier, /* xShmBarrier */ 226 vstatShmUnmap, /* xShmUnmap */ 227 vstatFetch, /* xFetch */ 228 vstatUnfetch /* xUnfetch */ 229 }; 230 231 232 233 /* 234 ** Close an vstat-file. 235 */ 236 static int vstatClose(sqlite3_file *pFile){ 237 VStatFile *p = (VStatFile *)pFile; 238 int rc = SQLITE_OK; 239 240 if( p->pReal->pMethods ){ 241 rc = p->pReal->pMethods->xClose(p->pReal); 242 } 243 return rc; 244 } 245 246 247 /* 248 ** Read data from an vstat-file. 249 */ 250 static int vstatRead( 251 sqlite3_file *pFile, 252 void *zBuf, 253 int iAmt, 254 sqlite_int64 iOfst 255 ){ 256 int rc; 257 VStatFile *p = (VStatFile *)pFile; 258 259 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); 260 STATCNT(p->eFiletype,VFSSTAT_READ)++; 261 if( rc==SQLITE_OK ){ 262 STATCNT(p->eFiletype,VFSSTAT_BYTESIN) += iAmt; 263 } 264 return rc; 265 } 266 267 /* 268 ** Write data to an vstat-file. 269 */ 270 static int vstatWrite( 271 sqlite3_file *pFile, 272 const void *z, 273 int iAmt, 274 sqlite_int64 iOfst 275 ){ 276 int rc; 277 VStatFile *p = (VStatFile *)pFile; 278 279 rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst); 280 STATCNT(p->eFiletype,VFSSTAT_WRITE)++; 281 if( rc==SQLITE_OK ){ 282 STATCNT(p->eFiletype,VFSSTAT_BYTESOUT) += iAmt; 283 } 284 return rc; 285 } 286 287 /* 288 ** Truncate an vstat-file. 289 */ 290 static int vstatTruncate(sqlite3_file *pFile, sqlite_int64 size){ 291 int rc; 292 VStatFile *p = (VStatFile *)pFile; 293 rc = p->pReal->pMethods->xTruncate(p->pReal, size); 294 return rc; 295 } 296 297 /* 298 ** Sync an vstat-file. 299 */ 300 static int vstatSync(sqlite3_file *pFile, int flags){ 301 int rc; 302 VStatFile *p = (VStatFile *)pFile; 303 rc = p->pReal->pMethods->xSync(p->pReal, flags); 304 STATCNT(p->eFiletype,VFSSTAT_SYNC)++; 305 return rc; 306 } 307 308 /* 309 ** Return the current file-size of an vstat-file. 310 */ 311 static int vstatFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ 312 int rc; 313 VStatFile *p = (VStatFile *)pFile; 314 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); 315 return rc; 316 } 317 318 /* 319 ** Lock an vstat-file. 320 */ 321 static int vstatLock(sqlite3_file *pFile, int eLock){ 322 int rc; 323 VStatFile *p = (VStatFile *)pFile; 324 rc = p->pReal->pMethods->xLock(p->pReal, eLock); 325 STATCNT(p->eFiletype,VFSSTAT_LOCK)++; 326 return rc; 327 } 328 329 /* 330 ** Unlock an vstat-file. 331 */ 332 static int vstatUnlock(sqlite3_file *pFile, int eLock){ 333 int rc; 334 VStatFile *p = (VStatFile *)pFile; 335 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); 336 STATCNT(p->eFiletype,VFSSTAT_LOCK)++; 337 return rc; 338 } 339 340 /* 341 ** Check if another file-handle holds a RESERVED lock on an vstat-file. 342 */ 343 static int vstatCheckReservedLock(sqlite3_file *pFile, int *pResOut){ 344 int rc; 345 VStatFile *p = (VStatFile *)pFile; 346 rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); 347 STATCNT(p->eFiletype,VFSSTAT_LOCK)++; 348 return rc; 349 } 350 351 /* 352 ** File control method. For custom operations on an vstat-file. 353 */ 354 static int vstatFileControl(sqlite3_file *pFile, int op, void *pArg){ 355 VStatFile *p = (VStatFile *)pFile; 356 int rc; 357 rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); 358 if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){ 359 *(char**)pArg = sqlite3_mprintf("vstat/%z", *(char**)pArg); 360 } 361 return rc; 362 } 363 364 /* 365 ** Return the sector-size in bytes for an vstat-file. 366 */ 367 static int vstatSectorSize(sqlite3_file *pFile){ 368 int rc; 369 VStatFile *p = (VStatFile *)pFile; 370 rc = p->pReal->pMethods->xSectorSize(p->pReal); 371 return rc; 372 } 373 374 /* 375 ** Return the device characteristic flags supported by an vstat-file. 376 */ 377 static int vstatDeviceCharacteristics(sqlite3_file *pFile){ 378 int rc; 379 VStatFile *p = (VStatFile *)pFile; 380 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); 381 return rc; 382 } 383 384 /* Create a shared memory file mapping */ 385 static int vstatShmMap( 386 sqlite3_file *pFile, 387 int iPg, 388 int pgsz, 389 int bExtend, 390 void volatile **pp 391 ){ 392 VStatFile *p = (VStatFile *)pFile; 393 return p->pReal->pMethods->xShmMap(p->pReal, iPg, pgsz, bExtend, pp); 394 } 395 396 /* Perform locking on a shared-memory segment */ 397 static int vstatShmLock(sqlite3_file *pFile, int offset, int n, int flags){ 398 VStatFile *p = (VStatFile *)pFile; 399 return p->pReal->pMethods->xShmLock(p->pReal, offset, n, flags); 400 } 401 402 /* Memory barrier operation on shared memory */ 403 static void vstatShmBarrier(sqlite3_file *pFile){ 404 VStatFile *p = (VStatFile *)pFile; 405 p->pReal->pMethods->xShmBarrier(p->pReal); 406 } 407 408 /* Unmap a shared memory segment */ 409 static int vstatShmUnmap(sqlite3_file *pFile, int deleteFlag){ 410 VStatFile *p = (VStatFile *)pFile; 411 return p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag); 412 } 413 414 /* Fetch a page of a memory-mapped file */ 415 static int vstatFetch( 416 sqlite3_file *pFile, 417 sqlite3_int64 iOfst, 418 int iAmt, 419 void **pp 420 ){ 421 VStatFile *p = (VStatFile *)pFile; 422 return p->pReal->pMethods->xFetch(p->pReal, iOfst, iAmt, pp); 423 } 424 425 /* Release a memory-mapped page */ 426 static int vstatUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ 427 VStatFile *p = (VStatFile *)pFile; 428 return p->pReal->pMethods->xUnfetch(p->pReal, iOfst, pPage); 429 } 430 431 /* 432 ** Open an vstat file handle. 433 */ 434 static int vstatOpen( 435 sqlite3_vfs *pVfs, 436 const char *zName, 437 sqlite3_file *pFile, 438 int flags, 439 int *pOutFlags 440 ){ 441 int rc; 442 VStatFile *p = (VStatFile*)pFile; 443 444 p->pReal = (sqlite3_file*)&p[1]; 445 rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); 446 if( flags & SQLITE_OPEN_MAIN_DB ){ 447 p->eFiletype = VFSSTAT_MAIN; 448 }else if( flags & SQLITE_OPEN_MAIN_JOURNAL ){ 449 p->eFiletype = VFSSTAT_JOURNAL; 450 }else if( flags & SQLITE_OPEN_WAL ){ 451 p->eFiletype = VFSSTAT_WAL; 452 }else if( flags & SQLITE_OPEN_MASTER_JOURNAL ){ 453 p->eFiletype = VFSSTAT_MASTERJRNL; 454 }else if( flags & SQLITE_OPEN_SUBJOURNAL ){ 455 p->eFiletype = VFSSTAT_SUBJRNL; 456 }else if( flags & SQLITE_OPEN_TEMP_DB ){ 457 p->eFiletype = VFSSTAT_TEMPDB; 458 }else if( flags & SQLITE_OPEN_TEMP_JOURNAL ){ 459 p->eFiletype = VFSSTAT_TEMPJRNL; 460 }else{ 461 p->eFiletype = VFSSTAT_TRANSIENT; 462 } 463 STATCNT(p->eFiletype,VFSSTAT_OPEN)++; 464 pFile->pMethods = rc ? 0 : &vstat_io_methods; 465 return rc; 466 } 467 468 /* 469 ** Delete the file located at zPath. If the dirSync argument is true, 470 ** ensure the file-system modifications are synced to disk before 471 ** returning. 472 */ 473 static int vstatDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 474 int rc; 475 rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync); 476 STATCNT(VFSSTAT_ANY,VFSSTAT_DELETE)++; 477 return rc; 478 } 479 480 /* 481 ** Test for access permissions. Return true if the requested permission 482 ** is available, or false otherwise. 483 */ 484 static int vstatAccess( 485 sqlite3_vfs *pVfs, 486 const char *zPath, 487 int flags, 488 int *pResOut 489 ){ 490 int rc; 491 rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut); 492 STATCNT(VFSSTAT_ANY,VFSSTAT_ACCESS)++; 493 return rc; 494 } 495 496 /* 497 ** Populate buffer zOut with the full canonical pathname corresponding 498 ** to the pathname in zPath. zOut is guaranteed to point to a buffer 499 ** of at least (INST_MAX_PATHNAME+1) bytes. 500 */ 501 static int vstatFullPathname( 502 sqlite3_vfs *pVfs, 503 const char *zPath, 504 int nOut, 505 char *zOut 506 ){ 507 STATCNT(VFSSTAT_ANY,VFSSTAT_FULLPATH)++; 508 return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); 509 } 510 511 /* 512 ** Open the dynamic library located at zPath and return a handle. 513 */ 514 static void *vstatDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 515 return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); 516 } 517 518 /* 519 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable 520 ** utf-8 string describing the most recent error encountered associated 521 ** with dynamic libraries. 522 */ 523 static void vstatDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ 524 REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); 525 } 526 527 /* 528 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. 529 */ 530 static void (*vstatDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ 531 return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym); 532 } 533 534 /* 535 ** Close the dynamic library handle pHandle. 536 */ 537 static void vstatDlClose(sqlite3_vfs *pVfs, void *pHandle){ 538 REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); 539 } 540 541 /* 542 ** Populate the buffer pointed to by zBufOut with nByte bytes of 543 ** random data. 544 */ 545 static int vstatRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 546 STATCNT(VFSSTAT_ANY,VFSSTAT_RANDOM)++; 547 return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); 548 } 549 550 /* 551 ** Sleep for nMicro microseconds. Return the number of microseconds 552 ** actually slept. 553 */ 554 static int vstatSleep(sqlite3_vfs *pVfs, int nMicro){ 555 STATCNT(VFSSTAT_ANY,VFSSTAT_SLEEP)++; 556 return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro); 557 } 558 559 /* 560 ** Return the current time as a Julian Day number in *pTimeOut. 561 */ 562 static int vstatCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 563 STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++; 564 return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); 565 } 566 567 static int vstatGetLastError(sqlite3_vfs *pVfs, int a, char *b){ 568 return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); 569 } 570 static int vstatCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ 571 STATCNT(VFSSTAT_ANY,VFSSTAT_CURTIME)++; 572 return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); 573 } 574 575 /* 576 ** A virtual table for accessing the stats collected by this VFS shim 577 */ 578 static int vstattabConnect(sqlite3*, void*, int, const char*const*, 579 sqlite3_vtab**,char**); 580 static int vstattabBestIndex(sqlite3_vtab*,sqlite3_index_info*); 581 static int vstattabDisconnect(sqlite3_vtab*); 582 static int vstattabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**); 583 static int vstattabClose(sqlite3_vtab_cursor*); 584 static int vstattabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, 585 int argc, sqlite3_value **argv); 586 static int vstattabNext(sqlite3_vtab_cursor*); 587 static int vstattabEof(sqlite3_vtab_cursor*); 588 static int vstattabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int); 589 static int vstattabRowid(sqlite3_vtab_cursor*,sqlite3_int64*); 590 static int vstattabUpdate(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); 591 592 /* A cursor for the vfsstat virtual table */ 593 typedef struct VfsStatCursor { 594 sqlite3_vtab_cursor base; /* Base class. Must be first */ 595 int i; /* Pointing to this aVfsCnt[] value */ 596 } VfsStatCursor; 597 598 599 static int vstattabConnect( 600 sqlite3 *db, 601 void *pAux, 602 int argc, const char *const*argv, 603 sqlite3_vtab **ppVtab, 604 char **pzErr 605 ){ 606 sqlite3_vtab *pNew; 607 int rc; 608 609 /* Column numbers */ 610 #define VSTAT_COLUMN_FILE 0 611 #define VSTAT_COLUMN_STAT 1 612 #define VSTAT_COLUMN_COUNT 2 613 614 rc = sqlite3_declare_vtab(db,"CREATE TABLE x(file,stat,count)"); 615 if( rc==SQLITE_OK ){ 616 pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); 617 if( pNew==0 ) return SQLITE_NOMEM; 618 memset(pNew, 0, sizeof(*pNew)); 619 } 620 return rc; 621 } 622 623 /* 624 ** This method is the destructor for vstat table object. 625 */ 626 static int vstattabDisconnect(sqlite3_vtab *pVtab){ 627 sqlite3_free(pVtab); 628 return SQLITE_OK; 629 } 630 631 /* 632 ** Constructor for a new vstat table cursor object. 633 */ 634 static int vstattabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ 635 VfsStatCursor *pCur; 636 pCur = sqlite3_malloc( sizeof(*pCur) ); 637 if( pCur==0 ) return SQLITE_NOMEM; 638 memset(pCur, 0, sizeof(*pCur)); 639 *ppCursor = &pCur->base; 640 return SQLITE_OK; 641 } 642 643 644 /* 645 ** Destructor for a VfsStatCursor. 646 */ 647 static int vstattabClose(sqlite3_vtab_cursor *cur){ 648 sqlite3_free(cur); 649 return SQLITE_OK; 650 } 651 652 653 /* 654 ** Advance a VfsStatCursor to its next row of output. 655 */ 656 static int vstattabNext(sqlite3_vtab_cursor *cur){ 657 ((VfsStatCursor*)cur)->i++; 658 return SQLITE_OK; 659 } 660 661 /* 662 ** Return values of columns for the row at which the VfsStatCursor 663 ** is currently pointing. 664 */ 665 static int vstattabColumn( 666 sqlite3_vtab_cursor *cur, /* The cursor */ 667 sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ 668 int i /* Which column to return */ 669 ){ 670 VfsStatCursor *pCur = (VfsStatCursor*)cur; 671 switch( i ){ 672 case VSTAT_COLUMN_FILE: { 673 sqlite3_result_text(ctx, azFile[pCur->i/VFSSTAT_nStat], -1, SQLITE_STATIC); 674 break; 675 } 676 case VSTAT_COLUMN_STAT: { 677 const char **az; 678 az = (pCur->i/VFSSTAT_nStat)==VFSSTAT_ANY ? azStatAny : azStat; 679 sqlite3_result_text(ctx, az[pCur->i%VFSSTAT_nStat], -1, SQLITE_STATIC); 680 break; 681 } 682 case VSTAT_COLUMN_COUNT: { 683 sqlite3_result_int64(ctx, aVfsCnt[pCur->i]); 684 break; 685 } 686 } 687 return SQLITE_OK; 688 } 689 690 /* 691 ** Return the rowid for the current row. 692 */ 693 static int vstattabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ 694 VfsStatCursor *pCur = (VfsStatCursor*)cur; 695 *pRowid = pCur->i; 696 return SQLITE_OK; 697 } 698 699 /* 700 ** Return TRUE if the cursor has been moved off of the last 701 ** row of output. 702 */ 703 static int vstattabEof(sqlite3_vtab_cursor *cur){ 704 VfsStatCursor *pCur = (VfsStatCursor*)cur; 705 return pCur->i >= VFSSTAT_MXCNT; 706 } 707 708 /* 709 ** Only a full table scan is supported. So xFilter simply rewinds to 710 ** the beginning. 711 */ 712 static int vstattabFilter( 713 sqlite3_vtab_cursor *pVtabCursor, 714 int idxNum, const char *idxStr, 715 int argc, sqlite3_value **argv 716 ){ 717 VfsStatCursor *pCur = (VfsStatCursor*)pVtabCursor; 718 pCur->i = 0; 719 return SQLITE_OK; 720 } 721 722 /* 723 ** Only a forwards full table scan is supported. xBestIndex is a no-op. 724 */ 725 static int vstattabBestIndex( 726 sqlite3_vtab *tab, 727 sqlite3_index_info *pIdxInfo 728 ){ 729 return SQLITE_OK; 730 } 731 732 /* 733 ** Any VSTAT_COLUMN_COUNT can be changed to a positive integer. 734 ** No deletions or insertions are allowed. No changes to other 735 ** columns are allowed. 736 */ 737 static int vstattabUpdate( 738 sqlite3_vtab *tab, 739 int argc, sqlite3_value **argv, 740 sqlite3_int64 *pRowid 741 ){ 742 sqlite3_int64 iRowid, x; 743 if( argc==1 ) return SQLITE_ERROR; 744 if( sqlite3_value_type(argv[0])!=SQLITE_INTEGER ) return SQLITE_ERROR; 745 iRowid = sqlite3_value_int64(argv[0]); 746 if( iRowid!=sqlite3_value_int64(argv[1]) ) return SQLITE_ERROR; 747 if( iRowid<0 || iRowid>=VFSSTAT_MXCNT ) return SQLITE_ERROR; 748 if( sqlite3_value_type(argv[VSTAT_COLUMN_COUNT+2])!=SQLITE_INTEGER ){ 749 return SQLITE_ERROR; 750 } 751 x = sqlite3_value_int64(argv[VSTAT_COLUMN_COUNT+2]); 752 if( x<0 ) return SQLITE_ERROR; 753 aVfsCnt[iRowid] = x; 754 return SQLITE_OK; 755 } 756 757 static sqlite3_module VfsStatModule = { 758 0, /* iVersion */ 759 0, /* xCreate */ 760 vstattabConnect, /* xConnect */ 761 vstattabBestIndex, /* xBestIndex */ 762 vstattabDisconnect, /* xDisconnect */ 763 0, /* xDestroy */ 764 vstattabOpen, /* xOpen - open a cursor */ 765 vstattabClose, /* xClose - close a cursor */ 766 vstattabFilter, /* xFilter - configure scan constraints */ 767 vstattabNext, /* xNext - advance a cursor */ 768 vstattabEof, /* xEof - check for end of scan */ 769 vstattabColumn, /* xColumn - read data */ 770 vstattabRowid, /* xRowid - read data */ 771 vstattabUpdate, /* xUpdate */ 772 0, /* xBegin */ 773 0, /* xSync */ 774 0, /* xCommit */ 775 0, /* xRollback */ 776 0, /* xFindMethod */ 777 0, /* xRename */ 778 }; 779 780 /* 781 ** This routine is an sqlite3_auto_extension() callback, invoked to register 782 ** the vfsstat virtual table for all new database connections. 783 */ 784 static int vstatRegister( 785 sqlite3 *db, 786 const char **pzErrMsg, 787 const struct sqlite3_api_routines *pThunk 788 ){ 789 return sqlite3_create_module(db, "vfsstat", &VfsStatModule, 0); 790 } 791 792 #ifdef _WIN32 793 __declspec(dllexport) 794 #endif 795 /* 796 ** This routine is called when the extension is loaded. 797 ** 798 ** Register the new VFS. Make arrangement to register the virtual table 799 ** for each new database connection. 800 */ 801 int sqlite3_vfsstat_init( 802 sqlite3 *db, 803 char **pzErrMsg, 804 const sqlite3_api_routines *pApi 805 ){ 806 int rc = SQLITE_OK; 807 SQLITE_EXTENSION_INIT2(pApi); 808 vstat_vfs.pVfs = sqlite3_vfs_find(0); 809 vstat_vfs.base.szOsFile = sizeof(VStatFile) + vstat_vfs.pVfs->szOsFile; 810 rc = sqlite3_vfs_register(&vstat_vfs.base, 1); 811 if( rc==SQLITE_OK ){ 812 rc = sqlite3_auto_extension(vstatRegister); 813 } 814 if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; 815 return rc; 816 } 817