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 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
++;
64 /* Remove trailing whitespace from the end of string z[] */
65 static void vtablog_trim_whitespace(char *z
){
67 while( n
>0 && isspace((unsigned char)z
[n
]) ) n
--;
71 /* Dequote the string */
72 static void vtablog_dequote(char *z
){
77 if( cQuote
!='\'' && cQuote
!='"' ) return;
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
++;
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 */
110 zValue
= vtablog_parameter(zParam
,(int)strlen(zParam
),zArg
);
111 if( zValue
==0 ) return 0;
113 *pzErr
= sqlite3_mprintf("more than one '%s' parameter", zParam
);
116 *pzVal
= sqlite3_mprintf("%s", zValue
);
118 *pzErr
= sqlite3_mprintf("out of memory");
121 vtablog_trim_whitespace(*pzVal
);
122 vtablog_dequote(*pzVal
);
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)
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)
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(
165 int argc
, const char *const*argv
,
166 sqlite3_vtab
**ppVtab
,
170 static int nInst
= 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
);
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
) ){
194 if( vtablog_string_parameter(pzErr
, "rows", z
, &zNRow
) ){
200 *pzErr
= sqlite3_mprintf("no schema defined");
203 rc
= sqlite3_declare_vtab(db
, zSchema
);
205 pNew
= sqlite3_malloc( sizeof(*pNew
) );
206 *ppVtab
= (sqlite3_vtab
*)pNew
;
207 if( pNew
==0 ) return SQLITE_NOMEM
;
208 memset(pNew
, 0, sizeof(*pNew
));
210 if( zNRow
) pNew
->nRow
= atoi(zNRow
);
215 static int vtablogCreate(
218 int argc
, const char *const*argv
,
219 sqlite3_vtab
**ppVtab
,
222 return vtablogConnectCreate(db
,pAux
,argc
,argv
,ppVtab
,pzErr
,1);
224 static int vtablogConnect(
227 int argc
, const char *const*argv
,
228 sqlite3_vtab
**ppVtab
,
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
);
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
);
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
;
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
);
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);
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
;
308 sqlite3_snprintf(sizeof(zVal
),zVal
,"%c%d",
309 "abcdefghijklmnopqrstuvwyz"[i
], pCur
->iRowid
);
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
);
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
;
333 ** Return TRUE if the cursor has been moved off of the last
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
);
346 ** Output an sqlite3_value object's value as an SQL literal.
348 static void vtablogQuote(sqlite3_value
*p
){
350 switch( sqlite3_value_type(p
) ){
355 case SQLITE_INTEGER
: {
356 sqlite3_snprintf(50,z
,"%lld", sqlite3_value_int64(p
));
361 sqlite3_snprintf(50,z
,"%!.20g", sqlite3_value_double(p
));
366 int n
= sqlite3_value_bytes(p
);
367 const unsigned char *z
= (const unsigned char*)sqlite3_value_blob(p
);
370 for(i
=0; i
<n
; i
++) printf("%02x", z
[i
]);
375 const char *z
= (const char*)sqlite3_value_text(p
);
378 for(i
=0; (c
= z
[i
])!=0 && c
!='\''; i
++){}
384 for(i
=0; (c
= z
[i
])!=0 && c
!='\''; i
++){}
387 printf("%.*s", i
, z
);
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
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
);
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
431 static int vtablogBestIndex(
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;
443 ** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
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(
452 sqlite3_value
**argv
,
455 vtablog_vtab
*pTab
= (vtablog_vtab
*)tab
;
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
]);
468 ** This following structure defines all the methods for the
469 ** vtablog virtual table.
471 static sqlite3_module vtablogModule
= {
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 */
498 __declspec(dllexport
)
500 int sqlite3_vtablog_init(
503 const sqlite3_api_routines
*pApi
506 SQLITE_EXTENSION_INIT2(pApi
);
507 rc
= sqlite3_create_module(db
, "vtablog", &vtablogModule
, 0);