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