OLD | NEW |
1 /* | 1 /* |
2 ** 2006 June 13 | 2 ** 2006 June 13 |
3 ** | 3 ** |
4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
6 ** | 6 ** |
7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
10 ** | 10 ** |
11 ************************************************************************* | 11 ************************************************************************* |
12 ** Code for testing the virtual table interfaces. This code | 12 ** Code for testing the virtual table interfaces. This code |
13 ** is not included in the SQLite library. It is used for automated | 13 ** is not included in the SQLite library. It is used for automated |
14 ** testing of the SQLite library. | 14 ** testing of the SQLite library. |
15 ** | 15 ** |
16 ** The emphasis of this file is a virtual table that provides | 16 ** The emphasis of this file is a virtual table that provides |
17 ** access to TCL variables. | 17 ** access to TCL variables. |
18 */ | 18 */ |
19 #include "sqliteInt.h" | 19 #include "sqliteInt.h" |
20 #include "tcl.h" | 20 #include "tcl.h" |
21 #include <stdlib.h> | 21 #include <stdlib.h> |
22 #include <string.h> | 22 #include <string.h> |
23 | 23 |
24 #ifndef SQLITE_OMIT_VIRTUALTABLE | 24 #ifndef SQLITE_OMIT_VIRTUALTABLE |
25 | 25 |
| 26 /* |
| 27 ** Characters that make up the idxStr created by xBestIndex for xFilter. |
| 28 */ |
| 29 #define TCLVAR_NAME_EQ 'e' |
| 30 #define TCLVAR_NAME_MATCH 'm' |
| 31 #define TCLVAR_VALUE_GLOB 'g' |
| 32 #define TCLVAR_VALUE_REGEXP 'r' |
| 33 #define TCLVAR_VALUE_LIKE 'l' |
| 34 |
26 typedef struct tclvar_vtab tclvar_vtab; | 35 typedef struct tclvar_vtab tclvar_vtab; |
27 typedef struct tclvar_cursor tclvar_cursor; | 36 typedef struct tclvar_cursor tclvar_cursor; |
28 | 37 |
29 /* | 38 /* |
30 ** A tclvar virtual-table object | 39 ** A tclvar virtual-table object |
31 */ | 40 */ |
32 struct tclvar_vtab { | 41 struct tclvar_vtab { |
33 sqlite3_vtab base; | 42 sqlite3_vtab base; |
34 Tcl_Interp *interp; | 43 Tcl_Interp *interp; |
35 }; | 44 }; |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 return 0; | 157 return 0; |
149 } | 158 } |
150 | 159 |
151 static int tclvarFilter( | 160 static int tclvarFilter( |
152 sqlite3_vtab_cursor *pVtabCursor, | 161 sqlite3_vtab_cursor *pVtabCursor, |
153 int idxNum, const char *idxStr, | 162 int idxNum, const char *idxStr, |
154 int argc, sqlite3_value **argv | 163 int argc, sqlite3_value **argv |
155 ){ | 164 ){ |
156 tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor; | 165 tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor; |
157 Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp; | 166 Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp; |
| 167 Tcl_Obj *p = Tcl_NewStringObj("tclvar_filter_cmd", -1); |
158 | 168 |
159 Tcl_Obj *p = Tcl_NewStringObj("info vars", -1); | 169 const char *zEq = ""; |
| 170 const char *zMatch = ""; |
| 171 const char *zGlob = ""; |
| 172 const char *zRegexp = ""; |
| 173 const char *zLike = ""; |
| 174 int i; |
| 175 |
| 176 for(i=0; idxStr[i]; i++){ |
| 177 switch( idxStr[i] ){ |
| 178 case TCLVAR_NAME_EQ: |
| 179 zEq = (const char*)sqlite3_value_text(argv[i]); |
| 180 break; |
| 181 case TCLVAR_NAME_MATCH: |
| 182 zMatch = (const char*)sqlite3_value_text(argv[i]); |
| 183 break; |
| 184 case TCLVAR_VALUE_GLOB: |
| 185 zGlob = (const char*)sqlite3_value_text(argv[i]); |
| 186 break; |
| 187 case TCLVAR_VALUE_REGEXP: |
| 188 zRegexp = (const char*)sqlite3_value_text(argv[i]); |
| 189 break; |
| 190 case TCLVAR_VALUE_LIKE: |
| 191 zLike = (const char*)sqlite3_value_text(argv[i]); |
| 192 break; |
| 193 default: |
| 194 assert( 0 ); |
| 195 } |
| 196 } |
| 197 |
160 Tcl_IncrRefCount(p); | 198 Tcl_IncrRefCount(p); |
| 199 Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zEq, -1)); |
| 200 Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zMatch, -1)); |
| 201 Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zGlob, -1)); |
| 202 Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zRegexp, -1)); |
| 203 Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zLike, -1)); |
161 | 204 |
162 assert( argc==0 || argc==1 ); | |
163 if( argc==1 ){ | |
164 Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1); | |
165 Tcl_ListObjAppendElement(0, p, pArg); | |
166 } | |
167 Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL); | 205 Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL); |
168 if( pCur->pList1 ){ | 206 if( pCur->pList1 ){ |
169 Tcl_DecrRefCount(pCur->pList1); | 207 Tcl_DecrRefCount(pCur->pList1); |
170 } | 208 } |
171 if( pCur->pList2 ){ | 209 if( pCur->pList2 ){ |
172 Tcl_DecrRefCount(pCur->pList2); | 210 Tcl_DecrRefCount(pCur->pList2); |
173 pCur->pList2 = 0; | 211 pCur->pList2 = 0; |
174 } | 212 } |
175 pCur->i1 = 0; | 213 pCur->i1 = 0; |
176 pCur->i2 = 0; | 214 pCur->i2 = 0; |
177 pCur->pList1 = Tcl_GetObjResult(interp); | 215 pCur->pList1 = Tcl_GetObjResult(interp); |
178 Tcl_IncrRefCount(pCur->pList1); | 216 Tcl_IncrRefCount(pCur->pList1); |
179 assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 ); | |
180 | 217 |
181 Tcl_DecrRefCount(p); | 218 Tcl_DecrRefCount(p); |
182 return tclvarNext(pVtabCursor); | 219 return tclvarNext(pVtabCursor); |
183 } | 220 } |
184 | 221 |
185 static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ | 222 static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ |
186 Tcl_Obj *p1; | 223 Tcl_Obj *p1; |
187 Tcl_Obj *p2; | 224 Tcl_Obj *p2; |
188 const char *z1; | 225 const char *z1; |
189 const char *z2 = ""; | 226 const char *z2 = ""; |
(...skipping 27 matching lines...) Expand all Loading... |
217 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | 254 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
218 *pRowid = 0; | 255 *pRowid = 0; |
219 return SQLITE_OK; | 256 return SQLITE_OK; |
220 } | 257 } |
221 | 258 |
222 static int tclvarEof(sqlite3_vtab_cursor *cur){ | 259 static int tclvarEof(sqlite3_vtab_cursor *cur){ |
223 tclvar_cursor *pCur = (tclvar_cursor*)cur; | 260 tclvar_cursor *pCur = (tclvar_cursor*)cur; |
224 return (pCur->pList2?0:1); | 261 return (pCur->pList2?0:1); |
225 } | 262 } |
226 | 263 |
| 264 /* |
| 265 ** If nul-terminated string zStr does not already contain the character |
| 266 ** passed as the second argument, append it and return 0. Or, if there is |
| 267 ** already an instance of x in zStr, do nothing return 1; |
| 268 ** |
| 269 ** There is guaranteed to be enough room in the buffer pointed to by zStr |
| 270 ** for the new character and nul-terminator. |
| 271 */ |
| 272 static int tclvarAddToIdxstr(char *zStr, char x){ |
| 273 int i; |
| 274 for(i=0; zStr[i]; i++){ |
| 275 if( zStr[i]==x ) return 1; |
| 276 } |
| 277 zStr[i] = x; |
| 278 zStr[i+1] = '\0'; |
| 279 return 0; |
| 280 } |
| 281 |
| 282 /* |
| 283 ** Return true if variable $::tclvar_set_omit exists and is set to true. |
| 284 ** False otherwise. |
| 285 */ |
| 286 static int tclvarSetOmit(Tcl_Interp *interp){ |
| 287 int rc; |
| 288 int res = 0; |
| 289 Tcl_Obj *pRes; |
| 290 rc = Tcl_Eval(interp, |
| 291 "expr {[info exists ::tclvar_set_omit] && $::tclvar_set_omit}" |
| 292 ); |
| 293 if( rc==TCL_OK ){ |
| 294 pRes = Tcl_GetObjResult(interp); |
| 295 rc = Tcl_GetBooleanFromObj(0, pRes, &res); |
| 296 } |
| 297 return (rc==TCL_OK && res); |
| 298 } |
| 299 |
| 300 /* |
| 301 ** The xBestIndex() method. This virtual table supports the following |
| 302 ** operators: |
| 303 ** |
| 304 ** name = ? (omit flag clear) |
| 305 ** name MATCH ? (omit flag set) |
| 306 ** value GLOB ? (omit flag set iff $::tclvar_set_omit) |
| 307 ** value REGEXP ? (omit flag set iff $::tclvar_set_omit) |
| 308 ** value LIKE ? (omit flag set iff $::tclvar_set_omit) |
| 309 ** |
| 310 ** For each constraint present, the corresponding TCLVAR_XXX character is |
| 311 ** appended to the idxStr value. |
| 312 */ |
227 static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | 313 static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
| 314 tclvar_vtab *pTab = (tclvar_vtab*)tab; |
228 int ii; | 315 int ii; |
| 316 char *zStr = sqlite3_malloc(32); |
| 317 int iStr = 0; |
| 318 |
| 319 if( zStr==0 ) return SQLITE_NOMEM; |
| 320 zStr[0] = '\0'; |
229 | 321 |
230 for(ii=0; ii<pIdxInfo->nConstraint; ii++){ | 322 for(ii=0; ii<pIdxInfo->nConstraint; ii++){ |
231 struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; | 323 struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; |
232 if( pCons->iColumn==0 && pCons->usable | 324 struct sqlite3_index_constraint_usage *pUsage; |
233 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ | 325 |
234 struct sqlite3_index_constraint_usage *pUsage; | 326 pUsage = &pIdxInfo->aConstraintUsage[ii]; |
235 pUsage = &pIdxInfo->aConstraintUsage[ii]; | 327 if( pCons->usable ){ |
236 pUsage->omit = 0; | 328 /* name = ? */ |
237 pUsage->argvIndex = 1; | 329 if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==0 ){ |
238 return SQLITE_OK; | 330 if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_EQ) ){ |
| 331 pUsage->argvIndex = ++iStr; |
| 332 pUsage->omit = 0; |
| 333 } |
| 334 } |
| 335 |
| 336 /* name MATCH ? */ |
| 337 if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH && pCons->iColumn==0 ){ |
| 338 if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_MATCH) ){ |
| 339 pUsage->argvIndex = ++iStr; |
| 340 pUsage->omit = 1; |
| 341 } |
| 342 } |
| 343 |
| 344 /* value GLOB ? */ |
| 345 if( pCons->op==SQLITE_INDEX_CONSTRAINT_GLOB && pCons->iColumn==2 ){ |
| 346 if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_GLOB) ){ |
| 347 pUsage->argvIndex = ++iStr; |
| 348 pUsage->omit = tclvarSetOmit(pTab->interp); |
| 349 } |
| 350 } |
| 351 |
| 352 /* value REGEXP ? */ |
| 353 if( pCons->op==SQLITE_INDEX_CONSTRAINT_REGEXP && pCons->iColumn==2 ){ |
| 354 if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_REGEXP) ){ |
| 355 pUsage->argvIndex = ++iStr; |
| 356 pUsage->omit = tclvarSetOmit(pTab->interp); |
| 357 } |
| 358 } |
| 359 |
| 360 /* value LIKE ? */ |
| 361 if( pCons->op==SQLITE_INDEX_CONSTRAINT_LIKE && pCons->iColumn==2 ){ |
| 362 if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_LIKE) ){ |
| 363 pUsage->argvIndex = ++iStr; |
| 364 pUsage->omit = tclvarSetOmit(pTab->interp); |
| 365 } |
| 366 } |
239 } | 367 } |
240 } | 368 } |
241 | 369 pIdxInfo->idxStr = zStr; |
242 for(ii=0; ii<pIdxInfo->nConstraint; ii++){ | 370 pIdxInfo->needToFreeIdxStr = 1; |
243 struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; | |
244 if( pCons->iColumn==0 && pCons->usable | |
245 && pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ | |
246 struct sqlite3_index_constraint_usage *pUsage; | |
247 pUsage = &pIdxInfo->aConstraintUsage[ii]; | |
248 pUsage->omit = 1; | |
249 pUsage->argvIndex = 1; | |
250 return SQLITE_OK; | |
251 } | |
252 } | |
253 | 371 |
254 return SQLITE_OK; | 372 return SQLITE_OK; |
255 } | 373 } |
256 | 374 |
257 /* | 375 /* |
258 ** A virtual table module that provides read-only access to a | 376 ** A virtual table module that provides read-only access to a |
259 ** Tcl global variable namespace. | 377 ** Tcl global variable namespace. |
260 */ | 378 */ |
261 static sqlite3_module tclvarModule = { | 379 static sqlite3_module tclvarModule = { |
262 0, /* iVersion */ | 380 0, /* iVersion */ |
(...skipping 25 matching lines...) Expand all Loading... |
288 | 406 |
289 /* | 407 /* |
290 ** Register the echo virtual table module. | 408 ** Register the echo virtual table module. |
291 */ | 409 */ |
292 static int register_tclvar_module( | 410 static int register_tclvar_module( |
293 ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ | 411 ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ |
294 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | 412 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
295 int objc, /* Number of arguments */ | 413 int objc, /* Number of arguments */ |
296 Tcl_Obj *CONST objv[] /* Command arguments */ | 414 Tcl_Obj *CONST objv[] /* Command arguments */ |
297 ){ | 415 ){ |
| 416 int rc = TCL_OK; |
298 sqlite3 *db; | 417 sqlite3 *db; |
299 if( objc!=2 ){ | 418 if( objc!=2 ){ |
300 Tcl_WrongNumArgs(interp, 1, objv, "DB"); | 419 Tcl_WrongNumArgs(interp, 1, objv, "DB"); |
301 return TCL_ERROR; | 420 return TCL_ERROR; |
302 } | 421 } |
303 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | 422 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
304 #ifndef SQLITE_OMIT_VIRTUALTABLE | 423 #ifndef SQLITE_OMIT_VIRTUALTABLE |
305 sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp); | 424 sqlite3_create_module(db, "tclvar", &tclvarModule, (void*)interp); |
| 425 rc = Tcl_Eval(interp, |
| 426 "proc like {pattern str} {\n" |
| 427 " set p [string map {% * _ ?} $pattern]\n" |
| 428 " string match $p $str\n" |
| 429 "}\n" |
| 430 "proc tclvar_filter_cmd {eq match glob regexp like} {\n" |
| 431 " set res {}\n" |
| 432 " set pattern $eq\n" |
| 433 " if {$pattern=={}} { set pattern $match }\n" |
| 434 " if {$pattern=={}} { set pattern * }\n" |
| 435 " foreach v [uplevel #0 info vars $pattern] {\n" |
| 436 " if {($glob=={} || [string match $glob [uplevel #0 set $v]])\n" |
| 437 " && ($like=={} || [like $like [uplevel #0 set $v]])\n" |
| 438 " && ($regexp=={} || [regexp $regexp [uplevel #0 set $v]])\n" |
| 439 " } {\n" |
| 440 " lappend res $v\n" |
| 441 " }\n" |
| 442 " }\n" |
| 443 " set res\n" |
| 444 "}\n" |
| 445 ); |
306 #endif | 446 #endif |
307 return TCL_OK; | 447 return rc; |
308 } | 448 } |
309 | 449 |
310 #endif | 450 #endif |
311 | 451 |
312 | 452 |
313 /* | 453 /* |
314 ** Register commands with the TCL interpreter. | 454 ** Register commands with the TCL interpreter. |
315 */ | 455 */ |
316 int Sqlitetesttclvar_Init(Tcl_Interp *interp){ | 456 int Sqlitetesttclvar_Init(Tcl_Interp *interp){ |
317 #ifndef SQLITE_OMIT_VIRTUALTABLE | 457 #ifndef SQLITE_OMIT_VIRTUALTABLE |
318 static struct { | 458 static struct { |
319 char *zName; | 459 char *zName; |
320 Tcl_ObjCmdProc *xProc; | 460 Tcl_ObjCmdProc *xProc; |
321 void *clientData; | 461 void *clientData; |
322 } aObjCmd[] = { | 462 } aObjCmd[] = { |
323 { "register_tclvar_module", register_tclvar_module, 0 }, | 463 { "register_tclvar_module", register_tclvar_module, 0 }, |
324 }; | 464 }; |
325 int i; | 465 int i; |
326 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ | 466 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ |
327 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, | 467 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, |
328 aObjCmd[i].xProc, aObjCmd[i].clientData, 0); | 468 aObjCmd[i].xProc, aObjCmd[i].clientData, 0); |
329 } | 469 } |
330 #endif | 470 #endif |
331 return TCL_OK; | 471 return TCL_OK; |
332 } | 472 } |
OLD | NEW |