4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
20 ** CREATE VIRTUAL TABLE temp.log USING vtablog(
21 ** schema='CREATE TABLE x(a,b,c)',
26 #include "sqlite3ext.h"
27 SQLITE_EXTENSION_INIT1
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
;
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
++;
65 /* Remove trailing whitespace from the end of string z[] */
66 static void vtablog_trim_whitespace(char *z
){
68 while( n
>0 && isspace((unsigned char)z
[n
]) ) n
--;
72 /* Dequote the string */
73 static void vtablog_dequote(char *z
){
78 if( cQuote
!='\'' && cQuote
!='"' ) return;
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
++;
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 */
111 zValue
= vtablog_parameter(zParam
,(int)strlen(zParam
),zArg
);
112 if( zValue
==0 ) return 0;
114 *pzErr
= sqlite3_mprintf("more than one '%s' parameter", zParam
);
117 *pzVal
= sqlite3_mprintf("%s", zValue
);
119 *pzErr
= sqlite3_mprintf("out of memory");
122 vtablog_trim_whitespace(*pzVal
);
123 vtablog_dequote(*pzVal
);
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)
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)
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(
166 int argc
, const char *const*argv
,
167 sqlite3_vtab
**ppVtab
,
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
);
183 printf("[%s]\n", argv
[i
]);
189 for(i
=3; i
<argc
; i
++){
190 const char *z
= argv
[i
];
191 if( vtablog_string_parameter(pzErr
, "schema", z
, &zSchema
) ){
193 goto vtablog_end_connect
;
195 if( vtablog_string_parameter(pzErr
, "rows", z
, &zNRow
) ){
197 goto vtablog_end_connect
;
201 zSchema
= sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
203 printf(" schema = '%s'\n", zSchema
);
204 rc
= sqlite3_declare_vtab(db
, zSchema
);
206 pNew
= sqlite3_malloc( sizeof(*pNew
) );
207 *ppVtab
= (sqlite3_vtab
*)pNew
;
208 if( pNew
==0 ) return SQLITE_NOMEM
;
209 memset(pNew
, 0, sizeof(*pNew
));
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]);
218 sqlite3_free(zSchema
);
222 static int vtablogCreate(
225 int argc
, const char *const*argv
,
226 sqlite3_vtab
**ppVtab
,
229 return vtablogConnectCreate(db
,pAux
,argc
,argv
,ppVtab
,pzErr
,1);
231 static int vtablogConnect(
234 int argc
, const char *const*argv
,
235 sqlite3_vtab
**ppVtab
,
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
);
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
);
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
,
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
;
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
);
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);
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
;
321 sqlite3_snprintf(sizeof(zVal
),zVal
,"%c%d",
322 "abcdefghijklmnopqrstuvwyz"[i
], pCur
->iRowid
);
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
);
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
;
346 ** Return TRUE if the cursor has been moved off of the last
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
);
359 ** Output an sqlite3_value object's value as an SQL literal.
361 static void vtablogQuote(sqlite3_value
*p
){
363 switch( sqlite3_value_type(p
) ){
368 case SQLITE_INTEGER
: {
369 sqlite3_snprintf(50,z
,"%lld", sqlite3_value_int64(p
));
374 sqlite3_snprintf(50,z
,"%!.20g", sqlite3_value_double(p
));
379 int n
= sqlite3_value_bytes(p
);
380 const unsigned char *z
= (const unsigned char*)sqlite3_value_blob(p
);
383 for(i
=0; i
<n
; i
++) printf("%02x", z
[i
]);
388 const char *z
= (const char*)sqlite3_value_text(p
);
391 for(i
=0; (c
= z
[i
])!=0 && c
!='\''; i
++){}
397 for(i
=0; (c
= z
[i
])!=0 && c
!='\''; i
++){}
400 printf("%.*s", i
, z
);
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
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
);
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
444 static int vtablogBestIndex(
446 sqlite3_index_info
*p
448 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
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
++){
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
);
481 ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
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(
490 sqlite3_value
**argv
,
493 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
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
]);
505 static int vtablogBegin(sqlite3_vtab
*tab
){
506 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
507 printf("%s.%s.xBegin()\n", pTab
->zDb
, pTab
->zName
);
510 static int vtablogSync(sqlite3_vtab
*tab
){
511 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
512 printf("%s.%s.xSync()\n", pTab
->zDb
, pTab
->zName
);
515 static int vtablogCommit(sqlite3_vtab
*tab
){
516 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
517 printf("%s.%s.xCommit()\n", pTab
->zDb
, pTab
->zName
);
520 static int vtablogRollback(sqlite3_vtab
*tab
){
521 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
522 printf("%s.%s.xRollback()\n", pTab
->zDb
, pTab
->zName
);
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
);
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
);
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
);
541 static int vtablogFindMethod(
545 void (**pxFunc
)(sqlite3_context
*,int,sqlite3_value
**),
548 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
549 printf("%s.%s.xFindMethod(nArg=%d, zName=%s)\n",
550 pTab
->zDb
, pTab
->zName
, nArg
, zName
);
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
);
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(
572 const char *zTabName
,
576 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
577 printf("%s.%s.xIntegrity(mFlags=0x%x)\n", pTab
->zDb
, pTab
->zName
, mFlags
);
582 ** This following structure defines all the methods for the
583 ** vtablog virtual table.
585 static sqlite3_module vtablogModule
= {
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 */
614 __declspec(dllexport
)
616 int sqlite3_vtablog_init(
619 const sqlite3_api_routines
*pApi
622 SQLITE_EXTENSION_INIT2(pApi
);
623 rc
= sqlite3_create_module(db
, "vtablog", &vtablogModule
, 0);