4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 ******************************************************************************
13 ** This file contains an implementation of the "sqlite_btreeinfo" virtual table.
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:
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
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.
39 ** The value added by this table comes in the next 5 fields.
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
46 ** The sqlite_dbpage virtual table must be available for this virtual table
51 ** Show the table btrees in a schema order with the tables with the most
52 ** rows occuring first:
54 ** SELECT name, nEntry
55 ** FROM sqlite_btreeinfo
57 ** ORDER BY nEntry DESC, name;
59 ** Show the names of all WITHOUT ROWID tables:
61 ** SELECT name FROM sqlite_btreeinfo
62 ** WHERE type='table' AND NOT hasRowid;
64 #if !defined(SQLITEINT_H)
65 #include "sqlite3ext.h"
67 SQLITE_EXTENSION_INIT1
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
84 /* Forward declarations */
85 typedef struct BinfoTable BinfoTable
;
86 typedef struct BinfoCursor BinfoCursor
;
88 /* A cursor for the sqlite_btreeinfo table */
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 */
101 /* The sqlite_btreeinfo table */
103 sqlite3_vtab base
; /* Base class. Must be first */
104 sqlite3
*db
; /* The databse connection */
108 ** Connect to the sqlite_btreeinfo virtual table.
110 static int binfoConnect(
113 int argc
, const char *const*argv
,
114 sqlite3_vtab
**ppVtab
,
117 BinfoTable
*pTab
= 0;
119 rc
= sqlite3_declare_vtab(db
,
126 " hasRowid BOOLEAN,\n"
131 " zSchema TEXT HIDDEN\n"
134 pTab
= (BinfoTable
*)sqlite3_malloc64(sizeof(BinfoTable
));
135 if( pTab
==0 ) rc
= SQLITE_NOMEM
;
137 assert( rc
==SQLITE_OK
|| pTab
==0 );
141 *ppVtab
= (sqlite3_vtab
*)pTab
;
146 ** Disconnect from or destroy a btreeinfo virtual table.
148 static int binfoDisconnect(sqlite3_vtab
*pVtab
){
156 ** 0 Use "main" for the schema
157 ** 1 Schema identified by parameter ?1
159 static int binfoBestIndex(sqlite3_vtab
*tab
, sqlite3_index_info
*pIdxInfo
){
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
];
166 && p
->iColumn
==BINFO_COLUMN_SCHEMA
167 && p
->op
==SQLITE_INDEX_CONSTRAINT_EQ
169 pIdxInfo
->estimatedCost
= 1000.0;
170 pIdxInfo
->idxNum
= 1;
171 pIdxInfo
->aConstraintUsage
[i
].argvIndex
= 1;
172 pIdxInfo
->aConstraintUsage
[i
].omit
= 1;
180 ** Open a new btreeinfo cursor.
182 static int binfoOpen(sqlite3_vtab
*pVTab
, sqlite3_vtab_cursor
**ppCursor
){
185 pCsr
= (BinfoCursor
*)sqlite3_malloc64(sizeof(BinfoCursor
));
189 memset(pCsr
, 0, sizeof(BinfoCursor
));
190 pCsr
->base
.pVtab
= pVTab
;
193 *ppCursor
= (sqlite3_vtab_cursor
*)pCsr
;
198 ** Close a btreeinfo cursor.
200 static int binfoClose(sqlite3_vtab_cursor
*pCursor
){
201 BinfoCursor
*pCsr
= (BinfoCursor
*)pCursor
;
202 sqlite3_finalize(pCsr
->pStmt
);
203 sqlite3_free(pCsr
->zSchema
);
209 ** Move a btreeinfo cursor to the next entry in the file.
211 static int binfoNext(sqlite3_vtab_cursor
*pCursor
){
212 BinfoCursor
*pCsr
= (BinfoCursor
*)pCursor
;
213 pCsr
->rc
= sqlite3_step(pCsr
->pStmt
);
215 return pCsr
->rc
==SQLITE_ERROR
? SQLITE_ERROR
: SQLITE_OK
;
218 /* We have reached EOF if previous sqlite3_step() returned
219 ** anything other than SQLITE_ROW;
221 static int binfoEof(sqlite3_vtab_cursor
*pCursor
){
222 BinfoCursor
*pCsr
= (BinfoCursor
*)pCursor
;
223 return pCsr
->rc
!=SQLITE_ROW
;
226 /* Position a cursor back to the beginning.
228 static int binfoFilter(
229 sqlite3_vtab_cursor
*pCursor
,
230 int idxNum
, const char *idxStr
,
231 int argc
, sqlite3_value
**argv
233 BinfoCursor
*pCsr
= (BinfoCursor
*)pCursor
;
234 BinfoTable
*pTab
= (BinfoTable
*)pCursor
->pVtab
;
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]));
242 pCsr
->zSchema
= sqlite3_mprintf("main");
244 zSql
= sqlite3_mprintf(
245 "SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
247 "SELECT rowid, type, name, tbl_name, rootpage, sql"
248 " FROM \"%w\".sqlite_schema WHERE rootpage>=1",
250 sqlite3_finalize(pCsr
->pStmt
);
253 rc
= sqlite3_prepare_v2(pTab
->db
, zSql
, -1, &pCsr
->pStmt
, 0);
256 rc
= binfoNext(pCursor
);
261 /* Decode big-endian integers */
262 static unsigned int get_uint16(unsigned char *a
){
263 return (a
[0]<<8)|a
[1];
265 static unsigned int get_uint32(unsigned char *a
){
266 return (a
[0]<<24)|(a
[1]<<16)|(a
[2]<<8)|a
[3];
269 /* Examine the b-tree rooted at pgno and estimate its size.
270 ** Return non-zero if anything goes wrong.
272 static int binfoCompute(sqlite3
*db
, int pgno
, BinfoCursor
*pCsr
){
273 sqlite3_int64 nEntry
= 1;
275 unsigned char *aData
;
276 sqlite3_stmt
*pStmt
= 0;
282 rc
= sqlite3_prepare_v2(db
,
283 "SELECT data FROM sqlite_dbpage('main') WHERE pgno=?1", -1,
288 sqlite3_bind_int(pStmt
, 1, pgno
);
289 rc
= sqlite3_step(pStmt
);
290 if( rc
!=SQLITE_ROW
){
294 pCsr
->szPage
= pgsz
= sqlite3_column_bytes(pStmt
, 0);
295 aData
= (unsigned char*)sqlite3_column_blob(pStmt
, 0);
304 pCsr
->hasRowid
= aData
[0]!=2 && aData
[0]!=10;
305 nCell
= get_uint16(aData
+3);
307 if( aData
[0]==10 || aData
[0]==13 ) break;
310 pgno
= get_uint32(aData
+8);
312 iCell
= get_uint16(aData
+12+2*(nCell
/2));
313 if( pgno
==1 ) iCell
-= 100;
314 if( iCell
<=12 || iCell
>=pgsz
-4 ){
318 pgno
= get_uint32(aData
+iCell
);
321 sqlite3_reset(pStmt
);
323 sqlite3_finalize(pStmt
);
325 pCsr
->nEntry
= nEntry
;
326 if( rc
==SQLITE_ROW
) rc
= SQLITE_OK
;
330 /* Return a column for the sqlite_btreeinfo table */
331 static int binfoColumn(
332 sqlite3_vtab_cursor
*pCursor
,
333 sqlite3_context
*ctx
,
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
);
342 pCursor
->pVtab
->zErrMsg
= sqlite3_mprintf("%s", sqlite3_errmsg(db
));
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));
355 case BINFO_COLUMN_HASROWID
: {
356 sqlite3_result_int(ctx
, pCsr
->hasRowid
);
359 case BINFO_COLUMN_NENTRY
: {
360 sqlite3_result_int64(ctx
, pCsr
->nEntry
);
363 case BINFO_COLUMN_NPAGE
: {
364 sqlite3_result_int(ctx
, pCsr
->nPage
);
367 case BINFO_COLUMN_DEPTH
: {
368 sqlite3_result_int(ctx
, pCsr
->depth
);
371 case BINFO_COLUMN_SCHEMA
: {
372 sqlite3_result_text(ctx
, pCsr
->zSchema
, -1, SQLITE_STATIC
);
379 /* Return the ROWID for the sqlite_btreeinfo table */
380 static int binfoRowid(sqlite3_vtab_cursor
*pCursor
, sqlite_int64
*pRowid
){
381 BinfoCursor
*pCsr
= (BinfoCursor
*)pCursor
;
382 *pRowid
= sqlite3_column_int64(pCsr
->pStmt
, 0);
387 ** Invoke this routine to register the "sqlite_btreeinfo" virtual table module
389 int sqlite3BinfoRegister(sqlite3
*db
){
390 static sqlite3_module binfo_module
= {
393 binfoConnect
, /* xConnect */
394 binfoBestIndex
, /* xBestIndex */
395 binfoDisconnect
, /* xDisconnect */
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 */
417 return sqlite3_create_module(db
, "sqlite_btreeinfo", &binfo_module
, 0);
421 __declspec(dllexport
)
423 int sqlite3_btreeinfo_init(
426 const sqlite3_api_routines
*pApi
428 SQLITE_EXTENSION_INIT2(pApi
);
429 return sqlite3BinfoRegister(db
);