summaryrefslogtreecommitdiff
path: root/sql/item.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item.cc')
-rw-r--r--sql/item.cc172
1 files changed, 172 insertions, 0 deletions
diff --git a/sql/item.cc b/sql/item.cc
index 604f2f6dd2c..d00fcb75d32 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1082,6 +1082,176 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
return 0;
}
+/******************************/
+static
+void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname)
+{
+ my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0),
+ c1.collation->name,c1.derivation_name(),
+ c2.collation->name,c2.derivation_name(),
+ fname);
+}
+
+
+static
+void my_coll_agg_error(DTCollation &c1, DTCollation &c2, DTCollation &c3,
+ const char *fname)
+{
+ my_error(ER_CANT_AGGREGATE_3COLLATIONS,MYF(0),
+ c1.collation->name,c1.derivation_name(),
+ c2.collation->name,c2.derivation_name(),
+ c3.collation->name,c3.derivation_name(),
+ fname);
+}
+
+
+static
+void my_coll_agg_error(Item** args, uint count, const char *fname)
+{
+ if (count == 2)
+ my_coll_agg_error(args[0]->collation, args[1]->collation, fname);
+ else if (count == 3)
+ my_coll_agg_error(args[0]->collation, args[1]->collation,
+ args[2]->collation, fname);
+ else
+ my_error(ER_CANT_AGGREGATE_NCOLLATIONS,MYF(0),fname);
+}
+
+
+bool agg_item_collations(DTCollation &c, const char *fname,
+ Item **av, uint count, uint flags)
+{
+ uint i;
+ c.set(av[0]->collation);
+ for (i= 1; i < count; i++)
+ {
+ if (c.aggregate(av[i]->collation, flags))
+ {
+ my_coll_agg_error(av, count, fname);
+ return TRUE;
+ }
+ }
+ if ((flags & MY_COLL_DISALLOW_NONE) &&
+ c.derivation == DERIVATION_NONE)
+ {
+ my_coll_agg_error(av, count, fname);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bool agg_item_collations_for_comparison(DTCollation &c, const char *fname,
+ Item **av, uint count, uint flags)
+{
+ return (agg_item_collations(c, fname, av, count,
+ flags | MY_COLL_DISALLOW_NONE));
+}
+
+
+/*
+ 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 agg_item_charsets(DTCollation &coll, const char *fname,
+ Item **args, uint nargs, uint flags)
+{
+ Item **arg, **last, *safe_args[2];
+ if (agg_item_collations(coll, fname, 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;
+ Query_arena *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.
+ */
+ arena= thd->change_arena_if_needed(&backup);
+
+ for (arg= args, last= args + nargs; arg < last; arg++)
+ {
+ Item* conv;
+ uint32 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, fname);
+ res= TRUE;
+ break; // we cannot return here, we need to restore "arena".
+ }
+ if ((*arg)->type() == Item::FIELD_ITEM)
+ ((Item_field *)(*arg))->no_const_subst= 1;
+ /*
+ If in statement prepare, then we create a converter for two
+ constant items, do it once and then reuse it.
+ If we're in execution of a prepared statement, arena is NULL,
+ and the conv was created in runtime memory. This can be
+ the case only if the argument is a parameter marker ('?'),
+ because for all true constants the charset converter has already
+ been created in prepare. In this case register the change for
+ rollback.
+ */
+ if (arena)
+ *arg= conv;
+ else
+ thd->change_item_tree(arg, conv);
+ /*
+ We do not check conv->fixed, because Item_func_conv_charset which can
+ be return by safe_charset_converter can't be fixed at creation
+ */
+ conv->fix_fields(thd, arg);
+ }
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ return res;
+}
+
+
+
+
+/**********************************************/
+
Item_field::Item_field(Field *f)
:Item_ident(0, NullS, *f->table_name, f->field_name),
item_equal(0), no_const_subst(0),
@@ -2018,6 +2188,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
void Item_param::reset()
{
+ DBUG_ENTER("Item_param::reset");
/* Shrink string buffer if it's bigger than max possible CHAR column */
if (str_value.alloced_length() > MAX_CHAR_WIDTH)
str_value.free();
@@ -2042,6 +2213,7 @@ void Item_param::reset()
DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_*
methods).
*/
+ DBUG_VOID_RETURN;
}