Home | History | Annotate | Download | only in src
      1 /*
      2 ** 2006 June 13
      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 ** The emphasis of this file is a virtual table that provides
     17 ** access to TCL variables.
     18 */
     19 #include "sqliteInt.h"
     20 #include "tcl.h"
     21 #include <stdlib.h>
     22 #include <string.h>
     23 
     24 #ifndef SQLITE_OMIT_VIRTUALTABLE
     25 
     26 typedef struct tclvar_vtab tclvar_vtab;
     27 typedef struct tclvar_cursor tclvar_cursor;
     28 
     29 /*
     30 ** A tclvar virtual-table object
     31 */
     32 struct tclvar_vtab {
     33   sqlite3_vtab base;
     34   Tcl_Interp *interp;
     35 };
     36 
     37 /* A tclvar cursor object */
     38 struct tclvar_cursor {
     39   sqlite3_vtab_cursor base;
     40 
     41   Tcl_Obj *pList1;     /* Result of [info vars ?pattern?] */
     42   Tcl_Obj *pList2;     /* Result of [array names [lindex $pList1 $i1]] */
     43   int i1;              /* Current item in pList1 */
     44   int i2;              /* Current item (if any) in pList2 */
     45 };
     46 
     47 /* Methods for the tclvar module */
     48 static int tclvarConnect(
     49   sqlite3 *db,
     50   void *pAux,
     51   int argc, const char *const*argv,
     52   sqlite3_vtab **ppVtab,
     53   char **pzErr
     54 ){
     55   tclvar_vtab *pVtab;
     56   static const char zSchema[] =
     57      "CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)";
     58   pVtab = sqlite3MallocZero( sizeof(*pVtab) );
     59   if( pVtab==0 ) return SQLITE_NOMEM;
     60   *ppVtab = &pVtab->base;
     61   pVtab->interp = (Tcl_Interp *)pAux;
     62   sqlite3_declare_vtab(db, zSchema);
     63   return SQLITE_OK;
     64 }
     65 /* Note that for this virtual table, the xCreate and xConnect
     66 ** methods are identical. */
     67 
     68 static int tclvarDisconnect(sqlite3_vtab *pVtab){
     69   sqlite3_free(pVtab);
     70   return SQLITE_OK;
     71 }
     72 /* The xDisconnect and xDestroy methods are also the same */
     73 
     74 /*
     75 ** Open a new tclvar cursor.
     76 */
     77 static int tclvarOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
     78   tclvar_cursor *pCur;
     79   pCur = sqlite3MallocZero(sizeof(tclvar_cursor));
     80   *ppCursor = &pCur->base;
     81   return SQLITE_OK;
     82 }
     83 
     84 /*
     85 ** Close a tclvar cursor.
     86 */
     87 static int tclvarClose(sqlite3_vtab_cursor *cur){
     88   tclvar_cursor *pCur = (tclvar_cursor *)cur;
     89   if( pCur->pList1 ){
     90     Tcl_DecrRefCount(pCur->pList1);
     91   }
     92   if( pCur->pList2 ){
     93     Tcl_DecrRefCount(pCur->pList2);
     94   }
     95   sqlite3_free(pCur);
     96   return SQLITE_OK;
     97 }
     98 
     99 /*
    100 ** Returns 1 if data is ready, or 0 if not.
    101 */
    102 static int next2(Tcl_Interp *interp, tclvar_cursor *pCur, Tcl_Obj *pObj){
    103   Tcl_Obj *p;
    104 
    105   if( pObj ){
    106     if( !pCur->pList2 ){
    107       p = Tcl_NewStringObj("array names", -1);
    108       Tcl_IncrRefCount(p);
    109       Tcl_ListObjAppendElement(0, p, pObj);
    110       Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
    111       Tcl_DecrRefCount(p);
    112       pCur->pList2 = Tcl_GetObjResult(interp);
    113       Tcl_IncrRefCount(pCur->pList2);
    114       assert( pCur->i2==0 );
    115     }else{
    116       int n = 0;
    117       pCur->i2++;
    118       Tcl_ListObjLength(0, pCur->pList2, &n);
    119       if( pCur->i2>=n ){
    120         Tcl_DecrRefCount(pCur->pList2);
    121         pCur->pList2 = 0;
    122         pCur->i2 = 0;
    123         return 0;
    124       }
    125     }
    126   }
    127 
    128   return 1;
    129 }
    130 
    131 static int tclvarNext(sqlite3_vtab_cursor *cur){
    132   Tcl_Obj *pObj;
    133   int n = 0;
    134   int ok = 0;
    135 
    136   tclvar_cursor *pCur = (tclvar_cursor *)cur;
    137   Tcl_Interp *interp = ((tclvar_vtab *)(cur->pVtab))->interp;
    138 
    139   Tcl_ListObjLength(0, pCur->pList1, &n);
    140   while( !ok && pCur->i1<n ){
    141     Tcl_ListObjIndex(0, pCur->pList1, pCur->i1, &pObj);
    142     ok = next2(interp, pCur, pObj);
    143     if( !ok ){
    144       pCur->i1++;
    145     }
    146   }
    147 
    148   return 0;
    149 }
    150 
    151 static int tclvarFilter(
    152   sqlite3_vtab_cursor *pVtabCursor,
    153   int idxNum, const char *idxStr,
    154   int argc, sqlite3_value **argv
    155 ){
    156   tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor;
    157   Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp;
    158 
    159   Tcl_Obj *p = Tcl_NewStringObj("info vars", -1);
    160   Tcl_IncrRefCount(p);
    161 
    162   assert( argc==0 || argc==1 );
    163   if( argc==1 ){
    164     Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1);
    165     Tcl_ListObjAppendElement(0, p, pArg);
    166   }
    167   Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
    168   if( pCur->pList1 ){
    169     Tcl_DecrRefCount(pCur->pList1);
    170   }
    171   if( pCur->pList2 ){
    172     Tcl_DecrRefCount(pCur->pList2);
    173     pCur->pList2 = 0;
    174   }
    175   pCur->i1 = 0;
    176   pCur->i2 = 0;
    177   pCur->pList1 = Tcl_GetObjResult(interp);
    178   Tcl_IncrRefCount(pCur->pList1);
    179   assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 );
    180 
    181   Tcl_DecrRefCount(p);
    182   return tclvarNext(pVtabCursor);
    183 }
    184 
    185 static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
    186   Tcl_Obj *p1;
    187   Tcl_Obj *p2;
    188   const char *z1;
    189   const char *z2 = "";
    190   tclvar_cursor *pCur = (tclvar_cursor*)cur;
    191   Tcl_Interp *interp = ((tclvar_vtab *)cur->pVtab)->interp;
    192 
    193   Tcl_ListObjIndex(interp, pCur->pList1, pCur->i1, &p1);
    194   Tcl_ListObjIndex(interp, pCur->pList2, pCur->i2, &p2);
    195   z1 = Tcl_GetString(p1);
    196   if( p2 ){
    197     z2 = Tcl_GetString(p2);
    198   }
    199   switch (i) {
    200     case 0: {
    201       sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
    202       break;
    203     }
    204     case 1: {
    205       sqlite3_result_text(ctx, z2, -1, SQLITE_TRANSIENT);
    206       break;
    207     }
    208     case 2: {
    209       Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
    210       sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
    211       break;
    212     }
    213   }
    214   return SQLITE_OK;
    215 }
    216 
    217 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
    218   *pRowid = 0;
    219   return SQLITE_OK;
    220 }
    221 
    222 static int tclvarEof(sqlite3_vtab_cursor *cur){
    223   tclvar_cursor *pCur = (tclvar_cursor*)cur;
    224   return (pCur->pList2?0:1);
    225 }
    226 
    227 static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
    228   int ii;
    229 
    230   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
    231     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
    232     if( pCons->iColumn==0 && pCons->usable
    233            && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
    234       struct sqlite3_index_constraint_usage *pUsage;
    235       pUsage = &pIdxInfo->aConstraintUsage[ii];
    236       pUsage->omit = 0;
    237       pUsage->argvIndex = 1;
    238       return SQLITE_OK;
    239     }
    240   }
    241 
    242   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
    243     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
    244     if( pCons->iColumn==0 && pCons->usable
    245            && pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
    246       struct sqlite3_index_constraint_usage *pUsage;
    247       pUsage = &pIdxInfo->aConstraintUsage[ii];
    248       pUsage->omit = 1;
    249       pUsage->argvIndex = 1;
    250       return SQLITE_OK;
    251     }
    252   }
    253 
    254   return SQLITE_OK;
    255 }
    256 
    257 /*
    258 ** A virtual table module that provides read-only access to a
    259 ** Tcl global variable namespace.
    260 */
    261 static sqlite3_module tclvarModule = {
    262   0,                         /* iVersion */
    263   tclvarConnect,
    264   tclvarConnect,
    265   tclvarBestIndex,
    266   tclvarDisconnect,
    267   tclvarDisconnect,
    268   tclvarOpen,                  /* xOpen - open a cursor */
    269   tclvarClose,                 /* xClose - close a cursor */
    270   tclvarFilter,                /* xFilter - configure scan constraints */
    271   tclvarNext,                  /* xNext - advance a cursor */
    272   tclvarEof,                   /* xEof - check for end of scan */
    273   tclvarColumn,                /* xColumn - read data */
    274   tclvarRowid,                 /* xRowid - read data */
    275   0,                           /* xUpdate */
    276   0,                           /* xBegin */
    277   0,                           /* xSync */
    278   0,                           /* xCommit */
    279   0,                           /* xRollback */
    280   0,                           /* xFindMethod */
    281   0,                           /* xRename */
    282 };
    283 
    284 /*
    285 ** Decode a pointer to an sqlite3 object.
    286 */
    287 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
    288 
    289 /*
    290 ** Register the echo virtual table module.
    291 */
    292 static int register_tclvar_module(
    293   ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
    294   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
    295   int objc,              /* Number of arguments */
    296   Tcl_Obj *CONST objv[]  /* Command arguments */
    297 ){
    298   sqlite3 *db;
    299   if( objc!=2 ){
    300     Tcl_WrongNumArgs(interp, 1, objv, "DB");
    301     return TCL_ERROR;
    302   }
    303   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
    304 #ifndef SQLITE_OMIT_VIRTUALTABLE
    305   sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp);
    306 #endif
    307   return TCL_OK;
    308 }
    309 
    310 #endif
    311 
    312 
    313 /*
    314 ** Register commands with the TCL interpreter.
    315 */
    316 int Sqlitetesttclvar_Init(Tcl_Interp *interp){
    317 #ifndef SQLITE_OMIT_VIRTUALTABLE
    318   static struct {
    319      char *zName;
    320      Tcl_ObjCmdProc *xProc;
    321      void *clientData;
    322   } aObjCmd[] = {
    323      { "register_tclvar_module",   register_tclvar_module, 0 },
    324   };
    325   int i;
    326   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    327     Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
    328         aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
    329   }
    330 #endif
    331   return TCL_OK;
    332 }
    333