1 /* 2 ** 2009 November 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 ** 13 ** This file implements a read-only VIRTUAL TABLE that contains the 14 ** content of a C-language array of integer values. See the corresponding 15 ** header file for full details. 16 ** 17 ** This virtual table is used for internal testing of SQLite only. It is 18 ** not recommended for use in production. For a similar virtual table that 19 ** is production-ready, see the "carray" virtual table over in ext/misc. 20 */ 21 #include "test_intarray.h" 22 #include <string.h> 23 #include <assert.h> 24 25 26 /* 27 ** Definition of the sqlite3_intarray object. 28 ** 29 ** The internal representation of an intarray object is subject 30 ** to change, is not externally visible, and should be used by 31 ** the implementation of intarray only. This object is opaque 32 ** to users. 33 */ 34 struct sqlite3_intarray { 35 int n; /* Number of elements in the array */ 36 sqlite3_int64 *a; /* Contents of the array */ 37 void (*xFree)(void*); /* Function used to free a[] */ 38 }; 39 40 /* Objects used internally by the virtual table implementation */ 41 typedef struct intarray_vtab intarray_vtab; 42 typedef struct intarray_cursor intarray_cursor; 43 44 /* An intarray table object */ 45 struct intarray_vtab { 46 sqlite3_vtab base; /* Base class */ 47 sqlite3_intarray *pContent; /* Content of the integer array */ 48 }; 49 50 /* An intarray cursor object */ 51 struct intarray_cursor { 52 sqlite3_vtab_cursor base; /* Base class */ 53 int i; /* Current cursor position */ 54 }; 55 56 /* 57 ** None of this works unless we have virtual tables. 58 */ 59 #ifndef SQLITE_OMIT_VIRTUALTABLE 60 61 /* 62 ** Free an sqlite3_intarray object. 63 */ 64 static void intarrayFree(sqlite3_intarray *p){ 65 if( p->xFree ){ 66 p->xFree(p->a); 67 } 68 sqlite3_free(p); 69 } 70 71 /* 72 ** Table destructor for the intarray module. 73 */ 74 static int intarrayDestroy(sqlite3_vtab *p){ 75 intarray_vtab *pVtab = (intarray_vtab*)p; 76 sqlite3_free(pVtab); 77 return 0; 78 } 79 80 /* 81 ** Table constructor for the intarray module. 82 */ 83 static int intarrayCreate( 84 sqlite3 *db, /* Database where module is created */ 85 void *pAux, /* clientdata for the module */ 86 int argc, /* Number of arguments */ 87 const char *const*argv, /* Value for all arguments */ 88 sqlite3_vtab **ppVtab, /* Write the new virtual table object here */ 89 char **pzErr /* Put error message text here */ 90 ){ 91 int rc = SQLITE_NOMEM; 92 intarray_vtab *pVtab = sqlite3_malloc64(sizeof(intarray_vtab)); 93 94 if( pVtab ){ 95 memset(pVtab, 0, sizeof(intarray_vtab)); 96 pVtab->pContent = (sqlite3_intarray*)pAux; 97 rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"); 98 } 99 *ppVtab = (sqlite3_vtab *)pVtab; 100 return rc; 101 } 102 103 /* 104 ** Open a new cursor on the intarray table. 105 */ 106 static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ 107 int rc = SQLITE_NOMEM; 108 intarray_cursor *pCur; 109 pCur = sqlite3_malloc64(sizeof(intarray_cursor)); 110 if( pCur ){ 111 memset(pCur, 0, sizeof(intarray_cursor)); 112 *ppCursor = (sqlite3_vtab_cursor *)pCur; 113 rc = SQLITE_OK; 114 } 115 return rc; 116 } 117 118 /* 119 ** Close a intarray table cursor. 120 */ 121 static int intarrayClose(sqlite3_vtab_cursor *cur){ 122 intarray_cursor *pCur = (intarray_cursor *)cur; 123 sqlite3_free(pCur); 124 return SQLITE_OK; 125 } 126 127 /* 128 ** Retrieve a column of data. 129 */ 130 static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ 131 intarray_cursor *pCur = (intarray_cursor*)cur; 132 intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab; 133 if( pCur->i>=0 && pCur->i<pVtab->pContent->n ){ 134 sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]); 135 } 136 return SQLITE_OK; 137 } 138 139 /* 140 ** Retrieve the current rowid. 141 */ 142 static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ 143 intarray_cursor *pCur = (intarray_cursor *)cur; 144 *pRowid = pCur->i; 145 return SQLITE_OK; 146 } 147 148 static int intarrayEof(sqlite3_vtab_cursor *cur){ 149 intarray_cursor *pCur = (intarray_cursor *)cur; 150 intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab; 151 return pCur->i>=pVtab->pContent->n; 152 } 153 154 /* 155 ** Advance the cursor to the next row. 156 */ 157 static int intarrayNext(sqlite3_vtab_cursor *cur){ 158 intarray_cursor *pCur = (intarray_cursor *)cur; 159 pCur->i++; 160 return SQLITE_OK; 161 } 162 163 /* 164 ** Reset a intarray table cursor. 165 */ 166 static int intarrayFilter( 167 sqlite3_vtab_cursor *pVtabCursor, 168 int idxNum, const char *idxStr, 169 int argc, sqlite3_value **argv 170 ){ 171 intarray_cursor *pCur = (intarray_cursor *)pVtabCursor; 172 pCur->i = 0; 173 return SQLITE_OK; 174 } 175 176 /* 177 ** Analyse the WHERE condition. 178 */ 179 static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ 180 return SQLITE_OK; 181 } 182 183 /* 184 ** A virtual table module that merely echos method calls into TCL 185 ** variables. 186 */ 187 static sqlite3_module intarrayModule = { 188 0, /* iVersion */ 189 intarrayCreate, /* xCreate - create a new virtual table */ 190 intarrayCreate, /* xConnect - connect to an existing vtab */ 191 intarrayBestIndex, /* xBestIndex - find the best query index */ 192 intarrayDestroy, /* xDisconnect - disconnect a vtab */ 193 intarrayDestroy, /* xDestroy - destroy a vtab */ 194 intarrayOpen, /* xOpen - open a cursor */ 195 intarrayClose, /* xClose - close a cursor */ 196 intarrayFilter, /* xFilter - configure scan constraints */ 197 intarrayNext, /* xNext - advance a cursor */ 198 intarrayEof, /* xEof */ 199 intarrayColumn, /* xColumn - read data */ 200 intarrayRowid, /* xRowid - read data */ 201 0, /* xUpdate */ 202 0, /* xBegin */ 203 0, /* xSync */ 204 0, /* xCommit */ 205 0, /* xRollback */ 206 0, /* xFindMethod */ 207 0, /* xRename */ 208 }; 209 210 #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ 211 212 /* 213 ** Invoke this routine to create a specific instance of an intarray object. 214 ** The new intarray object is returned by the 3rd parameter. 215 ** 216 ** Each intarray object corresponds to a virtual table in the TEMP table 217 ** with a name of zName. 218 ** 219 ** Destroy the intarray object by dropping the virtual table. If not done 220 ** explicitly by the application, the virtual table will be dropped implicitly 221 ** by the system when the database connection is closed. 222 */ 223 SQLITE_API int sqlite3_intarray_create( 224 sqlite3 *db, 225 const char *zName, 226 sqlite3_intarray **ppReturn 227 ){ 228 int rc = SQLITE_OK; 229 #ifndef SQLITE_OMIT_VIRTUALTABLE 230 sqlite3_intarray *p; 231 232 *ppReturn = p = sqlite3_malloc64( sizeof(*p) ); 233 if( p==0 ){ 234 return SQLITE_NOMEM; 235 } 236 memset(p, 0, sizeof(*p)); 237 rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p, 238 (void(*)(void*))intarrayFree); 239 if( rc==SQLITE_OK ){ 240 char *zSql; 241 zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q", 242 zName, zName); 243 rc = sqlite3_exec(db, zSql, 0, 0, 0); 244 sqlite3_free(zSql); 245 } 246 #endif 247 return rc; 248 } 249 250 /* 251 ** Bind a new array array of integers to a specific intarray object. 252 ** 253 ** The array of integers bound must be unchanged for the duration of 254 ** any query against the corresponding virtual table. If the integer 255 ** array does change or is deallocated undefined behavior will result. 256 */ 257 SQLITE_API int sqlite3_intarray_bind( 258 sqlite3_intarray *pIntArray, /* The intarray object to bind to */ 259 int nElements, /* Number of elements in the intarray */ 260 sqlite3_int64 *aElements, /* Content of the intarray */ 261 void (*xFree)(void*) /* How to dispose of the intarray when done */ 262 ){ 263 if( pIntArray->xFree ){ 264 pIntArray->xFree(pIntArray->a); 265 } 266 pIntArray->n = nElements; 267 pIntArray->a = aElements; 268 pIntArray->xFree = xFree; 269 return SQLITE_OK; 270 } 271 272 273 /***************************************************************************** 274 ** Everything below is interface for testing this module. 275 */ 276 #ifdef SQLITE_TEST 277 #if defined(INCLUDE_SQLITE_TCL_H) 278 # include "sqlite_tcl.h" 279 #else 280 # include "tcl.h" 281 # ifndef SQLITE_TCLAPI 282 # define SQLITE_TCLAPI 283 # endif 284 #endif 285 286 /* 287 ** Routines to encode and decode pointers 288 */ 289 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); 290 extern void *sqlite3TestTextToPtr(const char*); 291 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char *zPtr, void*); 292 extern const char *sqlite3ErrName(int); 293 294 /* 295 ** sqlite3_intarray_create DB NAME 296 ** 297 ** Invoke the sqlite3_intarray_create interface. A string that becomes 298 ** the first parameter to sqlite3_intarray_bind. 299 */ 300 static int SQLITE_TCLAPI test_intarray_create( 301 ClientData clientData, /* Not used */ 302 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 303 int objc, /* Number of arguments */ 304 Tcl_Obj *CONST objv[] /* Command arguments */ 305 ){ 306 sqlite3 *db; 307 const char *zName; 308 sqlite3_intarray *pArray; 309 int rc = SQLITE_OK; 310 char zPtr[100]; 311 312 if( objc!=3 ){ 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 zName = Tcl_GetString(objv[2]); 318 #ifndef SQLITE_OMIT_VIRTUALTABLE 319 rc = sqlite3_intarray_create(db, zName, &pArray); 320 #endif 321 if( rc!=SQLITE_OK ){ 322 Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0); 323 return TCL_ERROR; 324 } 325 sqlite3TestMakePointerStr(interp, zPtr, pArray); 326 Tcl_AppendResult(interp, zPtr, (char*)0); 327 return TCL_OK; 328 } 329 330 /* 331 ** sqlite3_intarray_bind INTARRAY ?VALUE ...? 332 ** 333 ** Invoke the sqlite3_intarray_bind interface on the given array of integers. 334 */ 335 static int SQLITE_TCLAPI test_intarray_bind( 336 ClientData clientData, /* Not used */ 337 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 338 int objc, /* Number of arguments */ 339 Tcl_Obj *CONST objv[] /* Command arguments */ 340 ){ 341 sqlite3_intarray *pArray; 342 int rc = SQLITE_OK; 343 int i, n; 344 sqlite3_int64 *a; 345 346 if( objc<2 ){ 347 Tcl_WrongNumArgs(interp, 1, objv, "INTARRAY"); 348 return TCL_ERROR; 349 } 350 pArray = (sqlite3_intarray*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 351 n = objc - 2; 352 #ifndef SQLITE_OMIT_VIRTUALTABLE 353 a = sqlite3_malloc64( sizeof(a[0])*n ); 354 if( a==0 ){ 355 Tcl_AppendResult(interp, "SQLITE_NOMEM", (char*)0); 356 return TCL_ERROR; 357 } 358 for(i=0; i<n; i++){ 359 Tcl_WideInt x = 0; 360 Tcl_GetWideIntFromObj(0, objv[i+2], &x); 361 a[i] = x; 362 } 363 rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free); 364 if( rc!=SQLITE_OK ){ 365 Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0); 366 return TCL_ERROR; 367 } 368 #endif 369 return TCL_OK; 370 } 371 372 /* 373 ** Register commands with the TCL interpreter. 374 */ 375 int Sqlitetestintarray_Init(Tcl_Interp *interp){ 376 static struct { 377 char *zName; 378 Tcl_ObjCmdProc *xProc; 379 void *clientData; 380 } aObjCmd[] = { 381 { "sqlite3_intarray_create", test_intarray_create, 0 }, 382 { "sqlite3_intarray_bind", test_intarray_bind, 0 }, 383 }; 384 int i; 385 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 386 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 387 aObjCmd[i].xProc, aObjCmd[i].clientData, 0); 388 } 389 return TCL_OK; 390 } 391 392 #endif /* SQLITE_TEST */ 393