From: drh Date: Thu, 10 Aug 2017 03:27:27 +0000 (+0000) Subject: Add a first draft of the "vtablog" extensions that implements a generic X-Git-Tag: version-3.21.0~175 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6fa9375c019c66b237a33ede0dfafedde1d2f3ba;p=thirdparty%2Fsqlite.git Add a first draft of the "vtablog" extensions that implements a generic virtual table useful for experimentation using the command-line shell. FossilOrigin-Name: e49279e65169a939b6058a0960dc1fe09ce4ee2d78992a1969773cbc7ce1043b --- diff --git a/ext/misc/vtablog.c b/ext/misc/vtablog.c new file mode 100644 index 0000000000..9e03fd455d --- /dev/null +++ b/ext/misc/vtablog.c @@ -0,0 +1,509 @@ +/* +** 2017-08-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. +** +************************************************************************* +** +** This file implements a virtual table that prints diagnostic information +** on stdout when its key interfaces are called. This is intended for +** interactive analysis and debugging of virtual table interfaces. +** +** Usage example: +** +** .load ./vtablog +** CREATE VIRTUAL TABLE temp.log USING vtablog( +** schema='CREATE TABLE x(a,b,c)', +** rows=25 +** ); +** SELECT * FROM log; +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include +#include +#include +#include + + +/* vtablog_vtab is a subclass of sqlite3_vtab which will +** serve as the underlying representation of a vtablog virtual table +*/ +typedef struct vtablog_vtab vtablog_vtab; +struct vtablog_vtab { + sqlite3_vtab base; /* Base class - must be first */ + int nRow; /* Number of rows in the table */ + int iInst; /* Instance number for this vtablog table */ + int nCursor; /* Number of cursors created */ +}; + +/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will +** serve as the underlying representation of a cursor that scans +** over rows of the result +*/ +typedef struct vtablog_cursor vtablog_cursor; +struct vtablog_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + int iCursor; /* Cursor number */ + sqlite3_int64 iRowid; /* The rowid */ +}; + +/* Skip leading whitespace. Return a pointer to the first non-whitespace +** character, or to the zero terminator if the string has only whitespace */ +static const char *vtablog_skip_whitespace(const char *z){ + while( isspace((unsigned char)z[0]) ) z++; + return z; +} + +/* Remove trailing whitespace from the end of string z[] */ +static void vtablog_trim_whitespace(char *z){ + size_t n = strlen(z); + while( n>0 && isspace((unsigned char)z[n]) ) n--; + z[n] = 0; +} + +/* Dequote the string */ +static void vtablog_dequote(char *z){ + int j; + char cQuote = z[0]; + size_t i, n; + + if( cQuote!='\'' && cQuote!='"' ) return; + n = strlen(z); + if( n<2 || z[n-1]!=z[0] ) return; + for(i=1, j=0; inRow = 10; + if( zNRow ) pNew->nRow = atoi(zNRow); + pNew->iInst = iInst; + } + return rc; +} +static int vtablogCreate( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1); +} +static int vtablogConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0); +} + + +/* +** This method is the destructor for vtablog_cursor objects. +*/ +static int vtablogDisconnect(sqlite3_vtab *pVtab){ + vtablog_vtab *pTab = (vtablog_vtab*)pVtab; + printf("vtablogDisconnect(%d)\n", pTab->iInst); + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** This method is the destructor for vtablog_cursor objects. +*/ +static int vtablogDestroy(sqlite3_vtab *pVtab){ + vtablog_vtab *pTab = (vtablog_vtab*)pVtab; + printf("vtablogDestroy(%d)\n", pTab->iInst); + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** Constructor for a new vtablog_cursor object. +*/ +static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + vtablog_vtab *pTab = (vtablog_vtab*)p; + vtablog_cursor *pCur; + printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor); + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + pCur->iCursor = pTab->nCursor; + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Destructor for a vtablog_cursor. +*/ +static int vtablogClose(sqlite3_vtab_cursor *cur){ + vtablog_cursor *pCur = (vtablog_cursor*)cur; + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; + printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor); + sqlite3_free(cur); + return SQLITE_OK; +} + + +/* +** Advance a vtablog_cursor to its next row of output. +*/ +static int vtablogNext(sqlite3_vtab_cursor *cur){ + vtablog_cursor *pCur = (vtablog_cursor*)cur; + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; + printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n", + pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1); + pCur->iRowid++; + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the vtablog_cursor +** is currently pointing. +*/ +static int vtablogColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + vtablog_cursor *pCur = (vtablog_cursor*)cur; + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; + char zVal[50]; + + if( i<26 ){ + sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", + "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid); + }else{ + sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid); + } + printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n", + pTab->iInst, pCur->iCursor, i, zVal); + sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT); + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. In this implementation, the +** rowid is the same as the output value. +*/ +static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + vtablog_cursor *pCur = (vtablog_cursor*)cur; + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; + printf("vtablogRowid(tab=%d, cursor=%d): %d\n", + pTab->iInst, pCur->iCursor, (int)pCur->iRowid); + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int vtablogEof(sqlite3_vtab_cursor *cur){ + vtablog_cursor *pCur = (vtablog_cursor*)cur; + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; + int rc = pCur->iRowid >= pTab->nRow; + printf("vtablogEof(tab=%d, cursor=%d): %d\n", + pTab->iInst, pCur->iCursor, rc); + return rc; +} + +/* +** Output an sqlite3_value object's value as an SQL literal. +*/ +static void vtablogQuote(sqlite3_value *p){ + char z[50]; + switch( sqlite3_value_type(p) ){ + case SQLITE_NULL: { + printf("NULL"); + break; + } + case SQLITE_INTEGER: { + sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p)); + printf("%s", z); + break; + } + case SQLITE_FLOAT: { + sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p)); + printf("%s", z); + break; + } + case SQLITE_BLOB: { + int n = sqlite3_value_bytes(p); + const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p); + int i; + printf("x'"); + for(i=0; ipVtab; + printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor); + pCur->iRowid = 0; + return SQLITE_OK; +} + +/* +** SQLite will invoke this method one or more times while planning a query +** that uses the vtablog virtual table. This routine needs to create +** a query plan for each invocation and compute an estimated cost for that +** plan. +*/ +static int vtablogBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + vtablog_vtab *pTab = (vtablog_vtab*)tab; + printf("vtablogBestIndex(tab=%d):\n", pTab->iInst); + pIdxInfo->estimatedCost = (double)500; + pIdxInfo->estimatedRows = 500; + return SQLITE_OK; +} + +/* +** SQLite invokes this method to INSERT, UPDATE, or DELETE content from +** the table. +** +** This implementation does not actually make any changes to the table +** content. It merely logs the fact that the method was invoked +*/ +static int vtablogUpdate( + sqlite3_vtab *tab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + vtablog_vtab *pTab = (vtablog_vtab*)tab; + int i; + printf("vtablogUpdate(tab=%d):\n", pTab->iInst); + printf(" argc=%d\n", argc); + for(i=0; i