1522efc62Sdrh /* 2522efc62Sdrh ** 2009 November 10 3522efc62Sdrh ** 4522efc62Sdrh ** The author disclaims copyright to this source code. In place of 5522efc62Sdrh ** a legal notice, here is a blessing: 6522efc62Sdrh ** 7522efc62Sdrh ** May you do good and not evil. 8522efc62Sdrh ** May you find forgiveness for yourself and forgive others. 9522efc62Sdrh ** May you share freely, never taking more than you give. 10522efc62Sdrh ** 11522efc62Sdrh ************************************************************************* 12522efc62Sdrh ** 13522efc62Sdrh ** This file implements a read-only VIRTUAL TABLE that contains the 14522efc62Sdrh ** content of a C-language array of integer values. See the corresponding 15522efc62Sdrh ** header file for full details. 16522efc62Sdrh */ 17522efc62Sdrh #include "test_intarray.h" 18522efc62Sdrh #include <string.h> 19522efc62Sdrh #include <assert.h> 20522efc62Sdrh 21522efc62Sdrh 22522efc62Sdrh /* 23522efc62Sdrh ** Definition of the sqlite3_intarray object. 24522efc62Sdrh ** 25522efc62Sdrh ** The internal representation of an intarray object is subject 26522efc62Sdrh ** to change, is not externally visible, and should be used by 27522efc62Sdrh ** the implementation of intarray only. This object is opaque 28522efc62Sdrh ** to users. 29522efc62Sdrh */ 30522efc62Sdrh struct sqlite3_intarray { 31522efc62Sdrh int n; /* Number of elements in the array */ 32522efc62Sdrh sqlite3_int64 *a; /* Contents of the array */ 33522efc62Sdrh void (*xFree)(void*); /* Function used to free a[] */ 34522efc62Sdrh }; 35522efc62Sdrh 36522efc62Sdrh /* Objects used internally by the virtual table implementation */ 37522efc62Sdrh typedef struct intarray_vtab intarray_vtab; 38522efc62Sdrh typedef struct intarray_cursor intarray_cursor; 39522efc62Sdrh 4060ec914cSpeter.d.reid /* An intarray table object */ 41522efc62Sdrh struct intarray_vtab { 42522efc62Sdrh sqlite3_vtab base; /* Base class */ 43522efc62Sdrh sqlite3_intarray *pContent; /* Content of the integer array */ 44522efc62Sdrh }; 45522efc62Sdrh 4660ec914cSpeter.d.reid /* An intarray cursor object */ 47522efc62Sdrh struct intarray_cursor { 48522efc62Sdrh sqlite3_vtab_cursor base; /* Base class */ 49522efc62Sdrh int i; /* Current cursor position */ 50522efc62Sdrh }; 51522efc62Sdrh 52522efc62Sdrh /* 53522efc62Sdrh ** None of this works unless we have virtual tables. 54522efc62Sdrh */ 55522efc62Sdrh #ifndef SQLITE_OMIT_VIRTUALTABLE 56522efc62Sdrh 57522efc62Sdrh /* 5863b38789Sdrh ** Free an sqlite3_intarray object. 59522efc62Sdrh */ 60522efc62Sdrh static void intarrayFree(sqlite3_intarray *p){ 61522efc62Sdrh if( p->xFree ){ 62522efc62Sdrh p->xFree(p->a); 63522efc62Sdrh } 64522efc62Sdrh sqlite3_free(p); 65522efc62Sdrh } 66522efc62Sdrh 67522efc62Sdrh /* 68522efc62Sdrh ** Table destructor for the intarray module. 69522efc62Sdrh */ 70522efc62Sdrh static int intarrayDestroy(sqlite3_vtab *p){ 71522efc62Sdrh intarray_vtab *pVtab = (intarray_vtab*)p; 72522efc62Sdrh sqlite3_free(pVtab); 73522efc62Sdrh return 0; 74522efc62Sdrh } 75522efc62Sdrh 76522efc62Sdrh /* 77522efc62Sdrh ** Table constructor for the intarray module. 78522efc62Sdrh */ 79522efc62Sdrh static int intarrayCreate( 80522efc62Sdrh sqlite3 *db, /* Database where module is created */ 81522efc62Sdrh void *pAux, /* clientdata for the module */ 82522efc62Sdrh int argc, /* Number of arguments */ 83522efc62Sdrh const char *const*argv, /* Value for all arguments */ 84522efc62Sdrh sqlite3_vtab **ppVtab, /* Write the new virtual table object here */ 85522efc62Sdrh char **pzErr /* Put error message text here */ 86522efc62Sdrh ){ 87522efc62Sdrh int rc = SQLITE_NOMEM; 88*f3cdcdccSdrh intarray_vtab *pVtab = sqlite3_malloc64(sizeof(intarray_vtab)); 89522efc62Sdrh 90522efc62Sdrh if( pVtab ){ 91522efc62Sdrh memset(pVtab, 0, sizeof(intarray_vtab)); 92522efc62Sdrh pVtab->pContent = (sqlite3_intarray*)pAux; 93522efc62Sdrh rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)"); 94522efc62Sdrh } 95522efc62Sdrh *ppVtab = (sqlite3_vtab *)pVtab; 96522efc62Sdrh return rc; 97522efc62Sdrh } 98522efc62Sdrh 99522efc62Sdrh /* 100522efc62Sdrh ** Open a new cursor on the intarray table. 101522efc62Sdrh */ 102522efc62Sdrh static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ 103522efc62Sdrh int rc = SQLITE_NOMEM; 104522efc62Sdrh intarray_cursor *pCur; 105*f3cdcdccSdrh pCur = sqlite3_malloc64(sizeof(intarray_cursor)); 106522efc62Sdrh if( pCur ){ 107522efc62Sdrh memset(pCur, 0, sizeof(intarray_cursor)); 108522efc62Sdrh *ppCursor = (sqlite3_vtab_cursor *)pCur; 109522efc62Sdrh rc = SQLITE_OK; 110522efc62Sdrh } 111522efc62Sdrh return rc; 112522efc62Sdrh } 113522efc62Sdrh 114522efc62Sdrh /* 115522efc62Sdrh ** Close a intarray table cursor. 116522efc62Sdrh */ 117522efc62Sdrh static int intarrayClose(sqlite3_vtab_cursor *cur){ 118522efc62Sdrh intarray_cursor *pCur = (intarray_cursor *)cur; 119522efc62Sdrh sqlite3_free(pCur); 120522efc62Sdrh return SQLITE_OK; 121522efc62Sdrh } 122522efc62Sdrh 123522efc62Sdrh /* 124522efc62Sdrh ** Retrieve a column of data. 125522efc62Sdrh */ 126522efc62Sdrh static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ 127522efc62Sdrh intarray_cursor *pCur = (intarray_cursor*)cur; 128522efc62Sdrh intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab; 129522efc62Sdrh if( pCur->i>=0 && pCur->i<pVtab->pContent->n ){ 130522efc62Sdrh sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]); 131522efc62Sdrh } 132522efc62Sdrh return SQLITE_OK; 133522efc62Sdrh } 134522efc62Sdrh 135522efc62Sdrh /* 136522efc62Sdrh ** Retrieve the current rowid. 137522efc62Sdrh */ 138522efc62Sdrh static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ 139522efc62Sdrh intarray_cursor *pCur = (intarray_cursor *)cur; 140522efc62Sdrh *pRowid = pCur->i; 141522efc62Sdrh return SQLITE_OK; 142522efc62Sdrh } 143522efc62Sdrh 144522efc62Sdrh static int intarrayEof(sqlite3_vtab_cursor *cur){ 145522efc62Sdrh intarray_cursor *pCur = (intarray_cursor *)cur; 146522efc62Sdrh intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab; 147522efc62Sdrh return pCur->i>=pVtab->pContent->n; 148522efc62Sdrh } 149522efc62Sdrh 150522efc62Sdrh /* 151522efc62Sdrh ** Advance the cursor to the next row. 152522efc62Sdrh */ 153522efc62Sdrh static int intarrayNext(sqlite3_vtab_cursor *cur){ 154522efc62Sdrh intarray_cursor *pCur = (intarray_cursor *)cur; 155522efc62Sdrh pCur->i++; 156522efc62Sdrh return SQLITE_OK; 157522efc62Sdrh } 158522efc62Sdrh 159522efc62Sdrh /* 160522efc62Sdrh ** Reset a intarray table cursor. 161522efc62Sdrh */ 162522efc62Sdrh static int intarrayFilter( 163522efc62Sdrh sqlite3_vtab_cursor *pVtabCursor, 164522efc62Sdrh int idxNum, const char *idxStr, 165522efc62Sdrh int argc, sqlite3_value **argv 166522efc62Sdrh ){ 167522efc62Sdrh intarray_cursor *pCur = (intarray_cursor *)pVtabCursor; 168522efc62Sdrh pCur->i = 0; 169522efc62Sdrh return SQLITE_OK; 170522efc62Sdrh } 171522efc62Sdrh 172522efc62Sdrh /* 173522efc62Sdrh ** Analyse the WHERE condition. 174522efc62Sdrh */ 175522efc62Sdrh static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ 176522efc62Sdrh return SQLITE_OK; 177522efc62Sdrh } 178522efc62Sdrh 179522efc62Sdrh /* 180522efc62Sdrh ** A virtual table module that merely echos method calls into TCL 181522efc62Sdrh ** variables. 182522efc62Sdrh */ 183522efc62Sdrh static sqlite3_module intarrayModule = { 184522efc62Sdrh 0, /* iVersion */ 185522efc62Sdrh intarrayCreate, /* xCreate - create a new virtual table */ 186522efc62Sdrh intarrayCreate, /* xConnect - connect to an existing vtab */ 187522efc62Sdrh intarrayBestIndex, /* xBestIndex - find the best query index */ 188522efc62Sdrh intarrayDestroy, /* xDisconnect - disconnect a vtab */ 189522efc62Sdrh intarrayDestroy, /* xDestroy - destroy a vtab */ 190522efc62Sdrh intarrayOpen, /* xOpen - open a cursor */ 191522efc62Sdrh intarrayClose, /* xClose - close a cursor */ 192522efc62Sdrh intarrayFilter, /* xFilter - configure scan constraints */ 193522efc62Sdrh intarrayNext, /* xNext - advance a cursor */ 194522efc62Sdrh intarrayEof, /* xEof */ 195522efc62Sdrh intarrayColumn, /* xColumn - read data */ 196522efc62Sdrh intarrayRowid, /* xRowid - read data */ 197522efc62Sdrh 0, /* xUpdate */ 198522efc62Sdrh 0, /* xBegin */ 199522efc62Sdrh 0, /* xSync */ 200522efc62Sdrh 0, /* xCommit */ 201522efc62Sdrh 0, /* xRollback */ 202522efc62Sdrh 0, /* xFindMethod */ 203522efc62Sdrh 0, /* xRename */ 204522efc62Sdrh }; 205522efc62Sdrh 206522efc62Sdrh #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ 207522efc62Sdrh 208522efc62Sdrh /* 209522efc62Sdrh ** Invoke this routine to create a specific instance of an intarray object. 210522efc62Sdrh ** The new intarray object is returned by the 3rd parameter. 211522efc62Sdrh ** 212522efc62Sdrh ** Each intarray object corresponds to a virtual table in the TEMP table 213522efc62Sdrh ** with a name of zName. 214522efc62Sdrh ** 215522efc62Sdrh ** Destroy the intarray object by dropping the virtual table. If not done 216522efc62Sdrh ** explicitly by the application, the virtual table will be dropped implicitly 217522efc62Sdrh ** by the system when the database connection is closed. 218522efc62Sdrh */ 219fb90841aSdrh SQLITE_API int sqlite3_intarray_create( 220522efc62Sdrh sqlite3 *db, 221522efc62Sdrh const char *zName, 222522efc62Sdrh sqlite3_intarray **ppReturn 223522efc62Sdrh ){ 2245c03f30aSdrh int rc = SQLITE_OK; 2255c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE 226522efc62Sdrh sqlite3_intarray *p; 227522efc62Sdrh 228*f3cdcdccSdrh *ppReturn = p = sqlite3_malloc64( sizeof(*p) ); 229522efc62Sdrh if( p==0 ){ 230522efc62Sdrh return SQLITE_NOMEM; 231522efc62Sdrh } 232522efc62Sdrh memset(p, 0, sizeof(*p)); 233522efc62Sdrh rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p, 234522efc62Sdrh (void(*)(void*))intarrayFree); 235522efc62Sdrh if( rc==SQLITE_OK ){ 236522efc62Sdrh char *zSql; 237522efc62Sdrh zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q", 238522efc62Sdrh zName, zName); 239522efc62Sdrh rc = sqlite3_exec(db, zSql, 0, 0, 0); 240522efc62Sdrh sqlite3_free(zSql); 241522efc62Sdrh } 2425c03f30aSdrh #endif 243522efc62Sdrh return rc; 244522efc62Sdrh } 245522efc62Sdrh 246522efc62Sdrh /* 247522efc62Sdrh ** Bind a new array array of integers to a specific intarray object. 248522efc62Sdrh ** 249522efc62Sdrh ** The array of integers bound must be unchanged for the duration of 250522efc62Sdrh ** any query against the corresponding virtual table. If the integer 251522efc62Sdrh ** array does change or is deallocated undefined behavior will result. 252522efc62Sdrh */ 253fb90841aSdrh SQLITE_API int sqlite3_intarray_bind( 254522efc62Sdrh sqlite3_intarray *pIntArray, /* The intarray object to bind to */ 255522efc62Sdrh int nElements, /* Number of elements in the intarray */ 256522efc62Sdrh sqlite3_int64 *aElements, /* Content of the intarray */ 257522efc62Sdrh void (*xFree)(void*) /* How to dispose of the intarray when done */ 258522efc62Sdrh ){ 259522efc62Sdrh if( pIntArray->xFree ){ 260522efc62Sdrh pIntArray->xFree(pIntArray->a); 261522efc62Sdrh } 262522efc62Sdrh pIntArray->n = nElements; 263522efc62Sdrh pIntArray->a = aElements; 264522efc62Sdrh pIntArray->xFree = xFree; 265522efc62Sdrh return SQLITE_OK; 266522efc62Sdrh } 267522efc62Sdrh 268522efc62Sdrh 269522efc62Sdrh /***************************************************************************** 270522efc62Sdrh ** Everything below is interface for testing this module. 271522efc62Sdrh */ 272522efc62Sdrh #ifdef SQLITE_TEST 273522efc62Sdrh #include <tcl.h> 274522efc62Sdrh 275522efc62Sdrh /* 276522efc62Sdrh ** Routines to encode and decode pointers 277522efc62Sdrh */ 278522efc62Sdrh extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); 279d8267b89Sdrh extern void *sqlite3TestTextToPtr(const char*); 280522efc62Sdrh extern int sqlite3TestMakePointerStr(Tcl_Interp*, char *zPtr, void*); 281e84d8d32Smistachkin extern const char *sqlite3ErrName(int); 282522efc62Sdrh 283522efc62Sdrh /* 284522efc62Sdrh ** sqlite3_intarray_create DB NAME 285522efc62Sdrh ** 286522efc62Sdrh ** Invoke the sqlite3_intarray_create interface. A string that becomes 287522efc62Sdrh ** the first parameter to sqlite3_intarray_bind. 288522efc62Sdrh */ 289522efc62Sdrh static int test_intarray_create( 290522efc62Sdrh ClientData clientData, /* Not used */ 291522efc62Sdrh Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 292522efc62Sdrh int objc, /* Number of arguments */ 293522efc62Sdrh Tcl_Obj *CONST objv[] /* Command arguments */ 294522efc62Sdrh ){ 295522efc62Sdrh sqlite3 *db; 296522efc62Sdrh const char *zName; 297522efc62Sdrh sqlite3_intarray *pArray; 2985c03f30aSdrh int rc = SQLITE_OK; 299522efc62Sdrh char zPtr[100]; 300522efc62Sdrh 301522efc62Sdrh if( objc!=3 ){ 302522efc62Sdrh Tcl_WrongNumArgs(interp, 1, objv, "DB"); 303522efc62Sdrh return TCL_ERROR; 304522efc62Sdrh } 305522efc62Sdrh if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 306522efc62Sdrh zName = Tcl_GetString(objv[2]); 3075c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE 308522efc62Sdrh rc = sqlite3_intarray_create(db, zName, &pArray); 3095c03f30aSdrh #endif 310522efc62Sdrh if( rc!=SQLITE_OK ){ 311522efc62Sdrh assert( pArray==0 ); 312e84d8d32Smistachkin Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0); 313522efc62Sdrh return TCL_ERROR; 314522efc62Sdrh } 315522efc62Sdrh sqlite3TestMakePointerStr(interp, zPtr, pArray); 316522efc62Sdrh Tcl_AppendResult(interp, zPtr, (char*)0); 317522efc62Sdrh return TCL_OK; 318522efc62Sdrh } 319522efc62Sdrh 320522efc62Sdrh /* 321522efc62Sdrh ** sqlite3_intarray_bind INTARRAY ?VALUE ...? 322522efc62Sdrh ** 323522efc62Sdrh ** Invoke the sqlite3_intarray_bind interface on the given array of integers. 324522efc62Sdrh */ 325522efc62Sdrh static int test_intarray_bind( 326522efc62Sdrh ClientData clientData, /* Not used */ 327522efc62Sdrh Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ 328522efc62Sdrh int objc, /* Number of arguments */ 329522efc62Sdrh Tcl_Obj *CONST objv[] /* Command arguments */ 330522efc62Sdrh ){ 331522efc62Sdrh sqlite3_intarray *pArray; 3325c03f30aSdrh int rc = SQLITE_OK; 333522efc62Sdrh int i, n; 334522efc62Sdrh sqlite3_int64 *a; 335522efc62Sdrh 336522efc62Sdrh if( objc<2 ){ 337522efc62Sdrh Tcl_WrongNumArgs(interp, 1, objv, "INTARRAY"); 338522efc62Sdrh return TCL_ERROR; 339522efc62Sdrh } 340522efc62Sdrh pArray = (sqlite3_intarray*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); 341522efc62Sdrh n = objc - 2; 3425c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE 343*f3cdcdccSdrh a = sqlite3_malloc64( sizeof(a[0])*n ); 344522efc62Sdrh if( a==0 ){ 345522efc62Sdrh Tcl_AppendResult(interp, "SQLITE_NOMEM", (char*)0); 346522efc62Sdrh return TCL_ERROR; 347522efc62Sdrh } 348522efc62Sdrh for(i=0; i<n; i++){ 349b3f787f4Sdrh Tcl_WideInt x = 0; 350b3f787f4Sdrh Tcl_GetWideIntFromObj(0, objv[i+2], &x); 351b3f787f4Sdrh a[i] = x; 352522efc62Sdrh } 353522efc62Sdrh rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free); 354522efc62Sdrh if( rc!=SQLITE_OK ){ 355e84d8d32Smistachkin Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0); 356522efc62Sdrh return TCL_ERROR; 357522efc62Sdrh } 3585c03f30aSdrh #endif 359522efc62Sdrh return TCL_OK; 360522efc62Sdrh } 361522efc62Sdrh 362522efc62Sdrh /* 363522efc62Sdrh ** Register commands with the TCL interpreter. 364522efc62Sdrh */ 365522efc62Sdrh int Sqlitetestintarray_Init(Tcl_Interp *interp){ 366522efc62Sdrh static struct { 367522efc62Sdrh char *zName; 368522efc62Sdrh Tcl_ObjCmdProc *xProc; 369522efc62Sdrh void *clientData; 370522efc62Sdrh } aObjCmd[] = { 371522efc62Sdrh { "sqlite3_intarray_create", test_intarray_create, 0 }, 372522efc62Sdrh { "sqlite3_intarray_bind", test_intarray_bind, 0 }, 373522efc62Sdrh }; 374522efc62Sdrh int i; 375522efc62Sdrh for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 376522efc62Sdrh Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 377522efc62Sdrh aObjCmd[i].xProc, aObjCmd[i].clientData, 0); 378522efc62Sdrh } 379522efc62Sdrh return TCL_OK; 380522efc62Sdrh } 381522efc62Sdrh 382522efc62Sdrh #endif /* SQLITE_TEST */ 383