Add further tests and related fixes for GLOB/REGEXP/LIKE support in virtual tables.
FossilOrigin-Name: c5e9fd0dc92a07db3d3b5f5c5ad8fb63b3425c2b
This commit is contained in:
parent
07bdba86d5
commit
43970dd774
21
manifest
21
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sexperimental\ssupport\sfor\sLIKE,\sGLOB\sand\sREGEXP\sto\sthe\svirtual\stable\sinterface.
|
||||
D 2015-11-23T21:09:54.478
|
||||
C Add\sfurther\stests\sand\srelated\sfixes\sfor\sGLOB/REGEXP/LIKE\ssupport\sin\svirtual\stables.
|
||||
D 2015-11-24T17:39:01.810
|
||||
F Makefile.in d828db6afa6c1fa060d01e33e4674408df1942a1
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc e928e68168df69b353300ac87c10105206653a03
|
||||
@ -389,7 +389,7 @@ F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
|
||||
F src/test_sqllog.c 0d138a8180a312bf996b37fa66da5c5799d4d57b
|
||||
F src/test_superlock.c 06797157176eb7085027d9dd278c0d7a105e3ec9
|
||||
F src/test_syscall.c 2e21ca7f7dc54a028f1967b63f1e76155c356f9b
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
F src/test_tclvar.c 2fd910e9f0ef7686889f50f448d33810c895da86
|
||||
F src/test_thread.c af391ec03d23486dffbcc250b7e58e073f172af9
|
||||
F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
@ -419,7 +419,7 @@ F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
||||
F src/where.c 6687fb2675d9c1c1936ceca77529e2f21fb3a769
|
||||
F src/whereInt.h 6afc0d70cf6213e58e8fbe10b6e50d1aa16f122f
|
||||
F src/wherecode.c 4c96182e7b25e4be54008dee2da5b9c2f8480b9b
|
||||
F src/whereexpr.c 12c6fa7576674d24bf0116364a39885925c89188
|
||||
F src/whereexpr.c 17d62d8bb7fd357920b46ee86851b5d6629412bf
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@ -1272,9 +1272,9 @@ F test/vtabA.test 1317f06a03597eee29f40a49b6c21e1aaba4285f
|
||||
F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
|
||||
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
|
||||
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
|
||||
F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
|
||||
F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe
|
||||
F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
|
||||
F test/vtabH.test 15e137d2af9b0b81fedca6697518eb8834c013f4
|
||||
F test/vtabH.test 694aa399eb28ed0db2aef59f2f37532781eeb957
|
||||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772
|
||||
@ -1405,10 +1405,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 60de5f23424552c98aa760ac89149a3d51f895be
|
||||
R b6557d1407b51115ef511b97d0fc16c1
|
||||
T *branch * vtab-like-operator
|
||||
T *sym-vtab-like-operator *
|
||||
T -sym-trunk *
|
||||
P 277a5b4027d4c2caba8143228a4f7d6df899dbb4
|
||||
R f59356e8fc475908365127ad504d9518
|
||||
U dan
|
||||
Z 249ced27266ff2c4ebd31fcaec9b55b5
|
||||
Z 673386578c6b1eaa8b17502c533df301
|
||||
|
@ -1 +1 @@
|
||||
277a5b4027d4c2caba8143228a4f7d6df899dbb4
|
||||
c5e9fd0dc92a07db3d3b5f5c5ad8fb63b3425c2b
|
@ -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;
|
||||
|
||||
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_EQ ){
|
||||
struct sqlite3_index_constraint_usage *pUsage;
|
||||
pUsage = &pIdxInfo->aConstraintUsage[ii];
|
||||
pUsage->omit = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
/* 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
|
||||
|
@ -277,7 +277,10 @@ 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.
|
||||
*/
|
||||
@ -289,12 +292,13 @@ static int isMatchOfColumn(
|
||||
const char *zOp;
|
||||
unsigned char eOp2;
|
||||
} aOp[] = {
|
||||
{ "match", SQLITE_INDEX_CONSTRAINT_MATCH },
|
||||
{ "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
|
||||
{ "like", SQLITE_INDEX_CONSTRAINT_LIKE },
|
||||
{ "regex", SQLITE_INDEX_CONSTRAINT_REGEXP }
|
||||
{ "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 ){
|
||||
@ -304,7 +308,8 @@ static int isMatchOfColumn(
|
||||
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;
|
||||
}
|
||||
for(i=0; i<ArraySize(aOp); i++){
|
||||
|
@ -46,3 +46,5 @@ do_test vtabE-1 {
|
||||
ORDER BY t1.value, t2.value;
|
||||
}
|
||||
} {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}}
|
||||
|
||||
finish_test
|
||||
|
@ -8,7 +8,9 @@
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
# This file implements regression tests for SQLite library. Specifically,
|
||||
# it tests that the GLOB, LIKE and REGEXP operators are correctly exposed
|
||||
# to virtual table implementations.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@ -46,4 +48,63 @@ foreach {tn sql expect} {
|
||||
} [list {*}$expect]
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
register_tclvar_module db
|
||||
set ::xyz 10
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE vars USING tclvar;
|
||||
SELECT * FROM vars WHERE name = 'xyz';
|
||||
} {xyz {} 10}
|
||||
|
||||
set x1 aback
|
||||
set x2 abaft
|
||||
set x3 abandon
|
||||
set x4 abandonint
|
||||
set x5 babble
|
||||
set x6 baboon
|
||||
set x7 backbone
|
||||
set x8 backarrow
|
||||
set x9 castle
|
||||
|
||||
db func glob gfunc
|
||||
proc gfunc {a b} {
|
||||
incr ::gfunc
|
||||
return 1
|
||||
}
|
||||
|
||||
db func like lfunc
|
||||
proc lfunc {a b} {
|
||||
incr ::gfunc 100
|
||||
return 1
|
||||
}
|
||||
|
||||
db func regexp rfunc
|
||||
proc rfunc {a b} {
|
||||
incr ::gfunc 10000
|
||||
return 1
|
||||
}
|
||||
|
||||
foreach ::tclvar_set_omit {0 1} {
|
||||
foreach {tn expr res cnt} {
|
||||
1 {value GLOB 'aban*'} {x3 abandon x4 abandonint} 2
|
||||
2 {value LIKE '%ac%'} {x1 aback x7 backbone x8 backarrow} 300
|
||||
3 {value REGEXP '^......$'} {x5 babble x6 baboon x9 castle} 30000
|
||||
} {
|
||||
if {$tn==3} breakpoint
|
||||
db cache flush
|
||||
set ::gfunc 0
|
||||
if {$::tclvar_set_omit} {set cnt 0}
|
||||
|
||||
do_test 2.$tclvar_set_omit.$tn.1 {
|
||||
execsql "SELECT name, value FROM vars WHERE name MATCH 'x*' AND $expr"
|
||||
} $res
|
||||
|
||||
do_test 2.$tclvar_set_omit.$tn.2 {
|
||||
set ::gfunc
|
||||
} $cnt
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user