summaryrefslogtreecommitdiff
path: root/sql/item_func.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r--sql/item_func.cc114
1 files changed, 98 insertions, 16 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 3cc0fd077a4..173b1b3fd82 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -79,7 +79,7 @@ static void my_coll_agg_error(Item** args, uint count, const char *fname)
bool Item_func::agg_arg_collations(DTCollation &c, Item **av, uint count,
- bool allow_superset_conversion)
+ uint flags)
{
uint i;
c.nagg= 0;
@@ -87,29 +87,27 @@ bool Item_func::agg_arg_collations(DTCollation &c, Item **av, uint count,
c.set(av[0]->collation);
for (i= 1; i < count; i++)
{
- if (c.aggregate(av[i]->collation, allow_superset_conversion))
+ if (c.aggregate(av[i]->collation, flags))
{
my_coll_agg_error(av, count, func_name());
return TRUE;
}
}
+ if ((flags & MY_COLL_DISALLOW_NONE) &&
+ c.derivation == DERIVATION_NONE)
+ {
+ my_coll_agg_error(av, count, func_name());
+ return TRUE;
+ }
return FALSE;
}
bool Item_func::agg_arg_collations_for_comparison(DTCollation &c,
Item **av, uint count,
- bool allow_superset_conv)
+ uint flags)
{
- if (agg_arg_collations(c, av, count, allow_superset_conv))
- return TRUE;
-
- if (c.derivation == DERIVATION_NONE)
- {
- my_coll_agg_error(av, count, func_name());
- return TRUE;
- }
- return FALSE;
+ return (agg_arg_collations(c, av, count, flags | MY_COLL_DISALLOW_NONE));
}
@@ -122,6 +120,89 @@ eval_const_cond(COND *cond)
}
+
+/*
+ Collect arguments' character sets together.
+ We allow to apply automatic character set conversion in some cases.
+ The conditions when conversion is possible are:
+ - arguments A and B have different charsets
+ - A wins according to coercibility rules
+ (i.e. a column is stronger than a string constant,
+ an explicit COLLATE clause is stronger than a column)
+ - character set of A is either superset for character set of B,
+ or B is a string constant which can be converted into the
+ character set of A without data loss.
+
+ If all of the above is true, then it's possible to convert
+ B into the character set of A, and then compare according
+ to the collation of A.
+
+ For functions with more than two arguments:
+
+ collect(A,B,C) ::= collect(collect(A,B),C)
+*/
+
+bool Item_func::agg_arg_charsets(DTCollation &coll,
+ Item **args, uint nargs, uint flags)
+{
+ Item **arg, **last, *safe_args[2];
+ if (agg_arg_collations(coll, args, nargs, flags))
+ return TRUE;
+
+ /*
+ For better error reporting: save the first and the second argument.
+ We need this only if the the number of args is 3 or 2:
+ - for a longer argument list, "Illegal mix of collations"
+ doesn't display each argument's characteristics.
+ - if nargs is 1, then this error cannot happen.
+ */
+ if (nargs >=2 && nargs <= 3)
+ {
+ safe_args[0]= args[0];
+ safe_args[1]= args[1];
+ }
+
+ THD *thd= current_thd;
+ Item_arena *arena= thd->current_arena, backup;
+ bool res= FALSE;
+ /*
+ In case we're in statement prepare, create conversion item
+ in its memory: it will be reused on each execute.
+ */
+ if (arena->is_stmt_prepare())
+ thd->set_n_backup_item_arena(arena, &backup);
+
+ for (arg= args, last= args + nargs; arg < last; arg++)
+ {
+ Item* conv;
+ uint dummy_offset;
+ if (!String::needs_conversion(0, coll.collation,
+ (*arg)->collation.collation,
+ &dummy_offset))
+ continue;
+
+ if (!(conv= (*arg)->safe_charset_converter(coll.collation)))
+ {
+ if (nargs >=2 && nargs <= 3)
+ {
+ /* restore the original arguments for better error message */
+ args[0]= safe_args[0];
+ args[1]= safe_args[1];
+ }
+ my_coll_agg_error(args, nargs, func_name());
+ res= TRUE;
+ break; // we cannot return here, we need to restore "arena".
+ }
+ conv->fix_fields(thd, 0, &conv);
+ *arg= conv;
+ }
+ if (arena->is_stmt_prepare())
+ thd->restore_backup_item_arena(arena, &backup);
+ return res;
+}
+
+
+
void Item_func::set_arguments(List<Item> &list)
{
allowed_arg_cols= 1;
@@ -1185,7 +1266,7 @@ void Item_func_min_max::fix_length_and_dec()
cmp_type=item_cmp_type(cmp_type,args[i]->result_type());
}
if (cmp_type == STRING_RESULT)
- agg_arg_collations_for_comparison(collation, args, arg_count);
+ agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV);
}
@@ -1339,7 +1420,7 @@ longlong Item_func_coercibility::val_int()
void Item_func_locate::fix_length_and_dec()
{
maybe_null=0; max_length=11;
- agg_arg_collations_for_comparison(cmp_collation, args, 2);
+ agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV);
}
@@ -1438,7 +1519,7 @@ void Item_func_field::fix_length_and_dec()
for (uint i=1; i < arg_count ; i++)
cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
if (cmp_type == STRING_RESULT)
- agg_arg_collations_for_comparison(cmp_collation, args, arg_count);
+ agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV);
}
@@ -3003,8 +3084,9 @@ void Item_func_match::init_search(bool no_order)
if (ft_tmp->charset() != cmp_collation.collation)
{
+ uint dummy_errors;
search_value.copy(ft_tmp->ptr(), ft_tmp->length(), ft_tmp->charset(),
- cmp_collation.collation);
+ cmp_collation.collation, &dummy_errors);
ft_tmp= &search_value;
}