summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/func_gconcat.result24
-rw-r--r--mysql-test/t/func_gconcat.test18
-rw-r--r--sql/item.cc164
-rw-r--r--sql/item.h9
-rw-r--r--sql/item_func.cc162
-rw-r--r--sql/item_func.h16
-rw-r--r--sql/item_sum.cc4
7 files changed, 232 insertions, 165 deletions
diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result
index 7a256edc91a..661793821a0 100644
--- a/mysql-test/r/func_gconcat.result
+++ b/mysql-test/r/func_gconcat.result
@@ -445,6 +445,30 @@ group_concat(distinct b order by b)
Warnings:
Warning 1260 2 line(s) were cut by GROUP_CONCAT()
drop table t1;
+create table t1 (a varchar(255) character set cp1250 collate cp1250_general_ci,
+b varchar(255) character set koi8r);
+insert into t1 values ('xxx','yyy');
+select collation(a) from t1;
+collation(a)
+cp1250_general_ci
+select collation(group_concat(a)) from t1;
+collation(group_concat(a))
+cp1250_general_ci
+create table t2 select group_concat(a) as a from t1;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` longtext character set cp1250
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select collation(group_concat(a,_koi8r'test')) from t1;
+collation(group_concat(a,_koi8r'test'))
+cp1250_general_ci
+select collation(group_concat(a,_koi8r 0xC1C2)) from t1;
+ERROR HY000: Illegal mix of collations (cp1250_general_ci,IMPLICIT) and (koi8r_general_ci,COERCIBLE) for operation 'group_concat'
+select collation(group_concat(a,b)) from t1;
+ERROR HY000: Illegal mix of collations (cp1250_general_ci,IMPLICIT) and (koi8r_general_ci,IMPLICIT) for operation 'group_concat'
+drop table t1;
+drop table t2;
CREATE TABLE t1 (id int);
SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL;
gc
diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test
index 5f02db7707c..edb4d42e3c2 100644
--- a/mysql-test/t/func_gconcat.test
+++ b/mysql-test/t/func_gconcat.test
@@ -264,6 +264,24 @@ select group_concat(distinct b order by b) from t1 group by a;
drop table t1;
#
+# Bug#10201
+#
+create table t1 (a varchar(255) character set cp1250 collate cp1250_general_ci,
+ b varchar(255) character set koi8r);
+insert into t1 values ('xxx','yyy');
+select collation(a) from t1;
+select collation(group_concat(a)) from t1;
+create table t2 select group_concat(a) as a from t1;
+show create table t2;
+select collation(group_concat(a,_koi8r'test')) from t1;
+--error 1267
+select collation(group_concat(a,_koi8r 0xC1C2)) from t1;
+--error 1267
+select collation(group_concat(a,b)) from t1;
+drop table t1;
+drop table t2;
+
+#
# bug #7769: group_concat returning null is checked in having
#
CREATE TABLE t1 (id int);
diff --git a/sql/item.cc b/sql/item.cc
index 71940c824e7..84dbc382a52 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -523,6 +523,170 @@ 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;
+ Item_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".
+ }
+ conv->fix_fields(thd, 0, &conv);
+ /*
+ 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);
+ }
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ return res;
+}
+
+
+
+
+/**********************************************/
+
Item_field::Item_field(Field *f)
:Item_ident(NullS, f->table_name, f->field_name)
{
diff --git a/sql/item.h b/sql/item.h
index 895463ceeca..825b37fe64c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -330,6 +330,15 @@ public:
};
+bool agg_item_collations(DTCollation &c, const char *name,
+ Item **items, uint nitems, uint flags= 0);
+bool agg_item_collations_for_comparison(DTCollation &c, const char *name,
+ Item **items, uint nitems,
+ uint flags= 0);
+bool agg_item_charsets(DTCollation &c, const char *name,
+ Item **items, uint nitems, uint flags= 0);
+
+
class Item_num: public Item
{
public:
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 44917eb48e4..2514a4beacf 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -39,73 +39,6 @@ bool check_reserved_words(LEX_STRING *name)
}
-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 Item_func::agg_arg_collations(DTCollation &c, 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, 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,
- uint flags)
-{
- return (agg_arg_collations(c, av, count, flags | MY_COLL_DISALLOW_NONE));
-}
-
-
/* return TRUE if item is a constant */
bool
@@ -115,101 +48,6 @@ 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, 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, func_name());
- res= TRUE;
- break; // we cannot return here, we need to restore "arena".
- }
- conv->fix_fields(thd, 0, &conv);
- /*
- 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);
- }
- if (arena)
- thd->restore_backup_item_arena(arena, &backup);
- return res;
-}
-
-
-
void Item_func::set_arguments(List<Item> &list)
{
allowed_arg_cols= 1;
diff --git a/sql/item_func.h b/sql/item_func.h
index 5e36f9863bb..5d6cc445317 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -145,12 +145,22 @@ public:
Item *get_tmp_table_item(THD *thd);
bool agg_arg_collations(DTCollation &c, Item **items, uint nitems,
- uint flags= 0);
+ uint flags= 0)
+ {
+ return agg_item_collations(c, func_name(), items, nitems, flags);
+ }
bool agg_arg_collations_for_comparison(DTCollation &c,
Item **items, uint nitems,
- uint flags= 0);
+ uint flags= 0)
+ {
+ return agg_item_collations_for_comparison(c, func_name(),
+ items, nitems, flags);
+ }
bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems,
- uint flags= 0);
+ uint flags= 0)
+ {
+ return agg_item_charsets(c, func_name(), items, nitems, flags);
+ }
bool walk(Item_processor processor, byte *arg);
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 0e252259f53..6ca3f024f61 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1925,6 +1925,10 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
maybe_null|= args[i]->maybe_null;
}
+ if (agg_item_charsets(collation, func_name(),
+ args, arg_count, MY_COLL_ALLOW_CONV))
+ return 1;
+
result_field= 0;
null_value= 1;
max_length= group_concat_max_len;