Skip to content

Commit

Permalink
Merge branch '10.1' of https://github.com/MariaDB/server into ok-debp…
Browse files Browse the repository at this point in the history
…kg-10.1
  • Loading branch information
ottok committed Sep 1, 2015
2 parents b007dfb + a3c24ee commit bd3864e
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 27 deletions.
64 changes: 64 additions & 0 deletions mysql-test/r/ctype_latin1.result
Original file line number Diff line number Diff line change
Expand Up @@ -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))))
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
#
31 changes: 31 additions & 0 deletions mysql-test/t/ctype_latin1.test
Original file line number Diff line number Diff line change
Expand Up @@ -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';
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 # End of 10.1 tests
--echo #
41 changes: 41 additions & 0 deletions sql/field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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)
{
Field::make_field(field);
Expand Down
16 changes: 16 additions & 0 deletions sql/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,21 @@ class Field
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,
const Item *item) const;
virtual bool can_optimize_hash_join(const Item_bool_func *cond,
Expand Down Expand Up @@ -1187,6 +1202,7 @@ class Field_str :public Field {
{
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 */
Expand Down
4 changes: 2 additions & 2 deletions sql/item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1814,7 +1814,7 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array,


static bool
left_is_superset(DTCollation *left, DTCollation *right)
left_is_superset(const DTCollation *left, const DTCollation *right)
{
/* Allow convert to Unicode */
if (left->collation->state & MY_CS_UNICODE &&
Expand Down Expand Up @@ -1873,7 +1873,7 @@ left_is_superset(DTCollation *left, DTCollation *right)
@endcode
*/

bool DTCollation::aggregate(DTCollation &dt, uint flags)
bool DTCollation::aggregate(const DTCollation &dt, uint flags)
{
if (!my_charset_same(collation, dt.collation))
{
Expand Down
9 changes: 8 additions & 1 deletion sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ class DTCollation {
derivation= derivation_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)
{
collation= dt.collation;
Expand Down Expand Up @@ -160,7 +167,7 @@ class DTCollation {
}
void set(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)
{ set(dt1); return aggregate(dt2, flags); }
const char *derivation_name() const
Expand Down
6 changes: 4 additions & 2 deletions sql/item_cmpfunc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
return 1;
}
if (comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i),
set_null))
if (comparators[i].set_cmp_func_and_arg_cmp_context(owner,
(*a)->addr(i),
(*b)->addr(i),
set_null))
return 1;
}
break;
Expand Down
11 changes: 11 additions & 0 deletions sql/item_cmpfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ class Arg_comparator: public Sql_alloc
item_cmp_type((*a1)->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)(); }

int compare_string(); // compare args[0] & args[1]
Expand Down
53 changes: 31 additions & 22 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12513,7 +12513,6 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
*/

static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
const Item_bool_func *item,
COND_EQUAL *cond_equal)
{
Item *orig_left_item= left_item;
Expand Down Expand Up @@ -12639,28 +12638,36 @@ static bool check_simple_equality(THD *thd, Item *left_item, Item *right_item,
}

if (const_item &&
field_item->result_type() == const_item->result_type())
field_item->field->test_if_equality_guarantees_uniqueness(const_item))
{
/*
field_item and const_item are arguments of a scalar or a row
comparison function:
WHERE column=constant
WHERE (column, ...) = (constant, ...)

The owner comparison function has previously called fix_fields(),
so field_item and const_item should be directly comparable items,
field_item->cmp_context and const_item->cmp_context should be set.
In case of string comparison, charsets and collations of
field_item and const_item should have already be aggregated
for comparison, all necessary character set converters installed
and fixed.

In case of string comparison, const_item can be either:
- a weaker constant that does not need to be converted to field_item:
WHERE latin1_field = 'latin1_const'
WHERE varbinary_field = 'latin1_const'
WHERE latin1_bin_field = 'latin1_general_ci_const'
- a stronger constant that does not need to be converted to field_item:
WHERE latin1_field = binary 0xDF
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;

if (field_item->cmp_type() == STRING_RESULT)
{
CHARSET_INFO *cs= field_item->field->charset();
if (!item)
{
Item_func_eq *eq_item;
if (!(eq_item= new (thd->mem_root) Item_func_eq(thd, orig_left_item,
orig_right_item)) ||
eq_item->set_cmp_func_and_arg_cmp_context())
return FALSE;
eq_item->quick_fix_field();
item= eq_item;
}
if ((cs != item->compare_collation()) ||
!cs->coll->propagate(cs, 0, 0))
return FALSE;
}

Item_equal *item_equal = find_item_equal(cond_equal,
field_item->field, &copyfl);
if (copyfl)
Expand Down Expand Up @@ -12737,7 +12744,7 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
}
else
{
is_converted= check_simple_equality(thd, left_item, right_item, 0,
is_converted= check_simple_equality(thd, left_item, right_item,
cond_equal);
}

Expand Down Expand Up @@ -12799,7 +12806,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
(Item_row *) right_item,
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);
}


Expand Down Expand Up @@ -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
constant. Need to discuss this with Bar).

See also Field::test_if_equality_guaranees_uniqueness(const Item *item);
*/
static bool
test_if_equality_guarantees_uniqueness(Item *l, Item *r)
Expand Down

0 comments on commit bd3864e

Please sign in to comment.