modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/misc/vtablog.c (about)

     1  /*
     2  ** 2017-08-10
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  **
    13  ** This file implements a virtual table that prints diagnostic information
    14  ** on stdout when its key interfaces are called.  This is intended for
    15  ** interactive analysis and debugging of virtual table interfaces.
    16  **
    17  ** Usage example:
    18  **
    19  **     .load ./vtablog
    20  **     CREATE VIRTUAL TABLE temp.log USING vtablog(
    21  **        schema='CREATE TABLE x(a,b,c)',
    22  **        rows=25
    23  **     );
    24  **     SELECT * FROM log;
    25  */
    26  #include "sqlite3ext.h"
    27  SQLITE_EXTENSION_INIT1
    28  #include <stdio.h>
    29  #include <stdlib.h>
    30  #include <assert.h>
    31  #include <string.h>
    32  #include <ctype.h>
    33  
    34  
    35  /* vtablog_vtab is a subclass of sqlite3_vtab which will
    36  ** serve as the underlying representation of a vtablog virtual table
    37  */
    38  typedef struct vtablog_vtab vtablog_vtab;
    39  struct vtablog_vtab {
    40    sqlite3_vtab base;  /* Base class - must be first */
    41    int nRow;           /* Number of rows in the table */
    42    int iInst;          /* Instance number for this vtablog table */
    43    int nCursor;        /* Number of cursors created */
    44  };
    45  
    46  /* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
    47  ** serve as the underlying representation of a cursor that scans
    48  ** over rows of the result
    49  */
    50  typedef struct vtablog_cursor vtablog_cursor;
    51  struct vtablog_cursor {
    52    sqlite3_vtab_cursor base;  /* Base class - must be first */
    53    int iCursor;               /* Cursor number */
    54    sqlite3_int64 iRowid;      /* The rowid */
    55  };
    56  
    57  /* Skip leading whitespace.  Return a pointer to the first non-whitespace
    58  ** character, or to the zero terminator if the string has only whitespace */
    59  static const char *vtablog_skip_whitespace(const char *z){
    60    while( isspace((unsigned char)z[0]) ) z++;
    61    return z;
    62  }
    63  
    64  /* Remove trailing whitespace from the end of string z[] */
    65  static void vtablog_trim_whitespace(char *z){
    66    size_t n = strlen(z);
    67    while( n>0 && isspace((unsigned char)z[n]) ) n--;
    68    z[n] = 0;
    69  }
    70  
    71  /* Dequote the string */
    72  static void vtablog_dequote(char *z){
    73    int j;
    74    char cQuote = z[0];
    75    size_t i, n;
    76  
    77    if( cQuote!='\'' && cQuote!='"' ) return;
    78    n = strlen(z);
    79    if( n<2 || z[n-1]!=z[0] ) return;
    80    for(i=1, j=0; i<n-1; i++){
    81      if( z[i]==cQuote && z[i+1]==cQuote ) i++;
    82      z[j++] = z[i];
    83    }
    84    z[j] = 0;
    85  }
    86  
    87  /* Check to see if the string is of the form:  "TAG = VALUE" with optional
    88  ** whitespace before and around tokens.  If it is, return a pointer to the
    89  ** first character of VALUE.  If it is not, return NULL.
    90  */
    91  static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
    92    z = vtablog_skip_whitespace(z);
    93    if( strncmp(zTag, z, nTag)!=0 ) return 0;
    94    z = vtablog_skip_whitespace(z+nTag);
    95    if( z[0]!='=' ) return 0;
    96    return vtablog_skip_whitespace(z+1);
    97  }
    98  
    99  /* Decode a parameter that requires a dequoted string.
   100  **
   101  ** Return non-zero on an error.
   102  */
   103  static int vtablog_string_parameter(
   104    char **pzErr,            /* Leave the error message here, if there is one */
   105    const char *zParam,      /* Parameter we are checking for */
   106    const char *zArg,        /* Raw text of the virtual table argment */
   107    char **pzVal             /* Write the dequoted string value here */
   108  ){
   109    const char *zValue;
   110    zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
   111    if( zValue==0 ) return 0;
   112    if( *pzVal ){
   113      *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
   114      return 1;
   115    }
   116    *pzVal = sqlite3_mprintf("%s", zValue);
   117    if( *pzVal==0 ){
   118      *pzErr = sqlite3_mprintf("out of memory");
   119      return 1;
   120    }
   121    vtablog_trim_whitespace(*pzVal);
   122    vtablog_dequote(*pzVal);
   123    return 0;
   124  }
   125  
   126  #if 0 /* not used - yet */
   127  /* Return 0 if the argument is false and 1 if it is true.  Return -1 if
   128  ** we cannot really tell.
   129  */
   130  static int vtablog_boolean(const char *z){
   131    if( sqlite3_stricmp("yes",z)==0
   132     || sqlite3_stricmp("on",z)==0
   133     || sqlite3_stricmp("true",z)==0
   134     || (z[0]=='1' && z[1]==0)
   135    ){
   136      return 1;
   137    }
   138    if( sqlite3_stricmp("no",z)==0
   139     || sqlite3_stricmp("off",z)==0
   140     || sqlite3_stricmp("false",z)==0
   141     || (z[0]=='0' && z[1]==0)
   142    ){
   143      return 0;
   144    }
   145    return -1;
   146  }
   147  #endif
   148  
   149  /*
   150  ** The vtablogConnect() method is invoked to create a new
   151  ** vtablog_vtab that describes the vtablog virtual table.
   152  **
   153  ** Think of this routine as the constructor for vtablog_vtab objects.
   154  **
   155  ** All this routine needs to do is:
   156  **
   157  **    (1) Allocate the vtablog_vtab object and initialize all fields.
   158  **
   159  **    (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
   160  **        result set of queries against vtablog will look like.
   161  */
   162  static int vtablogConnectCreate(
   163    sqlite3 *db,
   164    void *pAux,
   165    int argc, const char *const*argv,
   166    sqlite3_vtab **ppVtab,
   167    char **pzErr,
   168    int isCreate
   169  ){
   170    static int nInst = 0;
   171    vtablog_vtab *pNew;
   172    int i;
   173    int rc;
   174    int iInst = ++nInst;
   175    char *zSchema = 0;
   176    char *zNRow = 0;
   177  
   178    printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst);
   179    printf("  argc=%d\n", argc);
   180    for(i=0; i<argc; i++){
   181      printf("  argv[%d] = ", i);
   182      if( argv[i] ){
   183        printf("[%s]\n", argv[i]);
   184      }else{
   185        printf("NULL\n");
   186      }
   187    }
   188  
   189    for(i=3; i<argc; i++){
   190      const char *z = argv[i];
   191      if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
   192        return SQLITE_ERROR;
   193      }
   194      if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
   195        return SQLITE_ERROR;
   196      }
   197    }
   198  
   199    if( zSchema==0 ){
   200      *pzErr = sqlite3_mprintf("no schema defined");
   201      return SQLITE_ERROR;
   202    }
   203    rc = sqlite3_declare_vtab(db, zSchema);
   204    if( rc==SQLITE_OK ){
   205      pNew = sqlite3_malloc( sizeof(*pNew) );
   206      *ppVtab = (sqlite3_vtab*)pNew;
   207      if( pNew==0 ) return SQLITE_NOMEM;
   208      memset(pNew, 0, sizeof(*pNew));
   209      pNew->nRow = 10;
   210      if( zNRow ) pNew->nRow = atoi(zNRow);
   211      pNew->iInst = iInst;
   212    }
   213    return rc;
   214  }
   215  static int vtablogCreate(
   216    sqlite3 *db,
   217    void *pAux,
   218    int argc, const char *const*argv,
   219    sqlite3_vtab **ppVtab,
   220    char **pzErr
   221  ){
   222    return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
   223  }
   224  static int vtablogConnect(
   225    sqlite3 *db,
   226    void *pAux,
   227    int argc, const char *const*argv,
   228    sqlite3_vtab **ppVtab,
   229    char **pzErr
   230  ){
   231    return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
   232  }
   233  
   234  
   235  /*
   236  ** This method is the destructor for vtablog_cursor objects.
   237  */
   238  static int vtablogDisconnect(sqlite3_vtab *pVtab){
   239    vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
   240    printf("vtablogDisconnect(%d)\n", pTab->iInst);
   241    sqlite3_free(pVtab);
   242    return SQLITE_OK;
   243  }
   244  
   245  /*
   246  ** This method is the destructor for vtablog_cursor objects.
   247  */
   248  static int vtablogDestroy(sqlite3_vtab *pVtab){
   249    vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
   250    printf("vtablogDestroy(%d)\n", pTab->iInst);
   251    sqlite3_free(pVtab);
   252    return SQLITE_OK;
   253  }
   254  
   255  /*
   256  ** Constructor for a new vtablog_cursor object.
   257  */
   258  static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
   259    vtablog_vtab *pTab = (vtablog_vtab*)p;
   260    vtablog_cursor *pCur;
   261    printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor);
   262    pCur = sqlite3_malloc( sizeof(*pCur) );
   263    if( pCur==0 ) return SQLITE_NOMEM;
   264    memset(pCur, 0, sizeof(*pCur));
   265    pCur->iCursor = pTab->nCursor;
   266    *ppCursor = &pCur->base;
   267    return SQLITE_OK;
   268  }
   269  
   270  /*
   271  ** Destructor for a vtablog_cursor.
   272  */
   273  static int vtablogClose(sqlite3_vtab_cursor *cur){
   274    vtablog_cursor *pCur = (vtablog_cursor*)cur;
   275    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   276    printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor);
   277    sqlite3_free(cur);
   278    return SQLITE_OK;
   279  }
   280  
   281  
   282  /*
   283  ** Advance a vtablog_cursor to its next row of output.
   284  */
   285  static int vtablogNext(sqlite3_vtab_cursor *cur){
   286    vtablog_cursor *pCur = (vtablog_cursor*)cur;
   287    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   288    printf("vtablogNext(tab=%d, cursor=%d)  rowid %d -> %d\n", 
   289           pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1);
   290    pCur->iRowid++;
   291    return SQLITE_OK;
   292  }
   293  
   294  /*
   295  ** Return values of columns for the row at which the vtablog_cursor
   296  ** is currently pointing.
   297  */
   298  static int vtablogColumn(
   299    sqlite3_vtab_cursor *cur,   /* The cursor */
   300    sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
   301    int i                       /* Which column to return */
   302  ){
   303    vtablog_cursor *pCur = (vtablog_cursor*)cur;
   304    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   305    char zVal[50];
   306  
   307    if( i<26 ){
   308      sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", 
   309                       "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
   310    }else{
   311      sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
   312    }
   313    printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n",
   314           pTab->iInst, pCur->iCursor, i, zVal);
   315    sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
   316    return SQLITE_OK;
   317  }
   318  
   319  /*
   320  ** Return the rowid for the current row.  In this implementation, the
   321  ** rowid is the same as the output value.
   322  */
   323  static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   324    vtablog_cursor *pCur = (vtablog_cursor*)cur;
   325    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   326    printf("vtablogRowid(tab=%d, cursor=%d): %d\n",
   327           pTab->iInst, pCur->iCursor, (int)pCur->iRowid);
   328    *pRowid = pCur->iRowid;
   329    return SQLITE_OK;
   330  }
   331  
   332  /*
   333  ** Return TRUE if the cursor has been moved off of the last
   334  ** row of output.
   335  */
   336  static int vtablogEof(sqlite3_vtab_cursor *cur){
   337    vtablog_cursor *pCur = (vtablog_cursor*)cur;
   338    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   339    int rc = pCur->iRowid >= pTab->nRow;
   340    printf("vtablogEof(tab=%d, cursor=%d): %d\n",
   341           pTab->iInst, pCur->iCursor, rc);
   342    return rc;
   343  }
   344  
   345  /*
   346  ** Output an sqlite3_value object's value as an SQL literal.
   347  */
   348  static void vtablogQuote(sqlite3_value *p){
   349    char z[50];
   350    switch( sqlite3_value_type(p) ){
   351      case SQLITE_NULL: {
   352        printf("NULL");
   353        break;
   354      }
   355      case SQLITE_INTEGER: {
   356        sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
   357        printf("%s", z);
   358        break;
   359      }
   360      case SQLITE_FLOAT: {
   361        sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
   362        printf("%s", z);
   363        break;
   364      }
   365      case SQLITE_BLOB: {
   366        int n = sqlite3_value_bytes(p);
   367        const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
   368        int i;
   369        printf("x'");
   370        for(i=0; i<n; i++) printf("%02x", z[i]);
   371        printf("'");
   372        break;
   373      }
   374      case SQLITE_TEXT: {
   375        const char *z = (const char*)sqlite3_value_text(p);
   376        int i;
   377        char c;
   378        for(i=0; (c = z[i])!=0 && c!='\''; i++){}
   379        if( c==0 ){
   380          printf("'%s'",z);
   381        }else{
   382          printf("'");
   383          while( *z ){
   384            for(i=0; (c = z[i])!=0 && c!='\''; i++){}
   385            if( c=='\'' ) i++;
   386            if( i ){
   387              printf("%.*s", i, z);
   388              z += i;
   389            }
   390            if( c=='\'' ){
   391              printf("'");
   392              continue;
   393            }
   394            if( c==0 ){
   395              break;
   396            }
   397            z++;
   398          }
   399          printf("'");
   400        }
   401        break;
   402      }
   403    }
   404  }
   405  
   406  
   407  /*
   408  ** This method is called to "rewind" the vtablog_cursor object back
   409  ** to the first row of output.  This method is always called at least
   410  ** once prior to any call to vtablogColumn() or vtablogRowid() or 
   411  ** vtablogEof().
   412  */
   413  static int vtablogFilter(
   414    sqlite3_vtab_cursor *cur,
   415    int idxNum, const char *idxStr,
   416    int argc, sqlite3_value **argv
   417  ){
   418    vtablog_cursor *pCur = (vtablog_cursor *)cur;
   419    vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
   420    printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor);
   421    pCur->iRowid = 0;
   422    return SQLITE_OK;
   423  }
   424  
   425  /*
   426  ** SQLite will invoke this method one or more times while planning a query
   427  ** that uses the vtablog virtual table.  This routine needs to create
   428  ** a query plan for each invocation and compute an estimated cost for that
   429  ** plan.
   430  */
   431  static int vtablogBestIndex(
   432    sqlite3_vtab *tab,
   433    sqlite3_index_info *pIdxInfo
   434  ){
   435    vtablog_vtab *pTab = (vtablog_vtab*)tab;
   436    printf("vtablogBestIndex(tab=%d):\n", pTab->iInst);
   437    pIdxInfo->estimatedCost = (double)500;
   438    pIdxInfo->estimatedRows = 500;
   439    return SQLITE_OK;
   440  }
   441  
   442  /*
   443  ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
   444  ** the table. 
   445  **
   446  ** This implementation does not actually make any changes to the table
   447  ** content.  It merely logs the fact that the method was invoked
   448  */
   449  static int vtablogUpdate(
   450    sqlite3_vtab *tab,
   451    int argc,
   452    sqlite3_value **argv,
   453    sqlite_int64 *pRowid
   454  ){
   455    vtablog_vtab *pTab = (vtablog_vtab*)tab;
   456    int i;
   457    printf("vtablogUpdate(tab=%d):\n", pTab->iInst);
   458    printf("  argc=%d\n", argc);
   459    for(i=0; i<argc; i++){
   460      printf("  argv[%d]=", i);
   461      vtablogQuote(argv[i]);
   462      printf("\n");
   463    }
   464    return SQLITE_OK;
   465  }
   466  
   467  /*
   468  ** This following structure defines all the methods for the 
   469  ** vtablog virtual table.
   470  */
   471  static sqlite3_module vtablogModule = {
   472    0,                         /* iVersion */
   473    vtablogCreate,             /* xCreate */
   474    vtablogConnect,            /* xConnect */
   475    vtablogBestIndex,          /* xBestIndex */
   476    vtablogDisconnect,         /* xDisconnect */
   477    vtablogDestroy,            /* xDestroy */
   478    vtablogOpen,               /* xOpen - open a cursor */
   479    vtablogClose,              /* xClose - close a cursor */
   480    vtablogFilter,             /* xFilter - configure scan constraints */
   481    vtablogNext,               /* xNext - advance a cursor */
   482    vtablogEof,                /* xEof - check for end of scan */
   483    vtablogColumn,             /* xColumn - read data */
   484    vtablogRowid,              /* xRowid - read data */
   485    vtablogUpdate,             /* xUpdate */
   486    0,                         /* xBegin */
   487    0,                         /* xSync */
   488    0,                         /* xCommit */
   489    0,                         /* xRollback */
   490    0,                         /* xFindMethod */
   491    0,                         /* xRename */
   492    0,                         /* xSavepoint */
   493    0,                         /* xRelease */
   494    0,                         /* xRollbackTo */
   495  };
   496  
   497  #ifdef _WIN32
   498  __declspec(dllexport)
   499  #endif
   500  int sqlite3_vtablog_init(
   501    sqlite3 *db, 
   502    char **pzErrMsg, 
   503    const sqlite3_api_routines *pApi
   504  ){
   505    int rc;
   506    SQLITE_EXTENSION_INIT2(pApi);
   507    rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
   508    return rc;
   509  }