]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
An experimental virtual tables for showing the content of internal schema schema2-vtab
authordrh <drh@noemail.net>
Thu, 14 Nov 2013 15:35:43 +0000 (15:35 +0000)
committerdrh <drh@noemail.net>
Thu, 14 Nov 2013 15:35:43 +0000 (15:35 +0000)
objects.

FossilOrigin-Name: d1fbc6ca18383b97befd597aeb7ff84c6a04d243

main.mk
manifest
manifest.uuid
src/shell.c
src/test_schema2.c [new file with mode: 0644]

diff --git a/main.mk b/main.mk
index 9f063665665d034fc9aabde8aba6a4939c365e83..5c332441aa2c9f7fdd360dbad187155fe36915af 100644 (file)
--- 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
index efb3c6127d30c97b9c635ba2fc4006432fbc9d47..931c0f726600d5239cec60f4df85525b85703a26 100644 (file)
--- 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
index 56997654db69ab23859b12e05771b49495e9ae06..51d78311231a3d81591514aa1e3a0a3b87e36aaa 100644 (file)
@@ -1 +1 @@
-24ef16548eebcdb9d8b40308f6a16dabf8f8d474
\ No newline at end of file
+d1fbc6ca18383b97befd597aeb7ff84c6a04d243
\ No newline at end of file
index 61df7d6d52e4857b5f846ec0d4cface5441df2b5..23ecc9e0a42676f49c827145b780b9ce70dbc060 100644 (file)
@@ -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 (file)
index 0000000..8a12153
--- /dev/null
@@ -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; i<pIdx->nColumn; 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; i<pTab->nCol; 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; iDb<db->nDb; 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 <tcl.h>
+
+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 */