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