OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |