Fix for Bug# 4200 "Parse error on LIKE ESCAPE with parameter binding"
Now ESCAPE in LIKE will accept not only string literal but constant delimited expression.
This commit is contained in:
parent
6b45c24d39
commit
eea19e5235
@ -45,6 +45,12 @@ a\b
|
||||
select * from t1 where a like 'a\\%' escape '#' and a like 'a\\\\b';
|
||||
a
|
||||
a\b
|
||||
prepare stmt1 from 'select * from t1 where a like \'a\\%\' escape ?';
|
||||
set @esc='#';
|
||||
execute stmt1 using @esc;
|
||||
a
|
||||
a\b
|
||||
deallocate prepare stmt1;
|
||||
drop table t1;
|
||||
create table t1 (a datetime);
|
||||
insert into t1 values ('2004-03-11 12:00:21');
|
||||
|
@ -25,14 +25,23 @@ select * from t1 where a like "%abc\d%";
|
||||
|
||||
drop table t1;
|
||||
|
||||
create table t1 (a varchar(10), key(a));
|
||||
|
||||
#
|
||||
# Bug #2231
|
||||
#
|
||||
|
||||
create table t1 (a varchar(10), key(a));
|
||||
insert into t1 values ('a'), ('a\\b');
|
||||
select * from t1 where a like 'a\\%' escape '#';
|
||||
select * from t1 where a like 'a\\%' escape '#' and a like 'a\\\\b';
|
||||
|
||||
#
|
||||
# Bug #4200: Prepared statement parameter as argument to ESCAPE
|
||||
#
|
||||
prepare stmt1 from 'select * from t1 where a like \'a\\%\' escape ?';
|
||||
set @esc='#';
|
||||
execute stmt1 using @esc;
|
||||
deallocate prepare stmt1;
|
||||
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
|
@ -2151,49 +2151,62 @@ Item_func::optimize_type Item_func_like::select_optimize() const
|
||||
bool Item_func_like::fix_fields(THD *thd, TABLE_LIST *tlist, Item ** ref)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 0);
|
||||
if (Item_bool_func2::fix_fields(thd, tlist, ref))
|
||||
if (Item_bool_func2::fix_fields(thd, tlist, ref) ||
|
||||
escape_item->fix_fields(thd, tlist, &escape_item))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
We could also do boyer-more for non-const items, but as we would have to
|
||||
recompute the tables for each row it's not worth it.
|
||||
*/
|
||||
if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
|
||||
!(specialflag & SPECIAL_NO_NEW_FUNC))
|
||||
if (!escape_item->const_during_execution())
|
||||
{
|
||||
String* res2 = args[1]->val_str(&tmp_value2);
|
||||
if (!res2)
|
||||
return 0; // Null argument
|
||||
|
||||
const size_t len = res2->length();
|
||||
const char* first = res2->ptr();
|
||||
const char* last = first + len - 1;
|
||||
my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (escape_item->const_item())
|
||||
{
|
||||
/* If we are on execution stage */
|
||||
String *escape_str= escape_item->val_str(&tmp_value1);
|
||||
escape= escape_str ? *(escape_str->ptr()) : '\\';
|
||||
|
||||
/*
|
||||
len must be > 2 ('%pattern%')
|
||||
heuristic: only do TurboBM for pattern_len > 2
|
||||
We could also do boyer-more for non-const items, but as we would have to
|
||||
recompute the tables for each row it's not worth it.
|
||||
*/
|
||||
|
||||
if (len > MIN_TURBOBM_PATTERN_LEN + 2 &&
|
||||
*first == wild_many &&
|
||||
*last == wild_many)
|
||||
if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
|
||||
!(specialflag & SPECIAL_NO_NEW_FUNC))
|
||||
{
|
||||
const char* tmp = first + 1;
|
||||
for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ;
|
||||
canDoTurboBM = (tmp == last) && !use_mb(args[0]->collation.collation);
|
||||
}
|
||||
|
||||
if (canDoTurboBM)
|
||||
{
|
||||
pattern = first + 1;
|
||||
pattern_len = len - 2;
|
||||
DBUG_PRINT("info", ("Initializing pattern: '%s'", first));
|
||||
int *suff = (int*) thd->alloc(sizeof(int)*((pattern_len + 1)*2+
|
||||
alphabet_size));
|
||||
bmGs = suff + pattern_len + 1;
|
||||
bmBc = bmGs + pattern_len + 1;
|
||||
turboBM_compute_good_suffix_shifts(suff);
|
||||
turboBM_compute_bad_character_shifts();
|
||||
DBUG_PRINT("info",("done"));
|
||||
String* res2 = args[1]->val_str(&tmp_value2);
|
||||
if (!res2)
|
||||
return 0; // Null argument
|
||||
|
||||
const size_t len = res2->length();
|
||||
const char* first = res2->ptr();
|
||||
const char* last = first + len - 1;
|
||||
/*
|
||||
len must be > 2 ('%pattern%')
|
||||
heuristic: only do TurboBM for pattern_len > 2
|
||||
*/
|
||||
|
||||
if (len > MIN_TURBOBM_PATTERN_LEN + 2 &&
|
||||
*first == wild_many &&
|
||||
*last == wild_many)
|
||||
{
|
||||
const char* tmp = first + 1;
|
||||
for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ;
|
||||
canDoTurboBM = (tmp == last) && !use_mb(args[0]->collation.collation);
|
||||
}
|
||||
if (canDoTurboBM)
|
||||
{
|
||||
pattern = first + 1;
|
||||
pattern_len = len - 2;
|
||||
DBUG_PRINT("info", ("Initializing pattern: '%s'", first));
|
||||
int *suff = (int*) thd->alloc(sizeof(int)*((pattern_len + 1)*2+
|
||||
alphabet_size));
|
||||
bmGs = suff + pattern_len + 1;
|
||||
bmBc = bmGs + pattern_len + 1;
|
||||
turboBM_compute_good_suffix_shifts(suff);
|
||||
turboBM_compute_bad_character_shifts();
|
||||
DBUG_PRINT("info",("done"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -841,12 +841,14 @@ class Item_func_like :public Item_bool_func2
|
||||
bool turboBM_matches(const char* text, int text_len) const;
|
||||
enum { alphabet_size = 256 };
|
||||
|
||||
Item *escape_item;
|
||||
|
||||
public:
|
||||
char escape;
|
||||
|
||||
Item_func_like(Item *a,Item *b, char* escape_arg)
|
||||
Item_func_like(Item *a,Item *b, Item *escape_arg)
|
||||
:Item_bool_func2(a,b), canDoTurboBM(false), pattern(0), pattern_len(0),
|
||||
bmGs(0), bmBc(0), escape(*escape_arg) {}
|
||||
bmGs(0), bmBc(0), escape_item(escape_arg) {}
|
||||
longlong val_int();
|
||||
enum Functype functype() const { return LIKE_FUNC; }
|
||||
optimize_type select_optimize() const;
|
||||
|
@ -626,7 +626,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
|
||||
{
|
||||
Item *cond= new Item_func_like(new Item_field(pfname),
|
||||
new Item_string(mask,mlen,pfname->charset()),
|
||||
(char*) "\\");
|
||||
new Item_string("\\",1,&my_charset_latin1));
|
||||
if (thd->is_fatal_error)
|
||||
return 0; // OOM
|
||||
return prepare_simple_select(thd,cond,tables,table,error);
|
||||
|
@ -606,7 +606,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||
|
||||
%type <simple_string>
|
||||
remember_name remember_end opt_ident opt_db text_or_password
|
||||
opt_escape opt_constraint constraint
|
||||
opt_constraint constraint
|
||||
|
||||
%type <string>
|
||||
text_string opt_gconcat_separator
|
||||
@ -634,7 +634,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||
using_list expr_or_default set_expr_or_default interval_expr
|
||||
param_marker singlerow_subselect singlerow_subselect_init
|
||||
exists_subselect exists_subselect_init geometry_function
|
||||
signed_literal now_or_signed_literal
|
||||
signed_literal now_or_signed_literal opt_escape
|
||||
|
||||
%type <item_num>
|
||||
NUM_literal
|
||||
@ -3570,8 +3570,12 @@ having_clause:
|
||||
;
|
||||
|
||||
opt_escape:
|
||||
ESCAPE_SYM TEXT_STRING_literal { $$= $2.str; }
|
||||
| /* empty */ { $$= (char*) "\\"; };
|
||||
ESCAPE_SYM simple_expr { $$= $2; }
|
||||
| /* empty */
|
||||
{
|
||||
$$= new Item_string("\\", 1, &my_charset_latin1);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user