diff options
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r-- | sql/item_sum.cc | 1162 |
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); +} |