Skip to content

Commit 0302efc

Browse files
author
Alexander Barkov
committed
MDEV-8705 Wrong result for SELECT..WHERE latin1_bin_column='a' AND latin1_bin_column='A'
MDEV-8712 Wrong result for SELECT..WHERE latin1_bin_column=_latin1'a' AND latin1_bin_column='A'
1 parent 4aebba3 commit 0302efc

File tree

7 files changed

+130
-38
lines changed

7 files changed

+130
-38
lines changed

mysql-test/r/ctype_latin1.result

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8149,5 +8149,24 @@ Warnings:
81498149
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 'a') and (length(`test`.`t1`.`a`) = 2))
81508150
DROP TABLE t1;
81518151
#
8152+
# MDEV-8712 Wrong result for SELECT..WHERE latin1_bin_column=_latin1'a' AND latin1_bin_column='A'
8153+
#
8154+
SET NAMES latin1;
8155+
CREATE TABLE t1 (a VARCHAR(20) COLLATE latin1_bin);
8156+
INSERT INTO t1 VALUES ('a'),('b');
8157+
SELECT * FROM t1 WHERE a='A';
8158+
a
8159+
SELECT * FROM t1 WHERE a='A' AND a=_latin1'a';
8160+
a
8161+
SELECT * FROM t1 WHERE a=_latin1'a' AND a='A';
8162+
a
8163+
SELECT * FROM t1 WHERE a=_latin1'A';
8164+
a
8165+
SELECT * FROM t1 WHERE a=_latin1'A' AND a=_latin1'a';
8166+
a
8167+
SELECT * FROM t1 WHERE a=_latin1'a' AND a=_latin1'A';
8168+
a
8169+
DROP TABLE t1;
8170+
#
81528171
# End of 10.1 tests
81538172
#

mysql-test/r/ctype_uca.result

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13709,5 +13709,22 @@ SELECT '10' COLLATE utf8_general_ci XOR '20' COLLATE utf8_unicode_ci;
1370913709
'10' COLLATE utf8_general_ci XOR '20' COLLATE utf8_unicode_ci
1371013710
0
1371113711
#
13712+
# MDEV-8705 Wrong result for SELECT..WHERE latin1_bin_column='a' AND latin1_bin_column='A'
13713+
#
13714+
SET NAMES utf8 COLLATE utf8_german2_ci;
13715+
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin);
13716+
INSERT INTO t1 VALUES ('a'),('A');
13717+
SELECT * FROM t1 WHERE a='a';
13718+
a
13719+
a
13720+
SELECT * FROM t1 WHERE a=_utf8'a';
13721+
a
13722+
a
13723+
SELECT * FROM t1 WHERE a='a' AND a=_utf8'a';
13724+
a
13725+
a
13726+
DROP TABLE t1;
13727+
SET NAMES utf8;
13728+
#
1371213729
# End of MariaDB-10.1 tests
1371313730
#

mysql-test/t/ctype_latin1.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,19 @@ SELECT * FROM t1 WHERE a='a' AND LENGTH(a)=2;
349349
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND LENGTH(a)=2;
350350
DROP TABLE t1;
351351

352+
--echo #
353+
--echo # MDEV-8712 Wrong result for SELECT..WHERE latin1_bin_column=_latin1'a' AND latin1_bin_column='A'
354+
--echo #
355+
SET NAMES latin1;
356+
CREATE TABLE t1 (a VARCHAR(20) COLLATE latin1_bin);
357+
INSERT INTO t1 VALUES ('a'),('b');
358+
SELECT * FROM t1 WHERE a='A';
359+
SELECT * FROM t1 WHERE a='A' AND a=_latin1'a';
360+
SELECT * FROM t1 WHERE a=_latin1'a' AND a='A';
361+
SELECT * FROM t1 WHERE a=_latin1'A';
362+
SELECT * FROM t1 WHERE a=_latin1'A' AND a=_latin1'a';
363+
SELECT * FROM t1 WHERE a=_latin1'a' AND a=_latin1'A';
364+
DROP TABLE t1;
352365

353366
--echo #
354367
--echo # End of 10.1 tests

mysql-test/t/ctype_uca.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,18 @@ DROP TABLE t1;
630630
--echo #
631631
SELECT '10' COLLATE utf8_general_ci XOR '20' COLLATE utf8_unicode_ci;
632632

