From 954ce99c625f721c508c708f6c9003b0b10d0669 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Thu, 15 Jun 2006 15:59:19 +0000 Subject: [PATCH] Add test_schema.c, containing a module for viewing the database schema via a virtual table. (CVS 3257) FossilOrigin-Name: de8d32ac71a6e113e83b952813424cb3fb5a2e59 --- Makefile.in | 1 + main.mk | 1 + manifest | 17 +-- manifest.uuid | 2 +- src/tclsqlite.c | 4 +- src/test_schema.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 359 insertions(+), 10 deletions(-) create mode 100644 src/test_schema.c diff --git a/Makefile.in b/Makefile.in index 706246e062..90fc1c2dd2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -210,6 +210,7 @@ TESTSRC = \ $(TOP)/src/test8.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_md5.c \ + $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/utf.c \ diff --git a/main.mk b/main.mk index 1691de3146..05a53dd0d3 100644 --- a/main.mk +++ b/main.mk @@ -143,6 +143,7 @@ TESTSRC = \ $(TOP)/src/test8.c \ $(TOP)/src/test_async.c \ $(TOP)/src/test_md5.c \ + $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/utf.c \ diff --git a/manifest b/manifest index f482728c41..8c806f035f 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ -C Add\scolumn_value,\sdeclare_vtab\sand\screate_module\sto\sthe\sfunction\stable\sused\sby\sdynamic\sextensions.\s(CVS\s3256) -D 2006-06-15T15:38:42 -F Makefile.in 200f6dc376ecfd9b01e5359c4e0c10c02f649b34 +C Add\stest_schema.c,\scontaining\sa\smodule\sfor\sviewing\sthe\sdatabase\sschema\svia\sa\svirtual\stable.\s(CVS\s3257) +D 2006-06-15T15:59:19 +F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 F VERSION 301ed2b2c08f5cca242ea56e50a9ed0264a3eb76 @@ -19,7 +19,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826 -F main.mk 3fe86a381432abf3ba96d0276ea85618bcf9e5a1 +F main.mk 5dbef60b29006c0bc3fd9ce78c846f38750302e2 F mkdll.sh 919df5efde876194e3102c6ebc60657d38949909 F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d F mkopcodeh.awk cde995d269aa06c94adbf6455bea0acedb913fa5 @@ -76,7 +76,7 @@ F src/sqlite.h.in 1dc44da025da28a011d11ad1608c11a951047fab F src/sqlite3ext.h fc8647211af0caa9d8e49ab31624b357c1332380 F src/sqliteInt.h 5eb64f1dd9a8b237d147962bc57637d87e044ff4 F src/table.c f64ec4fbfe333f8df925bc6ba494f55e05b0e75e -F src/tclsqlite.c 4ad22f354b6e4e137889000e9f585a0590ca39c5 +F src/tclsqlite.c c03bf44bd9f629d4c8d6545788c647b2e846b523 F src/test1.c 40f20775903bc76d3be3e7c026dddcbc221c1cb0 F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b F src/test3.c 86e99724ee898b119ed575ef9f98618afe7e5e5d @@ -88,6 +88,7 @@ F src/test8.c 9579de4645c9b8be3f8de217224bcf9280da9b6a F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3 F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8 F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 +F src/test_schema.c 533a9ed125a4676a22a20d9e87e6379989d901e9 F src/test_server.c a6460daed0b92ecbc2531b6dc73717470e7a648c F src/test_tclvar.c c52f67fbe06d32804af2ba9a2d7aadfc15f5910c F src/tokenize.c 6ebcafa6622839968dda4418a7b6945f277a128f @@ -366,7 +367,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P fe3e70a7275d68acb6fb8ea5d62bed3e9d8d2766 -R 47a8b2f6537c22850ce9af0f2e0f04a2 +P 25c475087892fea83bce9d140b46651793b85a86 +R fb8cae10b8c9b6f899ced712e7826fd2 U danielk1977 -Z ec60b2bf5c12236743a6bbfcf541f438 +Z 593d3f2a16cc3c6c0ea1ba4705d0de74 diff --git a/manifest.uuid b/manifest.uuid index 11aca19e88..fa97314dda 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25c475087892fea83bce9d140b46651793b85a86 \ No newline at end of file +de8d32ac71a6e113e83b952813424cb3fb5a2e59 \ No newline at end of file diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 9c3890b99a..f988926f42 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.158 2006/06/13 23:51:35 drh Exp $ +** $Id: tclsqlite.c,v 1.159 2006/06/15 15:59:20 danielk1977 Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -2156,6 +2156,7 @@ int TCLSH_MAIN(int argc, char **argv){ extern int Sqlitetestsse_Init(Tcl_Interp*); extern int Sqlitetestasync_Init(Tcl_Interp*); extern int Sqlitetesttclvar_Init(Tcl_Interp*); + extern int Sqlitetestschema_Init(Tcl_Interp*); Sqlitetest1_Init(interp); Sqlitetest2_Init(interp); @@ -2167,6 +2168,7 @@ int TCLSH_MAIN(int argc, char **argv){ Sqlitetest8_Init(interp); Sqlitetestasync_Init(interp); Sqlitetesttclvar_Init(interp); + Sqlitetestschema_Init(interp); Md5_Init(interp); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); diff --git a/src/test_schema.c b/src/test_schema.c new file mode 100644 index 0000000000..85a75f1f45 --- /dev/null +++ b/src/test_schema.c @@ -0,0 +1,344 @@ +/* +** 2006 June 10 +** +** 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. +** +************************************************************************* +** Code for testing the virtual table interfaces. This code +** is not included in the SQLite library. It is used for automated +** testing of the SQLite library. +** +** $Id: test_schema.c,v 1.1 2006/06/15 15:59:20 danielk1977 Exp $ +*/ + +/* The code in this file defines a sqlite3 module that provides +** a read-only view of the current database schema. There is one +** row in the schema table for each column in the database. +*/ +#define SCHEMA \ +"CREATE TABLE x(" \ + "database," /* Name of database (i.e. main, temp etc.) */ \ + "tablename," /* Name of table */ \ + "cid," /* Column number (from left-to-right, 0 upward) */ \ + "name," /* Column name */ \ + "type," /* Specified type (i.e. VARCHAR(32)) */ \ + "not_null," /* Boolean. True if NOT NULL was specified */ \ + "dflt_value," /* Default value for this column */ \ + "pk" /* True if this column is part of the primary key */ \ +")" + +/* If SQLITE_TEST is defined this code is preprocessed for use as part +** of the sqlite test binary "testfixture". Otherwise it is preprocessed +** to be compiled into an sqlite dynamic extension. +*/ +#ifdef SQLITE_TEST + #include "sqliteInt.h" + #include "tcl.h" + #define MALLOC(x) sqliteRawMalloc(x) + #define FREE(x) sqliteFree(x) +#else + #include "sqlite3ext.h" + SQLITE_EXTENSION_INIT1 + #define MALLOC(x) malloc(x) + #define FREE(x) free(x) +#endif + +#include +#include +#include + +typedef struct schema_vtab schema_vtab; +typedef struct schema_cursor schema_cursor; + +/* A schema table object */ +struct schema_vtab { + sqlite3_vtab base; + sqlite3 *db; +}; + +/* A schema table cursor object */ +struct schema_cursor { + sqlite3_vtab_cursor base; + sqlite3_stmt *pDbList; + sqlite3_stmt *pTableList; + sqlite3_stmt *pColumnList; + int rowid; +}; + +/* +** Table destructor for the schema module. +*/ +static int schemaDestroy(sqlite3_vtab *pVtab){ + FREE(pVtab); + return 0; +} + +/* +** Table constructor for the schema module. +*/ +static int schemaCreate( + sqlite3 *db, + void *pAux, + int argc, char **argv, + sqlite3_vtab **ppVtab +){ + int rc = SQLITE_NOMEM; + schema_vtab *pVtab = MALLOC(sizeof(schema_vtab)); + if( pVtab ){ + memset(pVtab, 0, sizeof(schema_vtab)); + pVtab->db = db; + rc = sqlite3_declare_vtab(db, SCHEMA); + } + *ppVtab = (sqlite3_vtab *)pVtab; + return rc; +} + +/* +** Open a new cursor on the schema table. +*/ +static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + int rc = SQLITE_NOMEM; + schema_cursor *pCur; + pCur = MALLOC(sizeof(schema_cursor)); + if( pCur ){ + memset(pCur, 0, sizeof(schema_cursor)); + *ppCursor = (sqlite3_vtab_cursor *)pCur; + rc = SQLITE_OK; + } + return rc; +} + +/* +** Close a schema table cursor. +*/ +static int schemaClose(sqlite3_vtab_cursor *cur){ + schema_cursor *pCur = (schema_cursor *)cur; + sqlite3_finalize(pCur->pDbList); + sqlite3_finalize(pCur->pTableList); + sqlite3_finalize(pCur->pColumnList); + FREE(pCur); + return SQLITE_OK; +} + +/* +** Retrieve a column of data. +*/ +static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + schema_cursor *pCur = (schema_cursor *)cur; + switch( i ){ + case 0: + sqlite3_result_value(ctx, sqlite3_column_value(pCur->pDbList, 1)); + break; + case 1: + sqlite3_result_value(ctx, sqlite3_column_value(pCur->pTableList, 0)); + break; + default: + sqlite3_result_value(ctx, sqlite3_column_value(pCur->pColumnList, i-2)); + break; + } + return SQLITE_OK; +} + +/* +** Retrieve the current rowid. +*/ +static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + schema_cursor *pCur = (schema_cursor *)cur; + *pRowid = pCur->rowid; + return SQLITE_OK; +} + +static int finalize(sqlite3_stmt **ppStmt){ + int rc = sqlite3_finalize(*ppStmt); + *ppStmt = 0; + return rc; +} + +/* +** Advance the cursor to the next row. +*/ +static int schemaNext(sqlite3_vtab_cursor *cur){ + int rc; + schema_cursor *pCur = (schema_cursor *)cur; + schema_vtab *pVtab = (schema_vtab *)(cur->pVtab); + char *zSql = 0; + + while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){ + if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto fail; + + while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){ + if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto fail; + + assert(pCur->pDbList); + while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){ + if( SQLITE_OK!=(rc = finalize(&pCur->pDbList)) ) goto fail; + return 0; + } + + /* Set zSql to the SQL to pull the list of tables from the + ** sqlite_master (or sqlite_temp_master) table of the database + ** identfied by the row pointed to by the SQL statement pCur->pDbList + ** (iterating through a "PRAGMA database_list;" statement). + */ + if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ + zSql = sqlite3_mprintf( + "SELECT name FROM sqlite_temp_master WHERE type='table'" + ); + }else{ + sqlite3_stmt *pDbList = pCur->pDbList; + zSql = sqlite3_mprintf( + "SELECT name FROM %Q.sqlite_master WHERE type='table'", + sqlite3_column_text(pDbList, 1) + ); + } + if( !zSql ){ + rc = SQLITE_NOMEM; + goto fail; + } + + rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ) goto fail; + } + + /* Set zSql to the SQL to the table_info pragma for the table currently + ** identified by the rows pointed to by statements pCur->pDbList and + ** pCur->pTableList. + */ + zSql = sqlite3_mprintf("PRAGMA %Q.table_info(%Q)", + sqlite3_column_text(pCur->pDbList, 1), + sqlite3_column_text(pCur->pTableList, 0) + ); + + if( !zSql ){ + rc = SQLITE_NOMEM; + goto fail; + } + rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ) goto fail; + } + pCur->rowid++; + +fail: + /* TODO: Handle rc */ + return 1; +} + +/* +** Reset a schema table cursor. +*/ +static int schemaFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + int rc; + schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab); + schema_cursor *pCur = (schema_cursor *)pVtabCursor; + pCur->rowid = 0; + finalize(&pCur->pTableList); + finalize(&pCur->pColumnList); + finalize(&pCur->pDbList); + rc = sqlite3_prepare(pVtab->db,"PRAGMA database_list", -1, &pCur->pDbList, 0); + return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc); +} + +/* +** Analyse the WHERE condition. +*/ +static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + return SQLITE_OK; +} + +/* +** A virtual table module that merely echos method calls into TCL +** variables. +*/ +static sqlite3_module schemaModule = { + 0, /* iVersion */ + "schema", /* zName */ + schemaCreate, + schemaCreate, + schemaBestIndex, + schemaDestroy, + schemaDestroy, + schemaOpen, /* xOpen - open a cursor */ + schemaClose, /* xClose - close a cursor */ + schemaFilter, /* xFilter - configure scan constraints */ + schemaNext, /* xNext - advance a cursor */ + schemaColumn, /* xColumn - read data */ + schemaRowid, /* xRowid - read data */ +}; + + +#ifdef SQLITE_TEST + +/* +** Decode a pointer to an sqlite3 object. +*/ +static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){ + *ppDb = (sqlite3*)sqlite3TextToPtr(zA); + return TCL_OK; +} + +/* +** Register the schema virtual table module. +*/ +static int register_schema_module( + ClientData clientData, /* Not used */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3_create_module(db, "schema", &schemaModule, 0); +#endif + return TCL_OK; +} + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetestschema_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_schema_module", register_schema_module, 0 }, + }; + int i; + for(i=0; i