summaryrefslogtreecommitdiff
path: root/sql/item_sum.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r--sql/item_sum.cc1162
1 files changed, 1052 insertions, 110 deletions
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 4121fa65433..4b522cf06fa 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -17,16 +17,15 @@
/* Sum functions (COUNT, MIN...) */
-#ifdef __GNUC__
+#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
-
Item_sum::Item_sum(List<Item> &list)
+ :arg_count(list.elements)
{
- arg_count=list.elements;
if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
{
uint i=0;
@@ -38,36 +37,51 @@ Item_sum::Item_sum(List<Item> &list)
args[i++]= item;
}
}
- with_sum_func=1;
+ mark_as_sum_func();
list.empty(); // Fields are used
}
+/*
+ Constructor used in processing select with temporary tebles
+*/
+
+Item_sum::Item_sum(THD *thd, Item_sum *item):
+ Item_result_field(thd, item), arg_count(item->arg_count),
+ quick_group(item->quick_group)
+{
+ if (arg_count <= 2)
+ args=tmp_args;
+ else
+ if (!(args= (Item**) thd->alloc(sizeof(Item*)*arg_count)))
+ return;
+ memcpy(args, item->args, sizeof(Item*)*arg_count);
+}
+
+
+void Item_sum::mark_as_sum_func()
+{
+ current_thd->lex->current_select->with_sum_func= 1;
+ with_sum_func= 1;
+}
+
+
void Item_sum::make_field(Send_field *tmp_field)
{
if (args[0]->type() == Item::FIELD_ITEM && keep_field_type())
{
((Item_field*) args[0])->field->make_field(tmp_field);
+ tmp_field->db_name=(char*)"";
+ tmp_field->org_table_name=tmp_field->table_name=(char*)"";
+ tmp_field->org_col_name=tmp_field->col_name=name;
if (maybe_null)
tmp_field->flags&= ~NOT_NULL_FLAG;
}
else
- {
- tmp_field->flags=0;
- if (!maybe_null)
- tmp_field->flags|= NOT_NULL_FLAG;
- if (unsigned_flag)
- tmp_field->flags |= UNSIGNED_FLAG;
- tmp_field->length=max_length;
- tmp_field->decimals=decimals;
- tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG :
- result_type() == REAL_RESULT ? FIELD_TYPE_DOUBLE :
- FIELD_TYPE_VAR_STRING);
- }
- tmp_field->table_name=(char*)"";
- tmp_field->col_name=name;
+ init_make_field(tmp_field, field_type());
}
+
void Item_sum::print(String *str)
{
str->append(func_name());
@@ -89,14 +103,49 @@ void Item_sum::fix_num_length_and_dec()
max_length=float_length(decimals);
}
+Item *Item_sum::get_tmp_table_item(THD *thd)
+{
+ Item_sum* sum_item= (Item_sum *) copy_or_same(thd);
+ if (sum_item && sum_item->result_field) // If not a const sum func
+ {
+ Field *result_field_tmp= sum_item->result_field;
+ for (uint i=0 ; i < sum_item->arg_count ; i++)
+ {
+ Item *arg= sum_item->args[i];
+ if (!arg->const_item())
+ {
+ if (arg->type() == Item::FIELD_ITEM)
+ ((Item_field*) arg)->field= result_field_tmp++;
+ else
+ sum_item->args[i]= new Item_field(result_field_tmp++);
+ }
+ }
+ }
+ return sum_item;
+}
+
+bool Item_sum::walk (Item_processor processor, byte *argument)
+{
+ if (arg_count)
+ {
+ Item **arg,**arg_end;
+ for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
+ {
+ if ((*arg)->walk(processor, argument))
+ return 1;
+ }
+ }
+ return (this->*processor)(argument);
+}
String *
Item_sum_num::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
double nr=val();
if (null_value)
return 0;
- str->set(nr,decimals);
+ str->set(nr,decimals, &my_charset_bin);
return str;
}
@@ -104,20 +153,23 @@ Item_sum_num::val_str(String *str)
String *
Item_sum_int::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
longlong nr= val_int();
if (null_value)
return 0;
if (unsigned_flag)
- str->set((ulonglong) nr);
+ str->set((ulonglong) nr, &my_charset_bin);
else
- str->set(nr);
+ str->set(nr, &my_charset_bin);
return str;
}
bool
-Item_sum_num::fix_fields(THD *thd,TABLE_LIST *tables)
+Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
+ DBUG_ASSERT(fixed == 0);
+
if (!thd->allow_sum_func)
{
my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
@@ -128,7 +180,7 @@ Item_sum_num::fix_fields(THD *thd,TABLE_LIST *tables)
maybe_null=0;
for (uint i=0 ; i < arg_count ; i++)
{
- if (args[i]->fix_fields(thd,tables))
+ if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1))
return 1;
if (decimals < args[i]->decimals)
decimals=args[i]->decimals;
@@ -139,38 +191,56 @@ Item_sum_num::fix_fields(THD *thd,TABLE_LIST *tables)
null_value=1;
fix_length_and_dec();
thd->allow_sum_func=1; // Allow group functions
+ fixed= 1;
return 0;
}
bool
-Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables)
+Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
- Item *item=args[0];
+ DBUG_ASSERT(fixed == 0);
+
+ Item *item= args[0];
if (!thd->allow_sum_func)
{
my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
return 1;
}
thd->allow_sum_func=0; // No included group funcs
- if (item->fix_fields(thd,tables))
+
+ // 'item' can be changed during fix_fields
+ if (!item->fixed &&
+ item->fix_fields(thd, tables, args) ||
+ (item= args[0])->check_cols(1))
return 1;
- hybrid_type=item->result_type();
+
+ hybrid_type= item->result_type();
if (hybrid_type == INT_RESULT)
+ {
max_length=20;
+ }
else if (hybrid_type == REAL_RESULT)
+ {
max_length=float_length(decimals);
- else
+ }else
+ {
max_length=item->max_length;
+ }
decimals=item->decimals;
/* MIN/MAX can return NULL for empty set indepedent of the used column */
maybe_null= 1;
- binary=item->binary;
unsigned_flag=item->unsigned_flag;
+ collation.set(item->collation);
result_field=0;
null_value=1;
fix_length_and_dec();
thd->allow_sum_func=1; // Allow group functions
+ if (item->type() == Item::FIELD_ITEM)
+ hybrid_field_type= ((Item_field*) item)->field->type();
+ else
+ hybrid_field_type= Item::field_type();
+ fixed= 1;
return 0;
}
@@ -179,11 +249,18 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables)
** reset and add of sum_func
***********************************************************************/
-void Item_sum_sum::reset()
+Item *Item_sum_sum::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_sum(thd, this);
+}
+
+
+void Item_sum_sum::clear()
{
- null_value=1; sum=0.0; Item_sum_sum::add();
+ null_value=1; sum=0.0;
}
+
bool Item_sum_sum::add()
{
sum+=args[0]->val();
@@ -192,17 +269,26 @@ bool Item_sum_sum::add()
return 0;
}
+
double Item_sum_sum::val()
{
+ DBUG_ASSERT(fixed == 1);
return sum;
}
-void Item_sum_count::reset()
+Item *Item_sum_count::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_count(thd, this);
+}
+
+
+void Item_sum_count::clear()
{
- count=0; add();
+ count= 0;
}
+
bool Item_sum_count::add()
{
if (!args[0]->maybe_null)
@@ -218,18 +304,36 @@ bool Item_sum_count::add()
longlong Item_sum_count::val_int()
{
+ DBUG_ASSERT(fixed == 1);
return (longlong) count;
}
+
+void Item_sum_count::cleanup()
+{
+ DBUG_ENTER("Item_sum_count::cleanup");
+ Item_sum_int::cleanup();
+ used_table_cache= ~(table_map) 0;
+ DBUG_VOID_RETURN;
+}
+
+
/*
-** Avgerage
+ Avgerage
*/
-void Item_sum_avg::reset()
+Item *Item_sum_avg::copy_or_same(THD* thd)
{
- sum=0.0; count=0; Item_sum_avg::add();
+ return new (thd->mem_root) Item_sum_avg(thd, this);
}
+
+void Item_sum_avg::clear()
+{
+ sum=0.0; count=0;
+}
+
+
bool Item_sum_avg::add()
{
double nr=args[0]->val();
@@ -243,6 +347,7 @@ bool Item_sum_avg::add()
double Item_sum_avg::val()
{
+ DBUG_ASSERT(fixed == 1);
if (!count)
{
null_value=1;
@@ -254,15 +359,39 @@ double Item_sum_avg::val()
/*
-** Standard deviation
+ Standard deviation
*/
-void Item_sum_std::reset()
+double Item_sum_std::val()
+{
+ DBUG_ASSERT(fixed == 1);
+ double tmp= Item_sum_variance::val();
+ return tmp <= 0.0 ? 0.0 : sqrt(tmp);
+}
+
+Item *Item_sum_std::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_std(thd, this);
+}
+
+
+/*
+ Variance
+*/
+
+Item *Item_sum_variance::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_variance(thd, this);
+}
+
+
+void Item_sum_variance::clear()
{
- sum=sum_sqr=0.0; count=0; (void) Item_sum_std::add();
+ sum=sum_sqr=0.0;
+ count=0;
}
-bool Item_sum_std::add()
+bool Item_sum_variance::add()
{
double nr=args[0]->val();
if (!args[0]->null_value)
@@ -274,8 +403,9 @@ bool Item_sum_std::add()
return 0;
}
-double Item_sum_std::val()
+double Item_sum_variance::val()
{
+ DBUG_ASSERT(fixed == 1);
if (!count)
{
null_value=1;
@@ -285,11 +415,10 @@ double Item_sum_std::val()
/* Avoid problems when the precision isn't good enough */
double tmp=ulonglong2double(count);
double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
- return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+ return tmp2 <= 0.0 ? 0.0 : tmp2;
}
-
-void Item_sum_std::reset_field()
+void Item_sum_variance::reset_field()
{
double nr=args[0]->val();
char *res=result_field->ptr;
@@ -306,7 +435,7 @@ void Item_sum_std::reset_field()
}
}
-void Item_sum_std::update_field()
+void Item_sum_variance::update_field()
{
double nr,old_nr,old_sqr;
longlong field_count;
@@ -330,26 +459,44 @@ void Item_sum_std::update_field()
/* min & max */
+void Item_sum_hybrid::clear()
+{
+ sum= 0.0;
+ sum_int= 0;
+ value.length(0);
+ null_value= 1;
+}
+
double Item_sum_hybrid::val()
{
+ DBUG_ASSERT(fixed == 1);
+ int err;
+ char *end_not_used;
if (null_value)
return 0.0;
switch (hybrid_type) {
case STRING_RESULT:
String *res; res=val_str(&str_value);
- return res ? atof(res->c_ptr()) : 0.0;
+ return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
+ &end_not_used, &err) : 0.0);
case INT_RESULT:
if (unsigned_flag)
return ulonglong2double(sum_int);
return (double) sum_int;
case REAL_RESULT:
return sum;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
+ return 0;
}
return 0; // Keep compiler happy
}
longlong Item_sum_hybrid::val_int()
{
+ DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
if (hybrid_type == INT_RESULT)
@@ -361,24 +508,61 @@ longlong Item_sum_hybrid::val_int()
String *
Item_sum_hybrid::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
switch (hybrid_type) {
case STRING_RESULT:
return &value;
case REAL_RESULT:
- str->set(sum,decimals);
+ str->set(sum,decimals, &my_charset_bin);
break;
case INT_RESULT:
if (unsigned_flag)
- str->set((ulonglong) sum_int);
+ str->set((ulonglong) sum_int, &my_charset_bin);
else
- str->set((longlong) sum_int);
+ str->set((longlong) sum_int, &my_charset_bin);
+ break;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
break;
}
return str; // Keep compiler happy
}
+
+void Item_sum_hybrid::cleanup()
+{
+ DBUG_ENTER("Item_sum_hybrid::cleanup");
+ Item_sum::cleanup();
+ used_table_cache= ~(table_map) 0;
+
+ /*
+ by default it is TRUE to avoid TRUE reporting by
+ Item_func_not_all/Item_func_nop_all if this item was never called.
+
+ no_rows_in_result() set it to FALSE if was not results found.
+ If some results found it will be left unchanged.
+ */
+ was_values= TRUE;
+ DBUG_VOID_RETURN;
+}
+
+void Item_sum_hybrid::no_rows_in_result()
+{
+ was_values= FALSE;
+ clear();
+}
+
+
+Item *Item_sum_min::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_min(thd, this);
+}
+
+
bool Item_sum_min::add()
{
switch (hybrid_type) {
@@ -386,8 +570,7 @@ bool Item_sum_min::add()
{
String *result=args[0]->val_str(&tmp_value);
if (!args[0]->null_value &&
- (null_value ||
- (binary ? stringcmp(&value,result) : sortcmp(&value,result)) > 0))
+ (null_value || sortcmp(&value,result,collation.collation) > 0))
{
value.copy(*result);
null_value=0;
@@ -417,11 +600,22 @@ bool Item_sum_min::add()
}
}
break;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
+ break;
}
return 0;
}
+Item *Item_sum_max::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_max(thd, this);
+}
+
+
bool Item_sum_max::add()
{
switch (hybrid_type) {
@@ -429,8 +623,7 @@ bool Item_sum_max::add()
{
String *result=args[0]->val_str(&tmp_value);
if (!args[0]->null_value &&
- (null_value ||
- (binary ? stringcmp(&value,result) : sortcmp(&value,result)) < 0))
+ (null_value || sortcmp(&value,result,collation.collation) < 0))
{
value.copy(*result);
null_value=0;
@@ -460,6 +653,11 @@ bool Item_sum_max::add()
}
}
break;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
+ break;
}
return 0;
}
@@ -469,14 +667,22 @@ bool Item_sum_max::add()
longlong Item_sum_bit::val_int()
{
+ DBUG_ASSERT(fixed == 1);
return (longlong) bits;
}
-void Item_sum_bit::reset()
+
+void Item_sum_bit::clear()
+{
+ bits= reset_bits;
+}
+
+Item *Item_sum_or::copy_or_same(THD* thd)
{
- bits=reset_bits; add();
+ return new (thd->mem_root) Item_sum_or(thd, this);
}
+
bool Item_sum_or::add()
{
ulonglong value= (ulonglong) args[0]->val_int();
@@ -485,6 +691,26 @@ bool Item_sum_or::add()
return 0;
}
+Item *Item_sum_xor::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_xor(thd, this);
+}
+
+
+bool Item_sum_xor::add()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (!args[0]->null_value)
+ bits^=value;
+ return 0;
+}
+
+Item *Item_sum_and::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_and(thd, this);
+}
+
+
bool Item_sum_and::add()
{
ulonglong value= (ulonglong) args[0]->val_int();
@@ -521,7 +747,7 @@ void Item_sum_hybrid::reset_field()
if (hybrid_type == STRING_RESULT)
{
char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff)),*res;
+ String tmp(buff,sizeof(buff),result_field->charset()),*res;
res=args[0]->val_str(&tmp);
if (args[0]->null_value)
@@ -532,7 +758,7 @@ void Item_sum_hybrid::reset_field()
else
{
result_field->set_notnull();
- result_field->store(res->ptr(),res->length());
+ result_field->store(res->ptr(),res->length(),tmp.charset());
}
}
else if (hybrid_type == INT_RESULT)
@@ -705,12 +931,11 @@ Item_sum_hybrid::min_max_update_str_field()
if (!args[0]->null_value)
{
res_str->strip_sp();
- result_field->val_str(&tmp_value,&tmp_value);
+ result_field->val_str(&tmp_value);
if (result_field->is_null() ||
- (cmp_sign * (binary ? stringcmp(res_str,&tmp_value) :
- sortcmp(res_str,&tmp_value)) < 0))
- result_field->store(res_str->ptr(),res_str->length());
+ (cmp_sign * sortcmp(res_str,&tmp_value,collation.collation)) < 0)
+ result_field->store(res_str->ptr(),res_str->length(),res_str->charset());
result_field->set_notnull();
}
}
@@ -776,6 +1001,7 @@ Item_avg_field::Item_avg_field(Item_sum_avg *item)
double Item_avg_field::val()
{
+ // fix_fields() never calls for this Item
double nr;
longlong count;
float8get(nr,field->ptr);
@@ -793,14 +1019,27 @@ double Item_avg_field::val()
String *Item_avg_field::val_str(String *str)
{
+ // fix_fields() never calls for this Item
double nr=Item_avg_field::val();
if (null_value)
return 0;
- str->set(nr,decimals);
+ str->set(nr,decimals, &my_charset_bin);
return str;
}
Item_std_field::Item_std_field(Item_sum_std *item)
+ : Item_variance_field(item)
+{
+}
+
+double Item_std_field::val()
+{
+ // fix_fields() never calls for this Item
+ double tmp= Item_variance_field::val();
+ return tmp <= 0.0 ? 0.0 : sqrt(tmp);
+}
+
+Item_variance_field::Item_variance_field(Item_sum_variance *item)
{
name=item->name;
decimals=item->decimals;
@@ -809,8 +1048,9 @@ Item_std_field::Item_std_field(Item_sum_std *item)
maybe_null=1;
}
-double Item_std_field::val()
+double Item_variance_field::val()
{
+ // fix_fields() never calls for this Item
double sum,sum_sqr;
longlong count;
float8get(sum,field->ptr);
@@ -825,15 +1065,16 @@ double Item_std_field::val()
null_value=0;
double tmp= (double) count;
double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
- return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+ return tmp2 <= 0.0 ? 0.0 : tmp2;
}
-String *Item_std_field::val_str(String *str)
+String *Item_variance_field::val_str(String *str)
{
+ // fix_fields() never calls for this Item
double nr=val();
if (null_value)
return 0;
- str->set(nr,decimals);
+ str->set(nr,decimals, &my_charset_bin);
return str;
}
@@ -843,14 +1084,19 @@ String *Item_std_field::val_str(String *str)
#include "sql_select.h"
-static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
+int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
{
return memcmp(key1, key2, *(uint*) arg);
}
-static int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
+int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
{
- return my_sortcmp((char*) key1, (char*) key2, *(uint*) arg);
+ Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
+ CHARSET_INFO *cs=item->key_charset;
+ uint len=item->key_length;
+ return cs->coll->strnncollsp(cs,
+ (const uchar*) key1, len,
+ (const uchar*) key2, len);
}
/*
@@ -891,9 +1137,9 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)),
int error;
/*
The first item->rec_offset bytes are taken care of with
- restore_record(table,2) in setup()
+ restore_record(table,default_values) in setup()
*/
- memcpy(buf + item->rec_offset, key, item->tree.size_of_element);
+ memcpy(buf + item->rec_offset, key, item->tree->size_of_element);
if ((error = item->table->file->write_row(buf)))
{
if (error != HA_ERR_FOUND_DUPP_KEY &&
@@ -904,26 +1150,55 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)),
}
-Item_sum_count_distinct::~Item_sum_count_distinct()
+void Item_sum_count_distinct::cleanup()
{
- if (table)
- free_tmp_table(current_thd, table);
- delete tmp_table_param;
- if (use_tree)
- delete_tree(&tree);
+ DBUG_ENTER("Item_sum_count_distinct::cleanup");
+ Item_sum_int::cleanup();
+ /*
+ Free table and tree 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)
+ {
+ if (table)
+ {
+ free_tmp_table(current_thd, table);
+ table= 0;
+ }
+ delete tmp_table_param;
+ tmp_table_param= 0;
+ if (use_tree)
+ {
+ delete_tree(tree);
+ use_tree= 0;
+ }
+ }
+ DBUG_VOID_RETURN;
}
-bool Item_sum_count_distinct::fix_fields(THD *thd,TABLE_LIST *tables)
+
+/* This is used by rollup to create a separate usable copy of the function */
+
+void Item_sum_count_distinct::make_unique()
{
- if (Item_sum_num::fix_fields(thd,tables) ||
- !(tmp_table_param= new TMP_TABLE_PARAM))
- return 1;
- return 0;
+ table=0;
+ original= 0;
+ use_tree= 0; // to prevent delete_tree call on uninitialized tree
+ tree= &tree_base;
+ force_copy_fields= 1;
}
+
bool Item_sum_count_distinct::setup(THD *thd)
{
List<Item> list;
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
+ return 1;
+
+ if (!(tmp_table_param= new TMP_TABLE_PARAM))
+ return 1;
+
/* Create a table with an unique key over all parameters */
for (uint i=0; i < arg_count ; i++)
{
@@ -945,9 +1220,11 @@ bool Item_sum_count_distinct::setup(THD *thd)
free_tmp_table(thd, table);
tmp_table_param->cleanup();
}
- if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
- 0, 0,
- current_lex->select->options | thd->options)))
+ tmp_table_param->force_copy_fields= force_copy_fields;
+ if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
+ 0,
+ select_lex->options | thd->options,
+ HA_POS_ERROR, (char*)"")))
return 1;
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
table->no_rows=1;
@@ -960,7 +1237,7 @@ bool Item_sum_count_distinct::setup(THD *thd)
void* cmp_arg;
// to make things easier for dump_leaf if we ever have to dump to MyISAM
- restore_record(table,2);
+ restore_record(table,default_values);
if (table->fields == 1)
{
@@ -973,14 +1250,22 @@ bool Item_sum_count_distinct::setup(THD *thd)
Field* field = table->field[0];
switch(field->type())
{
- /*
- If we have a string, we must take care of charsets and case
- sensitivity
- */
case FIELD_TYPE_STRING:
case FIELD_TYPE_VAR_STRING:
- compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp:
- simple_str_key_cmp);
+ if (field->binary())
+ {
+ compare_key = (qsort_cmp2)simple_raw_key_cmp;
+ cmp_arg = (void*) &key_length;
+ }
+ else
+ {
+ /*
+ If we have a string, we must take care of charsets and case
+ sensitivity
+ */
+ compare_key = (qsort_cmp2)simple_str_key_cmp;
+ cmp_arg = (void*) this;
+ }
break;
default:
/*
@@ -988,11 +1273,12 @@ bool Item_sum_count_distinct::setup(THD *thd)
be compared with memcmp
*/
compare_key = (qsort_cmp2)simple_raw_key_cmp;
+ cmp_arg = (void*) &key_length;
break;
}
- key_length = field->pack_length();
- cmp_arg = (void*) &key_length;
- rec_offset = 1;
+ key_charset = field->charset();
+ key_length = field->pack_length();
+ rec_offset = 1;
}
else // too bad, cannot cheat - there is more than one field
{
@@ -1025,8 +1311,10 @@ bool Item_sum_count_distinct::setup(THD *thd)
}
}
- init_tree(&tree, min(thd->variables.max_heap_table_size,
- thd->variables.sortbuff_size/16), 0,
+ if (use_tree)
+ delete_tree(tree);
+ init_tree(tree, min(thd->variables.max_heap_table_size,
+ thd->variables.sortbuff_size/16), 0,
key_length, compare_key, 0, NULL, cmp_arg);
use_tree = 1;
@@ -1038,6 +1326,12 @@ bool Item_sum_count_distinct::setup(THD *thd)
*/
max_elements_in_tree = ((key_length) ?
thd->variables.max_heap_table_size/key_length : 1);
+
+ }
+ if (original)
+ {
+ original->table= table;
+ original->use_tree= use_tree;
}
return 0;
}
@@ -1047,25 +1341,31 @@ int Item_sum_count_distinct::tree_to_myisam()
{
if (create_myisam_from_heap(current_thd, table, tmp_table_param,
HA_ERR_RECORD_FILE_FULL, 1) ||
- tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this,
+ tree_walk(tree, (tree_walk_action)&dump_leaf, (void*)this,
left_root_right))
return 1;
- delete_tree(&tree);
+ delete_tree(tree);
use_tree = 0;
return 0;
}
-void Item_sum_count_distinct::reset()
+
+Item *Item_sum_count_distinct::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_count_distinct(thd, this);
+}
+
+
+void Item_sum_count_distinct::clear()
{
if (use_tree)
- reset_tree(&tree);
+ reset_tree(tree);
else if (table)
{
table->file->extra(HA_EXTRA_NO_CACHE);
table->file->delete_all_rows();
table->file->extra(HA_EXTRA_WRITE_CACHE);
}
- (void) add();
}
bool Item_sum_count_distinct::add()
@@ -1086,12 +1386,13 @@ bool Item_sum_count_distinct::add()
If the tree got too big, convert to MyISAM, otherwise insert into the
tree.
*/
- if (tree.elements_in_tree > max_elements_in_tree)
+ if (tree->elements_in_tree > max_elements_in_tree)
{
if (tree_to_myisam())
return 1;
}
- else if (!tree_insert(&tree, table->record[0] + rec_offset, 0))
+ else if (!tree_insert(tree, table->record[0] + rec_offset, 0,
+ tree->custom_arg))
return 1;
}
else if ((error=table->file->write_row(table->record[0])))
@@ -1107,16 +1408,26 @@ bool Item_sum_count_distinct::add()
return 0;
}
+
longlong Item_sum_count_distinct::val_int()
{
+ DBUG_ASSERT(fixed == 1);
if (!table) // Empty query
return LL(0);
if (use_tree)
- return tree.elements_in_tree;
+ return tree->elements_in_tree;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
return table->file->records;
}
+
+void Item_sum_count_distinct::print(String *str)
+{
+ str->append("count(distinct ", 15);
+ args[0]->print(str);
+ str->append(')');
+}
+
/****************************************************************************
** Functions to handle dynamic loadable aggregates
** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
@@ -1126,10 +1437,10 @@ longlong Item_sum_count_distinct::val_int()
#ifdef HAVE_DLOPEN
-void Item_udf_sum::reset()
+void Item_udf_sum::clear()
{
- DBUG_ENTER("Item_udf_sum::reset");
- udf.reset(&null_value);
+ DBUG_ENTER("Item_udf_sum::clear");
+ udf.clear();
DBUG_VOID_RETURN;
}
@@ -1140,8 +1451,25 @@ bool Item_udf_sum::add()
DBUG_RETURN(0);
}
+void Item_udf_sum::cleanup()
+{
+ /*
+ udf_handler::cleanup() nicely handles case when we have not
+ original item but one created by copy_or_same() method.
+ */
+ udf.cleanup();
+ Item_sum::cleanup();
+}
+
+
+Item *Item_sum_udf_float::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_udf_float(thd, this);
+}
+
double Item_sum_udf_float::val()
{
+ DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_float::val");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
@@ -1150,28 +1478,38 @@ double Item_sum_udf_float::val()
String *Item_sum_udf_float::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
double nr=val();
if (null_value)
return 0; /* purecov: inspected */
- str->set(nr,decimals);
+ str->set(nr,decimals, &my_charset_bin);
return str;
}
+Item *Item_sum_udf_int::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_udf_int(thd, this);
+}
+
+
longlong Item_sum_udf_int::val_int()
{
+ DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_int::val_int");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
DBUG_RETURN(udf.val_int(&null_value));
}
+
String *Item_sum_udf_int::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
longlong nr=val_int();
if (null_value)
return 0;
- str->set(nr);
+ str->set(nr, &my_charset_bin);
return str;
}
@@ -1186,8 +1524,16 @@ void Item_sum_udf_str::fix_length_and_dec()
DBUG_VOID_RETURN;
}
+
+Item *Item_sum_udf_str::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_sum_udf_str(thd, this);
+}
+
+
String *Item_sum_udf_str::val_str(String *str)
{
+ DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_sum_udf_str::str");
String *res=udf.val_str(str,&str_value);
null_value = !res;
@@ -1195,3 +1541,599 @@ String *Item_sum_udf_str::val_str(String *str)
}
#endif /* HAVE_DLOPEN */
+
+
+/*****************************************************************************
+ GROUP_CONCAT function
+
+ SQL SYNTAX:
+ GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...]
+ [SEPARATOR str_const])
+
+ concat of values from "group by" operation
+
+ BUGS
+ DISTINCT and ORDER BY only works if ORDER BY uses all fields and only fields
+ in expression list
+ Blobs doesn't work with DISTINCT or ORDER BY
+*****************************************************************************/
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(DISTINCT expr,...)
+*/
+
+int group_concat_key_cmp_with_distinct(void* arg, byte* key1,
+ byte* key2)
+{
+ Item_func_group_concat* grp_item= (Item_func_group_concat*)arg;
+ Item **field_item, **end;
+
+ for (field_item= grp_item->args, end= field_item + grp_item->arg_count_field;
+ field_item < end;
+ field_item++)
+ {
+ /*
+ We have to use get_tmp_table_field() instead of
+ real_item()->get_tmp_table_field() because we want the field in
+ the temporary table, not the original field
+ */
+ Field *field= (*field_item)->get_tmp_table_field();
+ /*
+ If field_item is a const item then either get_tp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (field && !(*field_item)->const_item())
+ {
+ int res;
+ uint offset= field->offset();
+ if ((res= field->key_cmp(key1 + offset, key2 + offset)))
+ return res;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(expr,... ORDER BY col,... )
+*/
+
+int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2)
+{
+ Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
+ ORDER **order_item, **end;
+
+ for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
+ order_item < end;
+ order_item++)
+ {
+ Item *item= *(*order_item)->item;
+ /*
+ We have to use get_tmp_table_field() instead of
+ real_item()->get_tmp_table_field() because we want the field in
+ the temporary table, not the original field
+ */
+ Field *field= item->get_tmp_table_field();
+ /*
+ If item is a const item then either get_tp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (field && !item->const_item())
+ {
+ int res;
+ uint offset= field->offset();
+ if ((res= field->key_cmp(key1 + offset, key2 + offset)))
+ return (*order_item)->asc ? res : -res;
+ }
+ }
+ /*
+ 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
+ if the the returned values are not the same we do the sort on.
+ */
+ return 1;
+}
+
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(DISTINCT expr,... ORDER BY col,... )
+
+ BUG:
+ This doesn't work in the case when the order by contains data that
+ is not part of the field list because tree-insert will not notice
+ the duplicated values when inserting things sorted by ORDER BY
+*/
+
+int group_concat_key_cmp_with_distinct_and_order(void* arg,byte* key1,
+ byte* key2)
+{
+ if (!group_concat_key_cmp_with_distinct(arg,key1,key2))
+ return 0;
+ return(group_concat_key_cmp_with_order(arg,key1,key2));
+}
+
+
+/*
+ Append data from current leaf to item->result
+*/
+
+int dump_leaf_key(byte* key, uint32 count __attribute__((unused)),
+ Item_func_group_concat *item)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp((char *)&buff,sizeof(buff),default_charset_info), tmp2;
+
+ if (item->no_appended)
+ item->no_appended= FALSE;
+ else
+ item->result.append(*item->separator);
+
+ tmp.length(0);
+
+ for (uint i= 0; i < item->arg_count_field; i++)
+ {
+ Item *show_item= item->args[i];
+ if (!show_item->const_item())
+ {
+ /*
+ We have to use get_tmp_table_field() instead of
+ real_item()->get_tmp_table_field() because we want the field in
+ the temporary table, not the original field
+ */
+ Field *field= show_item->get_tmp_table_field();
+ String *res;
+ char *save_ptr= field->ptr;
+ DBUG_ASSERT(field->offset() < item->table->reclength);
+ field->ptr= (char *) key + field->offset();
+ res= field->val_str(&tmp,&tmp2);
+ item->result.append(*res);
+ field->ptr= save_ptr;
+ }
+ else
+ {
+ String *res= show_item->val_str(&tmp);
+ if (res)
+ item->result.append(*res);
+ }
+ }
+
+ /* stop if length of result more than group_concat_max_len */
+ if (item->result.length() > item->group_concat_max_len)
+ {
+ item->count_cut_values++;
+ item->result.length(item->group_concat_max_len);
+ item->warning_for_row= TRUE;
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ Constructor of Item_func_group_concat
+ is_distinct - distinct
+ is_select - list of expression for show values
+ is_order - list of sort columns
+ is_separator - string value of separator
+*/
+
+Item_func_group_concat::Item_func_group_concat(bool is_distinct,
+ List<Item> *is_select,
+ SQL_LIST *is_order,
+ String *is_separator)
+ :Item_sum(), tmp_table_param(0), max_elements_in_tree(0), warning(0),
+ key_length(0), tree_mode(0), distinct(is_distinct), warning_for_row(0),
+ force_copy_fields(0),
+ separator(is_separator), tree(&tree_base), table(0),
+ order(0), tables_list(0),
+ arg_count_order(0), arg_count_field(0),
+ count_cut_values(0)
+{
+ Item *item_select;
+ Item **arg_ptr;
+
+ original= 0;
+ 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;
+
+ /*
+ We need to allocate:
+ args - arg_count_field+arg_count_order
+ (for possible order items in temporare tables)
+ order - arg_count_order
+ */
+ if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
+ sizeof(ORDER*)*arg_count_order)))
+ return;
+
+ order= (ORDER**)(args + arg_count);
+
+ /* fill args items of show and sort */
+ List_iterator_fast<Item> li(*is_select);
+
+ for (arg_ptr=args ; (item_select= li++) ; arg_ptr++)
+ *arg_ptr= item_select;
+
+ if (arg_count_order)
+ {
+ ORDER **order_ptr= order;
+ for (ORDER *order_item= (ORDER*) is_order->first;
+ order_item != NULL;
+ order_item= order_item->next)
+ {
+ (*order_ptr++)= order_item;
+ *arg_ptr= *order_item->item;
+ order_item->item= arg_ptr++;
+ }
+ }
+}
+
+
+Item_func_group_concat::Item_func_group_concat(THD *thd,
+ Item_func_group_concat *item)
+ :Item_sum(thd, item),item_thd(thd),
+ tmp_table_param(item->tmp_table_param),
+ max_elements_in_tree(item->max_elements_in_tree),
+ warning(item->warning),
+ key_length(item->key_length),
+ tree_mode(item->tree_mode),
+ distinct(item->distinct),
+ warning_for_row(item->warning_for_row),
+ force_copy_fields(item->force_copy_fields),
+ separator(item->separator),
+ tree(item->tree),
+ table(item->table),
+ order(item->order),
+ tables_list(item->tables_list),
+ group_concat_max_len(item->group_concat_max_len),
+ arg_count_order(item->arg_count_order),
+ arg_count_field(item->arg_count_field),
+ field_list_offset(item->field_list_offset),
+ count_cut_values(item->count_cut_values),
+ original(item)
+{
+ quick_group= item->quick_group;
+}
+
+
+
+void Item_func_group_concat::cleanup()
+{
+ THD *thd= current_thd;
+
+ DBUG_ENTER("Item_func_group_concat::cleanup");
+ Item_sum::cleanup();
+
+ /* Adjust warning message to include total number of cut values */
+ 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;
+ }
+
+ /*
+ Free table and tree 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)
+ {
+ if (table)
+ {
+ free_tmp_table(thd, table);
+ table= 0;
+ }
+ delete tmp_table_param;
+ tmp_table_param= 0;
+ if (tree_mode)
+ {
+ tree_mode= 0;
+ delete_tree(tree);
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+Item_func_group_concat::~Item_func_group_concat()
+{
+}
+
+
+Item *Item_func_group_concat::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_func_group_concat(thd, this);
+}
+
+
+void Item_func_group_concat::clear()
+{
+ result.length(0);
+ result.copy();
+ null_value= TRUE;
+ warning_for_row= FALSE;
+ no_appended= TRUE;
+ if (tree_mode)
+ reset_tree(tree);
+}
+
+
+bool Item_func_group_concat::add()
+{
+ if (always_null)
+ return 0;
+ copy_fields(tmp_table_param);
+ copy_funcs(tmp_table_param->items_to_copy);
+
+ for (Item **arg= args, **arg_end= args + arg_count_field;
+ arg < arg_end; arg++)
+ {
+ if (!(*arg)->const_item() &&
+ (*arg)->get_tmp_table_field()->is_null_in_record(
+ (const uchar*) table->record[0]))
+ return 0; // Skip row if it contains null
+ }
+
+ null_value= FALSE;
+
+ TREE_ELEMENT *el= 0; // Only for safety
+ if (tree_mode)
+ el= tree_insert(tree, table->record[0], 0, tree->custom_arg);
+ /*
+ If the row is not a duplicate (el->count == 1)
+ we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
+ instead of doing tree traverse later.
+ */
+ if (result.length() <= group_concat_max_len &&
+ !warning_for_row &&
+ (!tree_mode || (el->count == 1 && distinct && !arg_count_order)))
+ dump_leaf_key(table->record[0], 1, this);
+
+ return 0;
+}
+
+
+void Item_func_group_concat::reset_field()
+{
+ DBUG_ASSERT(0);
+}
+
+
+bool
+Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
+{
+ uint i; /* for loop variable */
+ DBUG_ASSERT(fixed == 0);
+
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+
+ thd->allow_sum_func= 0;
+ maybe_null= 1;
+ item_thd= thd;
+
+ /*
+ Fix fields for select list and ORDER clause
+ */
+
+ for (i=0 ; i < arg_count ; i++)
+ {
+ if ((!args[i]->fixed &&
+ args[i]->fix_fields(thd, tables, args + i)) ||
+ args[i]->check_cols(1))
+ return 1;
+ }
+
+ if (agg_item_charsets(collation, func_name(),
+ args, arg_count, MY_COLL_ALLOW_CONV))
+ return 1;
+
+ result.set_charset(collation.collation);
+ result_field= 0;
+ null_value= 1;
+ max_length= group_concat_max_len;
+ thd->allow_sum_func= 1;
+ tables_list= tables;
+ fixed= 1;
+ return 0;
+}
+
+
+bool Item_func_group_concat::setup(THD *thd)
+{
+ List<Item> list;
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ uint const_fields;
+ byte *record;
+ qsort_cmp2 compare_key;
+ DBUG_ENTER("Item_func_group_concat::setup");
+
+ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
+ DBUG_RETURN(1);
+
+ if (!(tmp_table_param= new TMP_TABLE_PARAM))
+ return 1;
+ /* We'll convert all blobs to varchar fields in the temporary table */
+ tmp_table_param->convert_blob_length= group_concat_max_len;
+
+ /*
+ push all not constant fields to list and create temp table
+ */
+ const_fields= 0;
+ always_null= 0;
+ for (uint i= 0; i < arg_count_field; i++)
+ {
+ Item *item= args[i];
+ if (list.push_back(item))
+ DBUG_RETURN(1);
+ if (item->const_item())
+ {
+ const_fields++;
+ (void) item->val_int();
+ if (item->null_value)
+ always_null= 1;
+ }
+ }
+ if (always_null)
+ DBUG_RETURN(0);
+
+ List<Item> all_fields(list);
+ if (arg_count_order)
+ {
+ bool hidden_group_fields;
+ setup_group(thd, args, tables_list, list, all_fields, *order,
+ &hidden_group_fields);
+ }
+
+ count_field_types(tmp_table_param,all_fields,0);
+ if (table)
+ {
+ /*
+ 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();
+ }
+ tmp_table_param->force_copy_fields= force_copy_fields;
+ /*
+ We have to create a temporary table to get descriptions of fields
+ (types, sizes and so on).
+
+ Note that in the table, we first have the ORDER BY fields, then the
+ field list.
+
+ 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.
+ */
+ if (!(table=create_tmp_table(thd, tmp_table_param, all_fields,
+ (ORDER*) 0, 0, TRUE,
+ select_lex->options | thd->options,
+ HA_POS_ERROR,(char *) "")))
+ DBUG_RETURN(1);
+ table->file->extra(HA_EXTRA_NO_ROWS);
+ table->no_rows= 1;
+
+ key_length= table->reclength;
+ record= table->record[0];
+
+ /* Offset to first result field in table */
+ field_list_offset= table->fields - (list.elements - const_fields);
+
+ if (tree_mode)
+ delete_tree(tree);
+
+ /* choose function of sort */
+ tree_mode= distinct || arg_count_order;
+ if (tree_mode)
+ {
+ if (arg_count_order)
+ {
+ if (distinct)
+ compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct_and_order;
+ else
+ compare_key= (qsort_cmp2) group_concat_key_cmp_with_order;
+ }
+ else
+ {
+ 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
+ values (according with syntax of the function). If function doesn't
+ contain DISTINCT and ORDER BY clauses, we don't create this tree.
+ */
+ init_tree(tree, min(thd->variables.max_heap_table_size,
+ thd->variables.sortbuff_size/16), 0,
+ 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);
+}
+
+
+/* This is used by rollup to create a separate usable copy of the function */
+
+void Item_func_group_concat::make_unique()
+{
+ table=0;
+ original= 0;
+ tree_mode= 0; // to prevent delete_tree call on uninitialized tree
+ tree= &tree_base;
+ force_copy_fields= 1;
+}
+
+
+String* Item_func_group_concat::val_str(String* str)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0;
+ if (count_cut_values && !warning)
+ /*
+ ER_CUT_VALUE_GROUP_CONCAT needs an argument, but this gets set in
+ Item_func_group_concat::cleanup().
+ */
+ warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_CUT_VALUE_GROUP_CONCAT,
+ ER(ER_CUT_VALUE_GROUP_CONCAT));
+ if (result.length())
+ return &result;
+ if (tree_mode)
+ {
+ tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
+ left_root_right);
+ }
+ return &result;
+}
+
+
+void Item_func_group_concat::print(String *str)
+{
+ str->append("group_concat(", 13);
+ if (distinct)
+ str->append("distinct ", 9);
+ for (uint i= 0; i < arg_count_field; i++)
+ {
+ if (i)
+ str->append(',');
+ args[i]->print(str);
+ }
+ if (arg_count_order)
+ {
+ str->append(" order by ", 10);
+ for (uint i= 0 ; i < arg_count_order ; i++)
+ {
+ if (i)
+ str->append(',');
+ (*order[i]->item)->print(str);
+ }
+ }
+ str->append(" separator \'", 12);
+ str->append(*separator);
+ str->append("\')", 2);
+}