123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- ** 2017-10-24
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- ******************************************************************************
- **
- ** This file contains an implementation of the "sqlite_btreeinfo" virtual table.
- **
- ** The sqlite_btreeinfo virtual table is a read-only eponymous-only virtual
- ** table that shows information about all btrees in an SQLite database file.
- ** The schema is like this:
- **
- ** CREATE TABLE sqlite_btreeinfo(
- ** type TEXT, -- "table" or "index"
- ** name TEXT, -- Name of table or index for this btree.
- ** tbl_name TEXT, -- Associated table
- ** rootpage INT, -- The root page of the btree
- ** sql TEXT, -- SQL for this btree - from sqlite_schema
- ** hasRowid BOOLEAN, -- True if the btree has a rowid
- ** nEntry INT, -- Estimated number of entries
- ** nPage INT, -- Estimated number of pages
- ** depth INT, -- Depth of the btree
- ** szPage INT, -- Size of each page in bytes
- ** zSchema TEXT HIDDEN -- The schema to which this btree belongs
- ** );
- **
- ** The first 5 fields are taken directly from the sqlite_schema table.
- ** Considering only the first 5 fields, the only difference between
- ** this virtual table and the sqlite_schema table is that this virtual
- ** table omits all entries that have a 0 or NULL rowid - in other words
- ** it omits triggers and views.
- **
- ** The value added by this table comes in the next 5 fields.
- **
- ** Note that nEntry and nPage are *estimated*. They are computed doing
- ** a single search from the root to a leaf, counting the number of cells
- ** at each level, and assuming that unvisited pages have a similar number
- ** of cells.
- **
- ** The sqlite_dbpage virtual table must be available for this virtual table
- ** to operate.
- **
- ** USAGE EXAMPLES:
- **
- ** Show the table btrees in a schema order with the tables with the most
- ** rows occuring first:
- **
- ** SELECT name, nEntry
- ** FROM sqlite_btreeinfo
- ** WHERE type='table'
- ** ORDER BY nEntry DESC, name;
- **
- ** Show the names of all WITHOUT ROWID tables:
- **
- ** SELECT name FROM sqlite_btreeinfo
- ** WHERE type='table' AND NOT hasRowid;
- */
- #if !defined(SQLITEINT_H)
- #include "sqlite3ext.h"
- #endif
- SQLITE_EXTENSION_INIT1
- #include <string.h>
- #include <assert.h>
- /* Columns available in this virtual table */
- #define BINFO_COLUMN_TYPE 0
- #define BINFO_COLUMN_NAME 1
- #define BINFO_COLUMN_TBL_NAME 2
- #define BINFO_COLUMN_ROOTPAGE 3
- #define BINFO_COLUMN_SQL 4
- #define BINFO_COLUMN_HASROWID 5
- #define BINFO_COLUMN_NENTRY 6
- #define BINFO_COLUMN_NPAGE 7
- #define BINFO_COLUMN_DEPTH 8
- #define BINFO_COLUMN_SZPAGE 9
- #define BINFO_COLUMN_SCHEMA 10
- /* Forward declarations */
- typedef struct BinfoTable BinfoTable;
- typedef struct BinfoCursor BinfoCursor;
- /* A cursor for the sqlite_btreeinfo table */
- struct BinfoCursor {
- sqlite3_vtab_cursor base; /* Base class. Must be first */
- sqlite3_stmt *pStmt; /* Query against sqlite_schema */
- int rc; /* Result of previous sqlite_step() call */
- int hasRowid; /* hasRowid value. Negative if unknown. */
- sqlite3_int64 nEntry; /* nEntry value */
- int nPage; /* nPage value */
- int depth; /* depth value */
- int szPage; /* size of a btree page. 0 if unknown */
- char *zSchema; /* Schema being interrogated */
- };
- /* The sqlite_btreeinfo table */
- struct BinfoTable {
- sqlite3_vtab base; /* Base class. Must be first */
- sqlite3 *db; /* The databse connection */
- };
- /*
- ** Connect to the sqlite_btreeinfo virtual table.
- */
- static int binfoConnect(
- sqlite3 *db,
- void *pAux,
- int argc, const char *const*argv,
- sqlite3_vtab **ppVtab,
- char **pzErr
- ){
- BinfoTable *pTab = 0;
- int rc = SQLITE_OK;
- rc = sqlite3_declare_vtab(db,
- "CREATE TABLE x(\n"
- " type TEXT,\n"
- " name TEXT,\n"
- " tbl_name TEXT,\n"
- " rootpage INT,\n"
- " sql TEXT,\n"
- " hasRowid BOOLEAN,\n"
- " nEntry INT,\n"
- " nPage INT,\n"
- " depth INT,\n"
- " szPage INT,\n"
- " zSchema TEXT HIDDEN\n"
- ")");
- if( rc==SQLITE_OK ){
- pTab = (BinfoTable *)sqlite3_malloc64(sizeof(BinfoTable));
- if( pTab==0 ) rc = SQLITE_NOMEM;
- }
- assert( rc==SQLITE_OK || pTab==0 );
- if( pTab ){
- pTab->db = db;
- }
- *ppVtab = (sqlite3_vtab*)pTab;
- return rc;
- }
- /*
- ** Disconnect from or destroy a btreeinfo virtual table.
- */
- static int binfoDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
- return SQLITE_OK;
- }
- /*
- ** idxNum:
- **
- ** 0 Use "main" for the schema
- ** 1 Schema identified by parameter ?1
- */
- static int binfoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
- int i;
- pIdxInfo->estimatedCost = 10000.0; /* Cost estimate */
- pIdxInfo->estimatedRows = 100;
- for(i=0; i<pIdxInfo->nConstraint; i++){
- struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
- if( p->usable
- && p->iColumn==BINFO_COLUMN_SCHEMA
- && p->op==SQLITE_INDEX_CONSTRAINT_EQ
- ){
- pIdxInfo->estimatedCost = 1000.0;
- pIdxInfo->idxNum = 1;
- pIdxInfo->aConstraintUsage[i].argvIndex = 1;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- break;
- }
- }
- return SQLITE_OK;
- }
- /*
- ** Open a new btreeinfo cursor.
- */
- static int binfoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
- BinfoCursor *pCsr;
- pCsr = (BinfoCursor *)sqlite3_malloc64(sizeof(BinfoCursor));
- if( pCsr==0 ){
- return SQLITE_NOMEM;
- }else{
- memset(pCsr, 0, sizeof(BinfoCursor));
- pCsr->base.pVtab = pVTab;
- }
- *ppCursor = (sqlite3_vtab_cursor *)pCsr;
- return SQLITE_OK;
- }
- /*
- ** Close a btreeinfo cursor.
- */
- static int binfoClose(sqlite3_vtab_cursor *pCursor){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- sqlite3_finalize(pCsr->pStmt);
- sqlite3_free(pCsr->zSchema);
- sqlite3_free(pCsr);
- return SQLITE_OK;
- }
- /*
- ** Move a btreeinfo cursor to the next entry in the file.
- */
- static int binfoNext(sqlite3_vtab_cursor *pCursor){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- pCsr->rc = sqlite3_step(pCsr->pStmt);
- pCsr->hasRowid = -1;
- return pCsr->rc==SQLITE_ERROR ? SQLITE_ERROR : SQLITE_OK;
- }
- /* We have reached EOF if previous sqlite3_step() returned
- ** anything other than SQLITE_ROW;
- */
- static int binfoEof(sqlite3_vtab_cursor *pCursor){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- return pCsr->rc!=SQLITE_ROW;
- }
- /* Position a cursor back to the beginning.
- */
- static int binfoFilter(
- sqlite3_vtab_cursor *pCursor,
- int idxNum, const char *idxStr,
- int argc, sqlite3_value **argv
- ){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- BinfoTable *pTab = (BinfoTable *)pCursor->pVtab;
- char *zSql;
- int rc;
- sqlite3_free(pCsr->zSchema);
- if( idxNum==1 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){
- pCsr->zSchema = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
- }else{
- pCsr->zSchema = sqlite3_mprintf("main");
- }
- zSql = sqlite3_mprintf(
- "SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
- "UNION ALL "
- "SELECT rowid, type, name, tbl_name, rootpage, sql"
- " FROM \"%w\".sqlite_schema WHERE rootpage>=1",
- pCsr->zSchema);
- sqlite3_finalize(pCsr->pStmt);
- pCsr->pStmt = 0;
- pCsr->hasRowid = -1;
- rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
- sqlite3_free(zSql);
- if( rc==SQLITE_OK ){
- rc = binfoNext(pCursor);
- }
- return rc;
- }
- /* Decode big-endian integers */
- static unsigned int get_uint16(unsigned char *a){
- return (a[0]<<8)|a[1];
- }
- static unsigned int get_uint32(unsigned char *a){
- return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
- }
- /* Examine the b-tree rooted at pgno and estimate its size.
- ** Return non-zero if anything goes wrong.
- */
- static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
- sqlite3_int64 nEntry = 1;
- int nPage = 1;
- unsigned char *aData;
- sqlite3_stmt *pStmt = 0;
- int rc = SQLITE_OK;
- int pgsz = 0;
- int nCell;
- int iCell;
- rc = sqlite3_prepare_v2(db,
- "SELECT data FROM sqlite_dbpage('main') WHERE pgno=?1", -1,
- &pStmt, 0);
- if( rc ) return rc;
- pCsr->depth = 1;
- while(1){
- sqlite3_bind_int(pStmt, 1, pgno);
- rc = sqlite3_step(pStmt);
- if( rc!=SQLITE_ROW ){
- rc = SQLITE_ERROR;
- break;
- }
- pCsr->szPage = pgsz = sqlite3_column_bytes(pStmt, 0);
- aData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
- if( aData==0 ){
- rc = SQLITE_NOMEM;
- break;
- }
- if( pgno==1 ){
- aData += 100;
- pgsz -= 100;
- }
- pCsr->hasRowid = aData[0]!=2 && aData[0]!=10;
- nCell = get_uint16(aData+3);
- nEntry *= (nCell+1);
- if( aData[0]==10 || aData[0]==13 ) break;
- nPage *= (nCell+1);
- if( nCell<=1 ){
- pgno = get_uint32(aData+8);
- }else{
- iCell = get_uint16(aData+12+2*(nCell/2));
- if( pgno==1 ) iCell -= 100;
- if( iCell<=12 || iCell>=pgsz-4 ){
- rc = SQLITE_CORRUPT;
- break;
- }
- pgno = get_uint32(aData+iCell);
- }
- pCsr->depth++;
- sqlite3_reset(pStmt);
- }
- sqlite3_finalize(pStmt);
- pCsr->nPage = nPage;
- pCsr->nEntry = nEntry;
- if( rc==SQLITE_ROW ) rc = SQLITE_OK;
- return rc;
- }
- /* Return a column for the sqlite_btreeinfo table */
- static int binfoColumn(
- sqlite3_vtab_cursor *pCursor,
- sqlite3_context *ctx,
- int i
- ){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- if( i>=BINFO_COLUMN_HASROWID && i<=BINFO_COLUMN_SZPAGE && pCsr->hasRowid<0 ){
- int pgno = sqlite3_column_int(pCsr->pStmt, BINFO_COLUMN_ROOTPAGE+1);
- sqlite3 *db = sqlite3_context_db_handle(ctx);
- int rc = binfoCompute(db, pgno, pCsr);
- if( rc ){
- pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
- return SQLITE_ERROR;
- }
- }
- switch( i ){
- case BINFO_COLUMN_NAME:
- case BINFO_COLUMN_TYPE:
- case BINFO_COLUMN_TBL_NAME:
- case BINFO_COLUMN_ROOTPAGE:
- case BINFO_COLUMN_SQL: {
- sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
- break;
- }
- case BINFO_COLUMN_HASROWID: {
- sqlite3_result_int(ctx, pCsr->hasRowid);
- break;
- }
- case BINFO_COLUMN_NENTRY: {
- sqlite3_result_int64(ctx, pCsr->nEntry);
- break;
- }
- case BINFO_COLUMN_NPAGE: {
- sqlite3_result_int(ctx, pCsr->nPage);
- break;
- }
- case BINFO_COLUMN_DEPTH: {
- sqlite3_result_int(ctx, pCsr->depth);
- break;
- }
- case BINFO_COLUMN_SCHEMA: {
- sqlite3_result_text(ctx, pCsr->zSchema, -1, SQLITE_STATIC);
- break;
- }
- }
- return SQLITE_OK;
- }
- /* Return the ROWID for the sqlite_btreeinfo table */
- static int binfoRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- BinfoCursor *pCsr = (BinfoCursor *)pCursor;
- *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
- return SQLITE_OK;
- }
- /*
- ** Invoke this routine to register the "sqlite_btreeinfo" virtual table module
- */
- int sqlite3BinfoRegister(sqlite3 *db){
- static sqlite3_module binfo_module = {
- 0, /* iVersion */
- 0, /* xCreate */
- binfoConnect, /* xConnect */
- binfoBestIndex, /* xBestIndex */
- binfoDisconnect, /* xDisconnect */
- 0, /* xDestroy */
- binfoOpen, /* xOpen - open a cursor */
- binfoClose, /* xClose - close a cursor */
- binfoFilter, /* xFilter - configure scan constraints */
- binfoNext, /* xNext - advance a cursor */
- binfoEof, /* xEof - check for end of scan */
- binfoColumn, /* xColumn - read data */
- binfoRowid, /* xRowid - read data */
- 0, /* xUpdate */
- 0, /* xBegin */
- 0, /* xSync */
- 0, /* xCommit */
- 0, /* xRollback */
- 0, /* xFindMethod */
- 0, /* xRename */
- 0, /* xSavepoint */
- 0, /* xRelease */
- 0, /* xRollbackTo */
- 0, /* xShadowName */
- 0 /* xIntegrity */
- };
- return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
- }
- #ifdef _WIN32
- __declspec(dllexport)
- #endif
- int sqlite3_btreeinfo_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
- ){
- SQLITE_EXTENSION_INIT2(pApi);
- return sqlite3BinfoRegister(db);
- }
|