1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03:00

MDEV-8661 Wrong result for SELECT..WHERE a='a' AND a='a' COLLATE latin1_bin

MDEV-8679 Equal field propagation is not used for VARCHAR when it safely could
This commit is contained in:
Alexander Barkov
2015-08-26 22:32:01 +04:00
parent c0b7bf2625
commit 1b6b44b6b5
9 changed files with 198 additions and 87 deletions

View File

@@ -7915,3 +7915,41 @@ _latin1 0x7E _latin1 X'7E' _latin1 B'01111110'
# #
# End of 10.0 tests # 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
#

View File

@@ -248,3 +248,34 @@ SELECT _latin1 0x7E, _latin1 X'7E', _latin1 B'01111110';
--echo # --echo #
--echo # End of 10.0 tests --echo # End of 10.0 tests
--echo # --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 #

View File

@@ -5356,11 +5356,14 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
FALSE otherwise 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 && if (cmp_context == STRING_RESULT &&
(*arg == (uchar *) Item::ANY_SUBST || ctx.compare_collation() != item_equal->compare_collation())
result_type() != STRING_RESULT || return false;
return (ctx.subst_constraint() == ANY_SUBST ||
field->result_type() != STRING_RESULT ||
(field->flags & BINARY_FLAG)); (field->flags & BINARY_FLAG));
} }
@@ -5418,14 +5421,23 @@ static void convert_zerofill_number_to_string(THD *thd, Item **item, Field_num *
- pointer to the field item, otherwise. - 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) if (no_const_subst)
return this; return this;
item_equal= find_item_equal((COND_EQUAL *) arg); item_equal= find_item_equal((COND_EQUAL *) arg);
Item *item= 0; Item *item= 0;
if (item_equal) if (item_equal)
{
if (!can_be_substituted_to_equal_item(ctx, item_equal))
{
item_equal= NULL;
return this;
}
item= item_equal->get_const(); item= item_equal->get_const();
}
/* /*
Disable const propagation for items used in different comparison contexts. Disable const propagation for items used in different comparison contexts.
This must be done because, for example, Item_hex_string->val_int() is not This must be done because, for example, Item_hex_string->val_int() is not
@@ -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 Set a pointer to the multiple equality the view field reference belongs to
(if any). (if any).
@@ -8180,7 +8155,7 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
of the compile method. of the compile method.
@note @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 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. from this field item and assigns it to this->item_equal.
@@ -8189,12 +8164,14 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
- pointer to the field item, otherwise. - 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(); Item *field_item= real_item();
if (field_item->type() != FIELD_ITEM) if (field_item->type() != FIELD_ITEM)
return this; 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()); set_item_equal(field_item->get_item_equal());
field_item->set_item_equal(NULL); field_item->set_item_equal(NULL);
if (item != field_item) if (item != field_item)

View File

@@ -1417,16 +1417,45 @@ public:
*/ */
enum Subst_constraint enum Subst_constraint
{ {
NO_SUBST= 0, /* No substitution for a field is allowed */
ANY_SUBST, /* Any 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 IDENTITY_SUBST /* Substitution for a field is allowed if any two
different values of the field type are not equal */ different values of the field type are not equal */
}; };
virtual bool subst_argument_checker(uchar **arg) /*
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
{ {
return (*arg != NULL); /*
} 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 @brief
@@ -1446,7 +1475,6 @@ public:
return trace_unsupported_by_check_vcol_func_processor(full_name()); 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; } virtual bool set_no_const_sub(uchar *arg) { return FALSE; }
/* arg points to REPLACE_EQUAL_FIELD_ARG object */ /* arg points to REPLACE_EQUAL_FIELD_ARG object */
virtual Item *replace_equal_field(THD *thd, uchar *arg) { return this; } virtual Item *replace_equal_field(THD *thd, uchar *arg) { return this; }
@@ -1587,7 +1615,7 @@ public:
} }
/** /**
Check whether this and the given item has compatible comparison context. 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 @return
TRUE if the context is the same TRUE if the context is the same
@@ -2403,8 +2431,9 @@ public:
Item_equal *get_item_equal() { return item_equal; } Item_equal *get_item_equal() { return item_equal; }
void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; }
Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item_equal *find_item_equal(COND_EQUAL *cond_equal);
bool subst_argument_checker(uchar **arg); bool can_be_substituted_to_equal_item(const Context &ctx,
Item *equal_fields_propagator(THD *thd, uchar *arg); const Item_equal *item);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
bool set_no_const_sub(uchar *arg); bool set_no_const_sub(uchar *arg);
Item *replace_equal_field(THD *thd, uchar *arg); Item *replace_equal_field(THD *thd, uchar *arg);
inline uint32 max_disp_length() { return field->max_display_length(); } inline uint32 max_disp_length() { return field->max_display_length(); }
@@ -3391,6 +3420,7 @@ protected:
return false; return false;
} }
bool transform_args(THD *thd, Item_transformer transformer, uchar *arg); bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
public: public:
uint arg_count; uint arg_count;
Item_args(void) Item_args(void)
@@ -3999,8 +4029,7 @@ public:
Item_equal *get_item_equal() { return item_equal; } Item_equal *get_item_equal() { return item_equal; }
void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; }
Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item_equal *find_item_equal(COND_EQUAL *cond_equal);
bool subst_argument_checker(uchar **arg); Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
Item *equal_fields_propagator(THD *thd, uchar *arg);
Item *replace_equal_field(THD *thd, uchar *arg); Item *replace_equal_field(THD *thd, uchar *arg);
table_map used_tables() const; table_map used_tables() const;
void update_used_tables(); void update_used_tables();

View File

@@ -3115,7 +3115,7 @@ void Item_func_case::fix_length_and_dec()
} }
/* /*
Set cmp_context of all WHEN arguments. This prevents 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 zerofill argument into a string constant. Such a change would
require rebuilding cmp_items. require rebuilding cmp_items.
*/ */
@@ -4110,7 +4110,7 @@ void Item_func_in::fix_length_and_dec()
} }
/* /*
Set cmp_context of all arguments. This prevents 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 argument into a string constant. Such a change would require rebuilding
cmp_itmes. cmp_itmes.
*/ */
@@ -4561,6 +4561,29 @@ Item *Item_cond::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
return Item_func::transform(thd, transformer, arg_t); 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 Item_cond::traverse_cond(Cond_traverser traverser,
void *arg, traverse_order order) void *arg, traverse_order order)
{ {

View File

@@ -369,9 +369,12 @@ public:
} }
Item *neg_transformer(THD *thd); Item *neg_transformer(THD *thd);
virtual Item *negated_item(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(); void fix_length_and_dec();
int set_cmp_func() int set_cmp_func()
@@ -418,9 +421,10 @@ public:
{ Item_func::print_op(str, query_type); } { Item_func::print_op(str, query_type); }
longlong val_int(); longlong val_int();
Item *neg_transformer(THD *thd); 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;
} }
}; };
@@ -692,7 +696,13 @@ public:
return this; return this;
} }
bool eq(const Item *item, bool binary_cmp) const; 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;
}
}; };
@@ -1841,7 +1851,7 @@ public:
void traverse_cond(Cond_traverser, void *arg, traverse_order order); void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd); void neg_arguments(THD *thd);
enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } 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 *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t); Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(uchar *opt_arg); bool eval_not_null_tables(uchar *opt_arg);

