| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #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, |
| 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); |
| } |
|
|