| 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 |