Fixes default log output to console for macOS
[sqlcipher.git] / ext / misc / vtablog.c
blob2cc29c285f91322ae48e16c33213f7307abe6ba8
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 char *zDb; /* Schema name. argv[1] of xConnect/xCreate */
42 char *zName; /* Table name. argv[2] of xConnect/xCreate */
43 int nRow; /* Number of rows in the table */
44 int nCursor; /* Number of cursors created */
47 /* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will
48 ** serve as the underlying representation of a cursor that scans
49 ** over rows of the result
51 typedef struct vtablog_cursor vtablog_cursor;
52 struct vtablog_cursor {
53 sqlite3_vtab_cursor base; /* Base class - must be first */
54 int iCursor; /* Cursor number */
55 sqlite3_int64 iRowid; /* The rowid */
58 /* Skip leading whitespace. Return a pointer to the first non-whitespace
59 ** character, or to the zero terminator if the string has only whitespace */
60 static const char *vtablog_skip_whitespace(const char *z){
61 while( isspace((unsigned char)z[0]) ) z++;
62 return z;
65 /* Remove trailing whitespace from the end of string z[] */
66 static void vtablog_trim_whitespace(char *z){
67 size_t n = strlen(z);
68 while( n>0 && isspace((unsigned char)z[n]) ) n--;
69 z[n] = 0;
72 /* Dequote the string */
73 static void vtablog_dequote(char *z){
74 int j;
75 char cQuote = z[0];
76 size_t i, n;
78 if( cQuote!='\'' && cQuote!='"' ) return;
79 n = strlen(z);
80 if( n<2 || z[n-1]!=z[0] ) return;
81 for(i=1, j=0; i<n-1; i++){
82 if( z[i]==cQuote && z[i+1]==cQuote ) i++;
83 z[j++] = z[i];
85 z[j] = 0;
88 /* Check to see if the string is of the form: "TAG = VALUE" with optional
89 ** whitespace before and around tokens. If it is, return a pointer to the
90 ** first character of VALUE. If it is not, return NULL.
92 static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
93 z = vtablog_skip_whitespace(z);
94 if( strncmp(zTag, z, nTag)!=0 ) return 0;
95 z = vtablog_skip_whitespace(z+nTag);
96 if( z[0]!='=' ) return 0;
97 return vtablog_skip_whitespace(z+1);
100 /* Decode a parameter that requires a dequoted string.
102 ** Return non-zero on an error.
104 static int vtablog_string_parameter(
105 char **pzErr, /* Leave the error message here, if there is one */
106 const char *zParam, /* Parameter we are checking for */
107 const char *zArg, /* Raw text of the virtual table argment */
108 char **pzVal /* Write the dequoted string value here */
110 const char *zValue;
111 zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
112 if( zValue==0 ) return 0;
113 if( *pzVal ){
114 *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
115 return 1;
117 *pzVal = sqlite3_mprintf("%s", zValue);
118 if( *pzVal==0 ){
119 *pzErr = sqlite3_mprintf("out of memory");
120 return 1;
122 vtablog_trim_whitespace(*pzVal);
123 vtablog_dequote(*pzVal);
124 return 0;
127 #if 0 /* not used - yet */
128 /* Return 0 if the argument is false and 1 if it is true. Return -1 if
129 ** we cannot really tell.
131 static int vtablog_boolean(const char *z){
132 if( sqlite3_stricmp("yes",z)==0
133 || sqlite3_stricmp("on",z)==0
134 || sqlite3_stricmp("true",z)==0
135 || (z[0]=='1' && z[1]==0)
137 return 1;
139 if( sqlite3_stricmp("no",z)==0
140 || sqlite3_stricmp("off",z)==0
141 || sqlite3_stricmp("false",z)==0
142 || (z[0]=='0' && z[1]==0)
144 return 0;
146 return -1;
148 #endif
151 ** The vtablogConnect() method is invoked to create a new
152 ** vtablog_vtab that describes the vtablog virtual table.
154 ** Think of this routine as the constructor for vtablog_vtab objects.
156 ** All this routine needs to do is:
158 ** (1) Allocate the vtablog_vtab object and initialize all fields.
160 ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
161 ** result set of queries against vtablog will look like.
163 static int vtablogConnectCreate(
164 sqlite3 *db,
165 void *pAux,
166 int argc, const char *const*argv,
167 sqlite3_vtab **ppVtab,
168 char **pzErr,
169 int isCreate
171 vtablog_vtab *pNew;
172 int i;
173 int rc;
174 char *zSchema = 0;
175 char *zNRow = 0;
177 printf("%s.%s.%s():\n", argv[1], argv[2],
178 isCreate ? "xCreate" : "xConnect");
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 rc = SQLITE_ERROR;
193 goto vtablog_end_connect;
195 if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
196 rc = SQLITE_ERROR;
197 goto vtablog_end_connect;
200 if( zSchema==0 ){
201 zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
203 printf(" schema = '%s'\n", zSchema);
204 rc = sqlite3_declare_vtab(db, zSchema);
205 if( rc==SQLITE_OK ){
206 pNew = sqlite3_malloc( sizeof(*pNew) );
207 *ppVtab = (sqlite3_vtab*)pNew;
208 if( pNew==0 ) return SQLITE_NOMEM;
209 memset(pNew, 0, sizeof(*pNew));
210 pNew->nRow = 10;
211 if( zNRow ) pNew->nRow = atoi(zNRow);
212 printf(" nrow = %d\n", pNew->nRow);
213 pNew->zDb = sqlite3_mprintf("%s", argv[1]);
214 pNew->zName = sqlite3_mprintf("%s", argv[2]);
217 vtablog_end_connect:
218 sqlite3_free(zSchema);
219 sqlite3_free(zNRow);
220 return rc;
222 static int vtablogCreate(
223 sqlite3 *db,
224 void *pAux,
225 int argc, const char *const*argv,
226 sqlite3_vtab **ppVtab,
227 char **pzErr
229 return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
231 static int vtablogConnect(
232 sqlite3 *db,
233 void *pAux,
234 int argc, const char *const*argv,
235 sqlite3_vtab **ppVtab,
236 char **pzErr
238 return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
243 ** This method is the destructor for vtablog_cursor objects.
245 static int vtablogDisconnect(sqlite3_vtab *pVtab){
246 vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
247 printf("%s.%s.xDisconnect()\n", pTab->zDb, pTab->zName);
248 sqlite3_free(pTab->zDb);
249 sqlite3_free(pTab->zName);
250 sqlite3_free(pVtab);
251 return SQLITE_OK;
255 ** This method is the destructor for vtablog_cursor objects.
257 static int vtablogDestroy(sqlite3_vtab *pVtab){
258 vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
259 printf("%s.%s.xDestroy()\n", pTab->zDb, pTab->zName);
260 sqlite3_free(pTab->zDb);
261 sqlite3_free(pTab->zName);
262 sqlite3_free(pVtab);
263 return SQLITE_OK;
267 ** Constructor for a new vtablog_cursor object.
269 static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
270 vtablog_vtab *pTab = (vtablog_vtab*)p;
271 vtablog_cursor *pCur;
272 printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
273 ++pTab->nCursor);
274 pCur = sqlite3_malloc( sizeof(*pCur) );
275 if( pCur==0 ) return SQLITE_NOMEM;
276 memset(pCur, 0, sizeof(*pCur));
277 pCur->iCursor = pTab->nCursor;
278 *ppCursor = &pCur->base;
279 return SQLITE_OK;
283 ** Destructor for a vtablog_cursor.
285 static int vtablogClose(sqlite3_vtab_cursor *cur){
286 vtablog_cursor *pCur = (vtablog_cursor*)cur;
287 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
288 printf("%s.%s.xClose(cursor=%d)\n", pTab->zDb, pTab->zName, pCur->iCursor);
289 sqlite3_free(cur);
290 return SQLITE_OK;
295 ** Advance a vtablog_cursor to its next row of output.
297 static int vtablogNext(sqlite3_vtab_cursor *cur){
298 vtablog_cursor *pCur = (vtablog_cursor*)cur;
299 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
300 printf("%s.%s.xNext(cursor=%d) rowid %d -> %d\n",
301 pTab->zDb, pTab->zName, pCur->iCursor,
302 (int)pCur->iRowid, (int)pCur->iRowid+1);
303 pCur->iRowid++;
304 return SQLITE_OK;
308 ** Return values of columns for the row at which the vtablog_cursor
309 ** is currently pointing.
311 static int vtablogColumn(
312 sqlite3_vtab_cursor *cur, /* The cursor */
313 sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
314 int i /* Which column to return */
316 vtablog_cursor *pCur = (vtablog_cursor*)cur;
317 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
318 char zVal[50];
320 if( i<26 ){
321 sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
322 "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
323 }else{
324 sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
326 printf("%s.%s.xColumn(cursor=%d, i=%d): [%s]\n",
327 pTab->zDb, pTab->zName, pCur->iCursor, i, zVal);
328 sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
329 return SQLITE_OK;
333 ** Return the rowid for the current row. In this implementation, the
334 ** rowid is the same as the output value.
336 static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
337 vtablog_cursor *pCur = (vtablog_cursor*)cur;
338 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
339 printf("%s.%s.xRowid(cursor=%d): %d\n",
340 pTab->zDb, pTab->zName, pCur->iCursor, (int)pCur->iRowid);
341 *pRowid = pCur->iRowid;
342 return SQLITE_OK;
346 ** Return TRUE if the cursor has been moved off of the last
347 ** row of output.
349 static int vtablogEof(sqlite3_vtab_cursor *cur){
350 vtablog_cursor *pCur = (vtablog_cursor*)cur;
351 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
352 int rc = pCur->iRowid >= pTab->nRow;
353 printf("%s.%s.xEof(cursor=%d): %d\n",
354 pTab->zDb, pTab->zName, pCur->iCursor, rc);
355 return rc;
359 ** Output an sqlite3_value object's value as an SQL literal.
361 static void vtablogQuote(sqlite3_value *p){
362 char z[50];
363 switch( sqlite3_value_type(p) ){
364 case SQLITE_NULL: {
365 printf("NULL");
366 break;
368 case SQLITE_INTEGER: {
369 sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
370 printf("%s", z);
371 break;
373 case SQLITE_FLOAT: {
374 sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
375 printf("%s", z);
376 break;
378 case SQLITE_BLOB: {
379 int n = sqlite3_value_bytes(p);
380 const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
381 int i;
382 printf("x'");
383 for(i=0; i<n; i++) printf("%02x", z[i]);
384 printf("'");
385 break;
387 case SQLITE_TEXT: {
388 const char *z = (const char*)sqlite3_value_text(p);
389 int i;
390 char c;
391 for(i=0; (c = z[i])!=0 && c!='\''; i++){}
392 if( c==0 ){
393 printf("'%s'",z);
394 }else{
395 printf("'");
396 while( *z ){
397 for(i=0; (c = z[i])!=0 && c!='\''; i++){}
398 if( c=='\'' ) i++;
399 if( i ){
400 printf("%.*s", i, z);
401 z += i;
403 if( c=='\'' ){
404 printf("'");
405 continue;
407 if( c==0 ){
408 break;
410 z++;
412 printf("'");
414 break;
421 ** This method is called to "rewind" the vtablog_cursor object back
422 ** to the first row of output. This method is always called at least
423 ** once prior to any call to vtablogColumn() or vtablogRowid() or
424 ** vtablogEof().
426 static int vtablogFilter(
427 sqlite3_vtab_cursor *cur,
428 int idxNum, const char *idxStr,
429 int argc, sqlite3_value **argv
431 vtablog_cursor *pCur = (vtablog_cursor *)cur;
432 vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
433 printf("%s.%s.xFilter(cursor=%d):\n", pTab->zDb, pTab->zName, pCur->iCursor);
434 pCur->iRowid = 0;
435 return SQLITE_OK;
439 ** SQLite will invoke this method one or more times while planning a query
440 ** that uses the vtablog virtual table. This routine needs to create
441 ** a query plan for each invocation and compute an estimated cost for that
442 ** plan.
444 static int vtablogBestIndex(
445 sqlite3_vtab *tab,
446 sqlite3_index_info *p
448 vtablog_vtab *pTab = (vtablog_vtab*)tab;
449 int i;
450 printf("%s.%s.xBestIndex():\n", pTab->zDb, pTab->zName);
451 printf(" colUsed: 0x%016llx\n", p->colUsed);
452 printf(" nConstraint: %d\n", p->nConstraint);
453 for(i=0; i<p->nConstraint; i++){
454 printf(
455 " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
457 p->aConstraint[i].iColumn,
458 p->aConstraint[i].iTermOffset,
459 p->aConstraint[i].op,
460 p->aConstraint[i].usable,
461 sqlite3_vtab_collation(p,i));
463 printf(" nOrderBy: %d\n", p->nOrderBy);
464 for(i=0; i<p->nOrderBy; i++){
465 printf(" orderby[%d]: col=%d desc=%d\n",
467 p->aOrderBy[i].iColumn,
468 p->aOrderBy[i].desc);
470 p->estimatedCost = (double)500;
471 p->estimatedRows = 500;
472 printf(" idxNum=%d\n", p->idxNum);
473 printf(" idxStr=NULL\n");
474 printf(" orderByConsumed=%d\n", p->orderByConsumed);
475 printf(" estimatedCost=%g\n", p->estimatedCost);
476 printf(" estimatedRows=%lld\n", p->estimatedRows);
477 return SQLITE_OK;
481 ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
482 ** the table.
484 ** This implementation does not actually make any changes to the table
485 ** content. It merely logs the fact that the method was invoked
487 static int vtablogUpdate(
488 sqlite3_vtab *tab,
489 int argc,
490 sqlite3_value **argv,
491 sqlite_int64 *pRowid
493 vtablog_vtab *pTab = (vtablog_vtab*)tab;
494 int i;
495 printf("%s.%s.xUpdate():\n", pTab->zDb, pTab->zName);
496 printf(" argc=%d\n", argc);
497 for(i=0; i<argc; i++){
498 printf(" argv[%d]=", i);
499 vtablogQuote(argv[i]);
500 printf("\n");
502 return SQLITE_OK;
505 static int vtablogBegin(sqlite3_vtab *tab){
506 vtablog_vtab *pTab = (vtablog_vtab*)tab;
507 printf("%s.%s.xBegin()\n", pTab->zDb, pTab->zName);
508 return SQLITE_OK;
510 static int vtablogSync(sqlite3_vtab *tab){
511 vtablog_vtab *pTab = (vtablog_vtab*)tab;
512 printf("%s.%s.xSync()\n", pTab->zDb, pTab->zName);
513 return SQLITE_OK;
515 static int vtablogCommit(sqlite3_vtab *tab){
516 vtablog_vtab *pTab = (vtablog_vtab*)tab;
517 printf("%s.%s.xCommit()\n", pTab->zDb, pTab->zName);
518 return SQLITE_OK;
520 static int vtablogRollback(sqlite3_vtab *tab){
521 vtablog_vtab *pTab = (vtablog_vtab*)tab;
522 printf("%s.%s.xRollback()\n", pTab->zDb, pTab->zName);
523 return SQLITE_OK;
525 static int vtablogSavepoint(sqlite3_vtab *tab, int N){
526 vtablog_vtab *pTab = (vtablog_vtab*)tab;
527 printf("%s.%s.xSavepoint(%d)\n", pTab->zDb, pTab->zName, N);
528 return SQLITE_OK;
530 static int vtablogRelease(sqlite3_vtab *tab, int N){
531 vtablog_vtab *pTab = (vtablog_vtab*)tab;
532 printf("%s.%s.xRelease(%d)\n", pTab->zDb, pTab->zName, N);
533 return SQLITE_OK;
535 static int vtablogRollbackTo(sqlite3_vtab *tab, int N){
536 vtablog_vtab *pTab = (vtablog_vtab*)tab;
537 printf("%s.%s.xRollbackTo(%d)\n", pTab->zDb, pTab->zName, N);
538 return SQLITE_OK;
541 static int vtablogFindMethod(
542 sqlite3_vtab *tab,
543 int nArg,
544 const char *zName,
545 void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
546 void **ppArg
548 vtablog_vtab *pTab = (vtablog_vtab*)tab;
549 printf("%s.%s.xFindMethod(nArg=%d, zName=%s)\n",
550 pTab->zDb, pTab->zName, nArg, zName);
551 return SQLITE_OK;
553 static int vtablogRename(sqlite3_vtab *tab, const char *zNew){
554 vtablog_vtab *pTab = (vtablog_vtab*)tab;
555 printf("%s.%s.xRename('%s')\n", pTab->zDb, pTab->zName, zNew);
556 sqlite3_free(pTab->zName);
557 pTab->zName = sqlite3_mprintf("%s", zNew);
558 return SQLITE_OK;
561 /* Any table name that contains the text "shadow" is seen as a
562 ** shadow table. Nothing else is.
564 static int vtablogShadowName(const char *zName){
565 printf("vtablog.xShadowName('%s')\n", zName);
566 return sqlite3_strglob("*shadow*", zName)==0;
569 static int vtablogIntegrity(
570 sqlite3_vtab *tab,
571 const char *zSchema,
572 const char *zTabName,
573 int mFlags,
574 char **pzErr
576 vtablog_vtab *pTab = (vtablog_vtab*)tab;
577 printf("%s.%s.xIntegrity(mFlags=0x%x)\n", pTab->zDb, pTab->zName, mFlags);
578 return 0;
582 ** This following structure defines all the methods for the
583 ** vtablog virtual table.
585 static sqlite3_module vtablogModule = {
586 4, /* iVersion */
587 vtablogCreate, /* xCreate */
588 vtablogConnect, /* xConnect */
589 vtablogBestIndex, /* xBestIndex */
590 vtablogDisconnect, /* xDisconnect */
591 vtablogDestroy, /* xDestroy */
592 vtablogOpen, /* xOpen - open a cursor */
593 vtablogClose, /* xClose - close a cursor */
594 vtablogFilter, /* xFilter - configure scan constraints */
595 vtablogNext, /* xNext - advance a cursor */
596 vtablogEof, /* xEof - check for end of scan */
597 vtablogColumn, /* xColumn - read data */
598 vtablogRowid, /* xRowid - read data */
599 vtablogUpdate, /* xUpdate */
600 vtablogBegin, /* xBegin */
601 vtablogSync, /* xSync */
602 vtablogCommit, /* xCommit */
603 vtablogRollback, /* xRollback */
604 vtablogFindMethod, /* xFindMethod */
605 vtablogRename, /* xRename */
606 vtablogSavepoint, /* xSavepoint */
607 vtablogRelease, /* xRelease */
608 vtablogRollbackTo, /* xRollbackTo */
609 vtablogShadowName, /* xShadowName */
610 vtablogIntegrity /* xIntegrity */
613 #ifdef _WIN32
614 __declspec(dllexport)
615 #endif
616 int sqlite3_vtablog_init(
617 sqlite3 *db,
618 char **pzErrMsg,
619 const sqlite3_api_routines *pApi
621 int rc;
622 SQLITE_EXTENSION_INIT2(pApi);
623 rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
624 return rc;