summaryrefslogtreecommitdiff
path: root/sql/item_sum.cc
diff options
context:
space:
mode:
authorunknown <wax@kishkin.ru>2003-03-18 04:07:40 +0500
committerunknown <wax@kishkin.ru>2003-03-18 04:07:40 +0500
commit0b505fb437eedd1b31c99888247c2259539c095b (patch)
treed5655e0a1fd7bd5c629fbc934a708f44ea818663 /sql/item_sum.cc
parentc312cd45786f22f0fed89d4cb2d6bba7c6c87644 (diff)
downloadmariadb-git-0b505fb437eedd1b31c99888247c2259539c095b.tar.gz
This is full commit of group_concat with support subselects
include/mysqld_error.h: add warning sql/field.h: add friend class sql/item_sum.cc: add function sql/item_sum.h: add class sql/lex.h: add lex sql/mysql_priv.h: change push_warning sql/mysqld.cc: add new option sql/set_var.cc: add new system variable sql/share/english/errmsg.txt: add new message text sql/sql_class.h: change MY_ERROR class sql/sql_error.cc: change push_warning sql/sql_lex.h: add qorder_list for function sql/sql_yacc.yy: add structure of group_concat BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r--sql/item_sum.cc450
1 files changed, 450 insertions, 0 deletions
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index c2db50345d1..29294d8304d 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1320,3 +1320,453 @@ String *Item_sum_udf_str::val_str(String *str)
}
#endif /* HAVE_DLOPEN */
+
+
+/*****************************************************************************
+ GROUP_CONCAT function
+ Syntax:
+ GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...]
+ [SEPARATOR str_const])
+ concat of values from "group by" operation
+*****************************************************************************/
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(DISTINCT expr,...)
+*/
+
+static int group_concat_key_cmp_with_distinct(void* arg, byte* key1, byte* key2)
+{
+ Item_func_group_concat* item = (Item_func_group_concat*)arg;
+/*
+ DISTINCT
+*/
+ for (int i=0; i<item->arg_count_field; i++)
+ {
+ Item *field_item=item->expr[i];
+ Field *field = field_item->tmp_table_field();
+ if (field)
+ {
+ uint offset = field->offset();
+
+ int res = field->key_cmp(key1 + offset, key2 + offset);
+ if (res)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(expr,... ORDER BY col,... )
+*/
+
+static int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2)
+{
+ Item_func_group_concat* item = (Item_func_group_concat*)arg;
+/*
+ ORDER
+*/
+
+ for (int i=0; i<item->arg_count_order; i++)
+ {
+ ORDER *order_item = item->order[i];
+ Item *item=*order_item->item;
+ Field *field = item->tmp_table_field();
+ if (field)
+ {
+ uint offset = field->offset();
+
+ bool dir = order_item->asc;
+ int res = field->key_cmp(key1 + offset, key2 + offset);
+ if (res)
+ return dir ? res : -res;
+ }
+ }
+ return 1;
+}
+
+/*
+ function of sort for syntax:
+ GROUP_CONCAT(DISTINCT expr,... ORDER BY col,... )
+*/
+static int group_concat_key_cmp_with_distinct_and_order(void* arg, byte* key1, byte* key2)
+{
+ Item_func_group_concat* item = (Item_func_group_concat*)arg;
+/*
+ DISTINCT
+*/
+ if (!group_concat_key_cmp_with_distinct(arg,key1,key2))
+ return 0;
+/*
+ ORDER
+*/
+
+ return(group_concat_key_cmp_with_order(arg,key1,key2));
+}
+
+/*
+ create result
+ item is pointer to Item_func_group_concat
+*/
+static 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);
+ String tmp2((char *)&buff,sizeof(buff),default_charset_info);
+
+ tmp.length(0);
+
+ for (int i=0; i < item->arg_show_fields; i++)
+ {
+ Item *show_item = item->expr[i];
+ if (!show_item->const_item())
+ {
+ Field *f = show_item->tmp_table_field();
+ uint offset = f->offset();
+ char *sv = f->ptr;
+ f->ptr = (char *)key + offset;
+/*
+ We can't check this field on NULL, becouse if f->is_null() return that the
+ first field is NULL than it return and that all fields are NULL too. Maybe
+ is it bag?
+*/
+ String *res = f->val_str(&tmp,&tmp2);
+ if (res)
+ item->result.append(*res);
+ f->ptr = sv;
+ }
+ else
+ {
+ String *res = show_item->val_str(&tmp);
+ if (res)
+ item->result.append(*res);
+ }
+ }
+ item->show_elements++;
+ if (item->tree_mode)
+ {
+/*
+ Last item of tree
+*/
+ if (item->show_elements < item->tree->elements_in_tree)
+ item->result.append(*item->separator);
+ }
+ else
+ {
+ item->result.append(*item->separator);
+ }
+/*
+ if length of result more than group_concat_max_len - stop !
+*/
+ 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(int is_distinct,List<Item> *is_select,
+ SQL_LIST *is_order,String *is_separator):
+ Item_sum(),
+ tmp_table_param(0),
+ warning_available(false),
+ separator(is_separator),
+ tree(&tree_base),
+ table(0),
+ distinct(is_distinct),
+ tree_mode(0),
+ count_cut_values(0)
+{
+ original = 0;
+ quick_group = 0;
+ mark_as_sum_func();
+ SELECT_LEX *select_lex= current_lex->current_select->select_lex();
+
+ arg_show_fields = arg_count_field = is_select->elements;
+ arg_count_order = is_order ? is_order->elements : 0;
+ arg_count = arg_count_field;
+
+/*
+ fill args items of show and sort
+*/
+ int i = 0;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))&&
+ (expr=(Item**)sql_alloc(sizeof(Item*)*arg_count_field)))
+ {
+ List_iterator_fast<Item> li(*is_select);
+ Item *item_select;
+
+ while ((item_select=li++))
+ {
+ args[i] = item_select;
+ expr[i] = item_select;
+ i++;
+ }
+
+ if (arg_count_order)
+ {
+ order=(ORDER**)sql_alloc(sizeof(ORDER*)*arg_count_order);
+ if (order)
+ {
+ uint j = 0;
+ for (ORDER *order_item = (ORDER*)is_order->first;
+ order_item != NULL;
+ order_item = order_item->next)
+ {
+ order[j++] = order_item;
+ }
+ }
+ else
+ {
+ my_error(ER_OUTOFMEMORY,MYF(0));
+ }
+ }
+ else
+ {
+ order = 0;
+ }
+ }
+ else
+ {
+ my_error(ER_OUTOFMEMORY,MYF(0));
+ }
+}
+
+Item_func_group_concat::~Item_func_group_concat()
+{
+ if (!original)
+ {
+ if (warning_available)
+ {
+ char warn_buff[MYSQL_ERRMSG_SIZE];
+ sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values);
+ ((MYSQL_ERROR *)warning)->set_msg((char *)&warn_buff);
+ }
+ if (table)
+ free_tmp_table(current_thd, table);
+ if (tmp_table_param)
+ delete tmp_table_param;
+ if (tree_mode)
+ delete_tree(tree);
+ }
+}
+
+
+void Item_func_group_concat::reset()
+{
+ result.length(0);
+ result.copy();
+ warning_for_row = false;
+ if (table)
+ {
+ 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);
+ add();
+}
+
+bool Item_func_group_concat::add()
+{
+ copy_fields(tmp_table_param);
+ copy_funcs(tmp_table_param->items_to_copy);
+
+ if (tree_mode)
+ {
+ if (tree->elements_in_tree > max_elements_in_tree)
+ return 1;
+ else
+ {
+ if (!tree_insert(tree, table->record[0], 0,tree->custom_arg))
+ return 1;
+ }
+ }
+ else
+ {
+ if (result.length() <= group_concat_max_len && !warning_for_row)
+ dump_leaf_key(table->record[0],1,
+ (Item_func_group_concat*)this);
+ }
+ return 0;
+}
+
+void Item_func_group_concat::reset_field()
+{
+ if (tree_mode)
+ reset_tree(tree);
+ (void) add();
+}
+
+bool
+Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
+{
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+
+ thd->allow_sum_func=0;
+ maybe_null=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1))
+ return 1;
+ maybe_null |= args[i]->maybe_null;
+ }
+ for (int i=0 ; i < arg_count_field ; i++)
+ {
+ if (expr[i]->fix_fields(thd, tables, expr + i) || expr[i]->check_cols(1))
+ return 1;
+ maybe_null |= expr[i]->maybe_null;
+ }
+ for (int i=0 ; i < arg_count_order ; i++)
+ {
+ ORDER *order_item = order[i];
+ Item *item=*order_item->item;
+ if (item->fix_fields(thd, tables, &item) || item->check_cols(1))
+ return 1;
+ }
+ result_field=0;
+ null_value=1;
+ fix_length_and_dec();
+ thd->allow_sum_func=1;
+ if (!(tmp_table_param= new TMP_TABLE_PARAM))
+ return 1;
+ tables_list = tables;
+ fixed= 1;
+ return 0;
+}
+
+bool Item_func_group_concat::setup(THD *thd)
+{
+ List<Item> list;
+ SELECT_LEX *select_lex= current_lex->current_select->select_lex();
+
+ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
+ return 1;
+/*
+ all not constant fields are push to list and create temp table
+*/
+ for (uint i=0; i < arg_count; i++)
+ {
+ Item *item=args[i];
+ if (list.push_back(item))
+ return 1;
+ if (item->const_item())
+ {
+ (void) item->val_int();
+ if (item->null_value)
+ always_null=1;
+ }
+ }
+
+ 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);
+/*
+ check wrong cols in order list (incorrect number of coloum or value of name)
+*/
+ for (int i=0; i<arg_count_order; i++)
+ {
+ ORDER *order_item = order[i];
+ Item *item=*order_item->item;
+ if (item->const_item())
+ {
+ my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
+ MYF(0),item->full_name(),thd->where);
+ return 1;
+ }
+ }
+ }
+
+ count_field_types(tmp_table_param,all_fields,0);
+ if (!(table=create_tmp_table(thd, tmp_table_param, all_fields, order?*order:0,
+ 0, 0, 0,select_lex->options | thd->options/*, select_lex->master_unit()*/)))
+ return 1;
+ table->file->extra(HA_EXTRA_NO_ROWS);
+ table->no_rows=1;
+ qsort_cmp2 compare_key;
+
+ tree_mode = distinct || arg_count_order;
+/*
+ choise function of sort
+*/
+ 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
+ {
+ if (distinct)
+ compare_key = (qsort_cmp2) group_concat_key_cmp_with_distinct;
+ else
+ compare_key = NULL;
+ }
+/*
+ Create tree of sort
+*/
+ init_tree(tree, min(thd->variables.max_heap_table_size,
+ thd->variables.sortbuff_size/16), 0,
+ table->reclength, compare_key, 0, NULL, (void*) this);
+ max_elements_in_tree = ((table->reclength) ?
+ thd->variables.max_heap_table_size/table->reclength : 1);
+ };
+ item_thd = thd;
+
+ group_concat_max_len = thd->variables.group_concat_max_len;
+
+ if (original)
+ {
+ original->table= table;
+ original->tree_mode= tree_mode;
+ }
+ return 0;
+}
+
+String* Item_func_group_concat::val_str(String* str)
+{
+ if (tree_mode)
+ {
+ show_elements = 0;
+ tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
+ left_root_right);
+ }
+ else
+ {
+ if (!warning_for_row)
+ result.length(result.length()-separator->length());
+ }
+
+ null_value = result.length() == 0;
+ if (count_cut_values && !warning_available)
+ {
+ warning_available=TRUE;
+ warning = push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_CUT_VALUE_GROUP_CONCAT, NULL);
+ }
+ return &result;
+}