OLD | NEW |
| (Empty) |
1 /* | |
2 ** 2006 June 13 | |
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 ** Code for testing the virtual table interfaces. This code | |
13 ** is not included in the SQLite library. It is used for automated | |
14 ** testing of the SQLite library. | |
15 ** | |
16 ** The emphasis of this file is a virtual table that provides | |
17 ** access to TCL variables. | |
18 */ | |
19 #include "sqliteInt.h" | |
20 #include "tcl.h" | |
21 #include <stdlib.h> | |
22 #include <string.h> | |
23 | |
24 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
25 | |
26 typedef struct tclvar_vtab tclvar_vtab; | |
27 typedef struct tclvar_cursor tclvar_cursor; | |
28 | |
29 /* | |
30 ** A tclvar virtual-table object | |
31 */ | |
32 struct tclvar_vtab { | |
33 sqlite3_vtab base; | |
34 Tcl_Interp *interp; | |
35 }; | |
36 | |
37 /* A tclvar cursor object */ | |
38 struct tclvar_cursor { | |
39 sqlite3_vtab_cursor base; | |
40 | |
41 Tcl_Obj *pList1; /* Result of [info vars ?pattern?] */ | |
42 Tcl_Obj *pList2; /* Result of [array names [lindex $pList1 $i1]] */ | |
43 int i1; /* Current item in pList1 */ | |
44 int i2; /* Current item (if any) in pList2 */ | |
45 }; | |
46 | |
47 /* Methods for the tclvar module */ | |
48 static int tclvarConnect( | |
49 sqlite3 *db, | |
50 void *pAux, | |
51 int argc, const char *const*argv, | |
52 sqlite3_vtab **ppVtab, | |
53 char **pzErr | |
54 ){ | |
55 tclvar_vtab *pVtab; | |
56 static const char zSchema[] = | |
57 "CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)"; | |
58 pVtab = sqlite3MallocZero( sizeof(*pVtab) ); | |
59 if( pVtab==0 ) return SQLITE_NOMEM; | |
60 *ppVtab = &pVtab->base; | |
61 pVtab->interp = (Tcl_Interp *)pAux; | |
62 sqlite3_declare_vtab(db, zSchema); | |
63 return SQLITE_OK; | |
64 } | |
65 /* Note that for this virtual table, the xCreate and xConnect | |
66 ** methods are identical. */ | |
67 | |
68 static int tclvarDisconnect(sqlite3_vtab *pVtab){ | |
69 sqlite3_free(pVtab); | |
70 return SQLITE_OK; | |
71 } | |
72 /* The xDisconnect and xDestroy methods are also the same */ | |
73 | |
74 /* | |
75 ** Open a new tclvar cursor. | |
76 */ | |
77 static int tclvarOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ | |
78 tclvar_cursor *pCur; | |
79 pCur = sqlite3MallocZero(sizeof(tclvar_cursor)); | |
80 *ppCursor = &pCur->base; | |
81 return SQLITE_OK; | |
82 } | |
83 | |
84 /* | |
85 ** Close a tclvar cursor. | |
86 */ | |
87 static int tclvarClose(sqlite3_vtab_cursor *cur){ | |
88 tclvar_cursor *pCur = (tclvar_cursor *)cur; | |
89 if( pCur->pList1 ){ | |
90 Tcl_DecrRefCount(pCur->pList1); | |
91 } | |
92 if( pCur->pList2 ){ | |
93 Tcl_DecrRefCount(pCur->pList2); | |
94 } | |
95 sqlite3_free(pCur); | |
96 return SQLITE_OK; | |
97 } | |
98 | |
99 /* | |
100 ** Returns 1 if data is ready, or 0 if not. | |
101 */ | |
102 static int next2(Tcl_Interp *interp, tclvar_cursor *pCur, Tcl_Obj *pObj){ | |
103 Tcl_Obj *p; | |
104 | |
105 if( pObj ){ | |
106 if( !pCur->pList2 ){ | |
107 p = Tcl_NewStringObj("array names", -1); | |
108 Tcl_IncrRefCount(p); | |
109 Tcl_ListObjAppendElement(0, p, pObj); | |
110 Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL); | |
111 Tcl_DecrRefCount(p); | |
112 pCur->pList2 = Tcl_GetObjResult(interp); | |
113 Tcl_IncrRefCount(pCur->pList2); | |
114 assert( pCur->i2==0 ); | |
115 }else{ | |
116 int n = 0; | |
117 pCur->i2++; | |
118 Tcl_ListObjLength(0, pCur->pList2, &n); | |
119 if( pCur->i2>=n ){ | |
120 Tcl_DecrRefCount(pCur->pList2); | |
121 pCur->pList2 = 0; | |
122 pCur->i2 = 0; | |
123 return 0; | |
124 } | |
125 } | |
126 } | |
127 | |
128 return 1; | |
129 } | |
130 | |
131 static int tclvarNext(sqlite3_vtab_cursor *cur){ | |
132 Tcl_Obj *pObj; | |
133 int n = 0; | |
134 int ok = 0; | |
135 | |
136 tclvar_cursor *pCur = (tclvar_cursor *)cur; | |
137 Tcl_Interp *interp = ((tclvar_vtab *)(cur->pVtab))->interp; | |
138 | |
139 Tcl_ListObjLength(0, pCur->pList1, &n); | |
140 while( !ok && pCur->i1<n ){ | |
141 Tcl_ListObjIndex(0, pCur->pList1, pCur->i1, &pObj); | |
142 ok = next2(interp, pCur, pObj); | |
143 if( !ok ){ | |
144 pCur->i1++; | |
145 } | |
146 } | |
147 | |
148 return 0; | |
149 } | |
150 | |
151 static int tclvarFilter( | |
152 sqlite3_vtab_cursor *pVtabCursor, | |
153 int idxNum, const char *idxStr, | |
154 int argc, sqlite3_value **argv | |
155 ){ | |
156 tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor; | |
157 Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp; | |
158 | |
159 Tcl_Obj *p = Tcl_NewStringObj("info vars", -1); | |
160 Tcl_IncrRefCount(p); | |
161 | |
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); | |
168 if( pCur->pList1 ){ | |
169 Tcl_DecrRefCount(pCur->pList1); | |
170 } | |
171 if( pCur->pList2 ){ | |
172 Tcl_DecrRefCount(pCur->pList2); | |
173 pCur->pList2 = 0; | |
174 } | |
175 pCur->i1 = 0; | |
176 pCur->i2 = 0; | |
177 pCur->pList1 = Tcl_GetObjResult(interp); | |
178 Tcl_IncrRefCount(pCur->pList1); | |
179 assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 ); | |
180 | |
181 Tcl_DecrRefCount(p); | |
182 return tclvarNext(pVtabCursor); | |
183 } | |
184 | |
185 static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ | |
186 Tcl_Obj *p1; | |
187 Tcl_Obj *p2; | |
188 const char *z1; | |
189 const char *z2 = ""; | |
190 tclvar_cursor *pCur = (tclvar_cursor*)cur; | |
191 Tcl_Interp *interp = ((tclvar_vtab *)cur->pVtab)->interp; | |
192 | |
193 Tcl_ListObjIndex(interp, pCur->pList1, pCur->i1, &p1); | |
194 Tcl_ListObjIndex(interp, pCur->pList2, pCur->i2, &p2); | |
195 z1 = Tcl_GetString(p1); | |
196 if( p2 ){ | |
197 z2 = Tcl_GetString(p2); | |
198 } | |
199 switch (i) { | |
200 case 0: { | |
201 sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT); | |
202 break; | |
203 } | |
204 case 1: { | |
205 sqlite3_result_text(ctx, z2, -1, SQLITE_TRANSIENT); | |
206 break; | |
207 } | |
208 case 2: { | |
209 Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY); | |
210 sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT); | |
211 break; | |
212 } | |
213 } | |
214 return SQLITE_OK; | |
215 } | |
216 | |
217 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | |
218 *pRowid = 0; | |
219 return SQLITE_OK; | |
220 } | |
221 | |
222 static int tclvarEof(sqlite3_vtab_cursor *cur){ | |
223 tclvar_cursor *pCur = (tclvar_cursor*)cur; | |
224 return (pCur->pList2?0:1); | |
225 } | |
226 | |
227 static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | |
228 int ii; | |
229 | |
230 for(ii=0; ii<pIdxInfo->nConstraint; ii++){ | |
231 struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; | |
232 if( pCons->iColumn==0 && pCons->usable | |
233 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ | |
234 struct sqlite3_index_constraint_usage *pUsage; | |
235 pUsage = &pIdxInfo->aConstraintUsage[ii]; | |
236 pUsage->omit = 0; | |
237 pUsage->argvIndex = 1; | |
238 return SQLITE_OK; | |
239 } | |
240 } | |
241 | |
242 for(ii=0; ii<pIdxInfo->nConstraint; ii++){ | |
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 | |
254 return SQLITE_OK; | |
255 } | |
256 | |
257 /* | |
258 ** A virtual table module that provides read-only access to a | |
259 ** Tcl global variable namespace. | |
260 */ | |
261 static sqlite3_module tclvarModule = { | |
262 0, /* iVersion */ | |
263 tclvarConnect, | |
264 tclvarConnect, | |
265 tclvarBestIndex, | |
266 tclvarDisconnect, | |
267 tclvarDisconnect, | |
268 tclvarOpen, /* xOpen - open a cursor */ | |
269 tclvarClose, /* xClose - close a cursor */ | |
270 tclvarFilter, /* xFilter - configure scan constraints */ | |
271 tclvarNext, /* xNext - advance a cursor */ | |
272 tclvarEof, /* xEof - check for end of scan */ | |
273 tclvarColumn, /* xColumn - read data */ | |
274 tclvarRowid, /* xRowid - read data */ | |
275 0, /* xUpdate */ | |
276 0, /* xBegin */ | |
277 0, /* xSync */ | |
278 0, /* xCommit */ | |
279 0, /* xRollback */ | |
280 0, /* xFindMethod */ | |
281 0, /* xRename */ | |
282 }; | |
283 | |
284 /* | |
285 ** Decode a pointer to an sqlite3 object. | |
286 */ | |
287 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); | |
288 | |
289 /* | |
290 ** Register the echo virtual table module. | |
291 */ | |
292 static int register_tclvar_module( | |
293 ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ | |
294 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | |
295 int objc, /* Number of arguments */ | |
296 Tcl_Obj *CONST objv[] /* Command arguments */ | |
297 ){ | |
298 sqlite3 *db; | |
299 if( objc!=2 ){ | |
300 Tcl_WrongNumArgs(interp, 1, objv, "DB"); | |
301 return TCL_ERROR; | |
302 } | |
303 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | |
304 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
305 sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp); | |
306 #endif | |
307 return TCL_OK; | |
308 } | |
309 | |
310 #endif | |
311 | |
312 | |
313 /* | |
314 ** Register commands with the TCL interpreter. | |
315 */ | |
316 int Sqlitetesttclvar_Init(Tcl_Interp *interp){ | |
317 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
318 static struct { | |
319 char *zName; | |
320 Tcl_ObjCmdProc *xProc; | |
321 void *clientData; | |
322 } aObjCmd[] = { | |
323 { "register_tclvar_module", register_tclvar_module, 0 }, | |
324 }; | |
325 int i; | |
326 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ | |
327 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, | |
328 aObjCmd[i].xProc, aObjCmd[i].clientData, 0); | |
329 } | |
330 #endif | |
331 return TCL_OK; | |
332 } | |
OLD | NEW |