mirror of
https://github.com/MariaDB/server.git
synced 2025-08-09 22:24:09 +03:00
MDEV-8707 Wrong result for
SELECT..WHERE varchar_column=DATE'2001-01-01' AND varchar_column='2001-01-01'
This commit is contained in:
@@ -8039,5 +8039,69 @@ Warnings:
|
|||||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 'a') and (`test`.`t1`.`a` between 'a' and <cache>(('c' collate latin1_bin))))
|
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 'a') and (`test`.`t1`.`a` between 'a' and <cache>(('c' collate latin1_bin))))
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
#
|
#
|
||||||
|
# MDEV-8707 Wrong result for SELECT..WHERE varchar_column=DATE'2001-01-01' AND varchar_column='2001-01-01'
|
||||||
|
#
|
||||||
|
SET NAMES latin1;
|
||||||
|
CREATE TABLE t1 (a VARCHAR(40));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=DATE'2001-01-01' AND a='2001-01-01';
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE a='2001-01-01' AND a=DATE'2001-01-01';
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01x',DATE'2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01x
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect date value: '2001-01-01x'
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01x');
|
||||||
|
a
|
||||||
|
2001-01-01x
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect date value: '2001-01-01x'
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01',DATE'2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a ENUM('2001-01-01','2001-01-01x'));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=DATE'2001-01-01' AND a='2001-01-01';
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE a='2001-01-01' AND a=DATE'2001-01-01';
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01x',DATE'2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01x
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect date value: '2001-01-01x'
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01x');
|
||||||
|
a
|
||||||
|
2001-01-01x
|
||||||
|
Warnings:
|
||||||
|
Warning 1292 Truncated incorrect date value: '2001-01-01x'
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01',DATE'2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01');
|
||||||
|
a
|
||||||
|
2001-01-01
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a VARCHAR(40),b VARCHAR(40));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=b AND a=DATE'2001-01-01';
|
||||||
|
a b
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a ENUM('2001-01-01','2001-01-01x'),b ENUM('2001-01-01','2001-01-01x'));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=b AND a=DATE'2001-01-01';
|
||||||
|
a b
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
# End of 10.1 tests
|
# End of 10.1 tests
|
||||||
#
|
#
|
||||||
|
@@ -298,6 +298,37 @@ SELECT * FROM t1 WHERE a BETWEEN 'a' AND 'c' COLLATE latin1_bin AND a='a';
|
|||||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a BETWEEN 'a' AND 'c' COLLATE latin1_bin AND a='a';
|
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a BETWEEN 'a' AND 'c' COLLATE latin1_bin AND a='a';
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-8707 Wrong result for SELECT..WHERE varchar_column=DATE'2001-01-01' AND varchar_column='2001-01-01'
|
||||||
|
--echo #
|
||||||
|
SET NAMES latin1;
|
||||||
|
CREATE TABLE t1 (a VARCHAR(40));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=DATE'2001-01-01' AND a='2001-01-01';
|
||||||
|
SELECT * FROM t1 WHERE a='2001-01-01' AND a=DATE'2001-01-01';
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01x',DATE'2001-01-01');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01',DATE'2001-01-01');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01');
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a ENUM('2001-01-01','2001-01-01x'));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01'),('2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=DATE'2001-01-01' AND a='2001-01-01';
|
||||||
|
SELECT * FROM t1 WHERE a='2001-01-01' AND a=DATE'2001-01-01';
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01x',DATE'2001-01-01');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=('2001-01-01',DATE'2001-01-01');
|
||||||
|
SELECT * FROM t1 WHERE (a,a)=(DATE'2001-01-01','2001-01-01');
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a VARCHAR(40),b VARCHAR(40));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=b AND a=DATE'2001-01-01';
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a ENUM('2001-01-01','2001-01-01x'),b ENUM('2001-01-01','2001-01-01x'));
|
||||||
|
INSERT INTO t1 VALUES ('2001-01-01','2001-01-01x');
|
||||||
|
SELECT * FROM t1 WHERE a=b AND a=DATE'2001-01-01';
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.1 tests
|
--echo # End of 10.1 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
41
sql/field.cc
41
sql/field.cc
@@ -1246,6 +1246,21 @@ double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(cmp_type() != STRING_RESULT); // For STRING_RESULT see Field_str
|
||||||
|
/*
|
||||||
|
We use result_type() rather than cmp_type() in the below condition,
|
||||||
|
because it covers a special case that string literals guarantee uniqueness
|
||||||
|
for temporal columns, so the query:
|
||||||
|
WHERE temporal_column='string'
|
||||||
|
cannot return multiple distinct temporal values.
|
||||||
|
QQ: perhaps we could allow INT/DECIMAL/DOUBLE types for temporal items.
|
||||||
|
*/
|
||||||
|
return result_type() == item->result_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This handles all numeric and BIT data types.
|
This handles all numeric and BIT data types.
|
||||||
*/
|
*/
|
||||||
@@ -1843,6 +1858,32 @@ Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Can't guarantee uniqueness when comparing a CHAR/VARCHAR/TEXT,
|
||||||
|
BINARY/VARBINARY/BLOB, ENUM,SET columns to an item with cmp_type()
|
||||||
|
of INT_RESULT, DOUBLE_RESULT, DECIMAL_RESULT or TIME_RESULT.
|
||||||
|
Example:
|
||||||
|
SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
|
||||||
|
return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'.
|
||||||
|
*/
|
||||||
|
if (!field_charset->coll->propagate(field_charset, 0, 0) ||
|
||||||
|
item->cmp_type() != STRING_RESULT)
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
Can't guarantee uniqueness when comparing to
|
||||||
|
an item of a different collation.
|
||||||
|
Example:
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci
|
||||||
|
return non-unique values 'a' and 'A'.
|
||||||
|
*/
|
||||||
|
DTCollation tmp(field_charset, field_derivation, repertoire());
|
||||||
|
return !tmp.aggregate(item->collation) && tmp.collation == field_charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Field_num::make_field(Send_field *field)
|
void Field_num::make_field(Send_field *field)
|
||||||
{
|
{
|
||||||
Field::make_field(field);
|
Field::make_field(field);
|
||||||
|
16
sql/field.h
16
sql/field.h
@@ -1012,6 +1012,21 @@ public:
|
|||||||
return (double) 0.5;
|
return (double) 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check if comparison between the field and an item unambiguously
|
||||||
|
identifies a distinct field value.
|
||||||
|
|
||||||
|
Example1: SELECT * FROM t1 WHERE int_column=10;
|
||||||
|
This example returns distinct integer value of 10.
|
||||||
|
|
||||||
|
Example2: SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
|
||||||
|
This example returns non-distinct values.
|
||||||
|
Comparison as DATE will return '2001-01-01' and '2001-01-01x',
|
||||||
|
but these two values are not equal to each other as VARCHARs.
|
||||||
|
See also the function with the same name in sql_select.cc.
|
||||||
|
*/
|
||||||
|
virtual bool test_if_equality_guarantees_uniqueness(const Item *const_item)
|
||||||
|
const;
|
||||||
virtual bool can_optimize_keypart_ref(const Item_bool_func *cond,
|
virtual bool can_optimize_keypart_ref(const Item_bool_func *cond,
|
||||||
const Item *item) const;
|
const Item *item) const;
|
||||||
virtual bool can_optimize_hash_join(const Item_bool_func *cond,
|
virtual bool can_optimize_hash_join(const Item_bool_func *cond,
|
||||||
@@ -1187,6 +1202,7 @@ public:
|
|||||||
{
|
{
|
||||||
return pos_in_interval_val_str(min, max, length_size());
|
return pos_in_interval_val_str(min, max, length_size());
|
||||||
}
|
}
|
||||||
|
bool test_if_equality_guarantees_uniqueness(const Item *const_item) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* base class for Field_string, Field_varstring and Field_blob */
|
/* base class for Field_string, Field_varstring and Field_blob */
|
||||||
|
@@ -1814,7 +1814,7 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array,
|
|||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
left_is_superset(DTCollation *left, DTCollation *right)
|
left_is_superset(const DTCollation *left, const DTCollation *right)
|
||||||
{
|
{
|
||||||
/* Allow convert to Unicode */
|
/* Allow convert to Unicode */
|
||||||
if (left->collation->state & MY_CS_UNICODE &&
|
if (left->collation->state & MY_CS_UNICODE &&
|
||||||
@@ -1873,7 +1873,7 @@ left_is_superset(DTCollation *left, DTCollation *right)
|
|||||||
@endcode
|
@endcode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool DTCollation::aggregate(DTCollation &dt, uint flags)
|
bool DTCollation::aggregate(const DTCollation &dt, uint flags)
|
||||||
{
|
{
|
||||||
if (!my_charset_same(collation, dt.collation))
|
if (!my_charset_same(collation, dt.collation))
|
||||||
{
|
{
|
||||||
|
@@ -127,6 +127,13 @@ public:
|
|||||||
derivation= derivation_arg;
|
derivation= derivation_arg;
|
||||||
set_repertoire_from_charset(collation_arg);
|
set_repertoire_from_charset(collation_arg);
|
||||||
}
|
}
|
||||||
|
DTCollation(CHARSET_INFO *collation_arg,
|
||||||
|
Derivation derivation_arg,
|
||||||
|
uint repertoire_arg)
|
||||||
|
:collation(collation_arg),
|
||||||
|
derivation(derivation_arg),
|
||||||
|
repertoire(repertoire_arg)
|
||||||
|
{ }
|
||||||
void set(const DTCollation &dt)
|
void set(const DTCollation &dt)
|
||||||
{
|
{
|
||||||
collation= dt.collation;
|
collation= dt.collation;
|
||||||
@@ -160,7 +167,7 @@ public:
|
|||||||
}
|
}
|
||||||
void set(Derivation derivation_arg)
|
void set(Derivation derivation_arg)
|
||||||
{ derivation= derivation_arg; }
|
{ derivation= derivation_arg; }
|
||||||
bool aggregate(DTCollation &dt, uint flags= 0);
|
bool aggregate(const DTCollation &dt, uint flags= 0);
|
||||||
bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
|
bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0)
|
||||||
{ set(dt1); return aggregate(dt2, flags); }
|
{ set(dt1); return aggregate(dt2, flags); }
|
||||||
const char *derivation_name() const
|
const char *derivation_name() const
|
||||||
|
@@ -553,8 +553,10 @@ int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type)
|
|||||||
my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
|
my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i),
|
if (comparators[i].set_cmp_func_and_arg_cmp_context(owner,
|
||||||
set_null))
|
(*a)->addr(i),
|
||||||
|
(*b)->addr(i),
|
||||||
|
set_null))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@@ -79,6 +79,17 @@ public:
|
|||||||
item_cmp_type((*a1)->cmp_type(),
|
item_cmp_type((*a1)->cmp_type(),
|
||||||
(*a2)->cmp_type()));
|
(*a2)->cmp_type()));
|
||||||
}
|
}
|
||||||
|
int set_cmp_func_and_arg_cmp_context(Item_func_or_sum *owner_arg,
|
||||||
|
Item **a1, Item **a2,
|
||||||
|
bool set_null_arg)
|
||||||
|
{
|
||||||
|
set_null= set_null_arg;
|
||||||
|
Item_result type= item_cmp_type((*a1)->cmp_type(), (*a2)->cmp_type());
|
||||||
|
int rc= set_cmp_func(owner_arg, a1, a2, type);
|
||||||
|
if (!rc)
|
||||||
|
(*a1)->cmp_context= (*a2)->cmp_context= type;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
inline int compare() { return (this->*func)(); }
|
inline int compare() { return (this->*func)(); }
|
||||||
|
|
||||||
int compare_string(); // compare args[0] & args[1]
|
int compare_string(); // compare args[0] & args[1]
|
||||||
|
@@ -12513,7 +12513,6 @@ finish:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
|
static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
|
||||||
const Item_bool_func *item,
|
|
||||||
COND_EQUAL *cond_equal)
|
COND_EQUAL *cond_equal)
|
||||||
{
|
{
|
||||||
Item *orig_left_item= left_item;
|
Item *orig_left_item= left_item;
|
||||||
@@ -12639,27 +12638,35 @@ static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (const_item &&
|
if (const_item &&
|
||||||
field_item->result_type() == const_item->result_type())
|
field_item->field->test_if_equality_guarantees_uniqueness(const_item))
|
||||||
{
|
{
|
||||||
bool copyfl;
|
/*
|
||||||
|
field_item and const_item are arguments of a scalar or a row
|
||||||
|
comparison function:
|
||||||
|
WHERE column=constant
|
||||||
|
WHERE (column, ...) = (constant, ...)
|
||||||
|
|
||||||
if (field_item->cmp_type() == STRING_RESULT)
|
The owner comparison function has previously called fix_fields(),
|
||||||
{
|
so field_item and const_item should be directly comparable items,
|
||||||
CHARSET_INFO *cs= field_item->field->charset();
|
field_item->cmp_context and const_item->cmp_context should be set.
|
||||||
if (!item)
|
In case of string comparison, charsets and collations of
|
||||||
{
|
field_item and const_item should have already be aggregated
|
||||||
Item_func_eq *eq_item;
|
for comparison, all necessary character set converters installed
|
||||||
if (!(eq_item= new (thd->mem_root) Item_func_eq(thd, orig_left_item,
|
and fixed.
|
||||||
orig_right_item)) ||
|
|
||||||
eq_item->set_cmp_func_and_arg_cmp_context())
|
In case of string comparison, const_item can be either:
|
||||||
return FALSE;
|
- a weaker constant that does not need to be converted to field_item:
|
||||||
eq_item->quick_fix_field();
|
WHERE latin1_field = 'latin1_const'
|
||||||
item= eq_item;
|
WHERE varbinary_field = 'latin1_const'
|
||||||
}
|
WHERE latin1_bin_field = 'latin1_general_ci_const'
|
||||||
if ((cs != item->compare_collation()) ||
|
- a stronger constant that does not need to be converted to field_item:
|
||||||
!cs->coll->propagate(cs, 0, 0))
|
WHERE latin1_field = binary 0xDF
|
||||||
return FALSE;
|
WHERE latin1_field = 'a' COLLATE latin1_bin
|
||||||
}
|
- a result of conversion (e.g. from the session character set)
|
||||||
|
to the character set of field_item:
|
||||||
|
WHERE latin1_field = 'utf8_string_with_latin1_repertoire'
|
||||||
|
*/
|
||||||
|
bool copyfl;
|
||||||
|
|
||||||
Item_equal *item_equal = find_item_equal(cond_equal,
|
Item_equal *item_equal = find_item_equal(cond_equal,
|
||||||
field_item->field, ©fl);
|
field_item->field, ©fl);
|
||||||
@@ -12737,7 +12744,7 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
is_converted= check_simple_equality(thd, left_item, right_item, 0,
|
is_converted= check_simple_equality(thd, left_item, right_item,
|
||||||
cond_equal);
|
cond_equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12799,7 +12806,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
|
|||||||
(Item_row *) right_item,
|
(Item_row *) right_item,
|
||||||
cond_equal, eq_list);
|
cond_equal, eq_list);
|
||||||
}
|
}
|
||||||
return check_simple_equality(thd, left_item, right_item, this, cond_equal);
|
return check_simple_equality(thd, left_item, right_item, cond_equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -15457,6 +15464,8 @@ Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
|
|||||||
/*
|
/*
|
||||||
psergey-todo: this returns false for int_column='1234' (here '1234' is a
|
psergey-todo: this returns false for int_column='1234' (here '1234' is a
|
||||||
constant. Need to discuss this with Bar).
|
constant. Need to discuss this with Bar).
|
||||||
|
|
||||||
|
See also Field::test_if_equality_guaranees_uniqueness(const Item *item);
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
test_if_equality_guarantees_uniqueness(Item *l, Item *r)
|
test_if_equality_guarantees_uniqueness(Item *l, Item *r)
|
||||||
|
Reference in New Issue
Block a user