Add the experimental sqlite3_value_nochange() interface usable by xUpdate
[sqlite.git] / ext / misc / vtablog.c
blob9e03fd455de2259e423b5d5feec855b861d25727
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.
11 *************************************************************************
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.
17 ** Usage example:
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;
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>
35 /* vtablog_vtab is a subclass of sqlite3_vtab which will
36 ** serve as the underlying representation of a vtablog virtual table
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 */
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
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 */
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;
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;
71 /* Dequote the string */
72 static void vtablog_dequote(char *z){
73 int j;
74 char cQuote = z[0];
75 size_t i, n;
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];
84 z[j] = 0;
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.
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);
99 /* Decode a parameter that requires a dequoted string.
101 ** Return non-zero on an error.
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 */
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;
116 *pzVal = sqlite3_mprintf("%s", zValue);
117 if( *pzVal==0 ){
118 *pzErr = sqlite3_mprintf("out of memory");
119 return 1;
121 vtablog_trim_whitespace(*pzVal);
122 vtablog_dequote(*pzVal);
123 return 0;
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.
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)
136 return 1;
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)
143 return 0;
145 return -1;
147 #endif
150 ** The vtablogConnect() method is invoked to create a new
151 ** vtablog_vtab that describes the vtablog virtual table.
153 ** Think of this routine as the constructor for vtablog_vtab objects.
155 ** All this routine needs to do is:
157 ** (1) Allocate the vtablog_vtab object and initialize all fields.
159 ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
160 ** result set of queries against vtablog will look like.
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
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;
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");
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;
194 if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
195 return SQLITE_ERROR;
199 if( zSchema==0 ){
200 *pzErr = sqlite3_mprintf("no schema defined");
201 return SQLITE_ERROR;
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;
213 return rc;
215 static int vtablogCreate(
216 sqlite3 *db,
217 void *pAux,
218 int argc, const char *const*argv,
219 sqlite3_vtab **ppVtab,
220 char **pzErr
222 return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
224 static int vtablogConnect(
225 sqlite3 *db,
226 void *pAux,
227 int argc, const char *const*argv,
228 sqlite3_vtab **ppVtab,
229 char **pzErr
231 return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
236 ** This method is the destructor for vtablog_cursor objects.
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;
246 ** This method is the destructor for vtablog_cursor objects.
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;
256 ** Constructor for a new vtablog_cursor object.
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;
271 ** Destructor for a vtablog_cursor.
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;
283 ** Advance a vtablog_cursor to its next row of output.
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;
295 ** Return values of columns for the row at which the vtablog_cursor
296 ** is currently pointing.
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 */
303 vtablog_cursor *pCur = (vtablog_cursor*)cur;
304 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
305 char zVal[50];
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);
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;
320 ** Return the rowid for the current row. In this implementation, the
321 ** rowid is the same as the output value.
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;
333 ** Return TRUE if the cursor has been moved off of the last
334 ** row of output.
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;
346 ** Output an sqlite3_value object's value as an SQL literal.
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;
355 case SQLITE_INTEGER: {
356 sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
357 printf("%s", z);
358 break;
360 case SQLITE_FLOAT: {
361 sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
362 printf("%s", z);
363 break;
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;
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;
390 if( c=='\'' ){
391 printf("'");
392 continue;
394 if( c==0 ){
395 break;
397 z++;
399 printf("'");
401 break;
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().
413 static int vtablogFilter(
414 sqlite3_vtab_cursor *cur,
415 int idxNum, const char *idxStr,
416 int argc, sqlite3_value **argv
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;
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.
431 static int vtablogBestIndex(
432 sqlite3_vtab *tab,
433 sqlite3_index_info *pIdxInfo
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;
443 ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
444 ** the table.
446 ** This implementation does not actually make any changes to the table
447 ** content. It merely logs the fact that the method was invoked
449 static int vtablogUpdate(
450 sqlite3_vtab *tab,
451 int argc,
452 sqlite3_value **argv,
453 sqlite_int64 *pRowid
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");
464 return SQLITE_OK;
468 ** This following structure defines all the methods for the
469 ** vtablog virtual table.
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 */
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
505 int rc;
506 SQLITE_EXTENSION_INIT2(pApi);
507 rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
508 return rc;