From: drh Date: Thu, 14 Nov 2013 15:35:43 +0000 (+0000) Subject: An experimental virtual tables for showing the content of internal schema X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=656ba691e5f8aef6193022dd6d08afab736a5fac;p=thirdparty%2Fsqlite.git An experimental virtual tables for showing the content of internal schema objects. FossilOrigin-Name: d1fbc6ca18383b97befd597aeb7ff84c6a04d243 --- diff --git a/main.mk b/main.mk index 9f06366566..5c332441aa 100644 --- a/main.mk +++ b/main.mk @@ -379,9 +379,9 @@ libsqlite3.a: $(LIBOBJ) $(AR) libsqlite3.a $(LIBOBJ) $(RANLIB) libsqlite3.a -sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h +sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/src/test_schema2.c $(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) \ - $(TOP)/src/shell.c \ + -I$(TOP)/src $(TOP)/src/shell.c \ libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c diff --git a/manifest b/manifest index efb3c6127d..931c0f7266 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplification\sto\sthe\sprogress\scallback\scheck.\sOn\sbranch\sremoved. -D 2013-11-14T00:09:48.126 +C An\sexperimental\svirtual\stables\sfor\sshowing\sthe\scontent\sof\sinternal\sschema\nobjects. +D 2013-11-14T15:35:43.564 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 8a07bebafbfda0eb67728f4bd15a36201662d1a1 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -142,7 +142,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt f439556c5ce01ced70987e5ee86549a45165d9ff -F main.mk f57b51e64f50e9924b3f14df3c2e8b66893a8ce5 +F main.mk fc7620245363d451c167095df5fab8b13e6e61d6 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh d2a981497b404d6498f5ff3e3b1f3816bdfcb338 @@ -220,7 +220,7 @@ F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 F src/resolve.c fc4673cc49b116e51e7f12de074c0acf8f2388f9 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 7317406831ecced390edba972818f3c5f82238c0 -F src/shell.c 6ccc22b717afe4f6d7d3c8b6baa02e3b99a5fdf0 +F src/shell.c 044a79639c420ed293510cd560b358f75dae9472 F src/sqlite.h.in 4dedcab5b32358bf7a596badffe7363be1f1a82d F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc @@ -264,6 +264,7 @@ F src/test_quota.c 30c64f0ef84734f2231a686df41ed882b0c59bc0 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb F src/test_rtree.c f3d1d12538dccb75fd916e3fa58f250edbdd3b47 F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6 +F src/test_schema2.c c07c0826c77659ff37da580f8d8bc30e26849e3b F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e F src/test_stat.c 9898687a6c2beca733b0dd6fe19163d987826d31 @@ -1139,7 +1140,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 21f59b04f74738d08ebad693646bbaea24dc45ef -R 5afe90b6493eb21de1d0af561cfa69c0 +P 24ef16548eebcdb9d8b40308f6a16dabf8f8d474 +R d7a0355ea8d187cc731421105334e976 +T *branch * schema2-vtab +T *sym-schema2-vtab * +T -sym-trunk * U drh -Z d0339b99c91e151f95c1864799fb36e8 +Z ad6b62ac80e7bb705f75b7062e44d1db diff --git a/manifest.uuid b/manifest.uuid index 56997654db..51d7831123 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -24ef16548eebcdb9d8b40308f6a16dabf8f8d474 \ No newline at end of file +d1fbc6ca18383b97befd597aeb7ff84c6a04d243 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 61df7d6d52..23ecc9e0a4 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1591,6 +1591,11 @@ static char zTimerHelp[] = /* Forward reference */ static int process_input(struct callback_data *p, FILE *in); +/* Include the source code for various extensions when in debug mode: */ +#ifdef SQLITE_DEBUG +# include "test_schema2.c" +#endif + /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. @@ -1612,6 +1617,9 @@ static void open_db(struct callback_data *p, int keepAlive){ } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); +#endif +#ifdef SQLITE_DEBUG + sqlite3_schema2_register(p->db); #endif } } diff --git a/src/test_schema2.c b/src/test_schema2.c new file mode 100644 index 0000000000..8a12153e04 --- /dev/null +++ b/src/test_schema2.c @@ -0,0 +1,398 @@ +/* +** 2013-11-14 +** +** 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 "schema2" virtual table +** for displaying the content of various internal objects associated with +** the parsed schema. +*/ + +#ifndef SQLITE_AMALGAMATION +# include "sqliteInt.h" +#endif + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +static const char azSchema2[] = + "CREATE TABLE x(" + " dbname STRING," /* Name of attached database */ + " tblname STRING," /* Name of the table */ + " idxname STRING," /* Index name or NULL if not applicable */ + " cnum INT," /* Column number or NULL if not applicable */ + " attr STRING," /* Attribute name */ + " value STRING " /* Attribute value */ + ");" +; + +typedef struct Schema2Table Schema2Table; +typedef struct Schema2Cursor Schema2Cursor; +typedef struct Schema2Row Schema2Row; + +/* +** A single row of the result +*/ +struct Schema2Row { + const char *zDb; /* Schema name. db->aDb[].zName */ + const char *zTbl; /* Table name. Might be NULL */ + const char *zIdx; /* Index name. Might be NULL */ + int iCol; /* Column number */ + const char *zAttr; /* Attribute */ + char *zValue; /* Value of the attribute */ + Schema2Row *pNext; /* Next row */ +}; + +/* +** A cursor for iterating through internal schema information +*/ +struct Schema2Cursor { + sqlite3_vtab_cursor base; /* Base class. Must be first */ + int iRowid; /* Current rowid */ + Schema2Row *pAll; /* All rows */ + Schema2Row *pCurrent; /* Current row */ + Schema2Row *pLast; /* Last row */ +}; + +/* +** The complete Schema2 virtual table +*/ +struct Schema2Table { + sqlite3_vtab base; /* Base class. Must be first */ + sqlite3 *db; /* The database connection that owns this table */ +}; + +/* +** Connect to or create an Schema2 virtual table. +*/ +static int schema2Connect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + Schema2Table *pTab; + + pTab = (Schema2Table *)sqlite3_malloc(sizeof(Schema2Table)); + memset(pTab, 0, sizeof(Schema2Table)); + pTab->db = db; + + sqlite3_declare_vtab(db, azSchema2); + *ppVtab = &pTab->base; + return SQLITE_OK; +} + +/* +** Disconnect from or destroy an Schema2 virtual table. +*/ +static int schema2Disconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** There is no "best-index". This virtual table always does a complete +** scan. +*/ +static int schema2BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + pIdxInfo->estimatedCost = 10.0; + return SQLITE_OK; +} + +/* +** Open a new Schema2 cursor. +*/ +static int schema2Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + Schema2Cursor *pCsr; + pCsr = (Schema2Cursor *)sqlite3_malloc(sizeof(Schema2Cursor)); + memset(pCsr, 0, sizeof(Schema2Cursor)); + pCsr->base.pVtab = pVTab; + *ppCursor = (sqlite3_vtab_cursor *)pCsr; + return SQLITE_OK; +} + +static void schema2ResetCsr(Schema2Cursor *pCsr){ + Schema2Row *pRow, *pNextRow; + for(pRow=pCsr->pAll; pRow; pRow=pNextRow){ + pNextRow = pRow->pNext; + sqlite3_free(pRow->zValue); + sqlite3_free(pRow); + } + pCsr->pAll = 0; + pCsr->pCurrent = 0; + pCsr->pLast = 0; + pCsr->iRowid = 0; +} + +/* +** Close a statvfs cursor. +*/ +static int schema2Close(sqlite3_vtab_cursor *pCursor){ + Schema2Cursor *pCsr = (Schema2Cursor *)pCursor; + schema2ResetCsr(pCsr); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +/* +** Move a statvfs cursor to the next entry. +*/ +static int schema2Next(sqlite3_vtab_cursor *pCursor){ + Schema2Cursor *pCsr = (Schema2Cursor *)pCursor; + if( pCsr->pCurrent==0 ){ + pCsr->pCurrent = pCsr->pAll; + }else{ + pCsr->pCurrent = pCsr->pCurrent->pNext; + } + pCsr->iRowid++; + return SQLITE_OK; +} + +static int schema2Eof(sqlite3_vtab_cursor *pCursor){ + Schema2Cursor *pCsr = (Schema2Cursor *)pCursor; + return pCsr->pCurrent==0 && pCsr->iRowid>0; +} + +/* Append a single to the cursor pCsr */ +static void schema2AppendRow( + Schema2Cursor *pCsr, + const char *zDb, + const char *zTbl, + const char *zIdx, + int iCol, + const char *zAttr, + const char *zValue, + ... +){ + Schema2Row *pRow = sqlite3_malloc( sizeof(*pRow) ); + va_list ap; + if( pRow==0 ); + pRow->zDb = zDb; + pRow->zTbl = zTbl; + pRow->zIdx = zIdx; + pRow->iCol = iCol; + pRow->zAttr = zAttr; + va_start(ap, zValue); + pRow->zValue = sqlite3_vmprintf(zValue, ap); + va_end(ap); + if( pCsr->pLast==0 ){ + pCsr->pAll = pRow; + }else{ + pCsr->pLast->pNext = pRow; + } + pCsr->pLast = pRow; +} + +/* Append rows for index pIdx of table pTab which is part of the zDb schema */ +static void schema2AppendIndex( + Schema2Cursor *pCsr, + const char *zDb, + Table *pTab, + Index *pIdx +){ + const char *zTbl = pTab->zName; + const char *zIdx = pIdx->zName; + int i; + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"zColAff","%s",pIdx->zColAff); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"tnum","%d",pIdx->tnum); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"szIdxRow","%d",pIdx->szIdxRow); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"nKeyCol","%d",pIdx->nKeyCol); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"nColumn","%d",pIdx->nColumn); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"onError","%d",pIdx->onError); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"autoIndex","%d",pIdx->autoIndex); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"bUnordered","%d",pIdx->bUnordered); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"uniqNotNull","%d",pIdx->uniqNotNull); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"isResized","%d",pIdx->isResized); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,-1,"isCovering","%d",pIdx->isCovering); + for(i=0; inColumn; i++){ + i16 x = pIdx->aiColumn[i]; + schema2AppendRow(pCsr,zDb,zTbl,zIdx,i,"zName","%s", + x>=0?pTab->aCol[x].zName:"rowid"); + schema2AppendRow(pCsr,zDb,zTbl,zIdx,i,"aiRowEst","%lld", + (sqlite3_int64)pIdx->aiRowEst[i]); + } +} + +/* Append rows for table pTab which is part of the zDb schema */ +static void schema2AppendTable( + Schema2Cursor *pCsr, + const char *zDb, + Table *pTab +){ + const char *zTbl = pTab->zName; + int i; + Index *pIdx; + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"zColAff","%s",pTab->zColAff); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"nRowEst", + "%lld",(sqlite3_int64)pTab->nRowEst); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"tnum","%d",pTab->tnum); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"iPKey","%d",pTab->iPKey); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"nCol","%d",pTab->nCol); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"nRef","%d",pTab->nRef); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"szTabRow","%d",pTab->szTabRow); + schema2AppendRow(pCsr,zDb,zTbl,0,-1,"tabFlags","%d",pTab->tabFlags); + for(i=0; inCol; i++){ + const Column *p = pTab->aCol + i; + schema2AppendRow(pCsr,zDb,zTbl,0,i,"zName","%s",p->zName); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"zDflt","%s",p->zDflt); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"zType","%s",p->zType); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"zColl","%s",p->zColl); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"notNull","%d",p->notNull); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"affinity","%c",p->affinity); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"szEst","%d",p->szEst); + schema2AppendRow(pCsr,zDb,zTbl,0,i,"colFlags","%04x",p->colFlags); + } + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + schema2AppendIndex(pCsr, zDb, pTab, pIdx); + } +} + + +/* Append rows for schema object pSchema */ +static void schema2AppendSchema( + Schema2Cursor *pCsr, + const char *zDb, + Schema *pSchema +){ + HashElem *i; + schema2AppendRow(pCsr,zDb,0,0,-1,"generation","%d", pSchema->iGeneration); + schema2AppendRow(pCsr,zDb,0,0,-1,"file_format","%d", pSchema->file_format); + schema2AppendRow(pCsr,zDb,0,0,-1,"enc","%d", pSchema->enc); + schema2AppendRow(pCsr,zDb,0,0,-1,"flags","%d", pSchema->flags); + schema2AppendRow(pCsr,zDb,0,0,-1,"cache_size","%d", pSchema->cache_size); + for(i=sqliteHashFirst(&pSchema->tblHash); i; i=sqliteHashNext(i)){ + schema2AppendTable(pCsr, zDb, (Table*)sqliteHashData(i)); + } +} + + +static int schema2Filter( + sqlite3_vtab_cursor *pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + int iDb; + Schema2Cursor *pCsr = (Schema2Cursor*)pCursor; + Schema2Table *pTab = (Schema2Table*)(pCursor->pVtab); + sqlite3 *db = pTab->db; + schema2ResetCsr(pCsr); + for(iDb=0; iDbnDb; iDb++){ + const char *zDb = db->aDb[iDb].zName; + Schema *pSchema = db->aDb[iDb].pSchema; + schema2AppendRow(pCsr,zDb,0,0,-1,"safety_level", + "%d", db->aDb[iDb].safety_level); + schema2AppendSchema(pCsr, zDb, pSchema); + } + return schema2Next(pCursor); +} + +static int schema2Column( + sqlite3_vtab_cursor *pCursor, + sqlite3_context *ctx, + int i +){ + Schema2Cursor *pCsr = (Schema2Cursor *)pCursor; + Schema2Row *pRow = pCsr->pCurrent; + if( pRow==0 ) return SQLITE_OK; + switch( i ){ + case 0: /* dbname */ + sqlite3_result_text(ctx, pRow->zDb, -1, SQLITE_STATIC); + break; + case 1: /* tblname */ + if( pRow->zTbl ) sqlite3_result_text(ctx, pRow->zTbl, -1, SQLITE_STATIC); + break; + case 2: /* idxname */ + if( pRow->zIdx ) sqlite3_result_text(ctx, pRow->zIdx, -1, SQLITE_STATIC); + break; + case 3: /* cnum */ + if( pRow->iCol>=0 ) sqlite3_result_int(ctx, pRow->iCol); + break; + case 4: /* attr */ + sqlite3_result_text(ctx, pRow->zAttr, -1, SQLITE_STATIC); + break; + case 5: /* value */ + if( pRow->zValue ) sqlite3_result_text(ctx,pRow->zValue,-1,SQLITE_STATIC); + break; + } + return SQLITE_OK; +} + +static int schema2Rowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + Schema2Cursor *pCsr = (Schema2Cursor *)pCursor; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} + +int sqlite3_schema2_register(sqlite3 *db){ + static sqlite3_module schema2_module = { + 0, /* iVersion */ + schema2Connect, /* xCreate */ + schema2Connect, /* xConnect */ + schema2BestIndex, /* xBestIndex */ + schema2Disconnect, /* xDisconnect */ + schema2Disconnect, /* xDestroy */ + schema2Open, /* xOpen - open a cursor */ + schema2Close, /* xClose - close a cursor */ + schema2Filter, /* xFilter - configure scan constraints */ + schema2Next, /* xNext - advance a cursor */ + schema2Eof, /* xEof - check for end of scan */ + schema2Column, /* xColumn - read data */ + schema2Rowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + }; + sqlite3_create_module(db, "schema2", &schema2_module, 0); + return SQLITE_OK; +} + +#endif + +#if defined(SQLITE_TEST) || TCLSH==2 +#include + +static int test_schema2( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ +#ifdef SQLITE_OMIT_VIRTUALTABLE + Tcl_AppendResult(interp, "schema2 not available because of " + "SQLITE_OMIT_VIRTUALTABLE", (void*)0); + return TCL_ERROR; +#else + struct SqliteDb { sqlite3 *db; }; + char *zDb; + Tcl_CmdInfo cmdInfo; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + + zDb = Tcl_GetString(objv[1]); + if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ + sqlite3* db = ((struct SqliteDb*)cmdInfo.objClientData)->db; + sqlite3_schema2_register(db); + } + return TCL_OK; +#endif +} + +int SqlitetestSchema2_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "register_schema2_vtab", test_schema2, 0, 0); + return TCL_OK; +} +#endif /* if defined(SQLITE_TEST) || TCLSH==2 */