modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test_bestindex.c (about)

     1  /*
     2  ** 2016-03-01
     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 xBestIndex method and the query
    13  ** planner.
    14  */
    15  
    16  
    17  /*
    18  ** INSTRUCTIONS
    19  **
    20  ** This module exports a single tcl command - [register_tcl_module]. When
    21  ** invoked, it registers a special virtual table module with a database
    22  ** connection.
    23  **
    24  ** The virtual table is currently read-only. And always returns zero rows.
    25  ** It is created with a single argument - the name of a Tcl command - as
    26  ** follows:
    27  **
    28  **   CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
    29  **
    30  ** The command [tcl_command] is invoked when the table is first created (or
    31  ** connected), when the xBestIndex() method is invoked and when the xFilter()
    32  ** method is called. When it is created (or connected), it is invoked as
    33  ** follows:
    34  **
    35  **   tcl_command xConnect
    36  **
    37  ** In this case the return value of the script is passed to the
    38  ** sqlite3_declare_vtab() function to create the virtual table schema.
    39  **
    40  ** When the xBestIndex() method is called by SQLite, the Tcl command is
    41  ** invoked as:
    42  **
    43  **   tcl_command xBestIndex CONSTRAINTS ORDERBY MASK
    44  **
    45  ** where CONSTRAINTS is a tcl representation of the aConstraints[] array,
    46  ** ORDERBY is a representation of the contents of the aOrderBy[] array and
    47  ** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual
    48  ** table is declared as:
    49  **
    50  **   CREATE TABLE x1(a, b, c)
    51  **
    52  ** and the query is:
    53  **
    54  **   SELECT * FROM x1 WHERE a=? AND c<? ORDER BY b, c;
    55  **
    56  ** then the Tcl command is:
    57  **
    58  **   tcl_command xBestIndex                                  \
    59  **     {{op eq column 0 usable 1} {op lt column 2 usable 1}} \
    60  **     {{column 1 desc 0} {column 2 desc 0}}                 \
    61  **     7
    62  **
    63  ** The return value of the script is a list of key-value pairs used to
    64  ** populate the output fields of the sqlite3_index_info structure. Possible
    65  ** keys and the usage of the accompanying values are:
    66  ** 
    67  **   "orderby"          (value of orderByConsumed flag)
    68  **   "cost"             (value of estimatedCost field)
    69  **   "rows"             (value of estimatedRows field)
    70  **   "use"              (index of used constraint in aConstraint[])
    71  **   "omit"             (like "use", but also sets omit flag)
    72  **   "idxnum"           (value of idxNum field)
    73  **   "idxstr"           (value of idxStr field)
    74  **
    75  ** Refer to code below for further details.
    76  **
    77  ** When SQLite calls the xFilter() method, this module invokes the following
    78  ** Tcl script:
    79  **
    80  **   tcl_command xFilter IDXNUM IDXSTR ARGLIST
    81  **
    82  ** IDXNUM and IDXSTR are the values of the idxNum and idxStr parameters
    83  ** passed to xFilter. ARGLIST is a Tcl list containing each of the arguments
    84  ** passed to xFilter in text form.
    85  **
    86  ** As with xBestIndex(), the return value of the script is interpreted as a
    87  ** list of key-value pairs. There is currently only one key defined - "sql".
    88  ** The value must be the full text of an SQL statement that returns the data
    89  ** for the current scan. The leftmost column returned by the SELECT is assumed
    90  ** to contain the rowid. Other columns must follow, in order from left to
    91  ** right.
    92  */
    93  
    94  
    95  #include "sqliteInt.h"
    96  #if defined(INCLUDE_SQLITE_TCL_H)
    97  #  include "sqlite_tcl.h"
    98  #else
    99  #  include "tcl.h"
   100  #endif
   101  
   102  #ifndef SQLITE_OMIT_VIRTUALTABLE
   103  
   104  typedef struct tcl_vtab tcl_vtab;
   105  typedef struct tcl_cursor tcl_cursor;
   106  
   107  /* 
   108  ** A fs virtual-table object 
   109  */
   110  struct tcl_vtab {
   111    sqlite3_vtab base;
   112    Tcl_Interp *interp;
   113    Tcl_Obj *pCmd;
   114    sqlite3 *db;
   115  };
   116  
   117  /* A tcl cursor object */
   118  struct tcl_cursor {
   119    sqlite3_vtab_cursor base;
   120    sqlite3_stmt *pStmt;            /* Read data from here */
   121  };
   122  
   123  /*
   124  ** Dequote string z in place.
   125  */
   126  static void tclDequote(char *z){
   127    char q = z[0];
   128  
   129    /* Set stack variable q to the close-quote character */
   130    if( q=='[' || q=='\'' || q=='"' || q=='`' ){
   131      int iIn = 1;
   132      int iOut = 0;
   133      if( q=='[' ) q = ']';  
   134  
   135      while( ALWAYS(z[iIn]) ){
   136        if( z[iIn]==q ){
   137          if( z[iIn+1]!=q ){
   138            /* Character iIn was the close quote. */
   139            iIn++;
   140            break;
   141          }else{
   142            /* Character iIn and iIn+1 form an escaped quote character. Skip
   143            ** the input cursor past both and copy a single quote character 
   144            ** to the output buffer. */
   145            iIn += 2;
   146            z[iOut++] = q;
   147          }
   148        }else{
   149          z[iOut++] = z[iIn++];
   150        }
   151      }
   152  
   153      z[iOut] = '\0';
   154    }
   155  }
   156  
   157  /*
   158  ** This function is the implementation of both the xConnect and xCreate
   159  ** methods of the fs virtual table.
   160  **
   161  ** The argv[] array contains the following:
   162  **
   163  **   argv[0]   -> module name  ("fs")
   164  **   argv[1]   -> database name
   165  **   argv[2]   -> table name
   166  **   argv[...] -> other module argument fields.
   167  */
   168  static int tclConnect(
   169    sqlite3 *db,
   170    void *pAux,
   171    int argc, const char *const*argv,
   172    sqlite3_vtab **ppVtab,
   173    char **pzErr
   174  ){
   175    Tcl_Interp *interp = (Tcl_Interp*)pAux;
   176    tcl_vtab *pTab = 0;
   177    char *zCmd = 0;
   178    Tcl_Obj *pScript = 0;
   179    int rc = SQLITE_OK;
   180  
   181    if( argc!=4 ){
   182      *pzErr = sqlite3_mprintf("wrong number of arguments");
   183      return SQLITE_ERROR;
   184    }
   185  
   186    zCmd = sqlite3_malloc64(strlen(argv[3])+1);
   187    pTab = (tcl_vtab*)sqlite3_malloc64(sizeof(tcl_vtab));
   188    if( zCmd && pTab ){
   189      memcpy(zCmd, argv[3], strlen(argv[3])+1);
   190      tclDequote(zCmd);
   191      memset(pTab, 0, sizeof(tcl_vtab));
   192  
   193      pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
   194      pTab->interp = interp;
   195      pTab->db = db;
   196      Tcl_IncrRefCount(pTab->pCmd);
   197  
   198      pScript = Tcl_DuplicateObj(pTab->pCmd);
   199      Tcl_IncrRefCount(pScript);
   200      Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
   201  
   202      rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
   203      if( rc!=TCL_OK ){
   204        *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
   205        rc = SQLITE_ERROR;
   206      }else{
   207        rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
   208      }
   209  
   210      if( rc!=SQLITE_OK ){
   211        sqlite3_free(pTab);
   212        pTab = 0;
   213      }
   214    }else{
   215      rc = SQLITE_NOMEM;
   216    }
   217  
   218    sqlite3_free(zCmd);
   219    *ppVtab = &pTab->base;
   220    return rc;
   221  }
   222  
   223  /* The xDisconnect and xDestroy methods are also the same */
   224  static int tclDisconnect(sqlite3_vtab *pVtab){
   225    tcl_vtab *pTab = (tcl_vtab*)pVtab;
   226    Tcl_DecrRefCount(pTab->pCmd);
   227    sqlite3_free(pTab);
   228    return SQLITE_OK;
   229  }
   230  
   231  /*
   232  ** Open a new tcl cursor.
   233  */
   234  static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
   235    tcl_cursor *pCur;
   236    pCur = sqlite3_malloc(sizeof(tcl_cursor));
   237    if( pCur==0 ) return SQLITE_NOMEM;
   238    memset(pCur, 0, sizeof(tcl_cursor));
   239    *ppCursor = &pCur->base;
   240    return SQLITE_OK;
   241  }
   242  
   243  /*
   244  ** Close a tcl cursor.
   245  */
   246  static int tclClose(sqlite3_vtab_cursor *cur){
   247    tcl_cursor *pCur = (tcl_cursor *)cur;
   248    if( pCur ){
   249      sqlite3_finalize(pCur->pStmt);
   250      sqlite3_free(pCur);
   251    }
   252    return SQLITE_OK;
   253  }
   254  
   255  static int tclNext(sqlite3_vtab_cursor *pVtabCursor){
   256    tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
   257    if( pCsr->pStmt ){
   258      tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
   259      int rc = sqlite3_step(pCsr->pStmt);
   260      if( rc!=SQLITE_ROW ){
   261        const char *zErr;
   262        rc = sqlite3_finalize(pCsr->pStmt);
   263        pCsr->pStmt = 0;
   264        if( rc!=SQLITE_OK ){
   265          zErr = sqlite3_errmsg(pTab->db);
   266          pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   267        }
   268      }
   269    }
   270    return SQLITE_OK;
   271  }
   272  
   273  static int tclFilter(
   274    sqlite3_vtab_cursor *pVtabCursor, 
   275    int idxNum, const char *idxStr,
   276    int argc, sqlite3_value **argv
   277  ){
   278    tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
   279    tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
   280    Tcl_Interp *interp = pTab->interp;
   281    Tcl_Obj *pScript;
   282    Tcl_Obj *pArg;
   283    int ii;
   284    int rc;
   285  
   286    pScript = Tcl_DuplicateObj(pTab->pCmd);
   287    Tcl_IncrRefCount(pScript);
   288    Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xFilter", -1));
   289    Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(idxNum));
   290    if( idxStr ){
   291      Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(idxStr, -1));
   292    }else{
   293      Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("", -1));
   294    }
   295  
   296    pArg = Tcl_NewObj();
   297    Tcl_IncrRefCount(pArg);
   298    for(ii=0; ii<argc; ii++){
   299      const char *zVal = (const char*)sqlite3_value_text(argv[ii]);
   300      Tcl_Obj *pVal;
   301      if( zVal==0 ){
   302        pVal = Tcl_NewObj();
   303      }else{
   304        pVal = Tcl_NewStringObj(zVal, -1);
   305      }
   306      Tcl_ListObjAppendElement(interp, pArg, pVal);
   307    }
   308    Tcl_ListObjAppendElement(interp, pScript, pArg);
   309    Tcl_DecrRefCount(pArg);
   310  
   311    rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
   312    if( rc!=TCL_OK ){
   313      const char *zErr = Tcl_GetStringResult(interp);
   314      rc = SQLITE_ERROR;
   315      pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   316    }else{
   317      /* Analyze the scripts return value. The return value should be a tcl 
   318      ** list object with an even number of elements. The first element of each
   319      ** pair must be one of:
   320      ** 
   321      **   "sql"          (SQL statement to return data)
   322      */
   323      Tcl_Obj *pRes = Tcl_GetObjResult(interp);
   324      Tcl_Obj **apElem = 0;
   325      int nElem;
   326      rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
   327      if( rc!=TCL_OK ){
   328        const char *zErr = Tcl_GetStringResult(interp);
   329        rc = SQLITE_ERROR;
   330        pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   331      }else{
   332        for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
   333          const char *zCmd = Tcl_GetString(apElem[ii]);
   334          Tcl_Obj *p = apElem[ii+1];
   335          if( sqlite3_stricmp("sql", zCmd)==0 ){
   336            const char *zSql = Tcl_GetString(p);
   337            rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
   338            if( rc!=SQLITE_OK ){
   339              const char *zErr = sqlite3_errmsg(pTab->db);
   340              pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zErr);
   341            }
   342          }else{
   343            rc = SQLITE_ERROR;
   344            pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
   345          }
   346        }
   347      }
   348    }
   349  
   350    if( rc==SQLITE_OK ){
   351      rc = tclNext(pVtabCursor);
   352    }
   353    return rc;
   354  }
   355  
   356  static int tclColumn(
   357    sqlite3_vtab_cursor *pVtabCursor, 
   358    sqlite3_context *ctx, 
   359    int i
   360  ){
   361    tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
   362    sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
   363    return SQLITE_OK;
   364  }
   365  
   366  static int tclRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
   367    tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
   368    *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
   369    return SQLITE_OK;
   370  }
   371  
   372  static int tclEof(sqlite3_vtab_cursor *pVtabCursor){
   373    tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
   374    return (pCsr->pStmt==0);
   375  }
   376  
   377  static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
   378    tcl_vtab *pTab = (tcl_vtab*)tab;
   379    Tcl_Interp *interp = pTab->interp;
   380    Tcl_Obj *pArg;
   381    Tcl_Obj *pScript;
   382    int ii;
   383    int rc = SQLITE_OK;
   384  
   385    pScript = Tcl_DuplicateObj(pTab->pCmd);
   386    Tcl_IncrRefCount(pScript);
   387    Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
   388  
   389    pArg = Tcl_NewObj();
   390    Tcl_IncrRefCount(pArg);
   391    for(ii=0; ii<pIdxInfo->nConstraint; ii++){
   392      struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
   393      Tcl_Obj *pElem = Tcl_NewObj();
   394      const char *zOp = "?";
   395  
   396      Tcl_IncrRefCount(pElem);
   397  
   398      switch( pCons->op ){
   399        case SQLITE_INDEX_CONSTRAINT_EQ:
   400          zOp = "eq"; break;
   401        case SQLITE_INDEX_CONSTRAINT_GT:
   402          zOp = "gt"; break;
   403        case SQLITE_INDEX_CONSTRAINT_LE:
   404          zOp = "le"; break;
   405        case SQLITE_INDEX_CONSTRAINT_LT:
   406          zOp = "lt"; break;
   407        case SQLITE_INDEX_CONSTRAINT_GE:
   408          zOp = "ge"; break;
   409        case SQLITE_INDEX_CONSTRAINT_MATCH:
   410          zOp = "match"; break;
   411        case SQLITE_INDEX_CONSTRAINT_LIKE:
   412          zOp = "like"; break;
   413        case SQLITE_INDEX_CONSTRAINT_GLOB:
   414          zOp = "glob"; break;
   415        case SQLITE_INDEX_CONSTRAINT_REGEXP:
   416          zOp = "regexp"; break;
   417        case SQLITE_INDEX_CONSTRAINT_NE:
   418          zOp = "ne"; break;
   419        case SQLITE_INDEX_CONSTRAINT_ISNOT:
   420          zOp = "isnot"; break;
   421        case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
   422          zOp = "isnotnull"; break;
   423        case SQLITE_INDEX_CONSTRAINT_ISNULL:
   424          zOp = "isnull"; break;
   425        case SQLITE_INDEX_CONSTRAINT_IS:
   426          zOp = "is"; break;
   427      }
   428  
   429      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
   430      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1));
   431      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
   432      Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn));
   433      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
   434      Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
   435  
   436      Tcl_ListObjAppendElement(0, pArg, pElem);
   437      Tcl_DecrRefCount(pElem);
   438    }
   439  
   440    Tcl_ListObjAppendElement(0, pScript, pArg);
   441    Tcl_DecrRefCount(pArg);
   442  
   443    pArg = Tcl_NewObj();
   444    Tcl_IncrRefCount(pArg);
   445    for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
   446      struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
   447      Tcl_Obj *pElem = Tcl_NewObj();
   448      Tcl_IncrRefCount(pElem);
   449  
   450      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
   451      Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn));
   452      Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
   453      Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
   454  
   455      Tcl_ListObjAppendElement(0, pArg, pElem);
   456      Tcl_DecrRefCount(pElem);
   457    }
   458  
   459    Tcl_ListObjAppendElement(0, pScript, pArg);
   460    Tcl_DecrRefCount(pArg);
   461  
   462    Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
   463  
   464    rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
   465    Tcl_DecrRefCount(pScript);
   466    if( rc!=TCL_OK ){
   467      const char *zErr = Tcl_GetStringResult(interp);
   468      rc = SQLITE_ERROR;
   469      pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   470    }else{
   471      /* Analyze the scripts return value. The return value should be a tcl 
   472      ** list object with an even number of elements. The first element of each
   473      ** pair must be one of:
   474      ** 
   475      **   "orderby"          (value of orderByConsumed flag)
   476      **   "cost"             (value of estimatedCost field)
   477      **   "rows"             (value of estimatedRows field)
   478      **   "use"              (index of used constraint in aConstraint[])
   479      **   "idxnum"           (value of idxNum field)
   480      **   "idxstr"           (value of idxStr field)
   481      **   "omit"             (index of omitted constraint in aConstraint[])
   482      */
   483      Tcl_Obj *pRes = Tcl_GetObjResult(interp);
   484      Tcl_Obj **apElem = 0;
   485      int nElem;
   486      rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
   487      if( rc!=TCL_OK ){
   488        const char *zErr = Tcl_GetStringResult(interp);
   489        rc = SQLITE_ERROR;
   490        pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   491      }else{
   492        int iArgv = 1;
   493        for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
   494          const char *zCmd = Tcl_GetString(apElem[ii]);
   495          Tcl_Obj *p = apElem[ii+1];
   496          if( sqlite3_stricmp("cost", zCmd)==0 ){
   497            rc = Tcl_GetDoubleFromObj(interp, p, &pIdxInfo->estimatedCost);
   498          }else
   499          if( sqlite3_stricmp("orderby", zCmd)==0 ){
   500            rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed);
   501          }else
   502          if( sqlite3_stricmp("idxnum", zCmd)==0 ){
   503            rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum);
   504          }else
   505          if( sqlite3_stricmp("idxstr", zCmd)==0 ){
   506            sqlite3_free(pIdxInfo->idxStr);
   507            pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p));
   508            pIdxInfo->needToFreeIdxStr = 1;
   509          }else
   510          if( sqlite3_stricmp("rows", zCmd)==0 ){
   511            Tcl_WideInt x = 0;
   512            rc = Tcl_GetWideIntFromObj(interp, p, &x);
   513            pIdxInfo->estimatedRows = (tRowcnt)x;
   514          }else
   515          if( sqlite3_stricmp("use", zCmd)==0 
   516           || sqlite3_stricmp("omit", zCmd)==0 
   517          ){
   518            int iCons;
   519            rc = Tcl_GetIntFromObj(interp, p, &iCons);
   520            if( rc==SQLITE_OK ){
   521              if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
   522                rc = SQLITE_ERROR;
   523                pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons);
   524              }else{
   525                int bOmit = (zCmd[0]=='o' || zCmd[0]=='O');
   526                pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++;
   527                pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
   528              }
   529            }
   530          }else{
   531            rc = SQLITE_ERROR;
   532            pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
   533          }
   534          if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){
   535            const char *zErr = Tcl_GetStringResult(interp);
   536            pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
   537          }
   538        }
   539      }
   540    }
   541  
   542    return rc;
   543  }
   544  
   545  /*
   546  ** A virtual table module that provides read-only access to a
   547  ** Tcl global variable namespace.
   548  */
   549  static sqlite3_module tclModule = {
   550    0,                         /* iVersion */
   551    tclConnect,
   552    tclConnect,
   553    tclBestIndex,
   554    tclDisconnect, 
   555    tclDisconnect,
   556    tclOpen,                      /* xOpen - open a cursor */
   557    tclClose,                     /* xClose - close a cursor */
   558    tclFilter,                    /* xFilter - configure scan constraints */
   559    tclNext,                      /* xNext - advance a cursor */
   560    tclEof,                       /* xEof - check for end of scan */
   561    tclColumn,                    /* xColumn - read data */
   562    tclRowid,                     /* xRowid - read data */
   563    0,                           /* xUpdate */
   564    0,                           /* xBegin */
   565    0,                           /* xSync */
   566    0,                           /* xCommit */
   567    0,                           /* xRollback */
   568    0,                           /* xFindMethod */
   569    0,                           /* xRename */
   570  };
   571  
   572  /*
   573  ** Decode a pointer to an sqlite3 object.
   574  */
   575  extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
   576  
   577  /*
   578  ** Register the echo virtual table module.
   579  */
   580  static int SQLITE_TCLAPI register_tcl_module(
   581    ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
   582    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   583    int objc,              /* Number of arguments */
   584    Tcl_Obj *CONST objv[]  /* Command arguments */
   585  ){
   586    sqlite3 *db;
   587    if( objc!=2 ){
   588      Tcl_WrongNumArgs(interp, 1, objv, "DB");
   589      return TCL_ERROR;
   590    }
   591    if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
   592  #ifndef SQLITE_OMIT_VIRTUALTABLE
   593    sqlite3_create_module(db, "tcl", &tclModule, (void *)interp);
   594  #endif
   595    return TCL_OK;
   596  }
   597  
   598  #endif
   599  
   600  
   601  /*
   602  ** Register commands with the TCL interpreter.
   603  */
   604  int Sqlitetesttcl_Init(Tcl_Interp *interp){
   605  #ifndef SQLITE_OMIT_VIRTUALTABLE
   606    static struct {
   607       char *zName;
   608       Tcl_ObjCmdProc *xProc;
   609       void *clientData;
   610    } aObjCmd[] = {
   611       { "register_tcl_module",   register_tcl_module, 0 },
   612    };
   613    int i;
   614    for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
   615      Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
   616          aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
   617    }
   618  #endif
   619    return TCL_OK;
   620  }