]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
New test cases and infrastructure for testing the xBestIndex method of
authordrh <drh@noemail.net>
Tue, 1 Mar 2016 22:48:00 +0000 (22:48 +0000)
committerdrh <drh@noemail.net>
Tue, 1 Mar 2016 22:48:00 +0000 (22:48 +0000)
virtual tables.

FossilOrigin-Name: 1d41c161165006d6c2af47e476f05fb13039f8b8

1  2 
manifest
manifest.uuid
src/test_bestindex.c
test/bestindex1.test

diff --cc manifest
index e8fac4d6a2b374eb2aef0f78e075a29b3902af36,775d6d0fdb47b82d970d8210be2317b6c2b33152..7784cb3c77dfb99f933d02729cb46d966919949b
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Improved\sdebugging\soutput\swith\swheretrace.\s\sFix\ssome\stypos\sin\stest\sscript\ncomments.
- D 2016-03-01T22:41:27.874
 -C Fix\sa\smemory\sleak\sin\sthe\stest\scode\son\sthis\sbranch.
 -D 2016-03-01T18:35:55.112
++C New\stest\scases\sand\sinfrastructure\sfor\stesting\sthe\sxBestIndex\smethod\sof\s\nvirtual\stables.
++D 2016-03-01T22:48:00.766
  F Makefile.in 4e90dc1521879022aa9479268a4cd141d1771142
  F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
  F Makefile.msc 4f319afb7c049d40aff7af6e8c4e7cc2ba18e079
@@@ -371,6 -371,7 +371,7 @@@ F src/test9.c bea1e8cf52aa93695487baded
  F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
  F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
  F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803
 -F src/test_bestindex.c 31ac82f4b21395572e26c6693bf47fdd032d6e61
++F src/test_bestindex.c cd2eb53d7abf082665bca161e6762c4ba395f17a
  F src/test_blob.c b2551a9b5573232db5f66f292307c37067937239
  F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
  F src/test_config.c 0dee90328e3dedf8ba002ee94b6a7e7ea7726fe4
@@@ -492,6 -493,7 +493,7 @@@ F test/backup_malloc.test 7162d604ec2b4
  F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
  F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
  F test/bc_common.tcl 3eda41ef9cda7d5f6c205462c96228b301da4191
 -F test/bestindex1.test 8d8ae7e96a98079f43518cae536535cc52aee49b
++F test/bestindex1.test 41c763428e403663b3e15b2acba7f07b40b22592
  F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
  F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
  F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
@@@ -1451,7 -1453,7 +1453,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P f2c16094a536e7ef62444d0fe38cbee2a4999426
- R 8bf9dc83caf7a5f48b09e10ce3dd712c
 -P 759b9d5b22aa60cc1d6b606f81eb7366c28cbcbe
 -R e9150850334c910fa82ad79b36b013cf
 -U dan
 -Z bc3fb098cb9eaf89cb42b29f1b53a09a
++P 13a37fd487ce7d4f98a12f7a67a9c05dadc66557 7a1add56341f43dc41adc7b370e58860f4dd50a3
++R 0985007e1a98dc617470e125c83c71f2
++T +closed 7a1add56341f43dc41adc7b370e58860f4dd50a3
 +U drh
- Z cbae5a3ad8eb0cbd1b6534802cfd9e9b
++Z 971ab7ec7b38808209f8a28c1ffc2c5d
diff --cc manifest.uuid
index 1fa5c8f4d5911a70c85d8b13065a6667f6dbd12a,0849f0a94150483288dc023767f8a519bbe71313..ab5413d9af0a204641dec4805ea697ad3884963d
@@@ -1,1 -1,1 +1,1 @@@
- 13a37fd487ce7d4f98a12f7a67a9c05dadc66557
 -7a1add56341f43dc41adc7b370e58860f4dd50a3
++1d41c161165006d6c2af47e476f05fb13039f8b8
index 0000000000000000000000000000000000000000,4fbf63bf191f23941c41be974d3995d263d08fbb..c0e67299f421f4a39aa21942eec08ba3f68e6a4c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,442 +1,442 @@@
 -** 2013 Jan 11