View File

@@ -415,6 +415,21 @@ Item *Item_func::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
return (this->*transformer)(thd, arg_t); return (this->*transformer)(thd, arg_t);
} }
void Item_args::propagate_equal_fields(THD *thd,
const Item::Context &ctx,
COND_EQUAL *cond)
{
Item **arg,**arg_end;
for (arg= args, arg_end= args + arg_count; arg != arg_end; arg++)
{
Item *new_item= (*arg)->propagate_equal_fields(thd, ctx, cond);
if (new_item && *arg != new_item)
thd->change_item_tree(arg, new_item);
}
}
/** /**
See comments in Item_cond::split_sum_func() See comments in Item_cond::split_sum_func()
*/ */

View File

@@ -341,19 +341,15 @@ public:
return FALSE; return FALSE;
} }
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
/* /*
By default only substitution for a field whose two different values By default only substitution for a field whose two different values
are never equal is allowed in the arguments of a function. are never equal is allowed in the arguments of a function.
This is overruled for the direct arguments of comparison functions. This is overruled for the direct arguments of comparison functions.
*/ */
bool subst_argument_checker(uchar **arg) Item_args::propagate_equal_fields(thd, IDENTITY_SUBST, cond);
{ return this;
if (*arg)
{
*arg= (uchar *) Item::IDENTITY_SUBST;
return TRUE;
}
return FALSE;
} }
/* /*

View File

@@ -13077,11 +13077,7 @@ COND *Item_func::build_equal_items(THD *thd, COND_EQUAL *inherited,
as soon the field is not of a string type or the field reference is as soon the field is not of a string type or the field reference is
an argument of a comparison predicate. an argument of a comparison predicate.
*/ */
uchar* is_subst_valid= (uchar *) Item::ANY_SUBST; COND *cond= propagate_equal_fields(thd, ANY_SUBST, inherited);
COND *cond= compile(thd, &Item::subst_argument_checker,
&is_subst_valid,
&Item::equal_fields_propagator,
(uchar *) inherited);
cond->update_used_tables(); cond->update_used_tables();
DBUG_ASSERT(cond == this); DBUG_ASSERT(cond == this);
DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]); DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
@@ -14906,11 +14902,7 @@ void propagate_new_equalities(THD *thd, Item *cond,
} }
else else
{ {
uchar* is_subst_valid= (uchar *) Item::ANY_SUBST; cond= cond->propagate_equal_fields(thd, Item::ANY_SUBST, inherited);
cond= cond->compile(thd, &Item::subst_argument_checker,
&is_subst_valid,
&Item::equal_fields_propagator,
(uchar *) inherited);
cond->update_used_tables(); cond->update_used_tables();
} }
} }