Skip to content

Commit

Permalink
MDEV-8661 Wrong result for SELECT..WHERE a='a' AND a='a' COLLATE lati…
Browse files Browse the repository at this point in the history
…n1_bin

MDEV-8679 Equal field propagation is not used for VARCHAR when it safely could
  • Loading branch information
Alexander Barkov committed Aug 26, 2015
1 parent c0b7bf2 commit 1b6b44b
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 87 deletions.
38 changes: 38 additions & 0 deletions mysql-test/r/ctype_latin1.result
Original file line number Diff line number Diff line change
Expand Up @@ -7915,3 +7915,41 @@ _latin1 0x7E _latin1 X'7E' _latin1 B'01111110'
#
# End of 10.0 tests
#
#
# Start of 10.1 tests
#
#
# MDEV-8661 Wrong result for SELECT..WHERE a='a' AND a='a' COLLATE latin1_bin
#
SET NAMES latin1;
CREATE TABLE t1 (a CHAR(10));
INSERT INTO t1 VALUES ('a'),('A');
SELECT * FROM t1 WHERE a='a' AND a='a' COLLATE latin1_bin;
a
a
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci);
INSERT INTO t1 VALUES ('a'),('A');
SELECT * FROM t1 WHERE a='a' COLLATE latin1_bin AND a='A' COLLATE latin1_swedish_ci;
a
a
DROP TABLE t1;
#
# MDEV-8679 Equal field propagation is not used for VARCHAR when it safely could
#
CREATE TABLE t1 (a VARCHAR(10));
INSERT INTO t1 VALUES ('10'),('11'),('12');
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='10' AND IF(a='10',1,0)=1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = '10')
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='10' AND CASE WHEN a='10' THEN 1 ELSE 0 END;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = '10')
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 @@ -248,3 +248,34 @@ SELECT _latin1 0x7E, _latin1 X'7E', _latin1 B'01111110';
--echo #
--echo # End of 10.0 tests
--echo #

--echo #
--echo # Start of 10.1 tests
--echo #

--echo #
--echo # MDEV-8661 Wrong result for SELECT..WHERE a='a' AND a='a' COLLATE latin1_bin
--echo #
SET NAMES latin1;
CREATE TABLE t1 (a CHAR(10));
INSERT INTO t1 VALUES ('a'),('A');
SELECT * FROM t1 WHERE a='a' AND a='a' COLLATE latin1_bin;
DROP TABLE t1;

CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_swedish_ci);
INSERT INTO t1 VALUES ('a'),('A');
SELECT * FROM t1 WHERE a='a' COLLATE latin1_bin AND a='A' COLLATE latin1_swedish_ci;
DROP TABLE t1;

--echo #
--echo # MDEV-8679 Equal field propagation is not used for VARCHAR when it safely could
--echo #
CREATE TABLE t1 (a VARCHAR(10));
INSERT INTO t1 VALUES ('10'),('11'),('12');
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='10' AND IF(a='10',1,0)=1;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='10' AND CASE WHEN a='10' THEN 1 ELSE 0 END;
DROP TABLE t1;

--echo #
--echo # End of 10.1 tests
--echo #
67 changes: 22 additions & 45 deletions sql/item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5356,11 +5356,14 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
FALSE otherwise
*/

