xref: /sqlite-3.40.0/ext/misc/vtablog.c (revision 84c501ba)
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