633+
--echo #
634+
--echo # MDEV-8705 Wrong result for SELECT..WHERE latin1_bin_column='a' AND latin1_bin_column='A'
635+
--echo #
636+
SET NAMES utf8 COLLATE utf8_german2_ci;
637+
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin);
638+
INSERT INTO t1 VALUES ('a'),('A');
639+
SELECT * FROM t1 WHERE a='a';
640+
SELECT * FROM t1 WHERE a=_utf8'a';
641+
# Make sure this does not return "Illegal mix of collations"
642+
SELECT * FROM t1 WHERE a='a' AND a=_utf8'a';
643+
DROP TABLE t1;
644+
SET NAMES utf8;
633645

634646
--echo #
635647
--echo # End of MariaDB-10.1 tests

sql/item_cmpfunc.cc

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5920,7 +5920,7 @@ Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item):
59205920
with_const= with_const_item;
59215921
equal_items.push_back(f1, thd->mem_root);
59225922
equal_items.push_back(f2, thd->mem_root);
5923-
compare_as_dates= with_const_item && f2->cmp_type() == TIME_RESULT;
5923+
cmp.cmp_collation.set(f2->collation);
59245924
cmp.set_compare_type(item_cmp_type(f1, f2));
59255925
upper_levels= NULL;
59265926
}
@@ -5950,7 +5950,7 @@ Item_equal::Item_equal(THD *thd, Item_equal *item_equal):
59505950
equal_items.push_back(item, thd->mem_root);
59515951
}
59525952
with_const= item_equal->with_const;
5953-
compare_as_dates= item_equal->compare_as_dates;
5953+
cmp.cmp_collation.set(item_equal->cmp.cmp_collation);
59545954
cmp.set_compare_type(item_equal->cmp.compare_type());
59555955
cond_false= item_equal->cond_false;
59565956
upper_levels= item_equal->upper_levels;
@@ -5971,41 +5971,85 @@ Item_equal::Item_equal(THD *thd, Item_equal *item_equal):
59715971
the list. Otherwise the value of c is compared with the value of the
59725972
constant item from equal_items. If they are not equal cond_false is set
59735973
to TRUE. This serves as an indicator that this Item_equal is always FALSE.
5974-
The optional parameter f is used to adjust the flag compare_as_dates.
59755974
*/
59765975

5977-
void Item_equal::add_const(THD *thd, Item *c, Item *f)
5976+
void Item_equal::add_const(THD *thd, Item *c)
59785977
{
59795978
if (cond_false)
59805979
return;
59815980
if (!with_const)
59825981
{
59835982
with_const= TRUE;
5984-
if (f)
5985-
compare_as_dates= f->cmp_type() == TIME_RESULT;
59865983
equal_items.push_front(c, thd->mem_root);
59875984
return;
59885985
}
59895986
Item *const_item= get_const();
5990-
if (compare_as_dates)
5991-
{
5992-
cmp.set_datetime_cmp_func(this, &c, &const_item);
5993-
cond_false= cmp.compare();
5994-
}
5995-
else
5996-
{
5997-
Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, c, const_item);
5998-
if (func->set_cmp_func())
5987+
switch (cmp.compare_type()) {
5988+
case TIME_RESULT:
59995989
{
5990+
cmp.set_datetime_cmp_func(this, &c, &const_item);
5991+
cond_false= cmp.compare();
5992+
break;
5993+
}
5994+
case STRING_RESULT:
5995+
{
5996+
String *str1, *str2;
60005997
/*
6001-
Setting a comparison function fails when trying to compare
6002-
incompatible charsets. Charset compatibility is checked earlier,
6003-
except for constant subqueries where we may do it here.
5998+
Suppose we have an expression (with a string type field) like this:
5999+
WHERE field=const1 AND field=const2 ...
6000+
6001+
For all pairs field=constXXX we know that:
6002+
6003+
- Item_func_eq::fix_length_and_dec() performed collation and character
6004+
set aggregation and added character set converters when needed.
6005+
Note, the case like:
6006+
WHERE field=const1 COLLATE latin1_bin AND field=const2
6007+
is not handled here, because the field would be replaced to
6008+
Item_func_set_collation, which cannot get into Item_equal.
6009+
So all constXXX that are handled by Item_equal
6010+
already have compatible character sets with "field".
6011+
6012+
- Also, Field_str::test_if_equality_guarantees_uniqueness() guarantees
6013+
that the comparison collation of all equalities handled by Item_equal
6014+
match the the collation of the field.
6015+
6016+
Therefore, at Item_equal::add_const() time all constants constXXX
6017+
should be directly comparable to each other without an additional
6018+
character set conversion.
6019+
It's safe to do val_str() for "const_item" and "c" and compare
6020+
them according to the collation of the *field*.
6021+
6022+
So in a script like this:
6023+
CREATE TABLE t1 (a VARCHAR(10) COLLATE xxx);
6024+
INSERT INTO t1 VALUES ('a'),('A');
6025+
SELECT * FROM t1 WHERE a='a' AND a='A';
6026+
Item_equal::add_const() effectively rewrites the condition to:
6027+
SELECT * FROM t1 WHERE a='a' AND 'a' COLLATE xxx='A';
6028+
and then to:
6029+
SELECT * FROM t1 WHERE a='a'; // if the two constants were equal
6030+
// e.g. in case of latin1_swedish_ci
6031+
or to:
6032+
SELECT * FROM t1 WHERE FALSE; // if the two constants were not equal
6033+
// e.g. in case of latin1_bin
6034+
6035+
Note, both "const_item" and "c" can return NULL, e.g.:
6036+
SELECT * FROM t1 WHERE a=NULL AND a='const';
6037+
SELECT * FROM t1 WHERE a='const' AND a=NULL;
6038+
SELECT * FROM t1 WHERE a='const' AND a=(SELECT MAX(a) FROM t2)
60046039
*/
6005-
return;
6040+
cond_false= !(str1= const_item->val_str(&cmp.value1)) ||
6041+
!(str2= c->val_str(&cmp.value2)) ||
6042+
!str1->eq(str2, compare_collation());
6043+
break;
6044+
}
6045+
default:
6046+
{
6047+
Item_func_eq *func= new (thd->mem_root) Item_func_eq(thd, c, const_item);
6048+
if (func->set_cmp_func())
6049+
return;
6050+
func->quick_fix_field();
6051+
cond_false= !func->val_int();
60066052
}
6007-
func->quick_fix_field();
6008-
cond_false= !func->val_int();
60096053
}
60106054
if (with_const && equal_items.elements == 1)
60116055
cond_true= TRUE;
@@ -6482,14 +6526,6 @@ void Item_equal::print(String *str, enum_query_type query_type)
64826526
}
64836527

