1 /*
2 ** 2017-10-24
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 "sqlite_btreeinfo" virtual table.
14 **
15 ** The sqlite_btreeinfo virtual table is a read-only eponymous-only virtual
16 ** table that shows information about all btrees in an SQLite database file.
17 ** The schema is like this:
18 **
19 ** CREATE TABLE sqlite_btreeinfo(
20 **    type TEXT,                   -- "table" or "index"
21 **    name TEXT,                   -- Name of table or index for this btree.
22 **    tbl_name TEXT,               -- Associated table
23 **    rootpage INT,                -- The root page of the btree
24 **    sql TEXT,                    -- SQL for this btree - from sqlite_schema
25 **    hasRowid BOOLEAN,            -- True if the btree has a rowid
26 **    nEntry INT,                  -- Estimated number of entries
27 **    nPage INT,                   -- Estimated number of pages
28 **    depth INT,                   -- Depth of the btree
29 **    szPage INT,                  -- Size of each page in bytes
30 **    zSchema TEXT HIDDEN          -- The schema to which this btree belongs
31 ** );
32 **
33 ** The first 5 fields are taken directly from the sqlite_schema table.
34 ** Considering only the first 5 fields, the only difference between
35 ** this virtual table and the sqlite_schema table is that this virtual
36 ** table omits all entries that have a 0 or NULL rowid - in other words
37 ** it omits triggers and views.
38 **
39 ** The value added by this table comes in the next 5 fields.
40 **
41 ** Note that nEntry and nPage are *estimated*.  They are computed doing
42 ** a single search from the root to a leaf, counting the number of cells
43 ** at each level, and assuming that unvisited pages have a similar number
44 ** of cells.
45 **
46 ** The sqlite_dbpage virtual table must be available for this virtual table
47 ** to operate.
48 **
49 ** USAGE EXAMPLES:
50 **
51 ** Show the table btrees in a schema order with the tables with the most
52 ** rows occuring first:
53 **
54 **      SELECT name, nEntry
55 **        FROM sqlite_btreeinfo
56 **       WHERE type='table'
57 **       ORDER BY nEntry DESC, name;
58 **
59 ** Show the names of all WITHOUT ROWID tables:
60 **
61 **      SELECT name FROM sqlite_btreeinfo
62 **       WHERE type='table' AND NOT hasRowid;
63 */
64 #if !defined(SQLITEINT_H)
65 #include "sqlite3ext.h"
66 #endif
67 SQLITE_EXTENSION_INIT1
68 #include <string.h>
69 #include <assert.h>
70 
71 /* Columns available in this virtual table */
72 #define BINFO_COLUMN_TYPE         0
73 #define BINFO_COLUMN_NAME         1
74 #define BINFO_COLUMN_TBL_NAME     2
75 #define BINFO_COLUMN_ROOTPAGE     3
76 #define BINFO_COLUMN_SQL          4
77 #define BINFO_COLUMN_HASROWID     5
78 #define BINFO_COLUMN_NENTRY       6
79 #define BINFO_COLUMN_NPAGE        7
80 #define BINFO_COLUMN_DEPTH        8
81 #define BINFO_COLUMN_SZPAGE       9
82 #define BINFO_COLUMN_SCHEMA      10
83 
84 /* Forward declarations */
85 typedef struct BinfoTable BinfoTable;
86 typedef struct BinfoCursor BinfoCursor;
87 
88 /* A cursor for the sqlite_btreeinfo table */
89 struct BinfoCursor {
90   sqlite3_vtab_cursor base;       /* Base class.  Must be first */
91   sqlite3_stmt *pStmt;            /* Query against sqlite_schema */
92   int rc;                         /* Result of previous sqlite_step() call */
93   int hasRowid;                   /* hasRowid value.  Negative if unknown. */
94   sqlite3_int64 nEntry;           /* nEntry value */
95   int nPage;                      /* nPage value */
96   int depth;                      /* depth value */
97   int szPage;                     /* size of a btree page.  0 if unknown */
98   char *zSchema;                  /* Schema being interrogated */
99 };
100 
101 /* The sqlite_btreeinfo table */
102 struct BinfoTable {
103   sqlite3_vtab base;              /* Base class.  Must be first */
104   sqlite3 *db;                    /* The databse connection */
105 };
106 
107 /*
108 ** Connect to the sqlite_btreeinfo virtual table.
109 */
binfoConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)110 static int binfoConnect(
111   sqlite3 *db,
112   void *pAux,
113   int argc, const char *const*argv,
114   sqlite3_vtab **ppVtab,
115   char **pzErr
116 ){
117   BinfoTable *pTab = 0;
118   int rc = SQLITE_OK;
119   rc = sqlite3_declare_vtab(db,
120       "CREATE TABLE x(\n"
121       " type TEXT,\n"
122       " name TEXT,\n"
123       " tbl_name TEXT,\n"
124       " rootpage INT,\n"
125       " sql TEXT,\n"
126       " hasRowid BOOLEAN,\n"
127       " nEntry INT,\n"
128       " nPage INT,\n"
129       " depth INT,\n"
130       " szPage INT,\n"
131       " zSchema TEXT HIDDEN\n"
132       ")");
133   if( rc==SQLITE_OK ){
134     pTab = (BinfoTable *)sqlite3_malloc64(sizeof(BinfoTable));
135     if( pTab==0 ) rc = SQLITE_NOMEM;
136   }
137   assert( rc==SQLITE_OK || pTab==0 );
138   if( pTab ){
139     pTab->db = db;
140   }
141   *ppVtab = (sqlite3_vtab*)pTab;
142   return rc;
143 }
144 
145 /*
146 ** Disconnect from or destroy a btreeinfo virtual table.
147 */
binfoDisconnect(sqlite3_vtab * pVtab)148 static int binfoDisconnect(sqlite3_vtab *pVtab){
149   sqlite3_free(pVtab);
150   return SQLITE_OK;
151 }
152 
153 /*
154 ** idxNum:
155 **
156 **     0     Use "main" for the schema
157 **     1     Schema identified by parameter ?1
158 */
binfoBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)159 static int binfoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
160   int i;
161   pIdxInfo->estimatedCost = 10000.0;  /* Cost estimate */
162   pIdxInfo->estimatedRows = 100;
163   for(i=0; i<pIdxInfo->nConstraint; i++){
164     struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
165     if( p->usable
166      && p->iColumn==BINFO_COLUMN_SCHEMA
167      && p->op==SQLITE_INDEX_CONSTRAINT_EQ
168     ){
169       pIdxInfo->estimatedCost = 1000.0;
170       pIdxInfo->idxNum = 1;
171       pIdxInfo->aConstraintUsage[i].argvIndex = 1;
172       pIdxInfo->aConstraintUsage[i].omit = 1;
173       break;
174     }
175   }
176   return SQLITE_OK;
177 }
178 
179 /*
180 ** Open a new btreeinfo cursor.
181 */
binfoOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)182 static int binfoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
183   BinfoCursor *pCsr;
184 
185   pCsr = (BinfoCursor *)sqlite3_malloc64(sizeof(BinfoCursor));
186   if( pCsr==0 ){
187     return SQLITE_NOMEM;
188   }else{
189     memset(pCsr, 0, sizeof(BinfoCursor));
190     pCsr->base.pVtab = pVTab;
191   }
192 
193   *ppCursor = (sqlite3_vtab_cursor *)pCsr;
194   return SQLITE_OK;
195 }
196 
197 /*
198 ** Close a btreeinfo cursor.
199 */
binfoClose(sqlite3_vtab_cursor * pCursor)200 static int binfoClose(sqlite3_vtab_cursor *pCursor){
201   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
202   sqlite3_finalize(pCsr->pStmt);
203   sqlite3_free(pCsr->zSchema);
204   sqlite3_free(pCsr);
205   return SQLITE_OK;
206 }
207 
208 /*
209 ** Move a btreeinfo cursor to the next entry in the file.
210 */
binfoNext(sqlite3_vtab_cursor * pCursor)211 static int binfoNext(sqlite3_vtab_cursor *pCursor){
212   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
213   pCsr->rc = sqlite3_step(pCsr->pStmt);
214   pCsr->hasRowid = -1;
215   return pCsr->rc==SQLITE_ERROR ? SQLITE_ERROR : SQLITE_OK;
216 }
217 
218 /* We have reached EOF if previous sqlite3_step() returned
219 ** anything other than SQLITE_ROW;
220 */
binfoEof(sqlite3_vtab_cursor * pCursor)221 static int binfoEof(sqlite3_vtab_cursor *pCursor){
222   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
223   return pCsr->rc!=SQLITE_ROW;
224 }
225 
226 /* Position a cursor back to the beginning.
227 */
binfoFilter(sqlite3_vtab_cursor * pCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)228 static int binfoFilter(
229   sqlite3_vtab_cursor *pCursor,
230   int idxNum, const char *idxStr,
231   int argc, sqlite3_value **argv
232 ){
233   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
234   BinfoTable *pTab = (BinfoTable *)pCursor->pVtab;
235   char *zSql;
236   int rc;
237 
238   sqlite3_free(pCsr->zSchema);
239   if( idxNum==1 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){
240     pCsr->zSchema = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
241   }else{
242     pCsr->zSchema = sqlite3_mprintf("main");
243   }
244   zSql = sqlite3_mprintf(
245       "SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
246       "UNION ALL "
247       "SELECT rowid, type, name, tbl_name, rootpage, sql"
248       " FROM \"%w\".sqlite_schema WHERE rootpage>=1",
249        pCsr->zSchema);
250   sqlite3_finalize(pCsr->pStmt);
251   pCsr->pStmt = 0;
252   pCsr->hasRowid = -1;
253   rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
254   sqlite3_free(zSql);
255   if( rc==SQLITE_OK ){
256     rc = binfoNext(pCursor);
257   }
258   return rc;
259 }
260 
261 /* Decode big-endian integers */
get_uint16(unsigned char * a)262 static unsigned int get_uint16(unsigned char *a){
263   return (a[0]<<8)|a[1];
264 }
get_uint32(unsigned char * a)265 static unsigned int get_uint32(unsigned char *a){
266   return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
267 }
268 
269 /* Examine the b-tree rooted at pgno and estimate its size.
270 ** Return non-zero if anything goes wrong.
271 */
binfoCompute(sqlite3 * db,int pgno,BinfoCursor * pCsr)272 static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
273   sqlite3_int64 nEntry = 1;
274   int nPage = 1;
275   unsigned char *aData;
276   sqlite3_stmt *pStmt = 0;
277   int rc = SQLITE_OK;
278   int pgsz = 0;
279   int nCell;
280   int iCell;
281 
282   rc = sqlite3_prepare_v2(db,
283            "SELECT data FROM sqlite_dbpage('main') WHERE pgno=?1", -1,
284            &pStmt, 0);
285   if( rc ) return rc;
286   pCsr->depth = 1;
287   while(1){
288     sqlite3_bind_int(pStmt, 1, pgno);
289     rc = sqlite3_step(pStmt);
290     if( rc!=SQLITE_ROW ){
291       rc = SQLITE_ERROR;
292       break;
293     }
294     pCsr->szPage = pgsz = sqlite3_column_bytes(pStmt, 0);
295     aData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
296     if( aData==0 ){
297       rc = SQLITE_NOMEM;
298       break;
299     }
300     if( pgno==1 ){
301       aData += 100;
302       pgsz -= 100;
303     }
304     pCsr->hasRowid = aData[0]!=2 && aData[0]!=10;
305     nCell = get_uint16(aData+3);
306     nEntry *= (nCell+1);
307     if( aData[0]==10 || aData[0]==13 ) break;
308     nPage *= (nCell+1);
309     if( nCell<=1 ){
310       pgno = get_uint32(aData+8);
311     }else{
312       iCell = get_uint16(aData+12+2*(nCell/2));
313       if( pgno==1 ) iCell -= 100;
314       if( iCell<=12 || iCell>=pgsz-4 ){
315         rc = SQLITE_CORRUPT;
316         break;
317       }
318       pgno = get_uint32(aData+iCell);
319     }
320     pCsr->depth++;
321     sqlite3_reset(pStmt);
322   }
323   sqlite3_finalize(pStmt);
324   pCsr->nPage = nPage;
325   pCsr->nEntry = nEntry;
326   if( rc==SQLITE_ROW ) rc = SQLITE_OK;
327   return rc;
328 }
329 
330 /* Return a column for the sqlite_btreeinfo table */
binfoColumn(sqlite3_vtab_cursor * pCursor,sqlite3_context * ctx,int i)331 static int binfoColumn(
332   sqlite3_vtab_cursor *pCursor,
333   sqlite3_context *ctx,
334   int i
335 ){
336   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
337   if( i>=BINFO_COLUMN_HASROWID && i<=BINFO_COLUMN_SZPAGE && pCsr->hasRowid<0 ){
338     int pgno = sqlite3_column_int(pCsr->pStmt, BINFO_COLUMN_ROOTPAGE+1);
339     sqlite3 *db = sqlite3_context_db_handle(ctx);
340     int rc = binfoCompute(db, pgno, pCsr);
341     if( rc ){
342       pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
343       return SQLITE_ERROR;
344     }
345   }
346   switch( i ){
347     case BINFO_COLUMN_NAME:
348     case BINFO_COLUMN_TYPE:
349     case BINFO_COLUMN_TBL_NAME:
350     case BINFO_COLUMN_ROOTPAGE:
351     case BINFO_COLUMN_SQL: {
352       sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
353       break;
354     }
355     case BINFO_COLUMN_HASROWID: {
356       sqlite3_result_int(ctx, pCsr->hasRowid);
357       break;
358     }
359     case BINFO_COLUMN_NENTRY: {
360       sqlite3_result_int64(ctx, pCsr->nEntry);
361       break;
362     }
363     case BINFO_COLUMN_NPAGE: {
364       sqlite3_result_int(ctx, pCsr->nPage);
365       break;
366     }
367     case BINFO_COLUMN_DEPTH: {
368       sqlite3_result_int(ctx, pCsr->depth);
369       break;
370     }
371     case BINFO_COLUMN_SCHEMA: {
372       sqlite3_result_text(ctx, pCsr->zSchema, -1, SQLITE_STATIC);
373       break;
374     }
375   }
376   return SQLITE_OK;
377 }
378 
379 /* Return the ROWID for the sqlite_btreeinfo table */
binfoRowid(sqlite3_vtab_cursor * pCursor,sqlite_int64 * pRowid)380 static int binfoRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
381   BinfoCursor *pCsr = (BinfoCursor *)pCursor;
382   *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
383   return SQLITE_OK;
384 }
385 
386 /*
387 ** Invoke this routine to register the "sqlite_btreeinfo" virtual table module
388 */
sqlite3BinfoRegister(sqlite3 * db)389 int sqlite3BinfoRegister(sqlite3 *db){
390   static sqlite3_module binfo_module = {
391     0,                           /* iVersion */
392     0,                           /* xCreate */
393     binfoConnect,                /* xConnect */
394     binfoBestIndex,              /* xBestIndex */
395     binfoDisconnect,             /* xDisconnect */
396     0,                           /* xDestroy */
397     binfoOpen,                   /* xOpen - open a cursor */
398     binfoClose,                  /* xClose - close a cursor */
399     binfoFilter,                 /* xFilter - configure scan constraints */
400     binfoNext,                   /* xNext - advance a cursor */
401     binfoEof,                    /* xEof - check for end of scan */
402     binfoColumn,                 /* xColumn - read data */
403     binfoRowid,                  /* xRowid - read data */
404     0,                           /* xUpdate */
405     0,                           /* xBegin */
406     0,                           /* xSync */
407     0,                           /* xCommit */
408     0,                           /* xRollback */
409     0,                           /* xFindMethod */
410     0,                           /* xRename */
411     0,                           /* xSavepoint */
412     0,                           /* xRelease */
413     0,                           /* xRollbackTo */
414     0                            /* xShadowName */
415   };
416   return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
417 }
418 
419 #ifdef _WIN32
420 __declspec(dllexport)
421 #endif
sqlite3_btreeinfo_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)422 int sqlite3_btreeinfo_init(
423   sqlite3 *db,
424   char **pzErrMsg,
425   const sqlite3_api_routines *pApi
426 ){
427   SQLITE_EXTENSION_INIT2(pApi);
428   return sqlite3BinfoRegister(db);
429 }
430