OLD | NEW |
| (Empty) |
1 /* | |
2 ** 2006 June 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 ** 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 | |
17 /* The code in this file defines a sqlite3 virtual-table module that | |
18 ** provides a read-only view of the current database schema. There is one | |
19 ** row in the schema table for each column in the database schema. | |
20 */ | |
21 #define SCHEMA \ | |
22 "CREATE TABLE x(" \ | |
23 "database," /* Name of database (i.e. main, temp etc.) */ \ | |
24 "tablename," /* Name of table */ \ | |
25 "cid," /* Column number (from left-to-right, 0 upward) */ \ | |
26 "name," /* Column name */ \ | |
27 "type," /* Specified type (i.e. VARCHAR(32)) */ \ | |
28 "not_null," /* Boolean. True if NOT NULL was specified */ \ | |
29 "dflt_value," /* Default value for this column */ \ | |
30 "pk" /* True if this column is part of the primary key */ \ | |
31 ")" | |
32 | |
33 /* If SQLITE_TEST is defined this code is preprocessed for use as part | |
34 ** of the sqlite test binary "testfixture". Otherwise it is preprocessed | |
35 ** to be compiled into an sqlite dynamic extension. | |
36 */ | |
37 #ifdef SQLITE_TEST | |
38 #include "sqliteInt.h" | |
39 #include "tcl.h" | |
40 #else | |
41 #include "sqlite3ext.h" | |
42 SQLITE_EXTENSION_INIT1 | |
43 #endif | |
44 | |
45 #include <stdlib.h> | |
46 #include <string.h> | |
47 #include <assert.h> | |
48 | |
49 typedef struct schema_vtab schema_vtab; | |
50 typedef struct schema_cursor schema_cursor; | |
51 | |
52 /* A schema table object */ | |
53 struct schema_vtab { | |
54 sqlite3_vtab base; | |
55 sqlite3 *db; | |
56 }; | |
57 | |
58 /* A schema table cursor object */ | |
59 struct schema_cursor { | |
60 sqlite3_vtab_cursor base; | |
61 sqlite3_stmt *pDbList; | |
62 sqlite3_stmt *pTableList; | |
63 sqlite3_stmt *pColumnList; | |
64 int rowid; | |
65 }; | |
66 | |
67 /* | |
68 ** None of this works unless we have virtual tables. | |
69 */ | |
70 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
71 | |
72 /* | |
73 ** Table destructor for the schema module. | |
74 */ | |
75 static int schemaDestroy(sqlite3_vtab *pVtab){ | |
76 sqlite3_free(pVtab); | |
77 return 0; | |
78 } | |
79 | |
80 /* | |
81 ** Table constructor for the schema module. | |
82 */ | |
83 static int schemaCreate( | |
84 sqlite3 *db, | |
85 void *pAux, | |
86 int argc, const char *const*argv, | |
87 sqlite3_vtab **ppVtab, | |
88 char **pzErr | |
89 ){ | |
90 int rc = SQLITE_NOMEM; | |
91 schema_vtab *pVtab = sqlite3_malloc(sizeof(schema_vtab)); | |
92 if( pVtab ){ | |
93 memset(pVtab, 0, sizeof(schema_vtab)); | |
94 pVtab->db = db; | |
95 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
96 rc = sqlite3_declare_vtab(db, SCHEMA); | |
97 #endif | |
98 } | |
99 *ppVtab = (sqlite3_vtab *)pVtab; | |
100 return rc; | |
101 } | |
102 | |
103 /* | |
104 ** Open a new cursor on the schema table. | |
105 */ | |
106 static int schemaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ | |
107 int rc = SQLITE_NOMEM; | |
108 schema_cursor *pCur; | |
109 pCur = sqlite3_malloc(sizeof(schema_cursor)); | |
110 if( pCur ){ | |
111 memset(pCur, 0, sizeof(schema_cursor)); | |
112 *ppCursor = (sqlite3_vtab_cursor *)pCur; | |
113 rc = SQLITE_OK; | |
114 } | |
115 return rc; | |
116 } | |
117 | |
118 /* | |
119 ** Close a schema table cursor. | |
120 */ | |
121 static int schemaClose(sqlite3_vtab_cursor *cur){ | |
122 schema_cursor *pCur = (schema_cursor *)cur; | |
123 sqlite3_finalize(pCur->pDbList); | |
124 sqlite3_finalize(pCur->pTableList); | |
125 sqlite3_finalize(pCur->pColumnList); | |
126 sqlite3_free(pCur); | |
127 return SQLITE_OK; | |
128 } | |
129 | |
130 /* | |
131 ** Retrieve a column of data. | |
132 */ | |
133 static int schemaColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ | |
134 schema_cursor *pCur = (schema_cursor *)cur; | |
135 switch( i ){ | |
136 case 0: | |
137 sqlite3_result_value(ctx, sqlite3_column_value(pCur->pDbList, 1)); | |
138 break; | |
139 case 1: | |
140 sqlite3_result_value(ctx, sqlite3_column_value(pCur->pTableList, 0)); | |
141 break; | |
142 default: | |
143 sqlite3_result_value(ctx, sqlite3_column_value(pCur->pColumnList, i-2)); | |
144 break; | |
145 } | |
146 return SQLITE_OK; | |
147 } | |
148 | |
149 /* | |
150 ** Retrieve the current rowid. | |
151 */ | |
152 static int schemaRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | |
153 schema_cursor *pCur = (schema_cursor *)cur; | |
154 *pRowid = pCur->rowid; | |
155 return SQLITE_OK; | |
156 } | |
157 | |
158 static int finalize(sqlite3_stmt **ppStmt){ | |
159 int rc = sqlite3_finalize(*ppStmt); | |
160 *ppStmt = 0; | |
161 return rc; | |
162 } | |
163 | |
164 static int schemaEof(sqlite3_vtab_cursor *cur){ | |
165 schema_cursor *pCur = (schema_cursor *)cur; | |
166 return (pCur->pDbList ? 0 : 1); | |
167 } | |
168 | |
169 /* | |
170 ** Advance the cursor to the next row. | |
171 */ | |
172 static int schemaNext(sqlite3_vtab_cursor *cur){ | |
173 int rc = SQLITE_OK; | |
174 schema_cursor *pCur = (schema_cursor *)cur; | |
175 schema_vtab *pVtab = (schema_vtab *)(cur->pVtab); | |
176 char *zSql = 0; | |
177 | |
178 while( !pCur->pColumnList || SQLITE_ROW!=sqlite3_step(pCur->pColumnList) ){ | |
179 if( SQLITE_OK!=(rc = finalize(&pCur->pColumnList)) ) goto next_exit; | |
180 | |
181 while( !pCur->pTableList || SQLITE_ROW!=sqlite3_step(pCur->pTableList) ){ | |
182 if( SQLITE_OK!=(rc = finalize(&pCur->pTableList)) ) goto next_exit; | |
183 | |
184 assert(pCur->pDbList); | |
185 while( SQLITE_ROW!=sqlite3_step(pCur->pDbList) ){ | |
186 rc = finalize(&pCur->pDbList); | |
187 goto next_exit; | |
188 } | |
189 | |
190 /* Set zSql to the SQL to pull the list of tables from the | |
191 ** sqlite_master (or sqlite_temp_master) table of the database | |
192 ** identified by the row pointed to by the SQL statement pCur->pDbList | |
193 ** (iterating through a "PRAGMA database_list;" statement). | |
194 */ | |
195 if( sqlite3_column_int(pCur->pDbList, 0)==1 ){ | |
196 zSql = sqlite3_mprintf( | |
197 "SELECT name FROM sqlite_temp_master WHERE type='table'" | |
198 ); | |
199 }else{ | |
200 sqlite3_stmt *pDbList = pCur->pDbList; | |
201 zSql = sqlite3_mprintf( | |
202 "SELECT name FROM %Q.sqlite_master WHERE type='table'", | |
203 sqlite3_column_text(pDbList, 1) | |
204 ); | |
205 } | |
206 if( !zSql ){ | |
207 rc = SQLITE_NOMEM; | |
208 goto next_exit; | |
209 } | |
210 | |
211 rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pTableList, 0); | |
212 sqlite3_free(zSql); | |
213 if( rc!=SQLITE_OK ) goto next_exit; | |
214 } | |
215 | |
216 /* Set zSql to the SQL to the table_info pragma for the table currently | |
217 ** identified by the rows pointed to by statements pCur->pDbList and | |
218 ** pCur->pTableList. | |
219 */ | |
220 zSql = sqlite3_mprintf("PRAGMA %Q.table_info(%Q)", | |
221 sqlite3_column_text(pCur->pDbList, 1), | |
222 sqlite3_column_text(pCur->pTableList, 0) | |
223 ); | |
224 | |
225 if( !zSql ){ | |
226 rc = SQLITE_NOMEM; | |
227 goto next_exit; | |
228 } | |
229 rc = sqlite3_prepare(pVtab->db, zSql, -1, &pCur->pColumnList, 0); | |
230 sqlite3_free(zSql); | |
231 if( rc!=SQLITE_OK ) goto next_exit; | |
232 } | |
233 pCur->rowid++; | |
234 | |
235 next_exit: | |
236 /* TODO: Handle rc */ | |
237 return rc; | |
238 } | |
239 | |
240 /* | |
241 ** Reset a schema table cursor. | |
242 */ | |
243 static int schemaFilter( | |
244 sqlite3_vtab_cursor *pVtabCursor, | |
245 int idxNum, const char *idxStr, | |
246 int argc, sqlite3_value **argv | |
247 ){ | |
248 int rc; | |
249 schema_vtab *pVtab = (schema_vtab *)(pVtabCursor->pVtab); | |
250 schema_cursor *pCur = (schema_cursor *)pVtabCursor; | |
251 pCur->rowid = 0; | |
252 finalize(&pCur->pTableList); | |
253 finalize(&pCur->pColumnList); | |
254 finalize(&pCur->pDbList); | |
255 rc = sqlite3_prepare(pVtab->db,"PRAGMA database_list", -1, &pCur->pDbList, 0); | |
256 return (rc==SQLITE_OK ? schemaNext(pVtabCursor) : rc); | |
257 } | |
258 | |
259 /* | |
260 ** Analyse the WHERE condition. | |
261 */ | |
262 static int schemaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | |
263 return SQLITE_OK; | |
264 } | |
265 | |
266 /* | |
267 ** A virtual table module that merely echos method calls into TCL | |
268 ** variables. | |
269 */ | |
270 static sqlite3_module schemaModule = { | |
271 0, /* iVersion */ | |
272 schemaCreate, | |
273 schemaCreate, | |
274 schemaBestIndex, | |
275 schemaDestroy, | |
276 schemaDestroy, | |
277 schemaOpen, /* xOpen - open a cursor */ | |
278 schemaClose, /* xClose - close a cursor */ | |
279 schemaFilter, /* xFilter - configure scan constraints */ | |
280 schemaNext, /* xNext - advance a cursor */ | |
281 schemaEof, /* xEof */ | |
282 schemaColumn, /* xColumn - read data */ | |
283 schemaRowid, /* xRowid - read data */ | |
284 0, /* xUpdate */ | |
285 0, /* xBegin */ | |
286 0, /* xSync */ | |
287 0, /* xCommit */ | |
288 0, /* xRollback */ | |
289 0, /* xFindMethod */ | |
290 0, /* xRename */ | |
291 }; | |
292 | |
293 #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ | |
294 | |
295 #ifdef SQLITE_TEST | |
296 | |
297 /* | |
298 ** Decode a pointer to an sqlite3 object. | |
299 */ | |
300 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); | |
301 | |
302 /* | |
303 ** Register the schema virtual table module. | |
304 */ | |
305 static int register_schema_module( | |
306 ClientData clientData, /* Not used */ | |
307 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | |
308 int objc, /* Number of arguments */ | |
309 Tcl_Obj *CONST objv[] /* Command arguments */ | |
310 ){ | |
311 sqlite3 *db; | |
312 if( objc!=2 ){ | |
313 Tcl_WrongNumArgs(interp, 1, objv, "DB"); | |
314 return TCL_ERROR; | |
315 } | |
316 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | |
317 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
318 sqlite3_create_module(db, "schema", &schemaModule, 0); | |
319 #endif | |
320 return TCL_OK; | |
321 } | |
322 | |
323 /* | |
324 ** Register commands with the TCL interpreter. | |
325 */ | |
326 int Sqlitetestschema_Init(Tcl_Interp *interp){ | |
327 static struct { | |
328 char *zName; | |
329 Tcl_ObjCmdProc *xProc; | |
330 void *clientData; | |
331 } aObjCmd[] = { | |
332 { "register_schema_module", register_schema_module, 0 }, | |
333 }; | |
334 int i; | |
335 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ | |
336 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, | |
337 aObjCmd[i].xProc, aObjCmd[i].clientData, 0); | |
338 } | |
339 return TCL_OK; | |
340 } | |
341 | |
342 #else | |
343 | |
344 /* | |
345 ** Extension load function. | |
346 */ | |
347 #ifdef _WIN32 | |
348 __declspec(dllexport) | |
349 #endif | |
350 int sqlite3_schema_init( | |
351 sqlite3 *db, | |
352 char **pzErrMsg, | |
353 const sqlite3_api_routines *pApi | |
354 ){ | |
355 SQLITE_EXTENSION_INIT2(pApi); | |
356 #ifndef SQLITE_OMIT_VIRTUALTABLE | |
357 sqlite3_create_module(db, "schema", &schemaModule, 0); | |
358 #endif | |
359 return 0; | |
360 } | |
361 | |
362 #endif | |
OLD | NEW |