mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Cleanup of Item_func_group_concat
mysql-test/r/func_gconcat.result: Test result fixed. sql/item_sum.cc: Cleanup of Item_func_group_concat: - last unobvious things commented - don't store NULLs in the tree. - remove unneeded variables. - use setup_order, not setup_group to setup group concat order list sql/item_sum.h: Cleanup for Item_func_group_concat: remove unneded variables, methods, move a bunch of variables to private: section.
This commit is contained in:
@ -166,7 +166,7 @@ set group_concat_max_len = 1024;
|
|||||||
select group_concat(sum(a)) from t1 group by grp;
|
select group_concat(sum(a)) from t1 group by grp;
|
||||||
ERROR HY000: Invalid use of group function
|
ERROR HY000: Invalid use of group function
|
||||||
select grp,group_concat(c order by 2) from t1 group by grp;
|
select grp,group_concat(c order by 2) from t1 group by grp;
|
||||||
ERROR 42S22: Unknown column '2' in 'group statement'
|
ERROR 42S22: Unknown column '2' in 'order clause'
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 ( URL_ID int(11), URL varchar(80));
|
create table t1 ( URL_ID int(11), URL varchar(80));
|
||||||
create table t2 ( REQ_ID int(11), URL_ID int(11));
|
create table t2 ( REQ_ID int(11), URL_ID int(11));
|
||||||
|
336
sql/item_sum.cc
336
sql/item_sum.cc
@ -2555,7 +2555,7 @@ String *Item_sum_udf_str::val_str(String *str)
|
|||||||
GROUP_CONCAT function
|
GROUP_CONCAT function
|
||||||
|
|
||||||
SQL SYNTAX:
|
SQL SYNTAX:
|
||||||
GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...]
|
GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...]
|
||||||
[SEPARATOR str_const])
|
[SEPARATOR str_const])
|
||||||
|
|
||||||
concat of values from "group by" operation
|
concat of values from "group by" operation
|
||||||
@ -2575,8 +2575,9 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1,
|
|||||||
byte* key2)
|
byte* key2)
|
||||||
{
|
{
|
||||||
Item_func_group_concat* grp_item= (Item_func_group_concat*)arg;
|
Item_func_group_concat* grp_item= (Item_func_group_concat*)arg;
|
||||||
|
TABLE *table= grp_item->table;
|
||||||
Item **field_item, **end;
|
Item **field_item, **end;
|
||||||
char *record= (char*) grp_item->table->record[0];
|
char *record= (char*) table->record[0] + table->s->null_bytes;
|
||||||
|
|
||||||
for (field_item= grp_item->args, end= field_item + grp_item->arg_count_field;
|
for (field_item= grp_item->args, end= field_item + grp_item->arg_count_field;
|
||||||
field_item < end;
|
field_item < end;
|
||||||
@ -2609,7 +2610,8 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2)
|
|||||||
{
|
{
|
||||||
Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
|
Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
|
||||||
ORDER **order_item, **end;
|
ORDER **order_item, **end;
|
||||||
char *record= (char*) grp_item->table->record[0];
|
TABLE *table= grp_item->table;
|
||||||
|
char *record= (char*) table->record[0] + table->s->null_bytes;
|
||||||
|
|
||||||
for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
|
for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
|
||||||
order_item < end;
|
order_item < end;
|
||||||
@ -2622,6 +2624,7 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2)
|
|||||||
the temporary table, not the original field
|
the temporary table, not the original field
|
||||||
*/
|
*/
|
||||||
Field *field= item->get_tmp_table_field();
|
Field *field= item->get_tmp_table_field();
|
||||||
|
/* If the item is a constant, there is no tmp table field */
|
||||||
if (field)
|
if (field)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@ -2633,7 +2636,7 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2)
|
|||||||
/*
|
/*
|
||||||
We can't return 0 because in that case the tree class would remove this
|
We can't return 0 because in that case the tree class would remove this
|
||||||
item as double value. This would cause problems for case-changes and
|
item as double value. This would cause problems for case-changes and
|
||||||
if the the returned values are not the same we do the sort on.
|
if the returned values are not the same we do the sort on.
|
||||||
*/
|
*/
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2665,48 +2668,48 @@ int group_concat_key_cmp_with_distinct_and_order(void* arg,byte* key1,
|
|||||||
int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
|
int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
|
||||||
Item_func_group_concat *item)
|
Item_func_group_concat *item)
|
||||||
{
|
{
|
||||||
char buff[MAX_FIELD_WIDTH];
|
TABLE *table= item->table;
|
||||||
String tmp((char *)&buff,sizeof(buff),default_charset_info), tmp2;
|
char *record= (char*) table->record[0] + table->s->null_bytes;
|
||||||
char *record= (char*) item->table->record[0];
|
String tmp(table->record[1], table->s->reclength, default_charset_info), tmp2;
|
||||||
|
String &result= item->result;
|
||||||
|
Item **arg= item->args, **arg_end= item->args + item->arg_count_field;
|
||||||
|
|
||||||
if (item->result.length())
|
if (result.length())
|
||||||
item->result.append(*item->separator);
|
result.append(*item->separator);
|
||||||
|
|
||||||
tmp.length(0);
|
tmp.length(0);
|
||||||
|
|
||||||
for (uint i= 0; i < item->arg_count_field; i++)
|
for (; arg < arg_end; arg++)
|
||||||
{
|
{
|
||||||
Item *show_item= item->args[i];
|
String *res;
|
||||||
if (!show_item->const_item())
|
if (! (*arg)->const_item())
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We have to use get_tmp_table_field() instead of
|
We have to use get_tmp_table_field() instead of
|
||||||
real_item()->get_tmp_table_field() because we want the field in
|
real_item()->get_tmp_table_field() because we want the field in
|
||||||
the temporary table, not the original field
|
the temporary table, not the original field
|
||||||
|
We also can't use table->field array to access the fields
|
||||||
|
because it contains both order and arg list fields.
|
||||||
*/
|
*/
|
||||||
Field *field= show_item->get_tmp_table_field();
|
Field *field= (*arg)->get_tmp_table_field();
|
||||||
String *res;
|
|
||||||
char *save_ptr= field->ptr;
|
char *save_ptr= field->ptr;
|
||||||
uint offset= (uint) (save_ptr - record);
|
uint offset= (uint) (save_ptr - record);
|
||||||
DBUG_ASSERT(offset < item->table->s->reclength);
|
DBUG_ASSERT(offset < table->s->reclength);
|
||||||
field->ptr= (char *) key + offset;
|
field->ptr= (char *) key + offset;
|
||||||
res= field->val_str(&tmp,&tmp2);
|
res= field->val_str(&tmp,&tmp2);
|
||||||
item->result.append(*res);
|
|
||||||
field->ptr= save_ptr;
|
field->ptr= save_ptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
res= (*arg)->val_str(&tmp);
|
||||||
String *res= show_item->val_str(&tmp);
|
if (res)
|
||||||
if (res)
|
result.append(*res);
|
||||||
item->result.append(*res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stop if length of result more than group_concat_max_len */
|
/* stop if length of result more than max_length */
|
||||||
if (item->result.length() > item->group_concat_max_len)
|
if (result.length() > item->max_length)
|
||||||
{
|
{
|
||||||
item->count_cut_values++;
|
item->count_cut_values++;
|
||||||
item->result.length(item->group_concat_max_len);
|
result.length(item->max_length);
|
||||||
item->warning_for_row= TRUE;
|
item->warning_for_row= TRUE;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2716,36 +2719,31 @@ int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Constructor of Item_func_group_concat
|
Constructor of Item_func_group_concat
|
||||||
is_distinct - distinct
|
distinct_arg - distinct
|
||||||
is_select - list of expression for show values
|
select_list - list of expression for show values
|
||||||
is_order - list of sort columns
|
order_list - list of sort columns
|
||||||
is_separator - string value of separator
|
separator_arg - string value of separator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Item_func_group_concat::Item_func_group_concat(bool is_distinct,
|
Item_func_group_concat::
|
||||||
List<Item> *is_select,
|
Item_func_group_concat(bool distinct_arg, List<Item> *select_list,
|
||||||
SQL_LIST *is_order,
|
SQL_LIST *order_list, String *separator_arg)
|
||||||
String *is_separator)
|
:tmp_table_param(0), warning(0),
|
||||||
:Item_sum(), tmp_table_param(0), max_elements_in_tree(0), warning(0),
|
separator(separator_arg), tree(0), table(0),
|
||||||
key_length(0), tree_mode(0), distinct(is_distinct), warning_for_row(0),
|
|
||||||
separator(is_separator), tree(&tree_base), table(0),
|
|
||||||
order(0), tables_list(0),
|
order(0), tables_list(0),
|
||||||
arg_count_order(0), arg_count_field(0),
|
arg_count_order(order_list ? order_list->elements : 0),
|
||||||
count_cut_values(0)
|
arg_count_field(select_list->elements),
|
||||||
|
count_cut_values(0),
|
||||||
|
distinct(distinct_arg),
|
||||||
|
warning_for_row(FALSE),
|
||||||
|
original(0)
|
||||||
{
|
{
|
||||||
Item *item_select;
|
Item *item_select;
|
||||||
Item **arg_ptr;
|
Item **arg_ptr;
|
||||||
|
|
||||||
original= 0;
|
quick_group= FALSE;
|
||||||
quick_group= 0;
|
|
||||||
mark_as_sum_func();
|
|
||||||
order= 0;
|
|
||||||
group_concat_max_len= current_thd->variables.group_concat_max_len;
|
|
||||||
|
|
||||||
arg_count_field= is_select->elements;
|
|
||||||
arg_count_order= is_order ? is_order->elements : 0;
|
|
||||||
arg_count= arg_count_field + arg_count_order;
|
arg_count= arg_count_field + arg_count_order;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We need to allocate:
|
We need to allocate:
|
||||||
args - arg_count_field+arg_count_order
|
args - arg_count_field+arg_count_order
|
||||||
@ -2753,23 +2751,23 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct,
|
|||||||
order - arg_count_order
|
order - arg_count_order
|
||||||
*/
|
*/
|
||||||
if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
|
if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
|
||||||
sizeof(ORDER*)*arg_count_order)))
|
sizeof(ORDER*)*arg_count_order)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
order= (ORDER**)(args + arg_count);
|
order= (ORDER**)(args + arg_count);
|
||||||
|
|
||||||
/* fill args items of show and sort */
|
/* fill args items of show and sort */
|
||||||
List_iterator_fast<Item> li(*is_select);
|
List_iterator_fast<Item> li(*select_list);
|
||||||
|
|
||||||
for (arg_ptr=args ; (item_select= li++) ; arg_ptr++)
|
for (arg_ptr=args ; (item_select= li++) ; arg_ptr++)
|
||||||
*arg_ptr= item_select;
|
*arg_ptr= item_select;
|
||||||
|
|
||||||
if (arg_count_order)
|
if (arg_count_order)
|
||||||
{
|
{
|
||||||
ORDER **order_ptr= order;
|
ORDER **order_ptr= order;
|
||||||
for (ORDER *order_item= (ORDER*) is_order->first;
|
for (ORDER *order_item= (ORDER*) order_list->first;
|
||||||
order_item != NULL;
|
order_item != NULL;
|
||||||
order_item= order_item->next)
|
order_item= order_item->next)
|
||||||
{
|
{
|
||||||
(*order_ptr++)= order_item;
|
(*order_ptr++)= order_item;
|
||||||
*arg_ptr= *order_item->item;
|
*arg_ptr= *order_item->item;
|
||||||
@ -2777,28 +2775,24 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Item_func_group_concat::Item_func_group_concat(THD *thd,
|
Item_func_group_concat::Item_func_group_concat(THD *thd,
|
||||||
Item_func_group_concat *item)
|
Item_func_group_concat *item)
|
||||||
:Item_sum(thd, item),item_thd(thd),
|
:Item_sum(thd, item),
|
||||||
tmp_table_param(item->tmp_table_param),
|
tmp_table_param(item->tmp_table_param),
|
||||||
max_elements_in_tree(item->max_elements_in_tree),
|
|
||||||
warning(item->warning),
|
warning(item->warning),
|
||||||
key_length(item->key_length),
|
|
||||||
tree_mode(item->tree_mode),
|
|
||||||
distinct(item->distinct),
|
|
||||||
warning_for_row(item->warning_for_row),
|
|
||||||
separator(item->separator),
|
separator(item->separator),
|
||||||
tree(item->tree),
|
tree(item->tree),
|
||||||
table(item->table),
|
table(item->table),
|
||||||
order(item->order),
|
order(item->order),
|
||||||
tables_list(item->tables_list),
|
tables_list(item->tables_list),
|
||||||
group_concat_max_len(item->group_concat_max_len),
|
|
||||||
arg_count_order(item->arg_count_order),
|
arg_count_order(item->arg_count_order),
|
||||||
arg_count_field(item->arg_count_field),
|
arg_count_field(item->arg_count_field),
|
||||||
field_list_offset(item->field_list_offset),
|
|
||||||
count_cut_values(item->count_cut_values),
|
count_cut_values(item->count_cut_values),
|
||||||
|
distinct(item->distinct),
|
||||||
|
warning_for_row(item->warning_for_row),
|
||||||
|
always_null(item->always_null),
|
||||||
original(item)
|
original(item)
|
||||||
{
|
{
|
||||||
quick_group= item->quick_group;
|
quick_group= item->quick_group;
|
||||||
@ -2817,36 +2811,33 @@ void Item_func_group_concat::cleanup()
|
|||||||
*/
|
*/
|
||||||
if (!original)
|
if (!original)
|
||||||
{
|
{
|
||||||
THD *thd= current_thd;
|
|
||||||
if (table)
|
|
||||||
{
|
|
||||||
free_tmp_table(thd, table);
|
|
||||||
table= 0;
|
|
||||||
}
|
|
||||||
delete tmp_table_param;
|
delete tmp_table_param;
|
||||||
tmp_table_param= 0;
|
tmp_table_param= 0;
|
||||||
if (tree_mode)
|
if (table)
|
||||||
{
|
{
|
||||||
tree_mode= 0;
|
THD *thd= table->in_use;
|
||||||
delete_tree(tree);
|
free_tmp_table(thd, table);
|
||||||
}
|
table= 0;
|
||||||
if (warning)
|
if (tree)
|
||||||
{
|
{
|
||||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
delete_tree(tree);
|
||||||
sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values);
|
tree= 0;
|
||||||
warning->set_msg(thd, warn_buff);
|
}
|
||||||
warning= 0;
|
if (warning)
|
||||||
|
{
|
||||||
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||||
|
sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values);
|
||||||
|
warning->set_msg(thd, warn_buff);
|
||||||
|
warning= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
DBUG_ASSERT(tree == 0);
|
||||||
|
DBUG_ASSERT(warning == 0);
|
||||||
}
|
}
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Item_func_group_concat::~Item_func_group_concat()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Item *Item_func_group_concat::copy_or_same(THD* thd)
|
Item *Item_func_group_concat::copy_or_same(THD* thd)
|
||||||
{
|
{
|
||||||
return new (thd->mem_root) Item_func_group_concat(thd, this);
|
return new (thd->mem_root) Item_func_group_concat(thd, this);
|
||||||
@ -2859,14 +2850,9 @@ void Item_func_group_concat::clear()
|
|||||||
result.copy();
|
result.copy();
|
||||||
null_value= TRUE;
|
null_value= TRUE;
|
||||||
warning_for_row= FALSE;
|
warning_for_row= FALSE;
|
||||||
if (table)
|
if (tree)
|
||||||
{
|
|
||||||
table->file->extra(HA_EXTRA_NO_CACHE);
|
|
||||||
table->file->delete_all_rows();
|
|
||||||
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
|
||||||
}
|
|
||||||
if (tree_mode)
|
|
||||||
reset_tree(tree);
|
reset_tree(tree);
|
||||||
|
/* No need to reset the table as we never call write_row */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2883,45 +2869,39 @@ bool Item_func_group_concat::add()
|
|||||||
if (!show_item->const_item())
|
if (!show_item->const_item())
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Here we use real_item as we want the original field data that should
|
Here we use real_item as we want the original field data that should
|
||||||
be written to table->record[0]
|
be written to table->record[0]
|
||||||
*/
|
*/
|
||||||
Field *f= show_item->real_item()->get_tmp_table_field();
|
Field *f= show_item->real_item()->get_tmp_table_field();
|
||||||
if (f->is_null())
|
if (f->is_null())
|
||||||
return 0; // Skip row if it contains null
|
return 0; // Skip row if it contains null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
null_value= FALSE;
|
null_value= FALSE;
|
||||||
|
|
||||||
TREE_ELEMENT *el= 0; // Only for safety
|
TREE_ELEMENT *el= 0; // Only for safety
|
||||||
if (tree_mode)
|
if (tree)
|
||||||
el= tree_insert(tree, table->record[0], 0, tree->custom_arg);
|
el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
|
||||||
|
tree->custom_arg);
|
||||||
/*
|
/*
|
||||||
If the row is not a duplicate (el->count == 1)
|
If the row is not a duplicate (el->count == 1)
|
||||||
we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
|
we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
|
||||||
instead of doing tree traverse later.
|
instead of doing tree traverse later.
|
||||||
*/
|
*/
|
||||||
if (result.length() <= group_concat_max_len &&
|
if (result.length() <= max_length &&
|
||||||
!warning_for_row &&
|
!warning_for_row &&
|
||||||
(!tree_mode || (el->count == 1 && distinct && !arg_count_order)))
|
(!tree || (el->count == 1 && distinct && !arg_count_order)))
|
||||||
dump_leaf_key(table->record[0], 1, this);
|
dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Item_func_group_concat::reset_field()
|
|
||||||
{
|
|
||||||
if (tree_mode)
|
|
||||||
reset_tree(tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
||||||
{
|
{
|
||||||
uint i; /* for loop variable */
|
uint i; /* for loop variable */
|
||||||
DBUG_ASSERT(fixed == 0);
|
DBUG_ASSERT(fixed == 0);
|
||||||
|
|
||||||
if (!thd->allow_sum_func)
|
if (!thd->allow_sum_func)
|
||||||
@ -2930,16 +2910,17 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
MYF(0));
|
MYF(0));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
if (!args) /* allocation in constructor may fail */
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
thd->allow_sum_func= 0;
|
thd->allow_sum_func= 0;
|
||||||
maybe_null= 0;
|
maybe_null= 0;
|
||||||
item_thd= thd;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Fix fields for select list and ORDER clause
|
Fix fields for select list and ORDER clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (i=0 ; i < arg_count ; i++)
|
for (i=0 ; i < arg_count ; i++)
|
||||||
{
|
{
|
||||||
if ((!args[i]->fixed &&
|
if ((!args[i]->fixed &&
|
||||||
args[i]->fix_fields(thd, tables, args + i)) ||
|
args[i]->fix_fields(thd, tables, args + i)) ||
|
||||||
@ -2951,12 +2932,8 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
|
|
||||||
result_field= 0;
|
result_field= 0;
|
||||||
null_value= 1;
|
null_value= 1;
|
||||||
max_length= group_concat_max_len;
|
|
||||||
thd->allow_sum_func= 1;
|
thd->allow_sum_func= 1;
|
||||||
if (!(tmp_table_param= new TMP_TABLE_PARAM))
|
max_length= thd->variables.group_concat_max_len;
|
||||||
return TRUE;
|
|
||||||
/* We'll convert all blobs to varchar fields in the temporary table */
|
|
||||||
tmp_table_param->convert_blob_length= group_concat_max_len;
|
|
||||||
tables_list= tables;
|
tables_list= tables;
|
||||||
fixed= 1;
|
fixed= 1;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -2967,82 +2944,83 @@ bool Item_func_group_concat::setup(THD *thd)
|
|||||||
{
|
{
|
||||||
List<Item> list;
|
List<Item> list;
|
||||||
SELECT_LEX *select_lex= thd->lex->current_select;
|
SELECT_LEX *select_lex= thd->lex->current_select;
|
||||||
uint const_fields;
|
|
||||||
qsort_cmp2 compare_key;
|
qsort_cmp2 compare_key;
|
||||||
DBUG_ENTER("Item_func_group_concat::setup");
|
DBUG_ENTER("Item_func_group_concat::setup");
|
||||||
|
|
||||||
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
push all not constant fields to list and create temp table
|
Currently setup() can be called twice. Please add
|
||||||
|
assertion here when this is fixed.
|
||||||
*/
|
*/
|
||||||
const_fields= 0;
|
if (table || tree)
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
|
if (!(tmp_table_param= new TMP_TABLE_PARAM))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
/* We'll convert all blobs to varchar fields in the temporary table */
|
||||||
|
tmp_table_param->convert_blob_length= max_length;
|
||||||
|
/* Push all not constant fields to the list and create a temp table */
|
||||||
always_null= 0;
|
always_null= 0;
|
||||||
for (uint i= 0; i < arg_count_field; i++)
|
for (uint i= 0; i < arg_count_field; i++)
|
||||||
{
|
{
|
||||||
Item *item= args[i];
|
Item *item= args[i];
|
||||||
if (list.push_back(item))
|
if (list.push_back(item))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(TRUE);
|
||||||
if (item->const_item())
|
if (item->const_item())
|
||||||
{
|
{
|
||||||
const_fields++;
|
|
||||||
(void) item->val_int();
|
(void) item->val_int();
|
||||||
if (item->null_value)
|
if (item->null_value)
|
||||||
always_null= 1;
|
{
|
||||||
|
always_null= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (always_null)
|
if (always_null)
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
List<Item> all_fields(list);
|
List<Item> all_fields(list);
|
||||||
if (arg_count_order)
|
/*
|
||||||
{
|
Try to find every ORDER expression in the list of GROUP_CONCAT
|
||||||
bool hidden_group_fields;
|
arguments. If an expression is not found, prepend it to
|
||||||
setup_group(thd, args, tables_list, list, all_fields, *order,
|
"all_fields". The resulting field list is used as input to create
|
||||||
&hidden_group_fields);
|
tmp table columns.
|
||||||
}
|
*/
|
||||||
|
if (arg_count_order &&
|
||||||
|
setup_order(thd, args, tables_list, list, all_fields, *order))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
count_field_types(tmp_table_param,all_fields,0);
|
count_field_types(tmp_table_param,all_fields,0);
|
||||||
if (table)
|
DBUG_ASSERT(table == 0);
|
||||||
{
|
|
||||||
/*
|
|
||||||
We come here when we are getting the result from a temporary table,
|
|
||||||
not the original tables used in the query
|
|
||||||
*/
|
|
||||||
free_tmp_table(thd, table);
|
|
||||||
tmp_table_param->cleanup();
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
We have to create a temporary table to get descriptions of fields
|
We have to create a temporary table to get descriptions of fields
|
||||||
(types, sizes and so on).
|
(types, sizes and so on).
|
||||||
|
|
||||||
Note that in the table, we first have the ORDER BY fields, then the
|
Note that in the table, we first have the ORDER BY fields, then the
|
||||||
field list.
|
field list.
|
||||||
|
|
||||||
We need to set set_sum_field in true for storing value of blob in buffer
|
We need to set set_sum_field in true for storing value of blob in buffer
|
||||||
of a record instead of a pointer of one.
|
of a record instead of a pointer of one.
|
||||||
*/
|
*/
|
||||||
if (!(table=create_tmp_table(thd, tmp_table_param, all_fields,
|
if (!(table= create_tmp_table(thd, tmp_table_param, all_fields,
|
||||||
(ORDER*) 0, 0, TRUE,
|
(ORDER*) 0, 0, TRUE,
|
||||||
select_lex->options | thd->options,
|
select_lex->options | thd->options,
|
||||||
HA_POS_ERROR,(char *) "")))
|
HA_POS_ERROR, (char*) "")))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(TRUE);
|
||||||
table->file->extra(HA_EXTRA_NO_ROWS);
|
table->file->extra(HA_EXTRA_NO_ROWS);
|
||||||
table->no_rows= 1;
|
table->no_rows= 1;
|
||||||
|
|
||||||
key_length= table->s->reclength;
|
|
||||||
|
|
||||||
/* Offset to first result field in table */
|
if (distinct || arg_count_order)
|
||||||
field_list_offset= table->s->fields - (list.elements - const_fields);
|
|
||||||
|
|
||||||
if (tree_mode)
|
|
||||||
delete_tree(tree);
|
|
||||||
|
|
||||||
/* choose function of sort */
|
|
||||||
tree_mode= distinct || arg_count_order;
|
|
||||||
if (tree_mode)
|
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
Need sorting: init tree and choose a function to sort.
|
||||||
|
Don't reserve space for NULLs: if any of gconcat arguments is NULL,
|
||||||
|
the row is not added to the result.
|
||||||
|
*/
|
||||||
|
uint tree_key_length= table->s->reclength - table->s->null_bytes;
|
||||||
|
|
||||||
|
tree= &tree_base;
|
||||||
if (arg_count_order)
|
if (arg_count_order)
|
||||||
{
|
{
|
||||||
if (distinct)
|
if (distinct)
|
||||||
@ -3055,27 +3033,16 @@ bool Item_func_group_concat::setup(THD *thd)
|
|||||||
compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct;
|
compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Create a tree of sort. Tree is used for a sort and a remove double
|
Create a tree for sorting. The tree is used to sort and to remove
|
||||||
values (according with syntax of the function). If function doesn't
|
duplicate values (according to the syntax of this function). If there
|
||||||
contain DISTINCT and ORDER BY clauses, we don't create this tree.
|
is no DISTINCT or ORDER BY clauses, we don't create this tree.
|
||||||
*/
|
*/
|
||||||
init_tree(tree, min(thd->variables.max_heap_table_size,
|
init_tree(tree, min(thd->variables.max_heap_table_size,
|
||||||
thd->variables.sortbuff_size/16), 0,
|
thd->variables.sortbuff_size/16), 0,
|
||||||
key_length, compare_key, 0, NULL, (void*) this);
|
tree_key_length, compare_key, 0, NULL, (void*) this);
|
||||||
max_elements_in_tree= (key_length ?
|
|
||||||
thd->variables.max_heap_table_size/key_length : 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copy table and tree_mode if they belong to this item (if item have not
|
|
||||||
pointer to original item from which was made copy => it own its objects)
|
|
||||||
*/
|
|
||||||
if (original)
|
|
||||||
{
|
|
||||||
original->table= table;
|
|
||||||
original->tree_mode= tree_mode;
|
|
||||||
}
|
}
|
||||||
DBUG_RETURN(0);
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3083,10 +3050,10 @@ bool Item_func_group_concat::setup(THD *thd)
|
|||||||
|
|
||||||
void Item_func_group_concat::make_unique()
|
void Item_func_group_concat::make_unique()
|
||||||
{
|
{
|
||||||
|
tmp_table_param= 0;
|
||||||
table=0;
|
table=0;
|
||||||
original= 0;
|
original= 0;
|
||||||
tree_mode= 0; // to prevent delete_tree call on uninitialized tree
|
tree= 0;
|
||||||
tree= &tree_base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3096,16 +3063,17 @@ String* Item_func_group_concat::val_str(String* str)
|
|||||||
if (null_value)
|
if (null_value)
|
||||||
return 0;
|
return 0;
|
||||||
if (count_cut_values && !warning)
|
if (count_cut_values && !warning)
|
||||||
warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
{
|
||||||
|
DBUG_ASSERT(table);
|
||||||
|
warning= push_warning(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||||
ER_CUT_VALUE_GROUP_CONCAT,
|
ER_CUT_VALUE_GROUP_CONCAT,
|
||||||
ER(ER_CUT_VALUE_GROUP_CONCAT));
|
ER(ER_CUT_VALUE_GROUP_CONCAT));
|
||||||
|
}
|
||||||
if (result.length())
|
if (result.length())
|
||||||
return &result;
|
return &result;
|
||||||
if (tree_mode)
|
if (tree)
|
||||||
{
|
|
||||||
tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
|
tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
|
||||||
left_root_right);
|
left_root_right);
|
||||||
}
|
|
||||||
return &result;
|
return &result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3127,7 +3095,7 @@ void Item_func_group_concat::print(String *str)
|
|||||||
for (uint i= 0 ; i < arg_count_order ; i++)
|
for (uint i= 0 ; i < arg_count_order ; i++)
|
||||||
{
|
{
|
||||||
if (i)
|
if (i)
|
||||||
str->append(',');
|
str->append(',');
|
||||||
(*order[i]->item)->print(str);
|
(*order[i]->item)->print(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,15 +834,26 @@ class MYSQL_ERROR;
|
|||||||
|
|
||||||
class Item_func_group_concat : public Item_sum
|
class Item_func_group_concat : public Item_sum
|
||||||
{
|
{
|
||||||
THD *item_thd;
|
|
||||||
TMP_TABLE_PARAM *tmp_table_param;
|
TMP_TABLE_PARAM *tmp_table_param;
|
||||||
uint max_elements_in_tree;
|
|
||||||
MYSQL_ERROR *warning;
|
MYSQL_ERROR *warning;
|
||||||
uint key_length;
|
String result;
|
||||||
bool tree_mode;
|
String *separator;
|
||||||
|
TREE tree_base;
|
||||||
|
TREE *tree;
|
||||||
|
TABLE *table;
|
||||||
|
ORDER **order;
|
||||||
|
TABLE_LIST *tables_list;
|
||||||
|
uint arg_count_order; // total count of ORDER BY items
|
||||||
|
uint arg_count_field; // count of arguments
|
||||||
|
uint count_cut_values;
|
||||||
bool distinct;
|
bool distinct;
|
||||||
bool warning_for_row;
|
bool warning_for_row;
|
||||||
bool always_null;
|
bool always_null;
|
||||||
|
/*
|
||||||
|
Following is 0 normal object and pointer to original one for copy
|
||||||
|
(to correctly free resources)
|
||||||
|
*/
|
||||||
|
Item_func_group_concat *original;
|
||||||
|
|
||||||
friend int group_concat_key_cmp_with_distinct(void* arg, byte* key1,
|
friend int group_concat_key_cmp_with_distinct(void* arg, byte* key1,
|
||||||
byte* key2);
|
byte* key2);
|
||||||
@ -854,30 +865,12 @@ class Item_func_group_concat : public Item_sum
|
|||||||
friend int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
|
friend int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
|
||||||
Item_func_group_concat *group_concat_item);
|
Item_func_group_concat *group_concat_item);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
String result;
|
|
||||||
String *separator;
|
|
||||||
TREE tree_base;
|
|
||||||
TREE *tree;
|
|
||||||
TABLE *table;
|
|
||||||
ORDER **order;
|
|
||||||
TABLE_LIST *tables_list;
|
|
||||||
ulong group_concat_max_len;
|
|
||||||
uint arg_count_order;
|
|
||||||
uint arg_count_field;
|
|
||||||
uint field_list_offset;
|
|
||||||
uint count_cut_values;
|
|
||||||
/*
|
|
||||||
Following is 0 normal object and pointer to original one for copy
|
|
||||||
(to correctly free resources)
|
|
||||||
*/
|
|
||||||
Item_func_group_concat *original;
|
|
||||||
|
|
||||||
Item_func_group_concat(bool is_distinct,List<Item> *is_select,
|
Item_func_group_concat(bool is_distinct,List<Item> *is_select,
|
||||||
SQL_LIST *is_order,String *is_separator);
|
SQL_LIST *is_order,String *is_separator);
|
||||||
|
|
||||||
Item_func_group_concat(THD *thd, Item_func_group_concat *item);
|
Item_func_group_concat(THD *thd, Item_func_group_concat *item);
|
||||||
~Item_func_group_concat();
|
~Item_func_group_concat() {}
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;}
|
enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;}
|
||||||
@ -885,11 +878,11 @@ class Item_func_group_concat : public Item_sum
|
|||||||
virtual Item_result result_type () const { return STRING_RESULT; }
|
virtual Item_result result_type () const { return STRING_RESULT; }
|
||||||
void clear();
|
void clear();
|
||||||
bool add();
|
bool add();
|
||||||
void reset_field();
|
void reset_field() {} // not used
|
||||||
|
void update_field() {} // not used
|
||||||
bool fix_fields(THD *, TABLE_LIST *, Item **);
|
bool fix_fields(THD *, TABLE_LIST *, Item **);
|
||||||
bool setup(THD *thd);
|
bool setup(THD *thd);
|
||||||
void make_unique();
|
void make_unique();
|
||||||
virtual void update_field() {}
|
|
||||||
double val_real()
|
double val_real()
|
||||||
{
|
{
|
||||||
String *res; res=val_str(&str_value);
|
String *res; res=val_str(&str_value);
|
||||||
|
Reference in New Issue
Block a user