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