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