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>