Commit 3709c7fc authored by Alexander Barkov's avatar Alexander Barkov

MDEV-8222 "string_field LIKE int_const" returns a wrong result in case of UCS2

MDEV-8257 Erroneous "Impossible where" when mixing decimal comparison and LIKE
parent 8f92a70e
...@@ -5542,5 +5542,14 @@ select collation(cast("a" as char(10) binary unicode)); ...@@ -5542,5 +5542,14 @@ select collation(cast("a" as char(10) binary unicode));
collation(cast("a" as char(10) binary unicode)) collation(cast("a" as char(10) binary unicode))
ucs2_bin ucs2_bin
# #
# MDEV-8222 "string_field LIKE int_const" returns a wrong result in case of UCS2
#
CREATE TABLE t1 (a VARCHAR(10) CHARSET ucs2);
INSERT INTO t1 VALUES ('1');
SELECT * FROM t1 WHERE a LIKE 1;
a
1
DROP TABLE t1;
#
# End of 10.1 tests # End of 10.1 tests
# #
...@@ -200,3 +200,32 @@ SELECT 'a' LIKE REPEAT('',0); ...@@ -200,3 +200,32 @@ SELECT 'a' LIKE REPEAT('',0);
SELECT 'a' LIKE EXTRACTVALUE('bar','qux'); SELECT 'a' LIKE EXTRACTVALUE('bar','qux');
'a' LIKE EXTRACTVALUE('bar','qux') 'a' LIKE EXTRACTVALUE('bar','qux')
0 0
#
# End of 10.0 tests
#
#
# Start of 10.1 tests
#
#
# MDEV-8257 Erroneous "Impossible where" when mixing decimal comparison and LIKE
#
CREATE TABLE t1 (a DECIMAL(8,2));
INSERT INTO t1 VALUES (10),(20);
SELECT * FROM t1 WHERE a=10.0;
a
10.00
SELECT * FROM t1 WHERE a LIKE 10.00;
a
10.00
SELECT * FROM t1 WHERE a=10.0 AND a LIKE 10.00;
a
10.00
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10.0 AND a LIKE 10.00;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 10.0) and (`test`.`t1`.`a` like 10.00))
DROP TABLE t1;
#
# End of 10.1 tests
#
...@@ -924,6 +924,14 @@ SELECT 'a','aa'; ...@@ -924,6 +924,14 @@ SELECT 'a','aa';
select collation(cast("a" as char(10) unicode binary)); select collation(cast("a" as char(10) unicode binary));
select collation(cast("a" as char(10) binary unicode)); select collation(cast("a" as char(10) binary unicode));
--echo #
--echo # MDEV-8222 "string_field LIKE int_const" returns a wrong result in case of UCS2
--echo #
CREATE TABLE t1 (a VARCHAR(10) CHARSET ucs2);
INSERT INTO t1 VALUES ('1');
SELECT * FROM t1 WHERE a LIKE 1;
DROP TABLE t1;
--echo # --echo #
--echo # End of 10.1 tests --echo # End of 10.1 tests
--echo # --echo #
...@@ -143,3 +143,26 @@ SELECT '' LIKE '1' ESCAPE COUNT(1); ...@@ -143,3 +143,26 @@ SELECT '' LIKE '1' ESCAPE COUNT(1);
--echo # --echo #
SELECT 'a' LIKE REPEAT('',0); SELECT 'a' LIKE REPEAT('',0);
SELECT 'a' LIKE EXTRACTVALUE('bar','qux'); SELECT 'a' LIKE EXTRACTVALUE('bar','qux');
--echo #
--echo # End of 10.0 tests
--echo #
--echo #
--echo # Start of 10.1 tests
--echo #
--echo #
--echo # MDEV-8257 Erroneous "Impossible where" when mixing decimal comparison and LIKE
--echo #
CREATE TABLE t1 (a DECIMAL(8,2));
INSERT INTO t1 VALUES (10),(20);
SELECT * FROM t1 WHERE a=10.0;
SELECT * FROM t1 WHERE a LIKE 10.00;
SELECT * FROM t1 WHERE a=10.0 AND a LIKE 10.00;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=10.0 AND a LIKE 10.00;
DROP TABLE t1;
--echo #
--echo # End of 10.1 tests
--echo #
...@@ -503,15 +503,15 @@ bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp) ...@@ -503,15 +503,15 @@ bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp)
args[0]->cmp_context= args[1]->cmp_context= args[0]->cmp_context= args[1]->cmp_context=
item_cmp_type(args[0]->result_type(), args[1]->result_type()); item_cmp_type(args[0]->result_type(), args[1]->result_type());
// Convert constants when compared to int/year field, unless this is LIKE // Convert constants when compared to int/year field
if (functype() != LIKE_FUNC) DBUG_ASSERT(functype() != LIKE_FUNC);
convert_const_compared_to_int_field(thd); convert_const_compared_to_int_field(thd);
return cmp->set_cmp_func(this, tmp_arg, tmp_arg + 1, true); return cmp->set_cmp_func(this, tmp_arg, tmp_arg + 1, true);
} }
void Item_bool_func2::fix_length_and_dec() void Item_bool_rowready_func2::fix_length_and_dec()
{ {
max_length= 1; // Function returns 0 or 1 max_length= 1; // Function returns 0 or 1
...@@ -1881,7 +1881,7 @@ longlong Item_func_eq::val_int() ...@@ -1881,7 +1881,7 @@ longlong Item_func_eq::val_int()
void Item_func_equal::fix_length_and_dec() void Item_func_equal::fix_length_and_dec()
{ {
Item_bool_func2::fix_length_and_dec(); Item_bool_rowready_func2::fix_length_and_dec();
maybe_null=null_value=0; maybe_null=null_value=0;
} }
...@@ -4784,13 +4784,13 @@ void Item_func_isnotnull::print(String *str, enum_query_type query_type) ...@@ -4784,13 +4784,13 @@ void Item_func_isnotnull::print(String *str, enum_query_type query_type)
longlong Item_func_like::val_int() longlong Item_func_like::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
String* res = args[0]->val_str(&cmp.value1); String* res= args[0]->val_str(&cmp_value1);
if (args[0]->null_value) if (args[0]->null_value)
{ {
null_value=1; null_value=1;
return 0; return 0;
} }
String* res2 = args[1]->val_str(&cmp.value2); String* res2= args[1]->val_str(&cmp_value2);
if (args[1]->null_value) if (args[1]->null_value)
{ {
null_value=1; null_value=1;
...@@ -4799,7 +4799,7 @@ longlong Item_func_like::val_int() ...@@ -4799,7 +4799,7 @@ longlong Item_func_like::val_int()
null_value=0; null_value=0;
if (canDoTurboBM) if (canDoTurboBM)
return turboBM_matches(res->ptr(), res->length()) ? 1 : 0; return turboBM_matches(res->ptr(), res->length()) ? 1 : 0;
return my_wildcmp(cmp.cmp_collation.collation, return my_wildcmp(cmp_collation.collation,
res->ptr(),res->ptr()+res->length(), res->ptr(),res->ptr()+res->length(),
res2->ptr(),res2->ptr()+res2->length(), res2->ptr(),res2->ptr()+res2->length(),
escape,wild_one,wild_many) ? 0 : 1; escape,wild_one,wild_many) ? 0 : 1;
...@@ -4815,7 +4815,7 @@ Item_func::optimize_type Item_func_like::select_optimize() const ...@@ -4815,7 +4815,7 @@ Item_func::optimize_type Item_func_like::select_optimize() const
if (!args[1]->const_item() || args[1]->is_expensive()) if (!args[1]->const_item() || args[1]->is_expensive())
return OPTIMIZE_NONE; return OPTIMIZE_NONE;
String* res2= args[1]->val_str((String *)&cmp.value2); String* res2= args[1]->val_str((String *) &cmp_value2);
if (!res2) if (!res2)
return OPTIMIZE_NONE; return OPTIMIZE_NONE;
...@@ -4845,7 +4845,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4845,7 +4845,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (escape_item->const_item()) if (escape_item->const_item())
{ {
/* If we are on execution stage */ /* If we are on execution stage */
String *escape_str= escape_item->val_str(&cmp.value1); String *escape_str= escape_item->val_str(&cmp_value1);
if (escape_str) if (escape_str)
{ {
const char *escape_str_ptr= escape_str->ptr(); const char *escape_str_ptr= escape_str->ptr();
...@@ -4858,7 +4858,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4858,7 +4858,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
return TRUE; return TRUE;
} }
if (use_mb(cmp.cmp_collation.collation)) if (use_mb(cmp_collation.collation))
{ {
CHARSET_INFO *cs= escape_str->charset(); CHARSET_INFO *cs= escape_str->charset();
my_wc_t wc; my_wc_t wc;
...@@ -4875,7 +4875,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4875,7 +4875,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
code instead of Unicode code as "escape" argument. code instead of Unicode code as "escape" argument.
Convert to "cs" if charset of escape differs. Convert to "cs" if charset of escape differs.
*/ */
CHARSET_INFO *cs= cmp.cmp_collation.collation; CHARSET_INFO *cs= cmp_collation.collation;
uint32 unused; uint32 unused;
if (escape_str->needs_conversion(escape_str->length(), if (escape_str->needs_conversion(escape_str->length(),
escape_str->charset(), cs, &unused)) escape_str->charset(), cs, &unused))
...@@ -4901,7 +4901,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) ...@@ -4901,7 +4901,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
if (args[1]->const_item() && !use_strnxfrm(collation.collation) && if (args[1]->const_item() && !use_strnxfrm(collation.collation) &&
!args[1]->is_expensive()) !args[1]->is_expensive())
{ {
String* res2 = args[1]->val_str(&cmp.value2); String* res2= args[1]->val_str(&cmp_value2);
if (!res2) if (!res2)
return FALSE; // Null argument return FALSE; // Null argument
...@@ -5182,7 +5182,7 @@ void Item_func_like::turboBM_compute_suffixes(int *suff) ...@@ -5182,7 +5182,7 @@ void Item_func_like::turboBM_compute_suffixes(int *suff)
int f = 0; int f = 0;
int g = plm1; int g = plm1;
int *const splm1 = suff + plm1; int *const splm1 = suff + plm1;
CHARSET_INFO *cs= cmp.cmp_collation.collation; CHARSET_INFO *cs= cmp_collation.collation;
*splm1 = pattern_len; *splm1 = pattern_len;
...@@ -5282,7 +5282,7 @@ void Item_func_like::turboBM_compute_bad_character_shifts() ...@@ -5282,7 +5282,7 @@ void Item_func_like::turboBM_compute_bad_character_shifts()
int *end = bmBc + alphabet_size; int *end = bmBc + alphabet_size;
int j; int j;
const int plm1 = pattern_len - 1; const int plm1 = pattern_len - 1;
CHARSET_INFO *cs= cmp.cmp_collation.collation; CHARSET_INFO *cs= cmp_collation.collation;
for (i = bmBc; i < end; i++) for (i = bmBc; i < end; i++)
*i = pattern_len; *i = pattern_len;
...@@ -5314,7 +5314,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const ...@@ -5314,7 +5314,7 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
int shift = pattern_len; int shift = pattern_len;
int j = 0; int j = 0;
int u = 0; int u = 0;
CHARSET_INFO *cs= cmp.cmp_collation.collation; CHARSET_INFO *cs= cmp_collation.collation;
const int plm1= pattern_len - 1; const int plm1= pattern_len - 1;
const int tlmpl= text_len - pattern_len; const int tlmpl= text_len - pattern_len;
......
...@@ -291,17 +291,9 @@ public: ...@@ -291,17 +291,9 @@ public:
class Item_bool_func2 :public Item_bool_func class Item_bool_func2 :public Item_bool_func
{ /* Bool with 2 string args */ { /* Bool with 2 string args */
protected:
Arg_comparator cmp;
public: public:
Item_bool_func2(Item *a,Item *b) Item_bool_func2(Item *a,Item *b)
:Item_bool_func(a,b), cmp(tmp_arg, tmp_arg+1) { sargable= TRUE; } :Item_bool_func(a,b) { sargable= TRUE; }
void fix_length_and_dec();
int set_cmp_func()
{
return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE);
}
optimize_type select_optimize() const { return OPTIMIZE_OP; } optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; } bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; }
...@@ -312,14 +304,6 @@ public: ...@@ -312,14 +304,6 @@ public:
} }
bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); } bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); }
CHARSET_INFO *compare_collation() const
{ return cmp.cmp_collation.collation; }
Arg_comparator *get_comparator() { return &cmp; }
void cleanup()
{
Item_bool_func::cleanup();
cmp.cleanup();
}
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value, COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level); bool top_level);
...@@ -327,8 +311,11 @@ public: ...@@ -327,8 +311,11 @@ public:
class Item_bool_rowready_func2 :public Item_bool_func2 class Item_bool_rowready_func2 :public Item_bool_func2
{ {
protected:
Arg_comparator cmp;
public: public:
Item_bool_rowready_func2(Item *a, Item *b) :Item_bool_func2(a, b) Item_bool_rowready_func2(Item *a, Item *b)
:Item_bool_func2(a, b), cmp(tmp_arg, tmp_arg+1)
{ {
allowed_arg_cols= 0; // Fetch this value from first argument allowed_arg_cols= 0; // Fetch this value from first argument
} }
...@@ -338,6 +325,19 @@ public: ...@@ -338,6 +325,19 @@ public:
{ {
return (*arg != NULL); return (*arg != NULL);
} }
void fix_length_and_dec();
int set_cmp_func()
{
return cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, true);
}
CHARSET_INFO *compare_collation() const
{ return cmp.cmp_collation.collation; }
Arg_comparator *get_comparator() { return &cmp; }
void cleanup()
{
Item_bool_func2::cleanup();
cmp.cleanup();
}
bool can_optimize_group_min_max(Item_field *min_max_arg_item, bool can_optimize_group_min_max(Item_field *min_max_arg_item,
const Item *const_item) const const Item *const_item) const
{ {
...@@ -1490,6 +1490,8 @@ class Item_func_like :public Item_bool_func2 ...@@ -1490,6 +1490,8 @@ class Item_func_like :public Item_bool_func2
bool escape_used_in_parsing; bool escape_used_in_parsing;
bool use_sampling; bool use_sampling;
DTCollation cmp_collation;
String cmp_value1, cmp_value2;
public: public:
int escape; int escape;
...@@ -1500,6 +1502,8 @@ public: ...@@ -1500,6 +1502,8 @@ public:
longlong val_int(); longlong val_int();
enum Functype functype() const { return LIKE_FUNC; } enum Functype functype() const { return LIKE_FUNC; }
optimize_type select_optimize() const; optimize_type select_optimize() const;
CHARSET_INFO *compare_collation() const
{ return cmp_collation.collation; }
cond_result eq_cmp_result() const cond_result eq_cmp_result() const
{ {
/** /**
...@@ -1539,6 +1543,12 @@ public: ...@@ -1539,6 +1543,12 @@ public:
table_map usable_tables, SARGABLE_PARAM **sargables); table_map usable_tables, SARGABLE_PARAM **sargables);
const char *func_name() const { return "like"; } const char *func_name() const { return "like"; }
bool fix_fields(THD *thd, Item **ref); bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
{
max_length= 1;
args[0]->cmp_context= args[1]->cmp_context= STRING_RESULT;
agg_arg_charsets_for_comparison(cmp_collation, args, 2);
}
void cleanup(); void cleanup();
bool find_selective_predicates_list_processor(uchar *arg); bool find_selective_predicates_list_processor(uchar *arg);
......
...@@ -13884,7 +13884,16 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, ...@@ -13884,7 +13884,16 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
if ((tmp2=new COND_CMP(and_father,func))) if ((tmp2=new COND_CMP(and_father,func)))
save_list->push_back(tmp2); save_list->push_back(tmp2);
} }
func->set_cmp_func(); /*
LIKE can be optimized for BINARY/VARBINARY/BLOB columns, e.g.:
from: WHERE CONCAT(c1)='const1' AND CONCAT(c1) LIKE 'const2'
to: WHERE CONCAT(c1)='const1' AND 'const1' LIKE 'const2'
So make sure to use set_cmp_func() only for non-LIKE operators.
*/
if (functype != Item_func::LIKE_FUNC)
((Item_bool_rowready_func2*) func)->set_cmp_func();
} }
} }
else if (can_change_cond_ref_to_const(func, left_item, right_item, else if (can_change_cond_ref_to_const(func, left_item, right_item,
...@@ -13907,7 +13916,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list, ...@@ -13907,7 +13916,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
if ((tmp2=new COND_CMP(and_father,func))) if ((tmp2=new COND_CMP(and_father,func)))
save_list->push_back(tmp2); save_list->push_back(tmp2);
} }
func->set_cmp_func(); if (functype != Item_func::LIKE_FUNC)
((Item_bool_rowready_func2*) func)->set_cmp_func();
} }
} }
} }
......
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