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