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 }