/ Check-in [1d41c161]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:New test cases and infrastructure for testing the xBestIndex method of virtual tables.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 1d41c161165006d6c2af47e476f05fb13039f8b8
User & Date: drh 2016-03-01 22:48:00
Context
2016-03-02
00:58
Add the optional -DSERIES_OMIT_CONSTRAINT_VERIFY=0 option to the series.c extension that implements the generate_series() virtual table. check-in: 3d9daa92 user: drh tags: trunk
2016-03-01
22:48
New test cases and infrastructure for testing the xBestIndex method of virtual tables. check-in: 1d41c161 user: drh tags: trunk
22:41
Improved debugging output with wheretrace. Fix some typos in test script comments. check-in: 13a37fd4 user: drh tags: trunk
18:35
Fix a memory leak in the test code on this branch. Closed-Leaf check-in: 7a1add56 user: dan tags: test-bestindex
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to main.mk.

   281    281     $(TOP)/src/test6.c \
   282    282     $(TOP)/src/test7.c \
   283    283     $(TOP)/src/test8.c \
   284    284     $(TOP)/src/test9.c \
   285    285     $(TOP)/src/test_autoext.c \
   286    286     $(TOP)/src/test_async.c \
   287    287     $(TOP)/src/test_backup.c \
          288  +  $(TOP)/src/test_bestindex.c \
   288    289     $(TOP)/src/test_blob.c \
   289    290     $(TOP)/src/test_btree.c \
   290    291     $(TOP)/src/test_config.c \
   291    292     $(TOP)/src/test_demovfs.c \
   292    293     $(TOP)/src/test_devsym.c \
   293    294     $(TOP)/src/test_fs.c \
   294    295     $(TOP)/src/test_func.c \

Changes to src/tclsqlite.c.

  3770   3770       extern int Sqlitetestrtree_Init(Tcl_Interp*);
  3771   3771       extern int Sqlitequota_Init(Tcl_Interp*);
  3772   3772       extern int Sqlitemultiplex_Init(Tcl_Interp*);
  3773   3773       extern int SqliteSuperlock_Init(Tcl_Interp*);
  3774   3774       extern int SqlitetestSyscall_Init(Tcl_Interp*);
  3775   3775       extern int Fts5tcl_Init(Tcl_Interp *);
  3776   3776       extern int SqliteRbu_Init(Tcl_Interp*);
         3777  +    extern int Sqlitetesttcl_Init(Tcl_Interp*);
  3777   3778   #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
  3778   3779       extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
  3779   3780   #endif
  3780   3781   
  3781   3782   #ifdef SQLITE_ENABLE_ZIPVFS
  3782   3783       extern int Zipvfs_Init(Tcl_Interp*);
  3783   3784       Zipvfs_Init(interp);
