Commit 0c2035b7 authored by bar@mysql.com's avatar bar@mysql.com

func_gconcat.result, func_gconcat.test:

  Adding test
item_sum.cc:
  Adding a call for collation/charset aggregation,
      to collect attributes from the arguments. The actual bug fix.
item_func.h, item_func.cc, item.h, item.cc:
  - Removing collation aggrgation functions from Item_func class
      in item.cc, and adding it as non-class functions in item.cc
      to be able to reuse this code for group_concat.
      - Adding replacement for these functions into Item_func class
      as wrappers for moved functions, to minizize patch size,
parent ef3bc773
...@@ -445,6 +445,30 @@ group_concat(distinct b order by b) ...@@ -445,6 +445,30 @@ group_concat(distinct b order by b)
Warnings: Warnings:
Warning 1260 2 line(s) were cut by GROUP_CONCAT() Warning 1260 2 line(s) were cut by GROUP_CONCAT()
drop table t1; 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); CREATE TABLE t1 (id int);
SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL; SELECT GROUP_CONCAT(id) AS gc FROM t1 HAVING gc IS NULL;
gc gc
......
...@@ -263,6 +263,24 @@ select group_concat(distinct b order by b) from t1 group by a; ...@@ -263,6 +263,24 @@ select group_concat(distinct b order by b) from t1 group by a;
drop table t1; 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 # bug #7769: group_concat returning null is checked in having
# #
......
...@@ -523,6 +523,170 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) ...@@ -523,6 +523,170 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
return 0; 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_field::Item_field(Field *f)
:Item_ident(NullS, f->table_name, f->field_name) :Item_ident(NullS, f->table_name, f->field_name)
{ {
......
...@@ -330,6 +330,15 @@ public: ...@@ -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 class Item_num: public Item
{ {
public: public:
......
...@@ -39,73 +39,6 @@ bool check_reserved_words(LEX_STRING *name) ...@@ -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 */ /* return TRUE if item is a constant */
bool bool
...@@ -115,101 +48,6 @@ eval_const_cond(COND *cond) ...@@ -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) void Item_func::set_arguments(List<Item> &list)
{ {
allowed_arg_cols= 1; allowed_arg_cols= 1;
......
...@@ -145,12 +145,22 @@ public: ...@@ -145,12 +145,22 @@ public:
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
bool agg_arg_collations(DTCollation &c, Item **items, uint nitems, 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, bool agg_arg_collations_for_comparison(DTCollation &c,
Item **items, uint nitems, 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, 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); bool walk(Item_processor processor, byte *arg);
}; };
......
...@@ -1925,6 +1925,10 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1925,6 +1925,10 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
maybe_null|= args[i]->maybe_null; 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; result_field= 0;
null_value= 1; null_value= 1;
max_length= group_concat_max_len; max_length= group_concat_max_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment