#if !defined(SQLITEINT_H)
#include "sqlite3ext.h"
#endif
SQLITE_EXTENSION_INIT1
#include <string.h>
#include <assert.h>
#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
typedef struct BinfoTable BinfoTable;
typedef struct BinfoCursor BinfoCursor;
struct BinfoCursor {
sqlite3_vtab_cursor base;
sqlite3_stmt *pStmt;
int rc;
int hasRowid;
sqlite3_int64 nEntry;
int nPage;
int depth;
int szPage;
char *zSchema;
};
struct BinfoTable {
sqlite3_vtab base;
sqlite3 *db;
};
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;
}
static int binfoDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
static int binfoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
pIdxInfo->estimatedCost = 10000.0;
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;
}
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;
}
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;
}
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;
}
static int binfoEof(sqlite3_vtab_cursor *pCursor){
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
return pCsr->rc!=SQLITE_ROW;
}
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;
}
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];
}
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;
}
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;
}
static int binfoRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
*pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
return SQLITE_OK;
}
int sqlite3BinfoRegister(sqlite3 *db){
static sqlite3_module binfo_module = {
0,
0,
binfoConnect,
binfoBestIndex,
binfoDisconnect,
0,
binfoOpen,
binfoClose,
binfoFilter,
binfoNext,
binfoEof,
binfoColumn,
binfoRowid,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
};
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);
}