................................................................................
  3814   3815       Sqlitetestrtree_Init(interp);
  3815   3816       Sqlitequota_Init(interp);
  3816   3817       Sqlitemultiplex_Init(interp);
  3817   3818       SqliteSuperlock_Init(interp);
  3818   3819       SqlitetestSyscall_Init(interp);
  3819   3820       Fts5tcl_Init(interp);
  3820   3821       SqliteRbu_Init(interp);
         3822  +    Sqlitetesttcl_Init(interp);
  3821   3823   
  3822   3824   #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
  3823   3825       Sqlitetestfts3_Init(interp);
  3824   3826   #endif
  3825   3827   
  3826   3828       Tcl_CreateObjCommand(
  3827   3829           interp, "load_testfixture_extensions", init_all_cmd, 0, 0

Added src/test_bestindex.c.

            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) and when the xBestIndex() method is invoked. When it is created
           32  +** (or connected), it is invoked as follows:
           33  +**
           34  +**   tcl_command xConnect
           35  +**
           36  +** In this case the return value of the script is passed to the
           37  +** sqlite3_declare_vtab() function to create the virtual table schema.
           38  +**
           39  +** When the xBestIndex() method is called by SQLite, the Tcl command is
           40  +** invoked as:
           41  +**
           42  +**   tcl_command xBestIndex CONSTRAINTS ORDERBY MASK
           43  +**
           44  +** where CONSTRAINTS is a tcl representation of the aConstraints[] array,
           45  +** ORDERBY is a representation of the contents of the aOrderBy[] array and
           46  +** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual
           47  +** table is declared as:
           48  +**
           49  +**   CREATE TABLE x1(a, b, c)
           50  +**
           51  +** and the query is:
           52  +**
           53  +**   SELECT * FROM x1 WHERE a=? AND c<? ORDER BY b, c;
           54  +**
           55  +** then the Tcl command is:
           56  +**
           57  +**   tcl_command xBestIndex                                  \
           58  +**     {{op eq column 0 usable 1} {op lt column 2 usable 1}} \
           59  +**     {{column 1 desc 0} {column 2 desc 0}}                 \
           60  +**     7
           61  +**
           62  +** The return value of the script is a list of key-value pairs used to
           63  +** populate the output fields of the sqlite3_index_info structure. Possible
           64  +** keys and the usage of the accompanying values are:
           65  +** 
           66  +**   "orderby"          (value of orderByConsumed flag)
           67  +**   "cost"             (value of estimatedCost field)
           68  +**   "rows"             (value of estimatedRows field)
           69  +**   "use"              (index of used constraint in aConstraint[])
           70  +**   "idxnum"           (value of idxNum field)
           71  +**   "idxstr"           (value of idxStr field)
           72  +**
           73  +** Refer to code below for further details.
           74  +*/
           75  +
           76  +
           77  +#include "sqliteInt.h"
           78  +#include "tcl.h"
           79  +
           80  +#ifndef SQLITE_OMIT_VIRTUALTABLE
           81  +
           82  +typedef struct tcl_vtab tcl_vtab;
           83  +typedef struct tcl_cursor tcl_cursor;
           84  +
           85  +/* 
           86  +** A fs virtual-table object 
           87  +*/
           88  +struct tcl_vtab {
           89  +  sqlite3_vtab base;
           90  +  Tcl_Interp *interp;
           91  +  Tcl_Obj *pCmd;
           92  +};
           93  +
           94  +/* A tcl cursor object */
           95  +struct tcl_cursor {
           96  +  sqlite3_vtab_cursor base;
           97  +};
           98  +
           99  +/*
          100  +** This function is the implementation of both the xConnect and xCreate
          101  +** methods of the fs virtual table.
          102  +**
          103  +** The argv[] array contains the following:
          104  +**
          105  +**   argv[0]   -> module name  ("fs")
          106  +**   argv[1]   -> database name
          107  +**   argv[2]   -> table name
          108  +**   argv[...] -> other module argument fields.
          109  +*/
          110  +static int tclConnect(
          111  +  sqlite3 *db,
          112  +  void *pAux,
          113  +  int argc, const char *const*argv,
          114  +  sqlite3_vtab **ppVtab,
          115  +  char **pzErr
          116  +){
          117  +  Tcl_Interp *interp = (Tcl_Interp*)pAux;
          118  +  tcl_vtab *pTab;
          119  +  const char *zCmd;
          120  +  Tcl_Obj *pScript = 0;
          121  +  int rc;
          122  +
          123  +  if( argc!=4 ){
          124  +    *pzErr = sqlite3_mprintf("wrong number of arguments");
          125  +    return SQLITE_ERROR;
          126  +  }
          127  +  zCmd = argv[3];
          128  +
          129  +  pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab));
          130  +  if( pTab==0 ) return SQLITE_NOMEM;
          131  +  memset(pTab, 0, sizeof(tcl_vtab));
          132  +
          133  +  pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
          134  +  pTab->interp = interp;
          135  +  Tcl_IncrRefCount(pTab->pCmd);
          136  +
          137  +  pScript = Tcl_DuplicateObj(pTab->pCmd);
          138  +  Tcl_IncrRefCount(pScript);
          139  +  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
          140  +
          141  +  rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
          142  +  if( rc!=TCL_OK ){
          143  +    *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
          144  +    rc = SQLITE_ERROR;
          145  +  }else{
          146  +    rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
          147  +  }
          148  +
          149  +  if( rc!=SQLITE_OK ){
          150  +    sqlite3_free(pTab);
          151  +    pTab = 0;
          152  +  }
          153  +
          154  +  *ppVtab = &pTab->base;
          155  +  return rc;
          156  +}
          157  +
          158  +/* The xDisconnect and xDestroy methods are also the same */
          159  +static int tclDisconnect(sqlite3_vtab *pVtab){
          160  +  tcl_vtab *pTab = (tcl_vtab*)pVtab;
          161  +  Tcl_DecrRefCount(pTab->pCmd);
          162  +  sqlite3_free(pTab);
          163  +  return SQLITE_OK;
          164  +}
          165  +
          166  +/*
          167  +** Open a new tcl cursor.
          168  +*/
          169  +static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
          170  +  tcl_cursor *pCur;
          171  +  pCur = sqlite3_malloc(sizeof(tcl_cursor));
          172  +  if( pCur==0 ) return SQLITE_NOMEM;
          173  +  memset(pCur, 0, sizeof(tcl_cursor));
          174  +  *ppCursor = &pCur->base;
          175  +  return SQLITE_OK;
          176  +}
          177  +
          178  +/*
          179  +** Close a tcl cursor.
          180  +*/
          181  +static int tclClose(sqlite3_vtab_cursor *cur){
          182  +  tcl_cursor *pCur = (tcl_cursor *)cur;
          183  +  sqlite3_free(pCur);
          184  +  return SQLITE_OK;
          185  +}
          186  +
          187  +static int tclNext(sqlite3_vtab_cursor *cur){
          188  +  return SQLITE_OK;
          189  +}
          190  +
          191  +static int tclFilter(
          192  +  sqlite3_vtab_cursor *pVtabCursor, 
          193  +  int idxNum, const char *idxStr,
          194  +  int argc, sqlite3_value **argv
          195  +){
          196  +  return SQLITE_OK;
          197  +}
          198  +
          199  +static int tclColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
          200  +  return SQLITE_OK;
          201  +}
          202  +
          203  +static int tclRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          204  +  return SQLITE_OK;
          205  +}
          206  +
          207  +static int tclEof(sqlite3_vtab_cursor *cur){
          208  +  return 1;
          209  +}
          210  +
          211  +static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
          212  +  tcl_vtab *pTab = (tcl_vtab*)tab;
          213  +  Tcl_Interp *interp = pTab->interp;
          214  +  Tcl_Obj *pArg;
          215  +  Tcl_Obj *pScript;
          216  +  int ii;
          217  +  int rc = SQLITE_OK;
          218  +
          219  +  pScript = Tcl_DuplicateObj(pTab->pCmd);
          220  +  Tcl_IncrRefCount(pScript);
          221  +  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
          222  +
          223  +  pArg = Tcl_NewObj();
          224  +  Tcl_IncrRefCount(pArg);
          225  +  for(ii=0; ii<pIdxInfo->nConstraint; ii++){
          226  +    struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
          227  +    Tcl_Obj *pElem = Tcl_NewObj();
          228  +    const char *zOp = "?";
          229  +
          230  +    Tcl_IncrRefCount(pElem);
          231  +
          232  +    switch( pCons->op ){
          233  +      case SQLITE_INDEX_CONSTRAINT_EQ:
          234  +        zOp = "eq"; break;
          235  +      case SQLITE_INDEX_CONSTRAINT_GT:
          236  +        zOp = "gt"; break;
          237  +      case SQLITE_INDEX_CONSTRAINT_LE:
          238  +        zOp = "le"; break;
          239  +      case SQLITE_INDEX_CONSTRAINT_LT:
          240  +        zOp = "lt"; break;
          241  +      case SQLITE_INDEX_CONSTRAINT_GE:
          242  +        zOp = "ge"; break;
          243  +      case SQLITE_INDEX_CONSTRAINT_MATCH:
          244  +        zOp = "match"; break;
          245  +      case SQLITE_INDEX_CONSTRAINT_LIKE:
          246  +        zOp = "like"; break;
          247  +      case SQLITE_INDEX_CONSTRAINT_GLOB:
          248  +        zOp = "glob"; break;
          249  +      case SQLITE_INDEX_CONSTRAINT_REGEXP:
          250  +        zOp = "regexp"; break;
          251  +    }
          252  +
          253  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
          254  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1));
          255  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
          256  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn));
          257  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
          258  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
          259  +
          260  +    Tcl_ListObjAppendElement(0, pArg, pElem);
          261  +    Tcl_DecrRefCount(pElem);
          262  +  }
          263  +
          264  +  Tcl_ListObjAppendElement(0, pScript, pArg);
          265  +  Tcl_DecrRefCount(pArg);
          266  +
          267  +  pArg = Tcl_NewObj();
          268  +  Tcl_IncrRefCount(pArg);
          269  +  for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
          270  +    struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
          271  +    Tcl_Obj *pElem = Tcl_NewObj();
          272  +    Tcl_IncrRefCount(pElem);
          273  +
          274  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
          275  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn));
          276  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
          277  +    Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
          278  +
          279  +    Tcl_ListObjAppendElement(0, pArg, pElem);
          280  +    Tcl_DecrRefCount(pElem);
          281  +  }
          282  +
          283  +  Tcl_ListObjAppendElement(0, pScript, pArg);
          284  +  Tcl_DecrRefCount(pArg);
          285  +
          286  +  Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
          287  +
          288  +  rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
          289  +  Tcl_DecrRefCount(pScript);
          290  +  if( rc!=TCL_OK ){
          291  +    const char *zErr = Tcl_GetStringResult(interp);
          292  +    rc = SQLITE_ERROR;
          293  +    pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
          294  +  }else{
          295  +    /* Analyze the scripts return value. The return value should be a tcl 
          296  +    ** list object with an even number of elements. The first element of each
          297  +    ** pair must be one of:
          298  +    ** 
          299  +    **   "orderby"          (value of orderByConsumed flag)
          300  +    **   "cost"             (value of estimatedCost field)
          301  +    **   "rows"             (value of estimatedRows field)
          302  +    **   "use"              (index of used constraint in aConstraint[])
          303  +    **   "idxnum"           (value of idxNum field)
          304  +    **   "idxstr"           (value of idxStr field)
          305  +    **   "omit"             (index of omitted constraint in aConstraint[])
          306  +    */
          307  +    Tcl_Obj *pRes = Tcl_GetObjResult(interp);
          308  +    Tcl_Obj **apElem = 0;
          309  +    int nElem;
          310  +    rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
          311  +    if( rc!=TCL_OK ){
          312  +      const char *zErr = Tcl_GetStringResult(interp);
          313  +      rc = SQLITE_ERROR;
          314  +      pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
          315  +    }else{
          316  +      int iArgv = 1;
          317  +      for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
          318  +        const char *zCmd = Tcl_GetString(apElem[ii]);
          319  +        Tcl_Obj *p = apElem[ii+1];
          320  +        if( sqlite3_stricmp("cost", zCmd)==0 ){
          321  +          rc = Tcl_GetDoubleFromObj(interp, p, &pIdxInfo->estimatedCost);
          322  +        }else
          323  +        if( sqlite3_stricmp("orderby", zCmd)==0 ){
          324  +          rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed);
          325  +        }else
          326  +        if( sqlite3_stricmp("idxnum", zCmd)==0 ){
          327  +          rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum);
          328  +        }else
          329  +        if( sqlite3_stricmp("idxstr", zCmd)==0 ){
          330  +          sqlite3_free(pIdxInfo->idxStr);
          331  +          pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p));
          332  +          pIdxInfo->needToFreeIdxStr = 1;
          333  +        }else
          334  +        if( sqlite3_stricmp("rows", zCmd)==0 ){
          335  +          rc = Tcl_GetWideIntFromObj(interp, p, &pIdxInfo->estimatedRows);
          336  +        }else
          337  +        if( sqlite3_stricmp("use", zCmd)==0 
          338  +         || sqlite3_stricmp("omit", zCmd)==0 
          339  +        ){
          340  +          int iCons;
          341  +          rc = Tcl_GetIntFromObj(interp, p, &iCons);
          342  +          if( rc==SQLITE_OK ){
          343  +            if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
          344  +              rc = SQLITE_ERROR;
          345  +              pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons);
          346  +            }else{
          347  +              int bOmit = (zCmd[0]=='o' || zCmd[0]=='O');
          348  +              pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++;
          349  +              pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
          350  +            }
          351  +          }
          352  +        }else{
          353  +          rc = SQLITE_ERROR;
          354  +          pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
          355  +        }
          356  +        if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){
          357  +          const char *zErr = Tcl_GetStringResult(interp);
          358  +          pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
          359  +        }
          360  +      }
          361  +    }
          362  +  }
          363  +
          364  +  return rc;
          365  +}
          366  +
          367  +/*
          368  +** A virtual table module that provides read-only access to a
          369  +** Tcl global variable namespace.
          370  +*/
          371  +static sqlite3_module tclModule = {
          372  +  0,                         /* iVersion */
          373  +  tclConnect,
          374  +  tclConnect,
          375  +  tclBestIndex,
          376  +  tclDisconnect, 
          377  +  tclDisconnect,
          378  +  tclOpen,                      /* xOpen - open a cursor */
          379  +  tclClose,                     /* xClose - close a cursor */
          380  +  tclFilter,                    /* xFilter - configure scan constraints */
          381  +  tclNext,                      /* xNext - advance a cursor */
          382  +  tclEof,                       /* xEof - check for end of scan */
          383  +  tclColumn,                    /* xColumn - read data */
          384  +  tclRowid,                     /* xRowid - read data */
          385  +  0,                           /* xUpdate */
          386  +  0,                           /* xBegin */
          387  +  0,                           /* xSync */
          388  +  0,                           /* xCommit */
          389  +  0,                           /* xRollback */
          390  +  0,                           /* xFindMethod */
          391  +  0,                           /* xRename */
          392  +};
          393  +
          394  +/*
          395  +** Decode a pointer to an sqlite3 object.
          396  +*/
          397  +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
          398  +
          399  +/*
          400  +** Register the echo virtual table module.
          401  +*/
          402  +static int register_tcl_module(
          403  +  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
          404  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
          405  +  int objc,              /* Number of arguments */
          406  +  Tcl_Obj *CONST objv[]  /* Command arguments */
          407  +){
          408  +  sqlite3 *db;
          409  +  if( objc!=2 ){
          410  +    Tcl_WrongNumArgs(interp, 1, objv, "DB");
          411  +    return TCL_ERROR;
          412  +  }
          413  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
          414  +#ifndef SQLITE_OMIT_VIRTUALTABLE
          415  +  sqlite3_create_module(db, "tcl", &tclModule, (void *)interp);
          416  +#endif
          417  +  return TCL_OK;
          418  +}
          419  +
          420  +#endif
          421  +
          422  +
          423  +/*
          424  +** Register commands with the TCL interpreter.
          425  +*/
          426  +int Sqlitetesttcl_Init(Tcl_Interp *interp){
          427  +#ifndef SQLITE_OMIT_VIRTUALTABLE
          428  +  static struct {
          429  +     char *zName;
          430  +     Tcl_ObjCmdProc *xProc;
          431  +     void *clientData;
          432  +  } aObjCmd[] = {
          433  +     { "register_tcl_module",   register_tcl_module, 0 },
          434  +  };
          435  +  int i;
          436  +  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
          437  +    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
          438  +        aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
          439  +  }
          440  +#endif
          441  +  return TCL_OK;
          442  +}