bool Item_field::subst_argument_checker(uchar **arg)
bool Item_field::can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item_equal)
{
return *arg &&
(*arg == (uchar *) Item::ANY_SUBST ||
result_type() != STRING_RESULT ||
if (cmp_context == STRING_RESULT &&
ctx.compare_collation() != item_equal->compare_collation())
return false;
return (ctx.subst_constraint() == ANY_SUBST ||
field->result_type() != STRING_RESULT ||
(field->flags & BINARY_FLAG));
}

Expand Down Expand Up @@ -5418,14 +5421,23 @@ static void convert_zerofill_number_to_string(THD *thd, Item **item, Field_num *
- pointer to the field item, otherwise.
*/

Item *Item_field::equal_fields_propagator(THD *thd, uchar *arg)
Item *Item_field::propagate_equal_fields(THD *thd,
const Context &ctx,
COND_EQUAL *arg)
{
if (no_const_subst)
return this;
item_equal= find_item_equal((COND_EQUAL *) arg);
Item *item= 0;
if (item_equal)
{
if (!can_be_substituted_to_equal_item(ctx, item_equal))
{
item_equal= NULL;
return this;
}
item= item_equal->get_const();
}
/*
Disable const propagation for items used in different comparison contexts.
This must be done because, for example, Item_hex_string->val_int() is not
Expand Down Expand Up @@ -8123,43 +8135,6 @@ Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal)
}


/**
Check whether a reference to field item can be substituted for an equal item
@details
The function checks whether a substitution of a reference to field item for
an equal item is valid.
@param arg *arg != NULL <-> the reference is in the context
where substitution for an equal item is valid
@note
See also the note for Item_field::subst_argument_checker
@retval
TRUE substitution is valid
@retval
FALSE otherwise
*/
bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
{
bool res= FALSE;
if (*arg)
{
Item *item= real_item();
if (item->type() == FIELD_ITEM &&
(*arg == (uchar *) Item::ANY_SUBST ||
result_type() != STRING_RESULT ||
(((Item_field *) item)->field->flags & BINARY_FLAG)))
res= TRUE;
}
/* Block any substitution into the wrapped object */
if (*arg)
*arg= NULL;
return res;
}


/**
Set a pointer to the multiple equality the view field reference belongs to
(if any).
Expand All @@ -8180,7 +8155,7 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
of the compile method.
@note
The function calls Item_field::equal_fields_propagator for the field item
The function calls Item_field::propagate_equal_fields() for the field item
this->real_item() to do the job. Then it takes the pointer to equal_item
from this field item and assigns it to this->item_equal.
Expand All @@ -8189,12 +8164,14 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
- pointer to the field item, otherwise.
*/

Item *Item_direct_view_ref::equal_fields_propagator(THD *thd, uchar *arg)
Item *Item_direct_view_ref::propagate_equal_fields(THD *thd,
const Context &ctx,
COND_EQUAL *cond)
{
Item *field_item= real_item();
if (field_item->type() != FIELD_ITEM)
return this;
Item *item= field_item->equal_fields_propagator(thd, arg);
Item *item= field_item->propagate_equal_fields(thd, ctx, cond);
set_item_equal(field_item->get_item_equal());
field_item->set_item_equal(NULL);
if (item != field_item)
Expand Down
51 changes: 40 additions & 11 deletions sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -1417,16 +1417,45 @@ class Item: public Type_std_attributes
*/
enum Subst_constraint
{
NO_SUBST= 0, /* No substitution for a field is allowed */
ANY_SUBST, /* Any substitution for a field is allowed */
IDENTITY_SUBST /* Substitution for a field is allowed if any two
different values of the field type are not equal */
};

virtual bool subst_argument_checker(uchar **arg)
{
return (*arg != NULL);
}
/*
Item context attributes.
Comparison functions pass their attributes to propagate_equal_fields().
For exmple, for string comparison, the collation of the comparison
operation is important inside propagate_equal_fields().
TODO: We should move Item::cmp_context to from Item to Item::Context
eventually.
*/
class Context
{
/*
Which type of propagation is allowed:
- SUBST_ANY (loose equality, according to the collation), or
- SUBST_IDENTITY (strict binary equality).
*/
Subst_constraint m_subst_constraint;
/*
Collation of the comparison operation.
Important only when SUBST_ANY.
*/
CHARSET_INFO *m_compare_collation;
public:
Context(Subst_constraint subst, CHARSET_INFO *cs)
:m_subst_constraint(subst), m_compare_collation(cs) { }
Context(Subst_constraint subst)
:m_subst_constraint(subst), m_compare_collation(&my_charset_bin) { }
Subst_constraint subst_constraint() const { return m_subst_constraint; }
CHARSET_INFO *compare_collation() const { return m_compare_collation; }
};

virtual Item* propagate_equal_fields(THD*, const Context &, COND_EQUAL *)
{
return this;
};

/*
@brief
Expand All @@ -1446,7 +1475,6 @@ class Item: public Type_std_attributes
return trace_unsupported_by_check_vcol_func_processor(full_name());
}

virtual Item *equal_fields_propagator(THD *thd, uchar * arg) { return this; }
virtual bool set_no_const_sub(uchar *arg) { return FALSE; }
/* arg points to REPLACE_EQUAL_FIELD_ARG object */
virtual Item *replace_equal_field(THD *thd, uchar *arg) { return this; }
Expand Down Expand Up @@ -1587,7 +1615,7 @@ class Item: public Type_std_attributes
}
/**
Check whether this and the given item has compatible comparison context.
Used by the equality propagation. See Item_field::equal_fields_propagator.
Used by the equality propagation. See Item_field::propagate_equal_fields()
@return
TRUE if the context is the same
Expand Down Expand Up @@ -2403,8 +2431,9 @@ class Item_field :public Item_ident
Item_equal *get_item_equal() { return item_equal; }
void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; }
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
bool subst_argument_checker(uchar **arg);
Item *equal_fields_propagator(THD *thd, uchar *arg);
bool can_be_substituted_to_equal_item(const Context &ctx,
const Item_equal *item);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
bool set_no_const_sub(uchar *arg);
Item *replace_equal_field(THD *thd, uchar *arg);
inline uint32 max_disp_length() { return field->max_display_length(); }
Expand Down Expand Up @@ -3391,6 +3420,7 @@ class Item_args
return false;
}
bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
public:
uint arg_count;
Item_args(void)
Expand Down Expand Up @@ -3999,8 +4029,7 @@ class Item_direct_view_ref :public Item_direct_ref
Item_equal *get_item_equal() { return item_equal; }
void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; }
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
bool subst_argument_checker(uchar **arg);
Item *equal_fields_propagator(THD *thd, uchar *arg);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
Item *replace_equal_field(THD *thd, uchar *arg);
table_map used_tables() const;
void update_used_tables();
Expand Down
27 changes: 25 additions & 2 deletions sql/item_cmpfunc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3115,7 +3115,7 @@ void Item_func_case::fix_length_and_dec()
}
/*
Set cmp_context of all WHEN arguments. This prevents
Item_field::equal_fields_propagator() from transforming a
Item_field::propagate_equal_fields() from transforming a
zerofill argument into a string constant. Such a change would
require rebuilding cmp_items.
*/
Expand Down Expand Up @@ -4110,7 +4110,7 @@ void Item_func_in::fix_length_and_dec()
}
/*
Set cmp_context of all arguments. This prevents
Item_field::equal_fields_propagator() from transforming a zerofill integer
Item_field::propagate_equal_fields() from transforming a zerofill integer
argument into a string constant. Such a change would require rebuilding
cmp_itmes.
*/
Expand Down Expand Up @@ -4561,6 +4561,29 @@ Item *Item_cond::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
return Item_func::transform(thd, transformer, arg_t);
}


Item *Item_cond::propagate_equal_fields(THD *thd,
const Context &ctx,
COND_EQUAL *cond)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT(arg_count == 0);
List_iterator<Item> li(list);
Item *item;
while ((item= li++))
{
/*
The exact value of the second parameter to propagate_equal_fields()
is not important at this point. Item_func derivants will create and
pass their own context to the arguments.
*/
Item *new_item= item->propagate_equal_fields(thd, ANY_SUBST, cond);
if (new_item && new_item != item)
thd->change_item_tree(li.ref(), new_item);
}
return this;
}

void Item_cond::traverse_cond(Cond_traverser traverser,
void *arg, traverse_order order)
{
Expand Down
22 changes: 16 additions & 6 deletions sql/item_cmpfunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,12 @@ class Item_bool_rowready_func2 :public Item_bool_func2
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item(THD *thd);
bool subst_argument_checker(uchar **arg)
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
return (*arg != NULL);
Item_args::propagate_equal_fields(thd,
Context(ANY_SUBST, compare_collation()),
cond);
return this;
}
void fix_length_and_dec();
int set_cmp_func()
Expand Down Expand Up @@ -418,9 +421,10 @@ class Item_func_xor :public Item_bool_func
{ Item_func::print_op(str, query_type); }
longlong val_int();
Item *neg_transformer(THD *thd);
bool subst_argument_checker(uchar **arg)
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
return (*arg != NULL);
Item_args::propagate_equal_fields(thd, ANY_SUBST, cond);
return this;
}
};

Expand Down Expand Up @@ -692,7 +696,13 @@ class Item_func_opt_neg :public Item_bool_func
return this;
}
bool eq(const Item *item, bool binary_cmp) const;
bool subst_argument_checker(uchar **arg) { return TRUE; }
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
Item_args::propagate_equal_fields(thd,
Context(ANY_SUBST, compare_collation()),
cond);
return this;
}
};


Expand Down Expand Up @@ -1841,7 +1851,7 @@ class Item_cond :public Item_bool_func
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
bool subst_argument_checker(uchar **arg) { return TRUE; }
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(uchar *opt_arg);
Expand Down
Loading

0 comments on commit 1b6b44b

Please sign in to comment.