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.
parent 6b45c24d
......@@ -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)
{
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)
if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
!(specialflag & SPECIAL_NO_NEW_FUNC))
{
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);
}
;
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment