diff options
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r-- | sql/item_func.cc | 114 |
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; } |