+ /*
++** 2016-03-01
+ **
+ ** The author disclaims copyright to this source code.  In place of
+ ** a legal notice, here is a blessing:
+ **
+ **    May you do good and not evil.
+ **    May you find forgiveness for yourself and forgive others.
+ **    May you share freely, never taking more than you give.
+ **
+ *************************************************************************
+ ** Code for testing the virtual table xBestIndex method and the query
+ ** planner.
+ */
+ /*
+ ** INSTRUCTIONS
+ **
+ ** This module exports a single tcl command - [register_tcl_module]. When
+ ** invoked, it registers a special virtual table module with a database
+ ** connection.
+ **
+ ** The virtual table is currently read-only. And always returns zero rows.
+ ** It is created with a single argument - the name of a Tcl command - as
+ ** follows:
+ **
+ **   CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
+ **
+ ** The command [tcl_command] is invoked when the table is first created (or
+ ** connected) and when the xBestIndex() method is invoked. When it is created
+ ** (or connected), it is invoked as follows:
+ **
+ **   tcl_command xConnect
+ **
+ ** In this case the return value of the script is passed to the
+ ** sqlite3_declare_vtab() function to create the virtual table schema.
+ **
+ ** When the xBestIndex() method is called by SQLite, the Tcl command is
+ ** invoked as:
+ **
+ **   tcl_command xBestIndex CONSTRAINTS ORDERBY MASK
+ **
+ ** where CONSTRAINTS is a tcl representation of the aConstraints[] array,
+ ** ORDERBY is a representation of the contents of the aOrderBy[] array and
+ ** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual
+ ** table is declared as:
+ **
+ **   CREATE TABLE x1(a, b, c)
+ **
+ ** and the query is:
+ **
+ **   SELECT * FROM x1 WHERE a=? AND c<? ORDER BY b, c;
+ **
+ ** then the Tcl command is:
+ **
+ **   tcl_command xBestIndex                                  \
+ **     {{op eq column 0 usable 1} {op lt column 2 usable 1}} \
+ **     {{column 1 desc 0} {column 2 desc 0}}                 \
+ **     7
+ **
+ ** The return value of the script is a list of key-value pairs used to
+ ** populate the output fields of the sqlite3_index_info structure. Possible
+ ** keys and the usage of the accompanying values are:
+ ** 
+ **   "orderby"          (value of orderByConsumed flag)
+ **   "cost"             (value of estimatedCost field)
+ **   "rows"             (value of estimatedRows field)
+ **   "use"              (index of used constraint in aConstraint[])
+ **   "idxnum"           (value of idxNum field)
+ **   "idxstr"           (value of idxStr field)
+ **
+ ** Refer to code below for further details.
+ */
+ #include "sqliteInt.h"
+ #include "tcl.h"
+ #ifndef SQLITE_OMIT_VIRTUALTABLE
+ typedef struct tcl_vtab tcl_vtab;
+ typedef struct tcl_cursor tcl_cursor;
+ /* 
+ ** A fs virtual-table object 
+ */
+ struct tcl_vtab {
+   sqlite3_vtab base;
+   Tcl_Interp *interp;
+   Tcl_Obj *pCmd;
+ };
+ /* A tcl cursor object */
+ struct tcl_cursor {
+   sqlite3_vtab_cursor base;
+ };
+ /*
+ ** This function is the implementation of both the xConnect and xCreate
+ ** methods of the fs virtual table.
+ **
+ ** The argv[] array contains the following:
+ **
+ **   argv[0]   -> module name  ("fs")
+ **   argv[1]   -> database name
+ **   argv[2]   -> table name
+ **   argv[...] -> other module argument fields.
+ */
+ static int tclConnect(
+   sqlite3 *db,
+   void *pAux,
+   int argc, const char *const*argv,
+   sqlite3_vtab **ppVtab,
+   char **pzErr
+ ){
+   Tcl_Interp *interp = (Tcl_Interp*)pAux;
+   tcl_vtab *pTab;
+   const char *zCmd;
+   Tcl_Obj *pScript = 0;
+   int rc;
+   if( argc!=4 ){
+     *pzErr = sqlite3_mprintf("wrong number of arguments");
+     return SQLITE_ERROR;
+   }
+   zCmd = argv[3];
+   pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab));
+   if( pTab==0 ) return SQLITE_NOMEM;
+   memset(pTab, 0, sizeof(tcl_vtab));
+   pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
+   pTab->interp = interp;
+   Tcl_IncrRefCount(pTab->pCmd);
+   pScript = Tcl_DuplicateObj(pTab->pCmd);
+   Tcl_IncrRefCount(pScript);
+   Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
+   rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+   if( rc!=TCL_OK ){
+     *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
+     rc = SQLITE_ERROR;
+   }else{
+     rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
+   }
+   if( rc!=SQLITE_OK ){
+     sqlite3_free(pTab);
+     pTab = 0;
+   }
+   *ppVtab = &pTab->base;
+   return rc;
+ }
+ /* The xDisconnect and xDestroy methods are also the same */
+ static int tclDisconnect(sqlite3_vtab *pVtab){
+   tcl_vtab *pTab = (tcl_vtab*)pVtab;
+   Tcl_DecrRefCount(pTab->pCmd);
+   sqlite3_free(pTab);
+   return SQLITE_OK;
+ }
+ /*
+ ** Open a new tcl cursor.
+ */
+ static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+   tcl_cursor *pCur;
+   pCur = sqlite3_malloc(sizeof(tcl_cursor));
+   if( pCur==0 ) return SQLITE_NOMEM;
+   memset(pCur, 0, sizeof(tcl_cursor));
+   *ppCursor = &pCur->base;
+   return SQLITE_OK;
+ }
+ /*
+ ** Close a tcl cursor.
+ */
+ static int tclClose(sqlite3_vtab_cursor *cur){
+   tcl_cursor *pCur = (tcl_cursor *)cur;
+   sqlite3_free(pCur);
+   return SQLITE_OK;
+ }
+ static int tclNext(sqlite3_vtab_cursor *cur){
+   return SQLITE_OK;
+ }
+ static int tclFilter(
+   sqlite3_vtab_cursor *pVtabCursor, 
+   int idxNum, const char *idxStr,
+   int argc, sqlite3_value **argv
+ ){
+   return SQLITE_OK;
+ }
+ static int tclColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
+   return SQLITE_OK;
+ }
+ static int tclRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+   return SQLITE_OK;
+ }
+ static int tclEof(sqlite3_vtab_cursor *cur){
+   return 1;
+ }
+ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+   tcl_vtab *pTab = (tcl_vtab*)tab;
+   Tcl_Interp *interp = pTab->interp;
+   Tcl_Obj *pArg;
+   Tcl_Obj *pScript;
+   int ii;
+   int rc = SQLITE_OK;
+   pScript = Tcl_DuplicateObj(pTab->pCmd);
+   Tcl_IncrRefCount(pScript);
+   Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
+   pArg = Tcl_NewObj();
+   Tcl_IncrRefCount(pArg);
+   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
+     Tcl_Obj *pElem = Tcl_NewObj();
+     const char *zOp = "?";
+     Tcl_IncrRefCount(pElem);
+     switch( pCons->op ){
+       case SQLITE_INDEX_CONSTRAINT_EQ:
+         zOp = "eq"; break;
+       case SQLITE_INDEX_CONSTRAINT_GT:
+         zOp = "gt"; break;
+       case SQLITE_INDEX_CONSTRAINT_LE:
+         zOp = "le"; break;
+       case SQLITE_INDEX_CONSTRAINT_LT:
+         zOp = "lt"; break;
+       case SQLITE_INDEX_CONSTRAINT_GE:
+         zOp = "ge"; break;
+       case SQLITE_INDEX_CONSTRAINT_MATCH:
+         zOp = "match"; break;
+       case SQLITE_INDEX_CONSTRAINT_LIKE:
+         zOp = "like"; break;
+       case SQLITE_INDEX_CONSTRAINT_GLOB:
+         zOp = "glob"; break;
+       case SQLITE_INDEX_CONSTRAINT_REGEXP:
+         zOp = "regexp"; break;
+     }
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
+     Tcl_ListObjAppendElement(0, pArg, pElem);
+     Tcl_DecrRefCount(pElem);
+   }
+   Tcl_ListObjAppendElement(0, pScript, pArg);
+   Tcl_DecrRefCount(pArg);
+   pArg = Tcl_NewObj();
+   Tcl_IncrRefCount(pArg);
+   for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
+     struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
+     Tcl_Obj *pElem = Tcl_NewObj();
+     Tcl_IncrRefCount(pElem);
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
+     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
+     Tcl_ListObjAppendElement(0, pArg, pElem);
+     Tcl_DecrRefCount(pElem);
+   }
+   Tcl_ListObjAppendElement(0, pScript, pArg);
+   Tcl_DecrRefCount(pArg);
+   Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
+   rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+   Tcl_DecrRefCount(pScript);
+   if( rc!=TCL_OK ){
+     const char *zErr = Tcl_GetStringResult(interp);
+     rc = SQLITE_ERROR;
+     pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+   }else{
+     /* Analyze the scripts return value. The return value should be a tcl 
+     ** list object with an even number of elements. The first element of each
+     ** pair must be one of:
+     ** 
+     **   "orderby"          (value of orderByConsumed flag)
+     **   "cost"             (value of estimatedCost field)
+     **   "rows"             (value of estimatedRows field)
+     **   "use"              (index of used constraint in aConstraint[])
+     **   "idxnum"           (value of idxNum field)
+     **   "idxstr"           (value of idxStr field)
+     **   "omit"             (index of omitted constraint in aConstraint[])
+     */
+     Tcl_Obj *pRes = Tcl_GetObjResult(interp);
+     Tcl_Obj **apElem = 0;
+     int nElem;
+     rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
+     if( rc!=TCL_OK ){
+       const char *zErr = Tcl_GetStringResult(interp);
+       rc = SQLITE_ERROR;
+       pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+     }else{
+       int iArgv = 1;
+       for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
+         const char *zCmd = Tcl_GetString(apElem[ii]);
+         Tcl_Obj *p = apElem[ii+1];
+         if( sqlite3_stricmp("cost", zCmd)==0 ){
+           rc = Tcl_GetDoubleFromObj(interp, p, &pIdxInfo->estimatedCost);
+         }else
+         if( sqlite3_stricmp("orderby", zCmd)==0 ){
+           rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed);
+         }else
+         if( sqlite3_stricmp("idxnum", zCmd)==0 ){
+           rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum);
+         }else
+         if( sqlite3_stricmp("idxstr", zCmd)==0 ){
+           sqlite3_free(pIdxInfo->idxStr);
+           pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p));
+           pIdxInfo->needToFreeIdxStr = 1;
+         }else
+         if( sqlite3_stricmp("rows", zCmd)==0 ){
+           rc = Tcl_GetWideIntFromObj(interp, p, &pIdxInfo->estimatedRows);
+         }else
+         if( sqlite3_stricmp("use", zCmd)==0 
+          || sqlite3_stricmp("omit", zCmd)==0 
+         ){
+           int iCons;
+           rc = Tcl_GetIntFromObj(interp, p, &iCons);
+           if( rc==SQLITE_OK ){
+             if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
+               rc = SQLITE_ERROR;
+               pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons);
+             }else{
+               int bOmit = (zCmd[0]=='o' || zCmd[0]=='O');
+               pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++;
+               pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
+             }
+           }
+         }else{
+           rc = SQLITE_ERROR;
+           pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
+         }
+         if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){
+           const char *zErr = Tcl_GetStringResult(interp);
+           pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+         }
+       }
+     }
+   }
+   return rc;
+ }
+ /*
+ ** A virtual table module that provides read-only access to a
+ ** Tcl global variable namespace.
+ */
+ static sqlite3_module tclModule = {
+   0,                         /* iVersion */
+   tclConnect,
+   tclConnect,
+   tclBestIndex,
+   tclDisconnect, 
+   tclDisconnect,
+   tclOpen,                      /* xOpen - open a cursor */
+   tclClose,                     /* xClose - close a cursor */
+   tclFilter,                    /* xFilter - configure scan constraints */
+   tclNext,                      /* xNext - advance a cursor */
+   tclEof,                       /* xEof - check for end of scan */
+   tclColumn,                    /* xColumn - read data */
+   tclRowid,                     /* xRowid - read data */
+   0,                           /* xUpdate */
+   0,                           /* xBegin */
+   0,                           /* xSync */
+   0,                           /* xCommit */
+   0,                           /* xRollback */
+   0,                           /* xFindMethod */
+   0,                           /* xRename */
+ };
+ /*
+ ** Decode a pointer to an sqlite3 object.
+ */
+ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+ /*
+ ** Register the echo virtual table module.
+ */
+ static int register_tcl_module(
+   ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+   int objc,              /* Number of arguments */
+   Tcl_Obj *CONST objv[]  /* Command arguments */
+ ){
+   sqlite3 *db;
+   if( objc!=2 ){
+     Tcl_WrongNumArgs(interp, 1, objv, "DB");
+     return TCL_ERROR;
+   }
+   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ #ifndef SQLITE_OMIT_VIRTUALTABLE
+   sqlite3_create_module(db, "tcl", &tclModule, (void *)interp);
+ #endif
+   return TCL_OK;
+ }
+ #endif
+ /*
+ ** Register commands with the TCL interpreter.
+ */
+ int Sqlitetesttcl_Init(Tcl_Interp *interp){
+ #ifndef SQLITE_OMIT_VIRTUALTABLE
+   static struct {
+      char *zName;
+      Tcl_ObjCmdProc *xProc;
+      void *clientData;
+   } aObjCmd[] = {
+      { "register_tcl_module",   register_tcl_module, 0 },
+   };
+   int i;
+   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+     Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
+         aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+   }
+ #endif
+   return TCL_OK;
+ }
index 0000000000000000000000000000000000000000,7b1fb8622840a0a8bbdf65508c1f0f071ed13bb3..840219b620387b376183396280ed2a4d5c4d1fb1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,61 +1,60 @@@
 -# 2016 February 19
