Annotation of embedaddon/sqlite3/src/test_stat.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2010 July 12
! 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 an implementation of the "dbstat" virtual table.
! 14: **
! 15: ** The dbstat virtual table is used to extract low-level formatting
! 16: ** information from an SQLite database in order to implement the
! 17: ** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
! 18: ** for an example implementation.
! 19: */
! 20:
! 21: #ifndef SQLITE_AMALGAMATION
! 22: # include "sqliteInt.h"
! 23: #endif
! 24:
! 25: #ifndef SQLITE_OMIT_VIRTUALTABLE
! 26:
! 27: /*
! 28: ** Page paths:
! 29: **
! 30: ** The value of the 'path' column describes the path taken from the
! 31: ** root-node of the b-tree structure to each page. The value of the
! 32: ** root-node path is '/'.
! 33: **
! 34: ** The value of the path for the left-most child page of the root of
! 35: ** a b-tree is '/000/'. (Btrees store content ordered from left to right
! 36: ** so the pages to the left have smaller keys than the pages to the right.)
! 37: ** The next to left-most child of the root page is
! 38: ** '/001', and so on, each sibling page identified by a 3-digit hex
! 39: ** value. The children of the 451st left-most sibling have paths such
! 40: ** as '/1c2/000/, '/1c2/001/' etc.
! 41: **
! 42: ** Overflow pages are specified by appending a '+' character and a
! 43: ** six-digit hexadecimal value to the path to the cell they are linked
! 44: ** from. For example, the three overflow pages in a chain linked from
! 45: ** the left-most cell of the 450th child of the root page are identified
! 46: ** by the paths:
! 47: **
! 48: ** '/1c2/000+000000' // First page in overflow chain
! 49: ** '/1c2/000+000001' // Second page in overflow chain
! 50: ** '/1c2/000+000002' // Third page in overflow chain
! 51: **
! 52: ** If the paths are sorted using the BINARY collation sequence, then
! 53: ** the overflow pages associated with a cell will appear earlier in the
! 54: ** sort-order than its child page:
! 55: **
! 56: ** '/1c2/000/' // Left-most child of 451st child of root
! 57: */
! 58: #define VTAB_SCHEMA \
! 59: "CREATE TABLE xx( " \
! 60: " name STRING, /* Name of table or index */" \
! 61: " path INTEGER, /* Path to page from root */" \
! 62: " pageno INTEGER, /* Page number */" \
! 63: " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \
! 64: " ncell INTEGER, /* Cells on page (0 for overflow) */" \
! 65: " payload INTEGER, /* Bytes of payload on this page */" \
! 66: " unused INTEGER, /* Bytes of unused space on this page */" \
! 67: " mx_payload INTEGER, /* Largest payload size of all cells */" \
! 68: " pgoffset INTEGER, /* Offset of page in file */" \
! 69: " pgsize INTEGER /* Size of the page */" \
! 70: ");"
! 71:
! 72:
! 73: typedef struct StatTable StatTable;
! 74: typedef struct StatCursor StatCursor;
! 75: typedef struct StatPage StatPage;
! 76: typedef struct StatCell StatCell;
! 77:
! 78: struct StatCell {
! 79: int nLocal; /* Bytes of local payload */
! 80: u32 iChildPg; /* Child node (or 0 if this is a leaf) */
! 81: int nOvfl; /* Entries in aOvfl[] */
! 82: u32 *aOvfl; /* Array of overflow page numbers */
! 83: int nLastOvfl; /* Bytes of payload on final overflow page */
! 84: int iOvfl; /* Iterates through aOvfl[] */
! 85: };
! 86:
! 87: struct StatPage {
! 88: u32 iPgno;
! 89: DbPage *pPg;
! 90: int iCell;
! 91:
! 92: char *zPath; /* Path to this page */
! 93:
! 94: /* Variables populated by statDecodePage(): */
! 95: u8 flags; /* Copy of flags byte */
! 96: int nCell; /* Number of cells on page */
! 97: int nUnused; /* Number of unused bytes on page */
! 98: StatCell *aCell; /* Array of parsed cells */
! 99: u32 iRightChildPg; /* Right-child page number (or 0) */
! 100: int nMxPayload; /* Largest payload of any cell on this page */
! 101: };
! 102:
! 103: struct StatCursor {
! 104: sqlite3_vtab_cursor base;
! 105: sqlite3_stmt *pStmt; /* Iterates through set of root pages */
! 106: int isEof; /* After pStmt has returned SQLITE_DONE */
! 107:
! 108: StatPage aPage[32];
! 109: int iPage; /* Current entry in aPage[] */
! 110:
! 111: /* Values to return. */
! 112: char *zName; /* Value of 'name' column */
! 113: char *zPath; /* Value of 'path' column */
! 114: u32 iPageno; /* Value of 'pageno' column */
! 115: char *zPagetype; /* Value of 'pagetype' column */
! 116: int nCell; /* Value of 'ncell' column */
! 117: int nPayload; /* Value of 'payload' column */
! 118: int nUnused; /* Value of 'unused' column */
! 119: int nMxPayload; /* Value of 'mx_payload' column */
! 120: i64 iOffset; /* Value of 'pgOffset' column */
! 121: int szPage; /* Value of 'pgSize' column */
! 122: };
! 123:
! 124: struct StatTable {
! 125: sqlite3_vtab base;
! 126: sqlite3 *db;
! 127: };
! 128:
! 129: #ifndef get2byte
! 130: # define get2byte(x) ((x)[0]<<8 | (x)[1])
! 131: #endif
! 132:
! 133: /*
! 134: ** Connect to or create a statvfs virtual table.
! 135: */
! 136: static int statConnect(
! 137: sqlite3 *db,
! 138: void *pAux,
! 139: int argc, const char *const*argv,
! 140: sqlite3_vtab **ppVtab,
! 141: char **pzErr
! 142: ){
! 143: StatTable *pTab;
! 144:
! 145: pTab = (StatTable *)sqlite3_malloc(sizeof(StatTable));
! 146: memset(pTab, 0, sizeof(StatTable));
! 147: pTab->db = db;
! 148:
! 149: sqlite3_declare_vtab(db, VTAB_SCHEMA);
! 150: *ppVtab = &pTab->base;
! 151: return SQLITE_OK;
! 152: }
! 153:
! 154: /*
! 155: ** Disconnect from or destroy a statvfs virtual table.
! 156: */
! 157: static int statDisconnect(sqlite3_vtab *pVtab){
! 158: sqlite3_free(pVtab);
! 159: return SQLITE_OK;
! 160: }
! 161:
! 162: /*
! 163: ** There is no "best-index". This virtual table always does a linear
! 164: ** scan of the binary VFS log file.
! 165: */
! 166: static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
! 167:
! 168: /* Records are always returned in ascending order of (name, path).
! 169: ** If this will satisfy the client, set the orderByConsumed flag so that
! 170: ** SQLite does not do an external sort.
! 171: */
! 172: if( ( pIdxInfo->nOrderBy==1
! 173: && pIdxInfo->aOrderBy[0].iColumn==0
! 174: && pIdxInfo->aOrderBy[0].desc==0
! 175: ) ||
! 176: ( pIdxInfo->nOrderBy==2
! 177: && pIdxInfo->aOrderBy[0].iColumn==0
! 178: && pIdxInfo->aOrderBy[0].desc==0
! 179: && pIdxInfo->aOrderBy[1].iColumn==1
! 180: && pIdxInfo->aOrderBy[1].desc==0
! 181: )
! 182: ){
! 183: pIdxInfo->orderByConsumed = 1;
! 184: }
! 185:
! 186: pIdxInfo->estimatedCost = 10.0;
! 187: return SQLITE_OK;
! 188: }
! 189:
! 190: /*
! 191: ** Open a new statvfs cursor.
! 192: */
! 193: static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
! 194: StatTable *pTab = (StatTable *)pVTab;
! 195: StatCursor *pCsr;
! 196: int rc;
! 197:
! 198: pCsr = (StatCursor *)sqlite3_malloc(sizeof(StatCursor));
! 199: memset(pCsr, 0, sizeof(StatCursor));
! 200: pCsr->base.pVtab = pVTab;
! 201:
! 202: rc = sqlite3_prepare_v2(pTab->db,
! 203: "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
! 204: " UNION ALL "
! 205: "SELECT name, rootpage, type FROM sqlite_master WHERE rootpage!=0"
! 206: " ORDER BY name", -1,
! 207: &pCsr->pStmt, 0
! 208: );
! 209: if( rc!=SQLITE_OK ){
! 210: sqlite3_free(pCsr);
! 211: return rc;
! 212: }
! 213:
! 214: *ppCursor = (sqlite3_vtab_cursor *)pCsr;
! 215: return SQLITE_OK;
! 216: }
! 217:
! 218: static void statClearPage(StatPage *p){
! 219: int i;
! 220: for(i=0; i<p->nCell; i++){
! 221: sqlite3_free(p->aCell[i].aOvfl);
! 222: }
! 223: sqlite3PagerUnref(p->pPg);
! 224: sqlite3_free(p->aCell);
! 225: sqlite3_free(p->zPath);
! 226: memset(p, 0, sizeof(StatPage));
! 227: }
! 228:
! 229: static void statResetCsr(StatCursor *pCsr){
! 230: int i;
! 231: sqlite3_reset(pCsr->pStmt);
! 232: for(i=0; i<ArraySize(pCsr->aPage); i++){
! 233: statClearPage(&pCsr->aPage[i]);
! 234: }
! 235: pCsr->iPage = 0;
! 236: sqlite3_free(pCsr->zPath);
! 237: pCsr->zPath = 0;
! 238: }
! 239:
! 240: /*
! 241: ** Close a statvfs cursor.
! 242: */
! 243: static int statClose(sqlite3_vtab_cursor *pCursor){
! 244: StatCursor *pCsr = (StatCursor *)pCursor;
! 245: statResetCsr(pCsr);
! 246: sqlite3_finalize(pCsr->pStmt);
! 247: sqlite3_free(pCsr);
! 248: return SQLITE_OK;
! 249: }
! 250:
! 251: static void getLocalPayload(
! 252: int nUsable, /* Usable bytes per page */
! 253: u8 flags, /* Page flags */
! 254: int nTotal, /* Total record (payload) size */
! 255: int *pnLocal /* OUT: Bytes stored locally */
! 256: ){
! 257: int nLocal;
! 258: int nMinLocal;
! 259: int nMaxLocal;
! 260:
! 261: if( flags==0x0D ){ /* Table leaf node */
! 262: nMinLocal = (nUsable - 12) * 32 / 255 - 23;
! 263: nMaxLocal = nUsable - 35;
! 264: }else{ /* Index interior and leaf nodes */
! 265: nMinLocal = (nUsable - 12) * 32 / 255 - 23;
! 266: nMaxLocal = (nUsable - 12) * 64 / 255 - 23;
! 267: }
! 268:
! 269: nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4);
! 270: if( nLocal>nMaxLocal ) nLocal = nMinLocal;
! 271: *pnLocal = nLocal;
! 272: }
! 273:
! 274: static int statDecodePage(Btree *pBt, StatPage *p){
! 275: int nUnused;
! 276: int iOff;
! 277: int nHdr;
! 278: int isLeaf;
! 279: int szPage;
! 280:
! 281: u8 *aData = sqlite3PagerGetData(p->pPg);
! 282: u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
! 283:
! 284: p->flags = aHdr[0];
! 285: p->nCell = get2byte(&aHdr[3]);
! 286: p->nMxPayload = 0;
! 287:
! 288: isLeaf = (p->flags==0x0A || p->flags==0x0D);
! 289: nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100;
! 290:
! 291: nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell;
! 292: nUnused += (int)aHdr[7];
! 293: iOff = get2byte(&aHdr[1]);
! 294: while( iOff ){
! 295: nUnused += get2byte(&aData[iOff+2]);
! 296: iOff = get2byte(&aData[iOff]);
! 297: }
! 298: p->nUnused = nUnused;
! 299: p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
! 300: szPage = sqlite3BtreeGetPageSize(pBt);
! 301:
! 302: if( p->nCell ){
! 303: int i; /* Used to iterate through cells */
! 304: int nUsable = szPage - sqlite3BtreeGetReserve(pBt);
! 305:
! 306: p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell));
! 307: memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
! 308:
! 309: for(i=0; i<p->nCell; i++){
! 310: StatCell *pCell = &p->aCell[i];
! 311:
! 312: iOff = get2byte(&aData[nHdr+i*2]);
! 313: if( !isLeaf ){
! 314: pCell->iChildPg = sqlite3Get4byte(&aData[iOff]);
! 315: iOff += 4;
! 316: }
! 317: if( p->flags==0x05 ){
! 318: /* A table interior node. nPayload==0. */
! 319: }else{
! 320: u32 nPayload; /* Bytes of payload total (local+overflow) */
! 321: int nLocal; /* Bytes of payload stored locally */
! 322: iOff += getVarint32(&aData[iOff], nPayload);
! 323: if( p->flags==0x0D ){
! 324: u64 dummy;
! 325: iOff += sqlite3GetVarint(&aData[iOff], &dummy);
! 326: }
! 327: if( nPayload>p->nMxPayload ) p->nMxPayload = nPayload;
! 328: getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
! 329: pCell->nLocal = nLocal;
! 330: assert( nPayload>=nLocal );
! 331: assert( nLocal<=(nUsable-35) );
! 332: if( nPayload>nLocal ){
! 333: int j;
! 334: int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
! 335: pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
! 336: pCell->nOvfl = nOvfl;
! 337: pCell->aOvfl = sqlite3_malloc(sizeof(u32)*nOvfl);
! 338: pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]);
! 339: for(j=1; j<nOvfl; j++){
! 340: int rc;
! 341: u32 iPrev = pCell->aOvfl[j-1];
! 342: DbPage *pPg = 0;
! 343: rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg);
! 344: if( rc!=SQLITE_OK ){
! 345: assert( pPg==0 );
! 346: return rc;
! 347: }
! 348: pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg));
! 349: sqlite3PagerUnref(pPg);
! 350: }
! 351: }
! 352: }
! 353: }
! 354: }
! 355:
! 356: return SQLITE_OK;
! 357: }
! 358:
! 359: /*
! 360: ** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
! 361: ** the current value of pCsr->iPageno.
! 362: */
! 363: static void statSizeAndOffset(StatCursor *pCsr){
! 364: StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab;
! 365: Btree *pBt = pTab->db->aDb[0].pBt;
! 366: Pager *pPager = sqlite3BtreePager(pBt);
! 367: sqlite3_file *fd;
! 368: sqlite3_int64 x[2];
! 369:
! 370: /* The default page size and offset */
! 371: pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
! 372: pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
! 373:
! 374: /* If connected to a ZIPVFS backend, override the page size and
! 375: ** offset with actual values obtained from ZIPVFS.
! 376: */
! 377: fd = sqlite3PagerFile(pPager);
! 378: x[0] = pCsr->iPageno;
! 379: if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
! 380: pCsr->iOffset = x[0];
! 381: pCsr->szPage = x[1];
! 382: }
! 383: }
! 384:
! 385: /*
! 386: ** Move a statvfs cursor to the next entry in the file.
! 387: */
! 388: static int statNext(sqlite3_vtab_cursor *pCursor){
! 389: int rc;
! 390: int nPayload;
! 391: StatCursor *pCsr = (StatCursor *)pCursor;
! 392: StatTable *pTab = (StatTable *)pCursor->pVtab;
! 393: Btree *pBt = pTab->db->aDb[0].pBt;
! 394: Pager *pPager = sqlite3BtreePager(pBt);
! 395:
! 396: sqlite3_free(pCsr->zPath);
! 397: pCsr->zPath = 0;
! 398:
! 399: if( pCsr->aPage[0].pPg==0 ){
! 400: rc = sqlite3_step(pCsr->pStmt);
! 401: if( rc==SQLITE_ROW ){
! 402: int nPage;
! 403: u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1);
! 404: sqlite3PagerPagecount(pPager, &nPage);
! 405: if( nPage==0 ){
! 406: pCsr->isEof = 1;
! 407: return sqlite3_reset(pCsr->pStmt);
! 408: }
! 409: rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg);
! 410: pCsr->aPage[0].iPgno = iRoot;
! 411: pCsr->aPage[0].iCell = 0;
! 412: pCsr->aPage[0].zPath = sqlite3_mprintf("/");
! 413: pCsr->iPage = 0;
! 414: }else{
! 415: pCsr->isEof = 1;
! 416: return sqlite3_reset(pCsr->pStmt);
! 417: }
! 418: }else{
! 419:
! 420: /* Page p itself has already been visited. */
! 421: StatPage *p = &pCsr->aPage[pCsr->iPage];
! 422:
! 423: while( p->iCell<p->nCell ){
! 424: StatCell *pCell = &p->aCell[p->iCell];
! 425: if( pCell->iOvfl<pCell->nOvfl ){
! 426: int nUsable = sqlite3BtreeGetPageSize(pBt)-sqlite3BtreeGetReserve(pBt);
! 427: pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
! 428: pCsr->iPageno = pCell->aOvfl[pCell->iOvfl];
! 429: pCsr->zPagetype = "overflow";
! 430: pCsr->nCell = 0;
! 431: pCsr->nMxPayload = 0;
! 432: pCsr->zPath = sqlite3_mprintf(
! 433: "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl
! 434: );
! 435: if( pCell->iOvfl<pCell->nOvfl-1 ){
! 436: pCsr->nUnused = 0;
! 437: pCsr->nPayload = nUsable - 4;
! 438: }else{
! 439: pCsr->nPayload = pCell->nLastOvfl;
! 440: pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
! 441: }
! 442: pCell->iOvfl++;
! 443: statSizeAndOffset(pCsr);
! 444: return SQLITE_OK;
! 445: }
! 446: if( p->iRightChildPg ) break;
! 447: p->iCell++;
! 448: }
! 449:
! 450: while( !p->iRightChildPg || p->iCell>p->nCell ){
! 451: statClearPage(p);
! 452: if( pCsr->iPage==0 ) return statNext(pCursor);
! 453: pCsr->iPage--;
! 454: p = &pCsr->aPage[pCsr->iPage];
! 455: }
! 456: pCsr->iPage++;
! 457: assert( p==&pCsr->aPage[pCsr->iPage-1] );
! 458:
! 459: if( p->iCell==p->nCell ){
! 460: p[1].iPgno = p->iRightChildPg;
! 461: }else{
! 462: p[1].iPgno = p->aCell[p->iCell].iChildPg;
! 463: }
! 464: rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg);
! 465: p[1].iCell = 0;
! 466: p[1].zPath = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
! 467: p->iCell++;
! 468: }
! 469:
! 470:
! 471: /* Populate the StatCursor fields with the values to be returned
! 472: ** by the xColumn() and xRowid() methods.
! 473: */
! 474: if( rc==SQLITE_OK ){
! 475: int i;
! 476: StatPage *p = &pCsr->aPage[pCsr->iPage];
! 477: pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
! 478: pCsr->iPageno = p->iPgno;
! 479:
! 480: statDecodePage(pBt, p);
! 481: statSizeAndOffset(pCsr);
! 482:
! 483: switch( p->flags ){
! 484: case 0x05: /* table internal */
! 485: case 0x02: /* index internal */
! 486: pCsr->zPagetype = "internal";
! 487: break;
! 488: case 0x0D: /* table leaf */
! 489: case 0x0A: /* index leaf */
! 490: pCsr->zPagetype = "leaf";
! 491: break;
! 492: default:
! 493: pCsr->zPagetype = "corrupted";
! 494: break;
! 495: }
! 496: pCsr->nCell = p->nCell;
! 497: pCsr->nUnused = p->nUnused;
! 498: pCsr->nMxPayload = p->nMxPayload;
! 499: pCsr->zPath = sqlite3_mprintf("%s", p->zPath);
! 500: nPayload = 0;
! 501: for(i=0; i<p->nCell; i++){
! 502: nPayload += p->aCell[i].nLocal;
! 503: }
! 504: pCsr->nPayload = nPayload;
! 505: }
! 506:
! 507: return rc;
! 508: }
! 509:
! 510: static int statEof(sqlite3_vtab_cursor *pCursor){
! 511: StatCursor *pCsr = (StatCursor *)pCursor;
! 512: return pCsr->isEof;
! 513: }
! 514:
! 515: static int statFilter(
! 516: sqlite3_vtab_cursor *pCursor,
! 517: int idxNum, const char *idxStr,
! 518: int argc, sqlite3_value **argv
! 519: ){
! 520: StatCursor *pCsr = (StatCursor *)pCursor;
! 521:
! 522: statResetCsr(pCsr);
! 523: return statNext(pCursor);
! 524: }
! 525:
! 526: static int statColumn(
! 527: sqlite3_vtab_cursor *pCursor,
! 528: sqlite3_context *ctx,
! 529: int i
! 530: ){
! 531: StatCursor *pCsr = (StatCursor *)pCursor;
! 532: switch( i ){
! 533: case 0: /* name */
! 534: sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_STATIC);
! 535: break;
! 536: case 1: /* path */
! 537: sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
! 538: break;
! 539: case 2: /* pageno */
! 540: sqlite3_result_int64(ctx, pCsr->iPageno);
! 541: break;
! 542: case 3: /* pagetype */
! 543: sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
! 544: break;
! 545: case 4: /* ncell */
! 546: sqlite3_result_int(ctx, pCsr->nCell);
! 547: break;
! 548: case 5: /* payload */
! 549: sqlite3_result_int(ctx, pCsr->nPayload);
! 550: break;
! 551: case 6: /* unused */
! 552: sqlite3_result_int(ctx, pCsr->nUnused);
! 553: break;
! 554: case 7: /* mx_payload */
! 555: sqlite3_result_int(ctx, pCsr->nMxPayload);
! 556: break;
! 557: case 8: /* pgoffset */
! 558: sqlite3_result_int64(ctx, pCsr->iOffset);
! 559: break;
! 560: case 9: /* pgsize */
! 561: sqlite3_result_int(ctx, pCsr->szPage);
! 562: break;
! 563: }
! 564: return SQLITE_OK;
! 565: }
! 566:
! 567: static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
! 568: StatCursor *pCsr = (StatCursor *)pCursor;
! 569: *pRowid = pCsr->iPageno;
! 570: return SQLITE_OK;
! 571: }
! 572:
! 573: int sqlite3_dbstat_register(sqlite3 *db){
! 574: static sqlite3_module dbstat_module = {
! 575: 0, /* iVersion */
! 576: statConnect, /* xCreate */
! 577: statConnect, /* xConnect */
! 578: statBestIndex, /* xBestIndex */
! 579: statDisconnect, /* xDisconnect */
! 580: statDisconnect, /* xDestroy */
! 581: statOpen, /* xOpen - open a cursor */
! 582: statClose, /* xClose - close a cursor */
! 583: statFilter, /* xFilter - configure scan constraints */
! 584: statNext, /* xNext - advance a cursor */
! 585: statEof, /* xEof - check for end of scan */
! 586: statColumn, /* xColumn - read data */
! 587: statRowid, /* xRowid - read data */
! 588: 0, /* xUpdate */
! 589: 0, /* xBegin */
! 590: 0, /* xSync */
! 591: 0, /* xCommit */
! 592: 0, /* xRollback */
! 593: 0, /* xFindMethod */
! 594: 0, /* xRename */
! 595: };
! 596: sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
! 597: return SQLITE_OK;
! 598: }
! 599:
! 600: #endif
! 601:
! 602: #if defined(SQLITE_TEST) || TCLSH==2
! 603: #include <tcl.h>
! 604:
! 605: static int test_dbstat(
! 606: void *clientData,
! 607: Tcl_Interp *interp,
! 608: int objc,
! 609: Tcl_Obj *CONST objv[]
! 610: ){
! 611: #ifdef SQLITE_OMIT_VIRTUALTABLE
! 612: Tcl_AppendResult(interp, "dbstat not available because of "
! 613: "SQLITE_OMIT_VIRTUALTABLE", (void*)0);
! 614: return TCL_ERROR;
! 615: #else
! 616: struct SqliteDb { sqlite3 *db; };
! 617: char *zDb;
! 618: Tcl_CmdInfo cmdInfo;
! 619:
! 620: if( objc!=2 ){
! 621: Tcl_WrongNumArgs(interp, 1, objv, "DB");
! 622: return TCL_ERROR;
! 623: }
! 624:
! 625: zDb = Tcl_GetString(objv[1]);
! 626: if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
! 627: sqlite3* db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
! 628: sqlite3_dbstat_register(db);
! 629: }
! 630: return TCL_OK;
! 631: #endif
! 632: }
! 633:
! 634: int SqlitetestStat_Init(Tcl_Interp *interp){
! 635: Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0);
! 636: return TCL_OK;
! 637: }
! 638: #endif /* if defined(SQLITE_TEST) || TCLSH==2 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>