64846528

6485-
CHARSET_INFO *Item_equal::compare_collation() const
6486-
{
6487-
Item_equal_fields_iterator it(*((Item_equal*) this));
6488-
Item *item= it++;
6489-
return item->collation.collation;
6490-
}
6491-
6492-
64936529
/*
64946530
@brief Get the first equal field of multiple equality.
64956531
@param[in] field the field to get equal field to

sql/item_cmpfunc.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,12 +2052,6 @@ class Item_equal: public Item_bool_func
20522052
the equal_items should be ignored.
20532053
*/
20542054
bool cond_true;
2055-
/*
2056-
compare_as_dates=TRUE <-> constants equal to fields from equal_items
2057-
must be compared as datetimes and not as strings.
2058-
compare_as_dates can be TRUE only if with_const=TRUE
2059-
*/
2060-
bool compare_as_dates;
20612055
/*
20622056
The comparator used to compare constants equal to fields from equal_items
20632057
as datetimes. The comparator is used only if compare_as_dates=TRUE
@@ -2080,7 +2074,7 @@ class Item_equal: public Item_bool_func
20802074
Item_equal(THD *thd, Item_equal *item_equal);
20812075
/* Currently the const item is always the first in the list of equal items */
20822076
inline Item* get_const() { return with_const ? equal_items.head() : NULL; }
2083-
void add_const(THD *thd, Item *c, Item *f = NULL);
2077+
void add_const(THD *thd, Item *c);
20842078
/** Add a non-constant item to the multiple equality */
20852079
void add(Item *f, MEM_ROOT *root) { equal_items.push_back(f, root); }
20862080
bool contains(Field *field);
@@ -2110,7 +2104,8 @@ class Item_equal: public Item_bool_func
21102104
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
21112105
virtual void print(String *str, enum_query_type query_type);
21122106
Item_result compare_type() const { return cmp.compare_type(); }
2113-
CHARSET_INFO *compare_collation() const;
2107+
CHARSET_INFO *compare_collation() const
2108+
{ return cmp.cmp_collation.collation; }
21142109

21152110
void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
21162111
void set_link_equal_fields(bool flag) { link_equal_fields= flag; }

sql/sql_select.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12699,7 +12699,7 @@ static bool check_simple_equality(THD *thd, const Item::Context &ctx,
1269912699
already contains a constant and its value is not equal to
1270012700
the value of const_item.
1270112701
*/
12702-
item_equal->add_const(thd, const_item2, orig_field_item);
12702+
item_equal->add_const(thd, const_item2);
1270312703
}
1270412704
else
1270512705
{

0 commit comments

Comments
 (0)