diff options
author | unknown <wax@kishkin.ru> | 2003-03-18 04:07:40 +0500 |
---|---|---|
committer | unknown <wax@kishkin.ru> | 2003-03-18 04:07:40 +0500 |
commit | 0b505fb437eedd1b31c99888247c2259539c095b (patch) | |
tree | d5655e0a1fd7bd5c629fbc934a708f44ea818663 /sql/item_sum.cc | |
parent | c312cd45786f22f0fed89d4cb2d6bba7c6c87644 (diff) | |
download | mariadb-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.cc | 450 |
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; +} |