16fa9375cSdrh /*
26fa9375cSdrh ** 2017-08-10
36fa9375cSdrh **
46fa9375cSdrh ** The author disclaims copyright to this source code. In place of
56fa9375cSdrh ** a legal notice, here is a blessing:
66fa9375cSdrh **
76fa9375cSdrh ** May you do good and not evil.
86fa9375cSdrh ** May you find forgiveness for yourself and forgive others.
96fa9375cSdrh ** May you share freely, never taking more than you give.
106fa9375cSdrh **
116fa9375cSdrh *************************************************************************
126fa9375cSdrh **
136fa9375cSdrh ** This file implements a virtual table that prints diagnostic information
146fa9375cSdrh ** on stdout when its key interfaces are called. This is intended for
156fa9375cSdrh ** interactive analysis and debugging of virtual table interfaces.
166fa9375cSdrh **
176fa9375cSdrh ** Usage example:
186fa9375cSdrh **
196fa9375cSdrh ** .load ./vtablog
206fa9375cSdrh ** CREATE VIRTUAL TABLE temp.log USING vtablog(
216fa9375cSdrh ** schema='CREATE TABLE x(a,b,c)',
226fa9375cSdrh ** rows=25
236fa9375cSdrh ** );
246fa9375cSdrh ** SELECT * FROM log;
256fa9375cSdrh */
266fa9375cSdrh #include "sqlite3ext.h"
276fa9375cSdrh SQLITE_EXTENSION_INIT1
286fa9375cSdrh #include <stdio.h>
296fa9375cSdrh #include <stdlib.h>
306fa9375cSdrh #include <assert.h>
316fa9375cSdrh #include <string.h>
326fa9375cSdrh #include <ctype.h>
336fa9375cSdrh
346fa9375cSdrh
356fa9375cSdrh /* vtablog_vtab is a subclass of sqlite3_vtab which will
366fa9375cSdrh ** serve as the underlying representation of a vtablog virtual table
376fa9375cSdrh */
386fa9375cSdrh typedef struct vtablog_vtab vtablog_vtab;
396fa9375cSdrh struct vtablog_vtab {
406fa9375cSdrh sqlite3_vtab base; /* Base class - must be first */
416fa9375cSdrh int nRow; /* Number of rows in the table */
426fa9375cSdrh int iInst; /* Instance number for this vtablog table */
436fa9375cSdrh int nCursor; /* Number of cursors created */
446fa9375cSdrh };
456fa9375cSdrh
466fa9375cSdrh /* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
476fa9375cSdrh ** serve as the underlying representation of a cursor that scans
486fa9375cSdrh ** over rows of the result
496fa9375cSdrh */
506fa9375cSdrh typedef struct vtablog_cursor vtablog_cursor;
516fa9375cSdrh struct vtablog_cursor {
526fa9375cSdrh sqlite3_vtab_cursor base; /* Base class - must be first */
536fa9375cSdrh int iCursor; /* Cursor number */
546fa9375cSdrh sqlite3_int64 iRowid; /* The rowid */
556fa9375cSdrh };
566fa9375cSdrh
576fa9375cSdrh /* Skip leading whitespace. Return a pointer to the first non-whitespace
586fa9375cSdrh ** character, or to the zero terminator if the string has only whitespace */
vtablog_skip_whitespace(const char * z)596fa9375cSdrh static const char *vtablog_skip_whitespace(const char *z){
606fa9375cSdrh while( isspace((unsigned char)z[0]) ) z++;
616fa9375cSdrh return z;
626fa9375cSdrh }
636fa9375cSdrh
646fa9375cSdrh /* Remove trailing whitespace from the end of string z[] */
vtablog_trim_whitespace(char * z)656fa9375cSdrh static void vtablog_trim_whitespace(char *z){
666fa9375cSdrh size_t n = strlen(z);
676fa9375cSdrh while( n>0 && isspace((unsigned char)z[n]) ) n--;
686fa9375cSdrh z[n] = 0;
696fa9375cSdrh }
706fa9375cSdrh
716fa9375cSdrh /* Dequote the string */
vtablog_dequote(char * z)726fa9375cSdrh static void vtablog_dequote(char *z){
736fa9375cSdrh int j;
746fa9375cSdrh char cQuote = z[0];
756fa9375cSdrh size_t i, n;
766fa9375cSdrh
776fa9375cSdrh if( cQuote!='\'' && cQuote!='"' ) return;
786fa9375cSdrh n = strlen(z);
796fa9375cSdrh if( n<2 || z[n-1]!=z[0] ) return;
806fa9375cSdrh for(i=1, j=0; i<n-1; i++){
816fa9375cSdrh if( z[i]==cQuote && z[i+1]==cQuote ) i++;
826fa9375cSdrh z[j++] = z[i];
836fa9375cSdrh }
846fa9375cSdrh z[j] = 0;
856fa9375cSdrh }
866fa9375cSdrh
876fa9375cSdrh /* Check to see if the string is of the form: "TAG = VALUE" with optional
886fa9375cSdrh ** whitespace before and around tokens. If it is, return a pointer to the
896fa9375cSdrh ** first character of VALUE. If it is not, return NULL.
906fa9375cSdrh */
vtablog_parameter(const char * zTag,int nTag,const char * z)916fa9375cSdrh static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
926fa9375cSdrh z = vtablog_skip_whitespace(z);
936fa9375cSdrh if( strncmp(zTag, z, nTag)!=0 ) return 0;
946fa9375cSdrh z = vtablog_skip_whitespace(z+nTag);
956fa9375cSdrh if( z[0]!='=' ) return 0;
966fa9375cSdrh return vtablog_skip_whitespace(z+1);
976fa9375cSdrh }
986fa9375cSdrh
996fa9375cSdrh /* Decode a parameter that requires a dequoted string.
1006fa9375cSdrh **
1016fa9375cSdrh ** Return non-zero on an error.
1026fa9375cSdrh */
vtablog_string_parameter(char ** pzErr,const char * zParam,const char * zArg,char ** pzVal)1036fa9375cSdrh static int vtablog_string_parameter(
1046fa9375cSdrh char **pzErr, /* Leave the error message here, if there is one */
1056fa9375cSdrh const char *zParam, /* Parameter we are checking for */
1066fa9375cSdrh const char *zArg, /* Raw text of the virtual table argment */
1076fa9375cSdrh char **pzVal /* Write the dequoted string value here */
1086fa9375cSdrh ){
1096fa9375cSdrh const char *zValue;
1106fa9375cSdrh zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
1116fa9375cSdrh if( zValue==0 ) return 0;
1126fa9375cSdrh if( *pzVal ){
1136fa9375cSdrh *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
1146fa9375cSdrh return 1;
1156fa9375cSdrh }
1166fa9375cSdrh *pzVal = sqlite3_mprintf("%s", zValue);
1176fa9375cSdrh if( *pzVal==0 ){
1186fa9375cSdrh *pzErr = sqlite3_mprintf("out of memory");
1196fa9375cSdrh return 1;
1206fa9375cSdrh }
1216fa9375cSdrh vtablog_trim_whitespace(*pzVal);
1226fa9375cSdrh vtablog_dequote(*pzVal);
1236fa9375cSdrh return 0;
1246fa9375cSdrh }
1256fa9375cSdrh
1266fa9375cSdrh #if 0 /* not used - yet */
1276fa9375cSdrh /* Return 0 if the argument is false and 1 if it is true. Return -1 if
1286fa9375cSdrh ** we cannot really tell.
1296fa9375cSdrh */
1306fa9375cSdrh static int vtablog_boolean(const char *z){
1316fa9375cSdrh if( sqlite3_stricmp("yes",z)==0
1326fa9375cSdrh || sqlite3_stricmp("on",z)==0
1336fa9375cSdrh || sqlite3_stricmp("true",z)==0
1346fa9375cSdrh || (z[0]=='1' && z[1]==0)
1356fa9375cSdrh ){
1366fa9375cSdrh return 1;
1376fa9375cSdrh }
1386fa9375cSdrh if( sqlite3_stricmp("no",z)==0
1396fa9375cSdrh || sqlite3_stricmp("off",z)==0
1406fa9375cSdrh || sqlite3_stricmp("false",z)==0
1416fa9375cSdrh || (z[0]=='0' && z[1]==0)
1426fa9375cSdrh ){
1436fa9375cSdrh return 0;
1446fa9375cSdrh }
1456fa9375cSdrh return -1;
1466fa9375cSdrh }
1476fa9375cSdrh #endif
1486fa9375cSdrh
1496fa9375cSdrh /*
1506fa9375cSdrh ** The vtablogConnect() method is invoked to create a new
1516fa9375cSdrh ** vtablog_vtab that describes the vtablog virtual table.
1526fa9375cSdrh **
1536fa9375cSdrh ** Think of this routine as the constructor for vtablog_vtab objects.
1546fa9375cSdrh **
1556fa9375cSdrh ** All this routine needs to do is:
1566fa9375cSdrh **
1576fa9375cSdrh ** (1) Allocate the vtablog_vtab object and initialize all fields.
1586fa9375cSdrh **
1596fa9375cSdrh ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
1606fa9375cSdrh ** result set of queries against vtablog will look like.
1616fa9375cSdrh */
vtablogConnectCreate(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr,int isCreate)1626fa9375cSdrh static int vtablogConnectCreate(
1636fa9375cSdrh sqlite3 *db,
1646fa9375cSdrh void *pAux,
1656fa9375cSdrh int argc, const char *const*argv,
1666fa9375cSdrh sqlite3_vtab **ppVtab,
1676fa9375cSdrh char **pzErr,
1686fa9375cSdrh int isCreate
1696fa9375cSdrh ){
1706fa9375cSdrh static int nInst = 0;
1716fa9375cSdrh vtablog_vtab *pNew;
1726fa9375cSdrh int i;
1736fa9375cSdrh int rc;
1746fa9375cSdrh int iInst = ++nInst;
1756fa9375cSdrh char *zSchema = 0;
1766fa9375cSdrh char *zNRow = 0;
1776fa9375cSdrh
1786fa9375cSdrh printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
1796fa9375cSdrh printf(" argc=%d\n", argc);
1806fa9375cSdrh for(i=0; i<argc; i++){
1816fa9375cSdrh printf(" argv[%d] = ", i);
1826fa9375cSdrh if( argv[i] ){
1836fa9375cSdrh printf("[%s]\n", argv[i]);
1846fa9375cSdrh }else{
1856fa9375cSdrh printf("NULL\n");
1866fa9375cSdrh }
1876fa9375cSdrh }
1886fa9375cSdrh
1896fa9375cSdrh for(i=3; i<argc; i++){
1906fa9375cSdrh const char *z = argv[i];
1916fa9375cSdrh if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
1926fa9375cSdrh return SQLITE_ERROR;
1936fa9375cSdrh }
1946fa9375cSdrh if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
1956fa9375cSdrh return SQLITE_ERROR;
1966fa9375cSdrh }
1976fa9375cSdrh }
1986fa9375cSdrh
1996fa9375cSdrh if( zSchema==0 ){
2006fa9375cSdrh *pzErr = sqlite3_mprintf("no schema defined");
2016fa9375cSdrh return SQLITE_ERROR;
2026fa9375cSdrh }
2036fa9375cSdrh rc = sqlite3_declare_vtab(db, zSchema);
2046fa9375cSdrh if( rc==SQLITE_OK ){
2056fa9375cSdrh pNew = sqlite3_malloc( sizeof(*pNew) );
2066fa9375cSdrh *ppVtab = (sqlite3_vtab*)pNew;
2076fa9375cSdrh if( pNew==0 ) return SQLITE_NOMEM;
2086fa9375cSdrh memset(pNew, 0, sizeof(*pNew));
2096fa9375cSdrh pNew->nRow = 10;
2106fa9375cSdrh if( zNRow ) pNew->nRow = atoi(zNRow);
2116fa9375cSdrh pNew->iInst = iInst;
2126fa9375cSdrh }
2136fa9375cSdrh return rc;
2146fa9375cSdrh }
vtablogCreate(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)2156fa9375cSdrh static int vtablogCreate(
2166fa9375cSdrh sqlite3 *db,
2176fa9375cSdrh void *pAux,
2186fa9375cSdrh int argc, const char *const*argv,
2196fa9375cSdrh sqlite3_vtab **ppVtab,
2206fa9375cSdrh char **pzErr
2216fa9375cSdrh ){
2226fa9375cSdrh return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
2236fa9375cSdrh }
vtablogConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)2246fa9375cSdrh static int vtablogConnect(
2256fa9375cSdrh sqlite3 *db,
2266fa9375cSdrh void *pAux,
2276fa9375cSdrh int argc, const char *const*argv,
2286fa9375cSdrh sqlite3_vtab **ppVtab,
2296fa9375cSdrh char **pzErr
2306fa9375cSdrh ){
2316fa9375cSdrh return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
2326fa9375cSdrh }
2336fa9375cSdrh
2346fa9375cSdrh
2356fa9375cSdrh /*
2366fa9375cSdrh ** This method is the destructor for vtablog_cursor objects.
2376fa9375cSdrh */
vtablogDisconnect(sqlite3_vtab * pVtab)2386fa9375cSdrh static int vtablogDisconnect(sqlite3_vtab *pVtab){
2396fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
2406fa9375cSdrh printf("vtablogDisconnect(%d)\n", pTab->iInst);
2416fa9375cSdrh sqlite3_free(pVtab);
2426fa9375cSdrh return SQLITE_OK;
2436fa9375cSdrh }
2446fa9375cSdrh
2456fa9375cSdrh /*
2466fa9375cSdrh ** This method is the destructor for vtablog_cursor objects.
2476fa9375cSdrh */
vtablogDestroy(sqlite3_vtab * pVtab)2486fa9375cSdrh static int vtablogDestroy(sqlite3_vtab *pVtab){
2496fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
2506fa9375cSdrh printf("vtablogDestroy(%d)\n", pTab->iInst);
2516fa9375cSdrh sqlite3_free(pVtab);
2526fa9375cSdrh return SQLITE_OK;
2536fa9375cSdrh }
2546fa9375cSdrh
2556fa9375cSdrh /*
2566fa9375cSdrh ** Constructor for a new vtablog_cursor object.
2576fa9375cSdrh */
vtablogOpen(sqlite3_vtab * p,sqlite3_vtab_cursor ** ppCursor)2586fa9375cSdrh static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
2596fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)p;
2606fa9375cSdrh vtablog_cursor *pCur;
2616fa9375cSdrh printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
2626fa9375cSdrh pCur = sqlite3_malloc( sizeof(*pCur) );
2636fa9375cSdrh if( pCur==0 ) return SQLITE_NOMEM;
2646fa9375cSdrh memset(pCur, 0, sizeof(*pCur));
2656fa9375cSdrh pCur->iCursor = pTab->nCursor;
2666fa9375cSdrh *ppCursor = &pCur->base;
2676fa9375cSdrh return SQLITE_OK;
2686fa9375cSdrh }
2696fa9375cSdrh
2706fa9375cSdrh /*
2716fa9375cSdrh ** Destructor for a vtablog_cursor.
2726fa9375cSdrh */
vtablogClose(sqlite3_vtab_cursor * cur)2736fa9375cSdrh static int vtablogClose(sqlite3_vtab_cursor *cur){
2746fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor*)cur;
2756fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
2766fa9375cSdrh printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
2776fa9375cSdrh sqlite3_free(cur);
2786fa9375cSdrh return SQLITE_OK;
2796fa9375cSdrh }
2806fa9375cSdrh
2816fa9375cSdrh
2826fa9375cSdrh /*
2836fa9375cSdrh ** Advance a vtablog_cursor to its next row of output.
2846fa9375cSdrh */
vtablogNext(sqlite3_vtab_cursor * cur)2856fa9375cSdrh static int vtablogNext(sqlite3_vtab_cursor *cur){
2866fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor*)cur;
2876fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
2886fa9375cSdrh printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n",
2896fa9375cSdrh pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
2906fa9375cSdrh pCur->iRowid++;
2916fa9375cSdrh return SQLITE_OK;
2926fa9375cSdrh }
2936fa9375cSdrh
2946fa9375cSdrh /*
2956fa9375cSdrh ** Return values of columns for the row at which the vtablog_cursor
2966fa9375cSdrh ** is currently pointing.
2976fa9375cSdrh */
vtablogColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)2986fa9375cSdrh static int vtablogColumn(
2996fa9375cSdrh sqlite3_vtab_cursor *cur, /* The cursor */
3006fa9375cSdrh sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
3016fa9375cSdrh int i /* Which column to return */
3026fa9375cSdrh ){
3036fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor*)cur;
3046fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
3056fa9375cSdrh char zVal[50];
3066fa9375cSdrh
3076fa9375cSdrh if( i<26 ){
3086fa9375cSdrh sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
3096fa9375cSdrh "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
3106fa9375cSdrh }else{
3116fa9375cSdrh sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
3126fa9375cSdrh }
3136fa9375cSdrh printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
3146fa9375cSdrh pTab->iInst, pCur->iCursor, i, zVal);
3156fa9375cSdrh sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
3166fa9375cSdrh return SQLITE_OK;
3176fa9375cSdrh }
3186fa9375cSdrh
3196fa9375cSdrh /*
3206fa9375cSdrh ** Return the rowid for the current row. In this implementation, the
3216fa9375cSdrh ** rowid is the same as the output value.
3226fa9375cSdrh */
vtablogRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)3236fa9375cSdrh static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
3246fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor*)cur;
3256fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
3266fa9375cSdrh printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
3276fa9375cSdrh pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
3286fa9375cSdrh *pRowid = pCur->iRowid;
3296fa9375cSdrh return SQLITE_OK;
3306fa9375cSdrh }
3316fa9375cSdrh
3326fa9375cSdrh /*
3336fa9375cSdrh ** Return TRUE if the cursor has been moved off of the last
3346fa9375cSdrh ** row of output.
3356fa9375cSdrh */
vtablogEof(sqlite3_vtab_cursor * cur)3366fa9375cSdrh static int vtablogEof(sqlite3_vtab_cursor *cur){
3376fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor*)cur;
3386fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
3396fa9375cSdrh int rc = pCur->iRowid >= pTab->nRow;
3406fa9375cSdrh printf("vtablogEof(tab=%d, cursor=%d): %d\n",
3416fa9375cSdrh pTab->iInst, pCur->iCursor, rc);
3426fa9375cSdrh return rc;
3436fa9375cSdrh }
3446fa9375cSdrh
3456fa9375cSdrh /*
3466fa9375cSdrh ** Output an sqlite3_value object's value as an SQL literal.
3476fa9375cSdrh */
vtablogQuote(sqlite3_value * p)3486fa9375cSdrh static void vtablogQuote(sqlite3_value *p){
3496fa9375cSdrh char z[50];
3506fa9375cSdrh switch( sqlite3_value_type(p) ){
3516fa9375cSdrh case SQLITE_NULL: {
3526fa9375cSdrh printf("NULL");
3536fa9375cSdrh break;
3546fa9375cSdrh }
3556fa9375cSdrh case SQLITE_INTEGER: {
3566fa9375cSdrh sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
3576fa9375cSdrh printf("%s", z);
3586fa9375cSdrh break;
3596fa9375cSdrh }
3606fa9375cSdrh case SQLITE_FLOAT: {
3616fa9375cSdrh sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
3626fa9375cSdrh printf("%s", z);
3636fa9375cSdrh break;
3646fa9375cSdrh }
3656fa9375cSdrh case SQLITE_BLOB: {
3666fa9375cSdrh int n = sqlite3_value_bytes(p);
3676fa9375cSdrh const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
3686fa9375cSdrh int i;
3696fa9375cSdrh printf("x'");
3706fa9375cSdrh for(i=0; i<n; i++) printf("%02x", z[i]);
3716fa9375cSdrh printf("'");
3726fa9375cSdrh break;
3736fa9375cSdrh }
3746fa9375cSdrh case SQLITE_TEXT: {
3756fa9375cSdrh const char *z = (const char*)sqlite3_value_text(p);
3766fa9375cSdrh int i;
3776fa9375cSdrh char c;
3786fa9375cSdrh for(i=0; (c = z[i])!=0 && c!='\''; i++){}
3796fa9375cSdrh if( c==0 ){
3806fa9375cSdrh printf("'%s'",z);
3816fa9375cSdrh }else{
3826fa9375cSdrh printf("'");
3836fa9375cSdrh while( *z ){
3846fa9375cSdrh for(i=0; (c = z[i])!=0 && c!='\''; i++){}
3856fa9375cSdrh if( c=='\'' ) i++;
3866fa9375cSdrh if( i ){
3876fa9375cSdrh printf("%.*s", i, z);
3886fa9375cSdrh z += i;
3896fa9375cSdrh }
3906fa9375cSdrh if( c=='\'' ){
3916fa9375cSdrh printf("'");
3926fa9375cSdrh continue;
3936fa9375cSdrh }
3946fa9375cSdrh if( c==0 ){
3956fa9375cSdrh break;
3966fa9375cSdrh }
3976fa9375cSdrh z++;
3986fa9375cSdrh }
3996fa9375cSdrh printf("'");
4006fa9375cSdrh }
4016fa9375cSdrh break;
4026fa9375cSdrh }
4036fa9375cSdrh }
4046fa9375cSdrh }
4056fa9375cSdrh
4066fa9375cSdrh
4076fa9375cSdrh /*
4086fa9375cSdrh ** This method is called to "rewind" the vtablog_cursor object back
4096fa9375cSdrh ** to the first row of output. This method is always called at least
4106fa9375cSdrh ** once prior to any call to vtablogColumn() or vtablogRowid() or
4116fa9375cSdrh ** vtablogEof().
4126fa9375cSdrh */
vtablogFilter(sqlite3_vtab_cursor * cur,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)4136fa9375cSdrh static int vtablogFilter(
4146fa9375cSdrh sqlite3_vtab_cursor *cur,
4156fa9375cSdrh int idxNum, const char *idxStr,
4166fa9375cSdrh int argc, sqlite3_value **argv
4176fa9375cSdrh ){
4186fa9375cSdrh vtablog_cursor *pCur = (vtablog_cursor *)cur;
4196fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
4206fa9375cSdrh printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
4216fa9375cSdrh pCur->iRowid = 0;
4226fa9375cSdrh return SQLITE_OK;
4236fa9375cSdrh }
4246fa9375cSdrh
4256fa9375cSdrh /*
4266fa9375cSdrh ** SQLite will invoke this method one or more times while planning a query
4276fa9375cSdrh ** that uses the vtablog virtual table. This routine needs to create
4286fa9375cSdrh ** a query plan for each invocation and compute an estimated cost for that
4296fa9375cSdrh ** plan.
4306fa9375cSdrh */
vtablogBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)4316fa9375cSdrh static int vtablogBestIndex(
4326fa9375cSdrh sqlite3_vtab *tab,
4336fa9375cSdrh sqlite3_index_info *pIdxInfo
4346fa9375cSdrh ){
4356fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)tab;
4366fa9375cSdrh printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
4376fa9375cSdrh pIdxInfo->estimatedCost = (double)500;
4386fa9375cSdrh pIdxInfo->estimatedRows = 500;
4396fa9375cSdrh return SQLITE_OK;
4406fa9375cSdrh }
4416fa9375cSdrh
4426fa9375cSdrh /*
4436fa9375cSdrh ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
4446fa9375cSdrh ** the table.
4456fa9375cSdrh **
4466fa9375cSdrh ** This implementation does not actually make any changes to the table
4476fa9375cSdrh ** content. It merely logs the fact that the method was invoked
4486fa9375cSdrh */
vtablogUpdate(sqlite3_vtab * tab,int argc,sqlite3_value ** argv,sqlite_int64 * pRowid)4496fa9375cSdrh static int vtablogUpdate(
4506fa9375cSdrh sqlite3_vtab *tab,
4516fa9375cSdrh int argc,
4526fa9375cSdrh sqlite3_value **argv,
4536fa9375cSdrh sqlite_int64 *pRowid
4546fa9375cSdrh ){
4556fa9375cSdrh vtablog_vtab *pTab = (vtablog_vtab*)tab;
4566fa9375cSdrh int i;
4576fa9375cSdrh printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
4586fa9375cSdrh printf(" argc=%d\n", argc);
4596fa9375cSdrh for(i=0; i<argc; i++){
4606fa9375cSdrh printf(" argv[%d]=", i);
4616fa9375cSdrh vtablogQuote(argv[i]);
4626fa9375cSdrh printf("\n");
4636fa9375cSdrh }
4646fa9375cSdrh return SQLITE_OK;
4656fa9375cSdrh }
4666fa9375cSdrh
4676fa9375cSdrh /*
4686fa9375cSdrh ** This following structure defines all the methods for the
4696fa9375cSdrh ** vtablog virtual table.
4706fa9375cSdrh */
4716fa9375cSdrh static sqlite3_module vtablogModule = {
4726fa9375cSdrh 0, /* iVersion */
4736fa9375cSdrh vtablogCreate, /* xCreate */
4746fa9375cSdrh vtablogConnect, /* xConnect */
4756fa9375cSdrh vtablogBestIndex, /* xBestIndex */
4766fa9375cSdrh vtablogDisconnect, /* xDisconnect */
4776fa9375cSdrh vtablogDestroy, /* xDestroy */
4786fa9375cSdrh vtablogOpen, /* xOpen - open a cursor */
4796fa9375cSdrh vtablogClose, /* xClose - close a cursor */
4806fa9375cSdrh vtablogFilter, /* xFilter - configure scan constraints */
4816fa9375cSdrh vtablogNext, /* xNext - advance a cursor */
4826fa9375cSdrh vtablogEof, /* xEof - check for end of scan */
4836fa9375cSdrh vtablogColumn, /* xColumn - read data */
4846fa9375cSdrh vtablogRowid, /* xRowid - read data */
4856fa9375cSdrh vtablogUpdate, /* xUpdate */
4866fa9375cSdrh 0, /* xBegin */
4876fa9375cSdrh 0, /* xSync */
4886fa9375cSdrh 0, /* xCommit */
4896fa9375cSdrh 0, /* xRollback */
4906fa9375cSdrh 0, /* xFindMethod */
4916fa9375cSdrh 0, /* xRename */
4926fa9375cSdrh 0, /* xSavepoint */
4936fa9375cSdrh 0, /* xRelease */
4946fa9375cSdrh 0, /* xRollbackTo */
495*84c501baSdrh 0, /* xShadowName */
4966fa9375cSdrh };
4976fa9375cSdrh
4986fa9375cSdrh #ifdef _WIN32
4996fa9375cSdrh __declspec(dllexport)
5006fa9375cSdrh #endif
sqlite3_vtablog_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)5016fa9375cSdrh int sqlite3_vtablog_init(
5026fa9375cSdrh sqlite3 *db,
5036fa9375cSdrh char **pzErrMsg,
5046fa9375cSdrh const sqlite3_api_routines *pApi
5056fa9375cSdrh ){
5066fa9375cSdrh int rc;
5076fa9375cSdrh SQLITE_EXTENSION_INIT2(pApi);
5086fa9375cSdrh rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
5096fa9375cSdrh return rc;
5106fa9375cSdrh }
511