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