Added test/bestindex1.test.

            1  +# 2016-03-01
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# 
           12  +#
           13  +
           14  +set testdir [file dirname $argv0]
           15  +source $testdir/tester.tcl
           16  +set testprefix bestindex1
           17  +
           18  +register_tcl_module db
           19  +
           20  +proc vtab_command {method args} {
           21  +  switch -- $method {
           22  +    xConnect {
           23  +      return "CREATE TABLE t1(a, b, c)"
           24  +    }
           25  +
           26  +    xBestIndex {
           27  +      set clist [lindex $args 0]
           28  +      if {[llength $clist]!=1} { error "unexpected constraint list" }
           29  +      catch { array unset C }
           30  +      array set C [lindex $clist 0]
           31  +      if {$C(usable)} {
           32  +        return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
           33  +      } else {
           34  +        return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
           35  +      }
           36  +    }
           37  +
           38  +  }
           39  +
           40  +  return {}
           41  +}
           42  +
           43  +do_execsql_test 1.0 {
           44  +  CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
           45  +} {}
           46  +
           47  +do_eqp_test 1.1 {
           48  +  SELECT * FROM x1 WHERE a = 'abc'
           49  +} {
           50  +  0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
           51  +}
           52  +
           53  +do_eqp_test 1.2 {
           54  +  SELECT * FROM x1 WHERE a IN ('abc', 'def');
           55  +} {
           56  +  0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
           57  +  0 0 0 {EXECUTE LIST SUBQUERY 1}
           58  +}
           59  +
           60  +finish_test