From eea19e52352e739cda7ea7b137710356809d04ec Mon Sep 17 00:00:00 2001 From: "dlenev@brandersnatch.localdomain" <> Date: Tue, 22 Jun 2004 19:27:16 +0400 Subject: [PATCH] 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. --- mysql-test/r/func_like.result | 6 +++ mysql-test/t/func_like.test | 13 +++++- sql/item_cmpfunc.cc | 87 ++++++++++++++++++++--------------- sql/item_cmpfunc.h | 6 ++- sql/sql_help.cc | 2 +- sql/sql_yacc.yy | 12 +++-- 6 files changed, 80 insertions(+), 46 deletions(-) diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result index 75692738caf..f2c11bc51f6 100644 --- a/mysql-test/r/func_like.result +++ b/mysql-test/r/func_like.result @@ -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'); diff --git a/mysql-test/t/func_like.test b/mysql-test/t/func_like.test index 91f55af48cc..ad83202afa0 100644 --- a/mysql-test/t/func_like.test +++ b/mysql-test/t/func_like.test @@ -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; # diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 446d72ac143..0c86b5f7d52 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -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; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ef80c060c03..4a7e7e06f10 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -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; diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 44293b8214f..c5a49cab3b5 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -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); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 89915852b9b..f4c249c2676 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -606,7 +606,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type remember_name remember_end opt_ident opt_db text_or_password - opt_escape opt_constraint constraint + opt_constraint constraint %type 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 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); + } + ; /*