++# 2016-03-01
+ #
+ # The author disclaims copyright to this source code.  In place of
+ # a legal notice, here is a blessing:
+ #
+ #    May you do good and not evil.
+ #    May you find forgiveness for yourself and forgive others.
+ #    May you share freely, never taking more than you give.
+ #
+ #***********************************************************************
+ # 
+ #
+ set testdir [file dirname $argv0]
+ source $testdir/tester.tcl
+ set testprefix bestindex1
+ register_tcl_module db
+ proc vtab_command {method args} {
+   switch -- $method {
+     xConnect {
+       return "CREATE TABLE t1(a, b, c)"
+     }
+     xBestIndex {
+       set clist [lindex $args 0]
+       if {[llength $clist]!=1} { error "unexpected constraint list" }
+       catch { array unset C }
+       array set C [lindex $clist 0]
+       if {$C(usable)} {
+         return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!"
+       } else {
+         return "cost 1000000 rows 0 idxnum 0 idxstr scan..."
+       }
+     }
+   }
+   return {}
+ }
+ do_execsql_test 1.0 {
+   CREATE VIRTUAL TABLE x1 USING tcl(vtab_command);
+ } {}
+ do_eqp_test 1.1 {
+   SELECT * FROM x1 WHERE a = 'abc'
+ } {
+   0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
+ }
+ do_eqp_test 1.2 {
+   SELECT * FROM x1 WHERE a IN ('abc', 'def');
+ } {
+   0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
+   0 0 0 {EXECUTE LIST SUBQUERY 1}
+ }
+ finish_test
 -