diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sqlite.h.in | 15 | ||||
-rw-r--r-- | src/test8.c | 6 | ||||
-rw-r--r-- | src/test_tclvar.c | 194 | ||||
-rw-r--r-- | src/where.c | 3 | ||||
-rw-r--r-- | src/whereInt.h | 1 | ||||
-rw-r--r-- | src/whereexpr.c | 37 |
6 files changed, 215 insertions, 41 deletions
diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 13c97d8d4..42652061f 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5709,12 +5709,15 @@ struct sqlite3_index_info { ** an operator that is part of a constraint term in the wHERE clause of ** a query that uses a [virtual table]. */ -#define SQLITE_INDEX_CONSTRAINT_EQ 2 -#define SQLITE_INDEX_CONSTRAINT_GT 4 -#define SQLITE_INDEX_CONSTRAINT_LE 8 -#define SQLITE_INDEX_CONSTRAINT_LT 16 -#define SQLITE_INDEX_CONSTRAINT_GE 32 -#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 +#define SQLITE_INDEX_CONSTRAINT_LIKE 65 +#define SQLITE_INDEX_CONSTRAINT_GLOB 66 +#define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* ** CAPI3REF: Register A Virtual Table Implementation diff --git a/src/test8.c b/src/test8.c index 7d3756aec..0c5dc0206 100644 --- a/src/test8.c +++ b/src/test8.c @@ -856,6 +856,12 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ** wrapping the bound parameter with literal '%'s). */ zOp = "LIKE"; 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; } if( zOp[0]=='L' ){ zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", diff --git a/src/test_tclvar.c b/src/test_tclvar.c index 1219190c0..975f07fc8 100644 --- a/src/test_tclvar.c +++ b/src/test_tclvar.c @@ -23,6 +23,15 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Characters that make up the idxStr created by xBestIndex for xFilter. +*/ +#define TCLVAR_NAME_EQ 'e' +#define TCLVAR_NAME_MATCH 'm' +#define TCLVAR_VALUE_GLOB 'g' +#define TCLVAR_VALUE_REGEXP 'r' +#define TCLVAR_VALUE_LIKE 'l' + typedef struct tclvar_vtab tclvar_vtab; typedef struct tclvar_cursor tclvar_cursor; @@ -155,15 +164,44 @@ static int tclvarFilter( ){ tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor; Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp; + Tcl_Obj *p = Tcl_NewStringObj("tclvar_filter_cmd", -1); - Tcl_Obj *p = Tcl_NewStringObj("info vars", -1); - Tcl_IncrRefCount(p); + const char *zEq = ""; + const char *zMatch = ""; + const char *zGlob = ""; + const char *zRegexp = ""; + const char *zLike = ""; + int i; - assert( argc==0 || argc==1 ); - if( argc==1 ){ - Tcl_Obj *pArg = Tcl_NewStringObj((char*)sqlite3_value_text(argv[0]), -1); - Tcl_ListObjAppendElement(0, p, pArg); + for(i=0; idxStr[i]; i++){ + switch( idxStr[i] ){ + case TCLVAR_NAME_EQ: + zEq = sqlite3_value_text(argv[i]); + break; + case TCLVAR_NAME_MATCH: + zMatch = sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_GLOB: + zGlob = sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_REGEXP: + zRegexp = sqlite3_value_text(argv[i]); + break; + case TCLVAR_VALUE_LIKE: + zLike = sqlite3_value_text(argv[i]); + break; + default: + assert( 0 ); + } } + + Tcl_IncrRefCount(p); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zEq, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zMatch, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zGlob, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zRegexp, -1)); + Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zLike, -1)); + Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL); if( pCur->pList1 ){ Tcl_DecrRefCount(pCur->pList1); @@ -176,7 +214,6 @@ static int tclvarFilter( pCur->i2 = 0; pCur->pList1 = Tcl_GetObjResult(interp); Tcl_IncrRefCount(pCur->pList1); - assert( pCur->i1==0 && pCur->i2==0 && pCur->pList2==0 ); Tcl_DecrRefCount(p); return tclvarNext(pVtabCursor); @@ -224,32 +261,113 @@ static int tclvarEof(sqlite3_vtab_cursor *cur){ return (pCur->pList2?0:1); } +/* +** If nul-terminated string zStr does not already contain the character +** passed as the second argument, append it and return 0. Or, if there is +** already an instance of x in zStr, do nothing return 1; +** +** There is guaranteed to be enough room in the buffer pointed to by zStr +** for the new character and nul-terminator. +*/ +static int tclvarAddToIdxstr(char *zStr, char x){ + int i; + for(i=0; zStr[i]; i++){ + if( zStr[i]==x ) return 1; + } + zStr[i] = x; + zStr[i+1] = '\0'; + return 0; +} + +/* +** Return true if variable $::tclvar_set_omit exists and is set to true. +** False otherwise. +*/ +static int tclvarSetOmit(Tcl_Interp *interp){ + int rc; + int res = 0; + Tcl_Obj *pRes; + rc = Tcl_Eval(interp, + "expr {[info exists ::tclvar_set_omit] && $::tclvar_set_omit}" + ); + if( rc==TCL_OK ){ + pRes = Tcl_GetObjResult(interp); + rc = Tcl_GetBooleanFromObj(0, pRes, &res); + } + return (rc==TCL_OK && res); +} + +/* +** The xBestIndex() method. This virtual table supports the following +** operators: +** +** name = ? (omit flag clear) +** name MATCH ? (omit flag set) +** value GLOB ? (omit flag set iff $::tclvar_set_omit) +** value REGEXP ? (omit flag set iff $::tclvar_set_omit) +** value LIKE ? (omit flag set iff $::tclvar_set_omit) +** +** For each constraint present, the corresponding TCLVAR_XXX character is +** appended to the idxStr value. +*/ static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + tclvar_vtab *pTab = (tclvar_vtab*)tab; int ii; + char *zStr = sqlite3_malloc(32); + int iStr = 0; - for(ii=0; ii<pIdxInfo->nConstraint; ii++){ - struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; - if( pCons->iColumn==0 && pCons->usable - && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - struct sqlite3_index_constraint_usage *pUsage; - pUsage = &pIdxInfo->aConstraintUsage[ii]; - pUsage->omit = 0; - pUsage->argvIndex = 1; - return SQLITE_OK; - } - } + if( zStr==0 ) return SQLITE_NOMEM; + zStr[0] = '\0'; for(ii=0; ii<pIdxInfo->nConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; - if( pCons->iColumn==0 && pCons->usable - && pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ - struct sqlite3_index_constraint_usage *pUsage; - pUsage = &pIdxInfo->aConstraintUsage[ii]; - pUsage->omit = 1; - pUsage->argvIndex = 1; - return SQLITE_OK; + struct sqlite3_index_constraint_usage *pUsage; + + pUsage = &pIdxInfo->aConstraintUsage[ii]; + if( pCons->usable ){ + /* name = ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==0 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_EQ) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = 0; + } + } + + /* name MATCH ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH && pCons->iColumn==0 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_MATCH) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = 1; + } + } + + /* value GLOB ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_GLOB && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_GLOB) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } + + /* value REGEXP ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_REGEXP && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_REGEXP) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } + + /* value LIKE ? */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_LIKE && pCons->iColumn==2 ){ + if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_LIKE) ){ + pUsage->argvIndex = ++iStr; + pUsage->omit = tclvarSetOmit(pTab->interp); + } + } } } + pIdxInfo->idxStr = zStr; + pIdxInfo->needToFreeIdxStr = 1; return SQLITE_OK; } @@ -295,6 +413,7 @@ static int register_tclvar_module( int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ + int rc = TCL_OK; sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); @@ -302,9 +421,30 @@ static int register_tclvar_module( } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; #ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3_create_module(db, "tclvar", &tclvarModule, (void *)interp); + sqlite3_create_module(db, "tclvar", &tclvarModule, (void*)interp); + rc = Tcl_Eval(interp, + "proc like {pattern str} {\n" + " set p [string map {% * _ ?} $pattern]\n" + " string match $p $str\n" + "}\n" + "proc tclvar_filter_cmd {eq match glob regexp like} {\n" + " set res {}\n" + " set pattern $eq\n" + " if {$pattern=={}} { set pattern $match }\n" + " if {$pattern=={}} { set pattern * }\n" + " foreach v [uplevel #0 info vars $pattern] {\n" + " if {($glob=={} || [string match $glob [uplevel #0 set $v]])\n" + " && ($like=={} || [like $like [uplevel #0 set $v]])\n" + " && ($regexp=={} || [regexp $regexp [uplevel #0 set $v]])\n" + " } {\n" + " lappend res $v\n" + " }\n" + " }\n" + " set res\n" + "}\n" + ); #endif - return TCL_OK; + return rc; } #endif diff --git a/src/where.c b/src/where.c index 1c87706ea..737bfc4e6 100644 --- a/src/where.c +++ b/src/where.c @@ -893,6 +893,9 @@ static sqlite3_index_info *allocateIndexInfo( pIdxCons[j].iTermOffset = i; op = (u8)pTerm->eOperator & WO_ALL; if( op==WO_IN ) op = WO_EQ; + if( op==WO_MATCH ){ + op = pTerm->eMatchOp; + } pIdxCons[j].op = op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The diff --git a/src/whereInt.h b/src/whereInt.h index cae09acc8..86164d8c1 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -253,6 +253,7 @@ struct WhereTerm { u16 eOperator; /* A WO_xx value describing <op> */ u16 wtFlags; /* TERM_xxx bit flags. See below */ u8 nChild; /* Number of children that must disable us */ + u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */ WhereClause *pWC; /* The clause this term is part of */ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */ diff --git a/src/whereexpr.c b/src/whereexpr.c index 21301ac04..8ef91b03e 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -277,29 +277,48 @@ static int isLikeOrGlob( /* ** Check to see if the given expression is of the form ** -** column MATCH expr +** column OP expr +** +** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a +** column of a virtual table. ** ** If it is then return TRUE. If not, return FALSE. */ static int isMatchOfColumn( - Expr *pExpr /* Test this expression */ + Expr *pExpr, /* Test this expression */ + unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */ ){ + struct Op2 { + const char *zOp; + unsigned char eOp2; + } aOp[] = { + { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, + { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, + { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, + { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } + }; ExprList *pList; + Expr *pCol; /* Column reference */ + int i; if( pExpr->op!=TK_FUNCTION ){ return 0; } - if( sqlite3StrICmp(pExpr->u.zToken,"match")!=0 ){ - return 0; - } pList = pExpr->x.pList; if( pList->nExpr!=2 ){ return 0; } - if( pList->a[1].pExpr->op != TK_COLUMN ){ + pCol = pList->a[1].pExpr; + if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){ return 0; } - return 1; + for(i=0; i<ArraySize(aOp); i++){ + if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){ + *peOp2 = aOp[i].eOp2; + return 1; + } + } + return 0; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -876,6 +895,7 @@ static void exprAnalyze( int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ + unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */ if( db->mallocFailed ){ return; @@ -1099,7 +1119,7 @@ static void exprAnalyze( ** virtual tables. The native query optimizer does not attempt ** to do anything with MATCH functions. */ - if( isMatchOfColumn(pExpr) ){ + if( isMatchOfColumn(pExpr, &eOp2) ){ int idxNew; Expr *pRight, *pLeft; WhereTerm *pNewTerm; @@ -1120,6 +1140,7 @@ static void exprAnalyze( pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_MATCH; + pNewTerm->eMatchOp = eOp2; markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; |