diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 097e07ac715..723a1952d79 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -1254,3 +1254,129 @@ DROP TABLE t1; # # End of 10.2 tests # +# +# Start of 10.3 tests +# +drop table if exists t1, t2; +create table t1 (grp int, a bigint unsigned, c char(10) , d char(10) not null); +insert into t1 values (1,1,NULL,"a"); +insert into t1 values (1,10,"b","a"); +insert into t1 values (1,11,"c","a"); +insert into t1 values (2,2,"c","a"); +insert into t1 values (2,3,"b","b"); +insert into t1 values (3,4,"E","a"); +insert into t1 values (3,5,"C","b"); +insert into t1 values (3,6,"D","c"); +insert into t1 values (3,7,"E","c"); +select grp,group_concat(c) from t1 group by grp; +grp group_concat(c) +1 b,c +2 c,b +3 E,C,D,E +select grp,group_concat(c limit 1 ) from t1 group by grp; +grp group_concat(c limit 1 ) +1 b +2 c +3 E +select grp,group_concat(c limit 1,1 ) from t1 group by grp; +grp group_concat(c limit 1,1 ) +1 c +2 b +3 C +select grp,group_concat(c limit 1,10 ) from t1 group by grp; +grp group_concat(c limit 1,10 ) +1 c +2 b +3 C,D,E +select grp,group_concat(c limit 1000) from t1 group by grp; +grp group_concat(c limit 1000) +1 b,c +2 c,b +3 E,C,D,E +select group_concat(grp limit 0) from t1; +group_concat(grp limit 0) + +select group_concat(grp limit "sdjadjs") from t1 +--error ER_PARSE_ERROR +select grp,group_concat(c limit 5.5) from t1 group by grp ; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '"sdjadjs") from t1 +--error ER_PARSE_ERROR +select grp,group_concat(c limit 5.5) f' at line 1 +select grp,group_concat(distinct c limit 1,10 ) from t1 group by grp; +grp group_concat(distinct c limit 1,10 ) +1 c +2 b +3 C,D +select grp,group_concat(c order by a) from t1 group by grp; +grp group_concat(c order by a) +1 b,c +2 c,b +3 E,C,D,E +select grp,group_concat(c order by a limit 2 ) from t1 group by grp; +grp group_concat(c order by a limit 2 ) +1 b,c +2 c,b +3 E,C +select grp,group_concat(c order by a limit 1,1 ) from t1 group by grp; +grp group_concat(c order by a limit 1,1 ) +1 c +2 b +3 C +select grp,group_concat(c order by c) from t1 group by grp; +grp group_concat(c order by c) +1 b,c +2 b,c +3 C,D,E,E +select grp,group_concat(c order by c limit 2) from t1 group by grp; +grp group_concat(c order by c limit 2) +1 b,c +2 b,c +3 C,D +select grp,group_concat(c order by c desc) from t1 group by grp; +grp group_concat(c order by c desc) +1 c,b +2 c,b +3 E,E,D,C +select grp,group_concat(c order by c desc limit 2) from t1 group by grp; +grp group_concat(c order by c desc limit 2) +1 c,b +2 c,b +3 E,E +drop table t1; +create table t2 (a int, b varchar(10)); +insert into t2 values(1,'a'),(1,'b'),(NULL,'c'),(2,'x'),(2,'y'); +select group_concat(a,b limit 2) from t2; +group_concat(a,b limit 2) +1a,1b +set @x=4; +prepare STMT from 'select group_concat(b limit ?) from t2'; +execute STMT using @x; +group_concat(b limit ?) +a,b,c,x +set @x=2; +execute STMT using @x; +group_concat(b limit ?) +a,b +set @x=1000; +execute STMT using @x; +group_concat(b limit ?) +a,b,c,x,y +set @x=0; +execute STMT using @x; +group_concat(b limit ?) + +set @x="adasfa"; +execute STMT using @x; +ERROR HY000: Limit only accepts integer values +set @x=-1; +execute STMT using @x; +ERROR HY000: Incorrect arguments to EXECUTE +set @x=4; +prepare STMT from 'select group_concat(a,b limit ?) from t2'; +execute STMT using @x; +group_concat(a,b limit ?) +1a,1b,2x,2y +drop table t2; +# +# End of 10.3 tests +# diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 1038fc0f6d0..5cbc6969e02 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -915,3 +915,76 @@ DROP TABLE t1; --echo # --echo # End of 10.2 tests --echo # + + +--echo # +--echo # Start of 10.3 tests +--echo # + +# +# MDEV-11297: Add support for LIMIT clause in GROUP_CONCAT() +# +--disable_warnings +drop table if exists t1, t2; +--enable_warnings + +create table t1 (grp int, a bigint unsigned, c char(10) , d char(10) not null); +insert into t1 values (1,1,NULL,"a"); +insert into t1 values (1,10,"b","a"); +insert into t1 values (1,11,"c","a"); +insert into t1 values (2,2,"c","a"); +insert into t1 values (2,3,"b","b"); +insert into t1 values (3,4,"E","a"); +insert into t1 values (3,5,"C","b"); +insert into t1 values (3,6,"D","c"); +insert into t1 values (3,7,"E","c"); + + +select grp,group_concat(c) from t1 group by grp; +select grp,group_concat(c limit 1 ) from t1 group by grp; +select grp,group_concat(c limit 1,1 ) from t1 group by grp; +select grp,group_concat(c limit 1,10 ) from t1 group by grp; +select grp,group_concat(c limit 1000) from t1 group by grp; +select group_concat(grp limit 0) from t1; +--error ER_PARSE_ERROR +select group_concat(grp limit "sdjadjs") from t1 +--error ER_PARSE_ERROR +select grp,group_concat(c limit 5.5) from t1 group by grp ; +select grp,group_concat(distinct c limit 1,10 ) from t1 group by grp; +select grp,group_concat(c order by a) from t1 group by grp; +select grp,group_concat(c order by a limit 2 ) from t1 group by grp; +select grp,group_concat(c order by a limit 1,1 ) from t1 group by grp; +select grp,group_concat(c order by c) from t1 group by grp; +select grp,group_concat(c order by c limit 2) from t1 group by grp; +select grp,group_concat(c order by c desc) from t1 group by grp; +select grp,group_concat(c order by c desc limit 2) from t1 group by grp; + +drop table t1; + +create table t2 (a int, b varchar(10)); +insert into t2 values(1,'a'),(1,'b'),(NULL,'c'),(2,'x'),(2,'y'); +select group_concat(a,b limit 2) from t2; + +set @x=4; +prepare STMT from 'select group_concat(b limit ?) from t2'; +execute STMT using @x; +set @x=2; +execute STMT using @x; +set @x=1000; +execute STMT using @x; +set @x=0; +execute STMT using @x; +set @x="adasfa"; +--error ER_INVALID_VALUE_TO_LIMIT +execute STMT using @x; +set @x=-1; +--error ER_WRONG_ARGUMENTS +execute STMT using @x; +set @x=4; +prepare STMT from 'select group_concat(a,b limit ?) from t2'; +execute STMT using @x; +drop table t2; + +--echo # +--echo # End of 10.3 tests +--echo # diff --git a/sql/item_sum.cc b/sql/item_sum.cc index aa61c19306d..d4abdfc614f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3533,6 +3533,11 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), Item **arg= item->args, **arg_end= item->args + item->arg_count_field; uint old_length= result->length(); + ulonglong *offset_limit= &item->copy_offset_limit; + ulonglong *row_limit = &item->copy_row_limit; + if (item->limit_clause && !(*row_limit)) + return 1; + if (item->no_appended) item->no_appended= FALSE; else @@ -3540,6 +3545,14 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), tmp.length(0); + if (item->limit_clause && (*offset_limit)) + { + item->row_count++; + item->no_appended= TRUE; + (*offset_limit)--; + return 0; + } + for (; arg < arg_end; arg++) { String *res; @@ -3569,6 +3582,8 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), result->append(*res); } + if (item->limit_clause) + (*row_limit)--; item->row_count++; /* stop if length of result more than max_length */ @@ -3617,7 +3632,8 @@ Item_func_group_concat:: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool distinct_arg, List *select_list, const SQL_I_List &order_list, - String *separator_arg) + String *separator_arg, bool limit_clause, + Item *row_limit_arg, Item *offset_limit_arg) :Item_sum(thd), tmp_table_param(0), separator(separator_arg), tree(0), unique_filter(NULL), table(0), order(0), context(context_arg), @@ -3626,7 +3642,9 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, row_count(0), distinct(distinct_arg), warning_for_row(FALSE), - force_copy_fields(0), original(0) + force_copy_fields(0), row_limit(NULL), + offset_limit(NULL), limit_clause(limit_clause), + copy_offset_limit(0), copy_row_limit(0), original(0) { Item *item_select; Item **arg_ptr; @@ -3668,6 +3686,11 @@ Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, /* orig_args is only used for print() */ orig_args= (Item**) (order + arg_count_order); memcpy(orig_args, args, sizeof(Item*) * arg_count); + if (limit_clause) + { + row_limit= row_limit_arg; + offset_limit= offset_limit_arg; + } } @@ -3687,7 +3710,9 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, warning_for_row(item->warning_for_row), always_null(item->always_null), force_copy_fields(item->force_copy_fields), - original(item) + row_limit(item->row_limit), offset_limit(item->offset_limit), + limit_clause(item->limit_clause),copy_offset_limit(item->copy_offset_limit), + copy_row_limit(item->copy_row_limit), original(item) { quick_group= item->quick_group; result.set_charset(collation.collation); @@ -3782,6 +3807,10 @@ void Item_func_group_concat::clear() null_value= TRUE; warning_for_row= FALSE; no_appended= TRUE; + if (offset_limit) + copy_offset_limit= offset_limit->val_int(); + if (row_limit) + copy_row_limit= row_limit->val_int(); if (tree) reset_tree(tree); if (unique_filter) @@ -4036,6 +4065,12 @@ bool Item_func_group_concat::setup(THD *thd) (void*)this, tree_key_length, ram_limitation(thd)); + if ((row_limit && row_limit->cmp_type() != INT_RESULT) || + (offset_limit && offset_limit->cmp_type() != INT_RESULT)) + { + my_error(ER_INVALID_VALUE_TO_LIMIT, MYF(0)); + DBUG_RETURN(TRUE); + } DBUG_RETURN(FALSE); } diff --git a/sql/item_sum.cc.orig b/sql/item_sum.cc.orig new file mode 100644 index 00000000000..aa61c19306d --- /dev/null +++ b/sql/item_sum.cc.orig @@ -0,0 +1,4113 @@ +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2015, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +/** + @file + + @brief + Sum functions (COUNT, MIN...) +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "mariadb.h" +#include "sql_priv.h" +#include "sql_select.h" +#include "uniques.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "sql_parse.h" +#include "sp_head.h" + +/** + Calculate the affordable RAM limit for structures like TREE or Unique + used in Item_sum_* +*/ + +size_t Item_sum::ram_limitation(THD *thd) +{ + return (size_t)MY_MIN(thd->variables.tmp_memory_table_size, + thd->variables.max_heap_table_size); +} + + +/** + Prepare an aggregate function item for checking context conditions. + + The function initializes the members of the Item_sum object created + for a set function that are used to check validity of the set function + occurrence. + If the set function is not allowed in any subquery where it occurs + an error is reported immediately. + + @param thd reference to the thread context info + + @note + This function is to be called for any item created for a set function + object when the traversal of trees built for expressions used in the query + is performed at the phase of context analysis. This function is to + be invoked at the descent of this traversal. + @retval + TRUE if an error is reported + @retval + FALSE otherwise +*/ + +bool Item_sum::init_sum_func_check(THD *thd) +{ + SELECT_LEX *curr_sel= thd->lex->current_select; + if (!curr_sel->name_visibility_map) + { + for (SELECT_LEX *sl= curr_sel; sl; sl= sl->context.outer_select()) + { + curr_sel->name_visibility_map|= (1 << sl-> nest_level); + } + } + if (!(thd->lex->allow_sum_func & curr_sel->name_visibility_map)) + { + my_message(ER_INVALID_GROUP_FUNC_USE, ER_THD(thd, ER_INVALID_GROUP_FUNC_USE), + MYF(0)); + return TRUE; + } + /* Set a reference to the nesting set function if there is any */ + in_sum_func= thd->lex->in_sum_func; + /* Save a pointer to object to be used in items for nested set functions */ + thd->lex->in_sum_func= this; + nest_level= thd->lex->current_select->nest_level; + ref_by= 0; + aggr_level= -1; + aggr_sel= NULL; + max_arg_level= -1; + max_sum_func_level= -1; + outer_fields.empty(); + return FALSE; +} + +/** + Check constraints imposed on a usage of a set function. + + The method verifies whether context conditions imposed on a usage + of any set function are met for this occurrence. + + The function first checks if we are using any window functions as + arguments to the set function. In that case it returns an error. + + Afterwards, it checks whether the set function occurs in the position where it + can be aggregated and, when it happens to occur in argument of another + set function, the method checks that these two functions are aggregated in + different subqueries. + If the context conditions are not met the method reports an error. + If the set function is aggregated in some outer subquery the method + adds it to the chain of items for such set functions that is attached + to the the st_select_lex structure for this subquery. + + A number of designated members of the object are used to check the + conditions. They are specified in the comment before the Item_sum + class declaration. + Additionally a bitmap variable called allow_sum_func is employed. + It is included into the thd->lex structure. + The bitmap contains 1 at n-th position if the set function happens + to occur under a construct of the n-th level subquery where usage + of set functions are allowed (i.e either in the SELECT list or + in the HAVING clause of the corresponding subquery) + Consider the query: + @code + SELECT SUM(t1.b) FROM t1 GROUP BY t1.a + HAVING t1.a IN (SELECT t2.c FROM t2 WHERE AVG(t1.b) > 20) AND + t1.a > (SELECT MIN(t2.d) FROM t2); + @endcode + allow_sum_func will contain: + - for SUM(t1.b) - 1 at the first position + - for AVG(t1.b) - 1 at the first position, 0 at the second position + - for MIN(t2.d) - 1 at the first position, 1 at the second position. + + @param thd reference to the thread context info + @param ref location of the pointer to this item in the embedding expression + + @note + This function is to be called for any item created for a set function + object when the traversal of trees built for expressions used in the query + is performed at the phase of context analysis. This function is to + be invoked at the ascent of this traversal. + + @retval + TRUE if an error is reported + @retval + FALSE otherwise +*/ + +bool Item_sum::check_sum_func(THD *thd, Item **ref) +{ + SELECT_LEX *curr_sel= thd->lex->current_select; + nesting_map allow_sum_func= (thd->lex->allow_sum_func & + curr_sel->name_visibility_map); + bool invalid= FALSE; + DBUG_ASSERT(curr_sel->name_visibility_map); // should be set already + + /* + Window functions can not be used as arguments to sum functions. + Aggregation happes before window function computation, so there + are no values to aggregate over. + */ + if (with_window_func) + { + my_message(ER_SUM_FUNC_WITH_WINDOW_FUNC_AS_ARG, + ER_THD(thd, ER_SUM_FUNC_WITH_WINDOW_FUNC_AS_ARG), + MYF(0)); + return TRUE; + } + + if (window_func_sum_expr_flag) + return false; + /* + The value of max_arg_level is updated if an argument of the set function + contains a column reference resolved against a subquery whose level is + greater than the current value of max_arg_level. + max_arg_level cannot be greater than nest level. + nest level is always >= 0 + */ + if (nest_level == max_arg_level) + { + /* + The function must be aggregated in the current subquery, + If it is there under a construct where it is not allowed + we report an error. + */ + invalid= !(allow_sum_func & ((nesting_map)1 << max_arg_level)); + } + else if (max_arg_level >= 0 || + !(allow_sum_func & ((nesting_map)1 << nest_level))) + { + /* + The set function can be aggregated only in outer subqueries. + Try to find a subquery where it can be aggregated; + If we fail to find such a subquery report an error. + */ + if (register_sum_func(thd, ref)) + return TRUE; + invalid= aggr_level < 0 && + !(allow_sum_func & ((nesting_map)1 << nest_level)); + if (!invalid && thd->variables.sql_mode & MODE_ANSI) + invalid= aggr_level < 0 && max_arg_level < nest_level; + } + if (!invalid && aggr_level < 0) + { + aggr_level= nest_level; + aggr_sel= curr_sel; + } + /* + By this moment we either found a subquery where the set function is + to be aggregated and assigned a value that is >= 0 to aggr_level, + or set the value of 'invalid' to TRUE to report later an error. + */ + /* + Additionally we have to check whether possible nested set functions + are acceptable here: they are not, if the level of aggregation of + some of them is less than aggr_level. + */ + if (!invalid) + invalid= aggr_level <= max_sum_func_level; + if (invalid) + { + my_message(ER_INVALID_GROUP_FUNC_USE, + ER_THD(thd, ER_INVALID_GROUP_FUNC_USE), + MYF(0)); + return TRUE; + } + + if (in_sum_func) + { + /* + If the set function is nested adjust the value of + max_sum_func_level for the nesting set function. + We take into account only enclosed set functions that are to be + aggregated on the same level or above of the nest level of + the enclosing set function. + But we must always pass up the max_sum_func_level because it is + the maximum nested level of all directly and indirectly enclosed + set functions. We must do that even for set functions that are + aggregated inside of their enclosing set function's nest level + because the enclosing function may contain another enclosing + function that is to be aggregated outside or on the same level + as its parent's nest level. + */ + if (in_sum_func->nest_level >= aggr_level) + set_if_bigger(in_sum_func->max_sum_func_level, aggr_level); + set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level); + } + + /* + Check that non-aggregated fields and sum functions aren't mixed in the + same select in the ONLY_FULL_GROUP_BY mode. + */ + if (outer_fields.elements) + { + Item_field *field; + /* + Here we compare the nesting level of the select to which an outer field + belongs to with the aggregation level of the sum function. All fields in + the outer_fields list are checked. + + If the nesting level is equal to the aggregation level then the field is + aggregated by this sum function. + If the nesting level is less than the aggregation level then the field + belongs to an outer select. In this case if there is an embedding sum + function add current field to functions outer_fields list. If there is + no embedding function then the current field treated as non aggregated + and the select it belongs to is marked accordingly. + If the nesting level is greater than the aggregation level then it means + that this field was added by an inner sum function. + Consider an example: + + select avg ( <-- we are here, checking outer.f1 + select ( + select sum(outer.f1 + inner.f1) from inner + ) from outer) + from most_outer; + + In this case we check that no aggregate functions are used in the + select the field belongs to. If there are some then an error is + raised. + */ + List_iterator of(outer_fields); + while ((field= of++)) + { + SELECT_LEX *sel= field->field->table->pos_in_table_list->select_lex; + if (sel->nest_level < aggr_level) + { + if (in_sum_func) + { + /* + Let upper function decide whether this field is a non + aggregated one. + */ + in_sum_func->outer_fields.push_back(field, thd->mem_root); + } + else + sel->set_non_agg_field_used(true); + } + if (sel->nest_level > aggr_level && + (sel->agg_func_used()) && + !sel->group_list.elements) + { + my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, + ER_THD(thd, ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); + return TRUE; + } + } + } + aggr_sel->set_agg_func_used(true); + update_used_tables(); + thd->lex->in_sum_func= in_sum_func; + return FALSE; +} + +/** + Attach a set function to the subquery where it must be aggregated. + + The function looks for an outer subquery where the set function must be + aggregated. If it finds such a subquery then aggr_level is set to + the nest level of this subquery and the item for the set function + is added to the list of set functions used in nested subqueries + inner_sum_func_list defined for each subquery. When the item is placed + there the field 'ref_by' is set to ref. + + @note + Now we 'register' only set functions that are aggregated in outer + subqueries. Actually it makes sense to link all set function for + a subquery in one chain. It would simplify the process of 'splitting' + for set functions. + + @param thd reference to the thread context info + @param ref location of the pointer to this item in the embedding expression + + @retval + FALSE if the executes without failures (currently always) + @retval + TRUE otherwise +*/ + +bool Item_sum::register_sum_func(THD *thd, Item **ref) +{ + SELECT_LEX *sl; + nesting_map allow_sum_func= thd->lex->allow_sum_func; + for (sl= thd->lex->current_select->context.outer_select() ; + sl && sl->nest_level > max_arg_level; + sl= sl->context.outer_select()) + { + if (aggr_level < 0 && + (allow_sum_func & ((nesting_map)1 << sl->nest_level))) + { + /* Found the most nested subquery where the function can be aggregated */ + aggr_level= sl->nest_level; + aggr_sel= sl; + } + } + if (sl && (allow_sum_func & ((nesting_map)1 << sl->nest_level))) + { + /* + We reached the subquery of level max_arg_level and checked + that the function can be aggregated here. + The set function will be aggregated in this subquery. + */ + aggr_level= sl->nest_level; + aggr_sel= sl; + + } + if (aggr_level >= 0) + { + ref_by= ref; + /* Add the object to the list of registered objects assigned to aggr_sel */ + if (!aggr_sel->inner_sum_func_list) + next= this; + else + { + next= aggr_sel->inner_sum_func_list->next; + aggr_sel->inner_sum_func_list->next= this; + } + aggr_sel->inner_sum_func_list= this; + aggr_sel->with_sum_func= 1; + + /* + Mark Item_subselect(s) as containing aggregate function all the way up + to aggregate function's calculation context. + Note that we must not mark the Item of calculation context itself + because with_sum_func on the calculation context st_select_lex is + already set above. + + with_sum_func being set for an Item means that this Item refers + (somewhere in it, e.g. one of its arguments if it's a function) directly + or through intermediate items to an aggregate function that is calculated + in a context "outside" of the Item (e.g. in the current or outer select). + + with_sum_func being set for an st_select_lex means that this st_select_lex + has aggregate functions directly referenced (i.e. not through a sub-select). + */ + for (sl= thd->lex->current_select; + sl && sl != aggr_sel && sl->master_unit()->item; + sl= sl->master_unit()->outer_select() ) + sl->master_unit()->item->with_sum_func= 1; + } + thd->lex->current_select->mark_as_dependent(thd, aggr_sel, NULL); + + if ((thd->lex->describe & DESCRIBE_EXTENDED) && aggr_sel) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_AGGFUNC_DEPENDENCE, + ER_THD(thd, ER_WARN_AGGFUNC_DEPENDENCE), + func_name(), + thd->lex->current_select->select_number, + aggr_sel->select_number); + } + return FALSE; +} + + +bool Item_sum::collect_outer_ref_processor(void *param) +{ + Collect_deps_prm *prm= (Collect_deps_prm *)param; + SELECT_LEX *ds; + if ((ds= depended_from()) && + ds->nest_level_base == prm->nest_level_base && + ds->nest_level < prm->nest_level) + { + if (prm->collect) + prm->parameters->add_unique(this, &cmp_items); + else + prm->count++; + } + return FALSE; +} + + +Item_sum::Item_sum(THD *thd, List &list): Item_func_or_sum(thd, list) +{ + if (!(orig_args= (Item **) thd->alloc(sizeof(Item *) * arg_count))) + { + args= NULL; + } + mark_as_sum_func(); + init_aggregator(); + list.empty(); // Fields are used +} + + +/** + Constructor used in processing select with temporary tebles. +*/ + +Item_sum::Item_sum(THD *thd, Item_sum *item): + Item_func_or_sum(thd, item), + aggr_sel(item->aggr_sel), + nest_level(item->nest_level), aggr_level(item->aggr_level), + quick_group(item->quick_group), + orig_args(NULL) +{ + if (arg_count <= 2) + { + orig_args=tmp_orig_args; + } + else + { + if (!(orig_args= (Item**) thd->alloc(sizeof(Item*)*arg_count))) + return; + } + memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count); + init_aggregator(); + with_distinct= item->with_distinct; + if (item->aggr) + set_aggregator(item->aggr->Aggrtype()); +} + + +void Item_sum::mark_as_sum_func() +{ + SELECT_LEX *cur_select= current_thd->lex->current_select; + cur_select->n_sum_items++; + cur_select->with_sum_func= 1; + const_item_cache= false; + with_sum_func= 1; + with_field= 0; + window_func_sum_expr_flag= false; +} + + +void Item_sum::print(String *str, enum_query_type query_type) +{ + /* orig_args is not filled with valid values until fix_fields() */ + Item **pargs= fixed ? orig_args : args; + str->append(func_name()); + /* + TODO: + The fact that func_name() may return a name with an extra '(' + is really annoying. This shoud be fixed. + */ + if (!is_aggr_sum_func()) + str->append('('); + for (uint i=0 ; i < arg_count ; i++) + { + if (i) + str->append(','); + pargs[i]->print(str, query_type); + } + str->append(')'); +} + +void Item_sum::fix_num_length_and_dec() +{ + decimals=0; + for (uint i=0 ; i < arg_count ; i++) + set_if_bigger(decimals,args[i]->decimals); + 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 (thd->mem_root) Item_temptable_field(thd, result_field_tmp++); + } + } + } + return sum_item; +} + + +void Item_sum::update_used_tables () +{ + if (!Item_sum::const_item()) + { + used_tables_cache= 0; + for (uint i=0 ; i < arg_count ; i++) + { + args[i]->update_used_tables(); + used_tables_cache|= args[i]->used_tables(); + } + /* + MariaDB: don't run the following { + + used_tables_cache&= PSEUDO_TABLE_BITS; + + // the aggregate function is aggregated into its local context + used_tables_cache|= ((table_map)1 << aggr_sel->join->tables) - 1; + + } because if we do it, table elimination will assume that + - constructs like "COUNT(*)" use columns from all tables + - so, it is not possible to eliminate any table + our solution for COUNT(*) is that it has + item->used_tables() == 0 && !item->const_item() + */ + } +} + + +Item *Item_sum::set_arg(uint i, THD *thd, Item *new_val) +{ + thd->change_item_tree(args + i, new_val); + return new_val; +} + + +int Item_sum::set_aggregator(Aggregator::Aggregator_type aggregator) +{ + /* + Dependent subselects may be executed multiple times, making + set_aggregator to be called multiple times. The aggregator type + will be the same, but it needs to be reset so that it is + reevaluated with the new dependent data. + This function may also be called multiple times during query optimization. + In this case, the type may change, so we delete the old aggregator, + and create a new one. + */ + if (aggr && aggregator == aggr->Aggrtype()) + { + aggr->clear(); + return FALSE; + } + + delete aggr; + switch (aggregator) + { + case Aggregator::DISTINCT_AGGREGATOR: + aggr= new Aggregator_distinct(this); + break; + case Aggregator::SIMPLE_AGGREGATOR: + aggr= new Aggregator_simple(this); + break; + }; + return aggr ? FALSE : TRUE; +} + + +void Item_sum::cleanup() +{ + if (aggr) + { + delete aggr; + aggr= NULL; + } + Item_result_field::cleanup(); + const_item_cache= false; +} + +Item *Item_sum::result_item(THD *thd, Field *field) +{ + return new (thd->mem_root) Item_field(thd, field); +} + +bool Item_sum::check_vcol_func_processor(void *arg) +{ + return mark_unsupported_function(func_name(), + is_aggr_sum_func() ? ")" : "()", + arg, VCOL_IMPOSSIBLE); +} + + +/** + Compare keys consisting of single field that cannot be compared as binary. + + Used by the Unique class to compare keys. Will do correct comparisons + for all field types. + + @param arg Pointer to the relevant Field class instance + @param key1 left key image + @param key2 right key image + @return comparison result + @retval < 0 if key1 < key2 + @retval = 0 if key1 = key2 + @retval > 0 if key1 > key2 +*/ + +int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2) +{ + Field *f= (Field*) arg; + return f->cmp(key1, key2); +} + + +C_MODE_START + +int count_distinct_walk(void *elem, element_count count, void *arg) +{ + (*((ulonglong*)arg))++; + return 0; +} + +C_MODE_END + + +/** + Correctly compare composite keys. + + Used by the Unique class to compare keys. Will do correct comparisons + for composite keys with various field types. + + @param arg Pointer to the relevant Aggregator_distinct instance + @param key1 left key image + @param key2 right key image + @return comparison result + @retval <0 if key1 < key2 + @retval =0 if key1 = key2 + @retval >0 if key1 > key2 +*/ + +int Aggregator_distinct::composite_key_cmp(void* arg, uchar* key1, uchar* key2) +{ + Aggregator_distinct *aggr= (Aggregator_distinct *) arg; + Field **field = aggr->table->field; + Field **field_end= field + aggr->table->s->fields; + uint32 *lengths=aggr->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->cmp(key1, key2); + if (res) + return res; + key1 += len; + key2 += len; + } + return 0; +} + + +/***************************************************************************/ + +C_MODE_START + +/* Declarations for auxilary C-callbacks */ + +int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) +{ + return memcmp(key1, key2, *(uint *) arg); +} + + +static int item_sum_distinct_walk_for_count(void *element, + element_count num_of_dups, + void *item) +{ + return ((Aggregator_distinct*) (item))->unique_walk_function_for_count(element); +} + + +static int item_sum_distinct_walk(void *element, element_count num_of_dups, + void *item) +{ + return ((Aggregator_distinct*) (item))->unique_walk_function(element); +} + +C_MODE_END + +/***************************************************************************/ +/** + Called before feeding the first row. Used to allocate/setup + the internal structures used for aggregation. + + @param thd Thread descriptor + @return status + @retval FALSE success + @retval TRUE faliure + + Prepares Aggregator_distinct to process the incoming stream. + Creates the temporary table and the Unique class if needed. + Called by Item_sum::aggregator_setup() +*/ + +bool Aggregator_distinct::setup(THD *thd) +{ + endup_done= FALSE; + /* + Setup can be called twice for ROLLUP items. This is a bug. + Please add DBUG_ASSERT(tree == 0) here when it's fixed. + */ + if (tree || table || tmp_table_param) + return FALSE; + + if (item_sum->setup(thd)) + return TRUE; + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + List list; + SELECT_LEX *select_lex= thd->lex->current_select; + + if (!(tmp_table_param= new TMP_TABLE_PARAM)) + return TRUE; + + /* Create a table with an unique key over all parameters */ + for (uint i=0; i < item_sum->get_arg_count() ; i++) + { + Item *item=item_sum->get_arg(i); + if (list.push_back(item, thd->mem_root)) + return TRUE; // End of memory + if (item->const_item() && item->is_null()) + always_null= true; + } + if (always_null) + return FALSE; + count_field_types(select_lex, tmp_table_param, list, 0); + tmp_table_param->force_copy_fields= item_sum->has_force_copy_fields(); + DBUG_ASSERT(table == 0); + /* + Make create_tmp_table() convert BIT columns to BIGINT. + This is needed because BIT fields store parts of their data in table's + null bits, and we don't have methods to compare two table records, which + is needed by Unique which is used when HEAP table is used. + */ + { + List_iterator_fast li(list); + Item *item; + while ((item= li++)) + { + if (item->type() == Item::FIELD_ITEM && + ((Item_field*)item)->field->type() == FIELD_TYPE_BIT) + item->marker=4; + } + } + if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, + 0, + (select_lex->options | thd->variables.option_bits), + HA_POS_ERROR, const_cast("")))) + return TRUE; + table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows + table->no_rows=1; + + if (table->s->db_type() == heap_hton) + { + /* + No blobs, otherwise it would have been MyISAM: set up a compare + function and its arguments to use with Unique. + */ + qsort_cmp2 compare_key; + void* cmp_arg; + Field **field= table->field; + Field **field_end= field + table->s->fields; + bool all_binary= TRUE; + + for (tree_key_length= 0; field < field_end; ++field) + { + Field *f= *field; + enum enum_field_types type= f->type(); + tree_key_length+= f->pack_length(); + if ((type == MYSQL_TYPE_VARCHAR) || + (!f->binary() && (type == MYSQL_TYPE_STRING || + type == MYSQL_TYPE_VAR_STRING))) + { + all_binary= FALSE; + break; + } + } + if (all_binary) + { + cmp_arg= (void*) &tree_key_length; + compare_key= (qsort_cmp2) simple_raw_key_cmp; + } + else + { + if (table->s->fields == 1) + { + /* + If we have only one field, which is the most common use of + count(distinct), it is much faster to use a simpler key + compare method that can take advantage of not having to worry + about other fields. + */ + compare_key= (qsort_cmp2) simple_str_key_cmp; + cmp_arg= (void*) table->field[0]; + /* tree_key_length has been set already */ + } + else + { + uint32 *length; + compare_key= (qsort_cmp2) composite_key_cmp; + cmp_arg= (void*) this; + field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32)); + for (tree_key_length= 0, length= field_lengths, field= table->field; + field < field_end; ++field, ++length) + { + *length= (*field)->pack_length(); + tree_key_length+= *length; + } + } + } + DBUG_ASSERT(tree == 0); + tree= new Unique(compare_key, cmp_arg, tree_key_length, + item_sum->ram_limitation(thd)); + /* + The only time tree_key_length could be 0 is if someone does + count(distinct) on a char(0) field - stupid thing to do, + but this has to be handled - otherwise someone can crash + the server with a DoS attack + */ + if (! tree) + return TRUE; + } + return FALSE; + } + else + { + Item *arg; + DBUG_ENTER("Aggregator_distinct::setup"); + /* It's legal to call setup() more than once when in a subquery */ + if (tree) + DBUG_RETURN(FALSE); + + /* + Virtual table and the tree are created anew on each re-execution of + PS/SP. Hence all further allocations are performed in the runtime + mem_root. + */ + + item_sum->null_value= item_sum->maybe_null= 1; + item_sum->quick_group= 0; + + DBUG_ASSERT(item_sum->get_arg(0)->fixed); + + arg= item_sum->get_arg(0); + if (arg->const_item()) + { + (void) arg->is_null(); + if (arg->null_value) + always_null= true; + } + + if (always_null) + DBUG_RETURN(FALSE); + + Field *field= arg->type_handler()-> + make_num_distinct_aggregator_field(thd->mem_root, arg); + if (!field || !(table= create_virtual_tmp_table(thd, field))) + DBUG_RETURN(TRUE); + + /* XXX: check that the case of CHAR(0) works OK */ + tree_key_length= table->s->reclength - table->s->null_bytes; + + /* + Unique handles all unique elements in a tree until they can't fit + in. Then the tree is dumped to the temporary file. We can use + simple_raw_key_cmp because the table contains numbers only; decimals + are converted to binary representation as well. + */ + tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length, + item_sum->ram_limitation(thd)); + + DBUG_RETURN(tree == 0); + } +} + + +/** + Invalidate calculated value and clear the distinct rows. + + Frees space used by the internal data structures. + Removes the accumulated distinct rows. Invalidates the calculated result. +*/ + +void Aggregator_distinct::clear() +{ + endup_done= FALSE; + item_sum->clear(); + if (tree) + tree->reset(); + /* tree and table can be both null only if always_null */ + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + if (!tree && table) + { + table->file->extra(HA_EXTRA_NO_CACHE); + table->file->ha_delete_all_rows(); + table->file->extra(HA_EXTRA_WRITE_CACHE); + } + } + else + { + item_sum->null_value= 1; + } +} + + +/** + Process incoming row. + + Add it to Unique/temp hash table if it's unique. Skip the row if + not unique. + Prepare Aggregator_distinct to process the incoming stream. + Create the temporary table and the Unique class if needed. + Called by Item_sum::aggregator_add(). + To actually get the result value in item_sum's buffers + Aggregator_distinct::endup() must be called. + + @return status + @retval FALSE success + @retval TRUE failure +*/ + +bool Aggregator_distinct::add() +{ + if (always_null) + return 0; + + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + int error; + copy_fields(tmp_table_param); + if (copy_funcs(tmp_table_param->items_to_copy, table->in_use)) + return TRUE; + + for (Field **field=table->field ; *field ; field++) + if ((*field)->is_real_null(0)) + return 0; // Don't count NULL + + if (tree) + { + /* + The first few bytes of record (at least one) are just markers + for deleted and NULLs. We want to skip them since they will + bloat the tree without providing any valuable info. Besides, + key_length used to initialize the tree didn't include space for them. + */ + return tree->unique_add(table->record[0] + table->s->null_bytes); + } + if ((error= table->file->ha_write_tmp_row(table->record[0])) && + table->file->is_fatal_error(error, HA_CHECK_DUP)) + return TRUE; + return FALSE; + } + else + { + item_sum->get_arg(0)->save_in_field(table->field[0], FALSE); + if (table->field[0]->is_null()) + return 0; + DBUG_ASSERT(tree); + item_sum->null_value= 0; + /* + '0' values are also stored in the tree. This doesn't matter + for SUM(DISTINCT), but is important for AVG(DISTINCT) + */ + return tree->unique_add(table->field[0]->ptr); + } +} + + +/** + Calculate the aggregate function value. + + Since Distinct_aggregator::add() just collects the distinct rows, + we must go over the distinct rows and feed them to the aggregation + function before returning its value. + This is what endup () does. It also sets the result validity flag + endup_done to TRUE so it will not recalculate the aggregate value + again if the Item_sum hasn't been reset. +*/ + +void Aggregator_distinct::endup() +{ + /* prevent consecutive recalculations */ + if (endup_done) + return; + + /* we are going to calculate the aggregate value afresh */ + item_sum->clear(); + + /* The result will definitely be null : no more calculations needed */ + if (always_null) + return; + + if (item_sum->sum_func() == Item_sum::COUNT_FUNC || + item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + { + DBUG_ASSERT(item_sum->fixed == 1); + Item_sum_count *sum= (Item_sum_count *)item_sum; + if (tree && tree->elements == 0) + { + /* everything fits in memory */ + sum->count= (longlong) tree->elements_in_tree(); + endup_done= TRUE; + } + if (!tree) + { + /* there were blobs */ + table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + sum->count= table->file->stats.records; + endup_done= TRUE; + } + } + + /* + We don't have a tree only if 'setup()' hasn't been called; + this is the case of sql_executor.cc:return_zero_rows. + */ + if (tree && !endup_done) + { + /* + All tree's values are not NULL. + Note that value of field is changed as we walk the tree, in + Aggregator_distinct::unique_walk_function, but it's always not NULL. + */ + table->field[0]->set_notnull(); + /* go over the tree of distinct keys and calculate the aggregate value */ + use_distinct_values= TRUE; + tree_walk_action func; + if (item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC) + func= item_sum_distinct_walk_for_count; + else + func= item_sum_distinct_walk; + tree->walk(table, func, (void*) this); + use_distinct_values= FALSE; + } + /* prevent consecutive recalculations */ + endup_done= TRUE; +} + + +String * +Item_sum_num::val_str(String *str) +{ + return val_string_from_real(str); +} + + +my_decimal *Item_sum_num::val_decimal(my_decimal *decimal_value) +{ + return val_decimal_from_real(decimal_value); +} + + +String * +Item_sum_int::val_str(String *str) +{ + return val_string_from_int(str); +} + + +my_decimal *Item_sum_int::val_decimal(my_decimal *decimal_value) +{ + return val_decimal_from_int(decimal_value); +} + + +bool +Item_sum_num::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + + if (init_sum_func_check(thd)) + return TRUE; + + decimals=0; + maybe_null= sum_func() != COUNT_FUNC; + for (uint i=0 ; i < arg_count ; i++) + { + if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) + return TRUE; + set_if_bigger(decimals, args[i]->decimals); + m_with_subquery|= args[i]->with_subquery(); + with_window_func|= args[i]->with_window_func; + } + result_field=0; + max_length=float_length(decimals); + null_value=1; + fix_length_and_dec(); + + if (check_sum_func(thd, ref)) + return TRUE; + + memcpy (orig_args, args, sizeof (Item *) * arg_count); + fixed= 1; + return FALSE; +} + + +bool +Item_sum_hybrid::fix_fields(THD *thd, Item **ref) +{ + DBUG_ENTER("Item_sum_hybrid::fix_fields"); + DBUG_ASSERT(fixed == 0); + + Item *item= args[0]; + + if (init_sum_func_check(thd)) + DBUG_RETURN(TRUE); + + // 'item' can be changed during fix_fields + if ((!item->fixed && item->fix_fields(thd, args)) || + (item= args[0])->check_cols(1)) + DBUG_RETURN(TRUE); + + m_with_subquery= args[0]->with_subquery(); + with_window_func|= args[0]->with_window_func; + + fix_length_and_dec(); + if (!is_window_func_sum_expr()) + setup_hybrid(thd, args[0], NULL); + result_field=0; + + if (check_sum_func(thd, ref)) + DBUG_RETURN(TRUE); + + orig_args[0]= args[0]; + fixed= 1; + DBUG_RETURN(FALSE); +} + + +void Item_sum_hybrid::fix_length_and_dec() +{ + DBUG_ASSERT(args[0]->field_type() == args[0]->real_item()->field_type()); + DBUG_ASSERT(args[0]->result_type() == args[0]->real_item()->result_type()); + (void) args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this); +} + + +/** + MIN/MAX function setup. + + @param item argument of MIN/MAX function + @param value_arg calculated value of MIN/MAX function + + @details + Setup cache/comparator of MIN/MAX functions. When called by the + copy_or_same function value_arg parameter contains calculated value + of the original MIN/MAX object and it is saved in this object's cache. + + We mark the value and arg_cache with 'RAND_TABLE_BIT' to ensure + that Arg_comparator::compare_datetime() doesn't allocate new + item inside of Arg_comparator. This would cause compare_datetime() + and Item_sum_min::add() to use different values! +*/ + +void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) +{ + DBUG_ENTER("Item_sum_hybrid::setup_hybrid"); + if (!(value= item->get_cache(thd))) + DBUG_VOID_RETURN; + value->setup(thd, item); + value->store(value_arg); + /* Don't cache value, as it will change */ + if (!item->const_item()) + value->set_used_tables(RAND_TABLE_BIT); + if (!(arg_cache= item->get_cache(thd))) + DBUG_VOID_RETURN; + arg_cache->setup(thd, item); + /* Don't cache value, as it will change */ + if (!item->const_item()) + arg_cache->set_used_tables(RAND_TABLE_BIT); + cmp= new Arg_comparator(); + if (cmp) + cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); + DBUG_VOID_RETURN; +} + + +Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) +{ + DBUG_ENTER("Item_sum_hybrid::create_tmp_field"); + + if (args[0]->type() == Item::FIELD_ITEM) + { + Field *field= ((Item_field*) args[0])->field; + if ((field= create_tmp_field_from_field(table->in_use, field, &name, + table, NULL))) + field->flags&= ~NOT_NULL_FLAG; + DBUG_RETURN(field); + } + DBUG_RETURN(tmp_table_field_from_field_type(table)); +} + +/*********************************************************************** +** Item_sum_sp class +***********************************************************************/ + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp, List &list) + :Item_sum(thd, list), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; +} + +Item_sum_sp::Item_sum_sp(THD *thd, Name_resolution_context *context_arg, + sp_name *name_arg, sp_head *sp) + :Item_sum(thd), Item_sp(thd, context_arg, name_arg) +{ + maybe_null= 1; + quick_group= 0; + m_sp= sp; +} + + +bool +Item_sum_sp::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + if (init_sum_func_check(thd)) + return TRUE; + decimals= 0; + + m_sp= m_sp ? m_sp : sp_handler_function.sp_find_routine(thd, m_name, true); + + if (!m_sp) + { + my_missing_function_error(m_name->m_name, ErrConvDQName(m_name).ptr()); + context->process_error(thd); + return TRUE; + } + + if (init_result_field(thd, max_length, maybe_null, &null_value, &name)) + return TRUE; + + for (uint i= 0 ; i < arg_count ; i++) + { + if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) + return TRUE; + set_if_bigger(decimals, args[i]->decimals); + m_with_subquery|= args[i]->with_subquery(); + with_window_func|= args[i]->with_window_func; + } + result_field= NULL; + max_length= float_length(decimals); + null_value= 1; + fix_length_and_dec(); + + if (check_sum_func(thd, ref)) + return TRUE; + + memcpy(orig_args, args, sizeof(Item *) * arg_count); + fixed= 1; + return FALSE; +} + +/** + Execute function to store value in result field. + This is called when we need the value to be returned for the function. + Here we send a signal in form of the server status that all rows have been + fetched and now we have to exit from the function with the return value. + @return Function returns error status. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::execute() +{ + THD *thd= current_thd; + bool res; + uint old_server_status= thd->server_status; + + /* We set server status so we can send a signal to exit from the + function with the return value. */ + + thd->server_status= SERVER_STATUS_LAST_ROW_SENT; + res= Item_sp::execute(thd, &null_value, args, arg_count); + thd->server_status= old_server_status; + return res; +} + +/** + Handles the aggregation of the values. + @note: See class description for more details on how and why this is done. + @return The error state. + @retval FALSE on success. + @retval TRUE if an error occurred. +*/ + +bool +Item_sum_sp::add() +{ + return execute_impl(current_thd, args, arg_count); +} + + +void +Item_sum_sp::clear() +{ + delete func_ctx; + func_ctx= NULL; + sp_query_arena->free_items(); + free_root(&sp_mem_root, MYF(0)); +} + +const Type_handler *Item_sum_sp::type_handler() const +{ + DBUG_ENTER("Item_sum_sp::type_handler"); + DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp)); + DBUG_ASSERT(sp_result_field); + // This converts ENUM/SET to STRING + const Type_handler *handler= sp_result_field->type_handler(); + DBUG_RETURN(handler->type_handler_for_item_field()); +} + +void +Item_sum_sp::cleanup() +{ + Item_sp::cleanup(); + Item_sum::cleanup(); +} + +/** + Initialize local members with values from the Field interface. + @note called from Item::fix_fields. +*/ + +void +Item_sum_sp::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_sp::fix_length_and_dec"); + DBUG_ASSERT(sp_result_field); + Type_std_attributes::set(sp_result_field); + Item_sum::fix_length_and_dec(); + DBUG_VOID_RETURN; +} + +const char * +Item_sum_sp::func_name() const +{ + THD *thd= current_thd; + return Item_sp::func_name(thd); +} + +/*********************************************************************** +** reset and add of sum_func +***********************************************************************/ + +/** + @todo + check if the following assignments are really needed +*/ +Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) + :Item_sum_num(thd, item), + Type_handler_hybrid_field_type(item), + direct_added(FALSE), direct_reseted_field(FALSE), + curr_dec_buff(item->curr_dec_buff), + count(item->count) +{ + /* TODO: check if the following assignments are really needed */ + if (result_type() == DECIMAL_RESULT) + { + my_decimal2decimal(item->dec_buffs, dec_buffs); + my_decimal2decimal(item->dec_buffs + 1, dec_buffs + 1); + } + else + sum= item->sum; +} + +Item *Item_sum_sum::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_sum(thd, this); +} + + +void Item_sum_sum::cleanup() +{ + DBUG_ENTER("Item_sum_sum::cleanup"); + direct_added= direct_reseted_field= FALSE; + Item_sum_num::cleanup(); + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::clear() +{ + DBUG_ENTER("Item_sum_sum::clear"); + null_value=1; + count= 0; + if (result_type() == DECIMAL_RESULT) + { + curr_dec_buff= 0; + my_decimal_set_zero(dec_buffs); + } + else + sum= 0.0; + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::fix_length_and_dec_double() +{ + set_handler(&type_handler_double); // Change FLOAT to DOUBLE + decimals= args[0]->decimals; + sum= 0.0; +} + + +void Item_sum_sum::fix_length_and_dec_decimal() +{ + set_handler(&type_handler_newdecimal); // Change temporal to new DECIMAL + decimals= args[0]->decimals; + /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ + int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS; + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); + curr_dec_buff= 0; + my_decimal_set_zero(dec_buffs); +} + + +void Item_sum_sum::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_sum::fix_length_and_dec"); + maybe_null=null_value=1; + args[0]->cast_to_int_type_handler()->Item_sum_sum_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int) decimals)); + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::direct_add(my_decimal *add_sum_decimal) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_decimal: %p", add_sum_decimal)); + direct_added= TRUE; + direct_reseted_field= FALSE; + if (add_sum_decimal) + { + direct_sum_is_null= FALSE; + direct_sum_decimal= *add_sum_decimal; + } + else + { + direct_sum_is_null= TRUE; + direct_sum_decimal= decimal_zero; + } + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::direct_add(double add_sum_real, bool add_sum_is_null) +{ + DBUG_ENTER("Item_sum_sum::direct_add"); + DBUG_PRINT("info", ("add_sum_real: %f", add_sum_real)); + direct_added= TRUE; + direct_reseted_field= FALSE; + direct_sum_is_null= add_sum_is_null; + direct_sum_real= add_sum_real; + DBUG_VOID_RETURN; +} + + +bool Item_sum_sum::add() +{ + DBUG_ENTER("Item_sum_sum::add"); + add_helper(false); + DBUG_RETURN(0); +} + +void Item_sum_sum::add_helper(bool perform_removal) +{ + DBUG_ENTER("Item_sum_sum::add_helper"); + + if (result_type() == DECIMAL_RESULT) + { + if (unlikely(direct_added)) + { + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (likely(!direct_sum_is_null)) + { + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), + &direct_sum_decimal, dec_buffs + curr_dec_buff); + curr_dec_buff^= 1; + null_value= 0; + } + } + else + { + direct_reseted_field= FALSE; + my_decimal value; + const my_decimal *val= aggr->arg_val_decimal(&value); + if (!aggr->arg_is_null(true)) + { + if (perform_removal) + { + if (count > 0) + { + my_decimal_sub(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + dec_buffs + curr_dec_buff, val); + count--; + } + else + DBUG_VOID_RETURN; + } + else + { + count++; + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff ^ 1), + val, dec_buffs + curr_dec_buff); + } + curr_dec_buff^= 1; + null_value= (count > 0) ? 0 : 1; + } + } + } + else + { + if (unlikely(direct_added)) + { + /* Add value stored by Item_sum_sum::direct_add */ + DBUG_ASSERT(!perform_removal); + + direct_added= FALSE; + if (!direct_sum_is_null) + { + sum+= direct_sum_real; + null_value= 0; + } + } + else + { + direct_reseted_field= FALSE; + if (perform_removal && count > 0) + sum-= aggr->arg_val_real(); + else + sum+= aggr->arg_val_real(); + if (!aggr->arg_is_null(true)) + { + if (perform_removal) + { + if (count > 0) + { + count--; + } + } + else + count++; + + null_value= (count > 0) ? 0 : 1; + } + } + } + DBUG_VOID_RETURN; +} + + +longlong Item_sum_sum::val_int() +{ + DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); + if (result_type() == DECIMAL_RESULT) + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag, + &result); + return result; + } + return val_int_from_real(); +} + + +double Item_sum_sum::val_real() +{ + DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); + if (result_type() == DECIMAL_RESULT) + my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum); + return sum; +} + + +String *Item_sum_sum::val_str(String *str) +{ + if (aggr) + aggr->endup(); + if (result_type() == DECIMAL_RESULT) + return val_string_from_decimal(str); + return val_string_from_real(str); +} + + +my_decimal *Item_sum_sum::val_decimal(my_decimal *val) +{ + if (aggr) + aggr->endup(); + if (result_type() == DECIMAL_RESULT) + return null_value ? NULL : (dec_buffs + curr_dec_buff); + return val_decimal_from_real(val); +} + +void Item_sum_sum::remove() +{ + DBUG_ENTER("Item_sum_sum::remove"); + add_helper(true); + DBUG_VOID_RETURN; +} + +/** + Aggregate a distinct row from the distinct hash table. + + Called for each row into the hash table 'Aggregator_distinct::table'. + Includes the current distinct row into the calculation of the + aggregate value. Uses the Field classes to get the value from the row. + This function is used for AVG/SUM(DISTINCT). For COUNT(DISTINCT) + it's called only when there are no blob arguments and the data don't + fit into memory (so Unique makes persisted trees on disk). + + @param element pointer to the row data. + + @return status + @retval FALSE success + @retval TRUE failure +*/ + +bool Aggregator_distinct::unique_walk_function(void *element) +{ + memcpy(table->field[0]->ptr, element, tree_key_length); + item_sum->add(); + return 0; +} + + +/* + A variant of unique_walk_function() that is to be used with Item_sum_count. + + COUNT is a special aggregate function: it doesn't need the values, it only + needs to count them. COUNT needs to know the values are not NULLs, but NULL + values are not put into the Unique, so we don't need to check for NULLs here. +*/ + +bool Aggregator_distinct::unique_walk_function_for_count(void *element) +{ + Item_sum_count *sum= (Item_sum_count *)item_sum; + sum->count++; + return 0; +} + + +Aggregator_distinct::~Aggregator_distinct() +{ + if (tree) + { + delete tree; + tree= NULL; + } + if (table) + { + free_tmp_table(table->in_use, table); + table=NULL; + } + if (tmp_table_param) + { + delete tmp_table_param; + tmp_table_param= NULL; + } +} + + +my_decimal *Aggregator_simple::arg_val_decimal(my_decimal *value) +{ + return item_sum->args[0]->val_decimal(value); +} + + +double Aggregator_simple::arg_val_real() +{ + return item_sum->args[0]->val_real(); +} + + +bool Aggregator_simple::arg_is_null(bool use_null_value) +{ + Item **item= item_sum->args; + const uint item_count= item_sum->arg_count; + if (use_null_value) + { + for (uint i= 0; i < item_count; i++) + { + if (item[i]->null_value) + return true; + } + } + else + { + for (uint i= 0; i < item_count; i++) + { + if (item[i]->maybe_null && item[i]->is_null()) + return true; + } + } + return false; +} + + +my_decimal *Aggregator_distinct::arg_val_decimal(my_decimal * value) +{ + return use_distinct_values ? table->field[0]->val_decimal(value) : + item_sum->args[0]->val_decimal(value); +} + + +double Aggregator_distinct::arg_val_real() +{ + return use_distinct_values ? table->field[0]->val_real() : + item_sum->args[0]->val_real(); +} + + +bool Aggregator_distinct::arg_is_null(bool use_null_value) +{ + if (use_distinct_values) + { + const bool rc= table->field[0]->is_null(); + DBUG_ASSERT(!rc); // NULLs are never stored in 'tree' + return rc; + } + return use_null_value ? + item_sum->args[0]->null_value : + (item_sum->args[0]->maybe_null && item_sum->args[0]->is_null()); +} + + +Item *Item_sum_count::copy_or_same(THD* thd) +{ + DBUG_ENTER("Item_sum_count::copy_or_same"); + DBUG_RETURN(new (thd->mem_root) Item_sum_count(thd, this)); +} + + +void Item_sum_count::direct_add(longlong add_count) +{ + DBUG_ENTER("Item_sum_count::direct_add"); + DBUG_PRINT("info", ("add_count: %lld", add_count)); + direct_counted= TRUE; + direct_reseted_field= FALSE; + direct_count= add_count; + DBUG_VOID_RETURN; +} + + +void Item_sum_count::clear() +{ + DBUG_ENTER("Item_sum_count::clear"); + count= 0; + DBUG_VOID_RETURN; +} + + +bool Item_sum_count::add() +{ + DBUG_ENTER("Item_sum_count::add"); + if (direct_counted) + { + direct_counted= FALSE; + count+= direct_count; + } + else + { + direct_reseted_field= FALSE; + if (aggr->arg_is_null(false)) + DBUG_RETURN(0); + count++; + } + DBUG_RETURN(0); +} + + +/* + Remove a row. This is used by window functions. +*/ + +void Item_sum_count::remove() +{ + DBUG_ASSERT(aggr->Aggrtype() == Aggregator::SIMPLE_AGGREGATOR); + if (aggr->arg_is_null(false)) + return; + if (count > 0) + count--; +} + +longlong Item_sum_count::val_int() +{ + DBUG_ENTER("Item_sum_count::val_int"); + DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); + DBUG_RETURN((longlong)count); +} + + +void Item_sum_count::cleanup() +{ + DBUG_ENTER("Item_sum_count::cleanup"); + count= 0; + direct_counted= FALSE; + direct_reseted_field= FALSE; + Item_sum_int::cleanup(); + DBUG_VOID_RETURN; +} + + +/* + Avgerage +*/ + +void Item_sum_avg::fix_length_and_dec_decimal() +{ + Item_sum_sum::fix_length_and_dec_decimal(); + int precision= args[0]->decimal_precision() + prec_increment; + decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); + f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION); + f_scale= args[0]->decimals; + dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); +} + + +void Item_sum_avg::fix_length_and_dec_double() +{ + Item_sum_sum::fix_length_and_dec_double(); + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS); + max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals)); +} + + +void Item_sum_avg::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_avg::fix_length_and_dec"); + prec_increment= current_thd->variables.div_precincrement; + maybe_null=null_value=1; + args[0]->cast_to_int_type_handler()->Item_sum_avg_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int) decimals)); + DBUG_VOID_RETURN; +} + + +Item *Item_sum_avg::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_avg(thd, this); +} + + +Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table) +{ + + if (group) + { + /* + We must store both value and counter in the temporary table in one field. + The easiest way is to do this is to store both value in a string + and unpack on access. + */ + Field *field= new (table->in_use->mem_root) + Field_string(((result_type() == DECIMAL_RESULT) ? + dec_bin_size : sizeof(double)) + sizeof(longlong), + 0, &name, &my_charset_bin); + if (field) + field->init(table); + return field; + } + return tmp_table_field_from_field_type(table); +} + + +void Item_sum_avg::clear() +{ + Item_sum_sum::clear(); + count=0; +} + + +bool Item_sum_avg::add() +{ + if (Item_sum_sum::add()) + return TRUE; + if (!aggr->arg_is_null(true)) + count++; + return FALSE; +} + +void Item_sum_avg::remove() +{ + Item_sum_sum::remove(); + if (!aggr->arg_is_null(true)) + { + if (count > 0) + count--; + } +} + +double Item_sum_avg::val_real() +{ + DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); + if (!count) + { + null_value=1; + return 0.0; + } + return Item_sum_sum::val_real() / ulonglong2double(count); +} + + +my_decimal *Item_sum_avg::val_decimal(my_decimal *val) +{ + my_decimal cnt; + const my_decimal *sum_dec; + DBUG_ASSERT(fixed == 1); + if (aggr) + aggr->endup(); + if (!count) + { + null_value=1; + return NULL; + } + + /* + For non-DECIMAL result_type() the division will be done in + Item_sum_avg::val_real(). + */ + if (result_type() != DECIMAL_RESULT) + return val_decimal_from_real(val); + + sum_dec= dec_buffs + curr_dec_buff; + int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt); + my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment); + return val; +} + + +String *Item_sum_avg::val_str(String *str) +{ + if (aggr) + aggr->endup(); + if (result_type() == DECIMAL_RESULT) + return val_string_from_decimal(str); + return val_string_from_real(str); +} + + +/* + Standard deviation +*/ + +double Item_sum_std::val_real() +{ + DBUG_ASSERT(fixed == 1); + double nr= Item_sum_variance::val_real(); + if (my_isinf(nr)) + return DBL_MAX; + DBUG_ASSERT(nr >= 0.0); + return sqrt(nr); +} + +Item *Item_sum_std::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_std(thd, this); +} + + +Item *Item_sum_std::result_item(THD *thd, Field *field) +{ + return new (thd->mem_root) Item_std_field(thd, this); +} + + +/* + Variance +*/ + + +/** + Variance implementation for floating-point implementations, without + catastrophic cancellation, from Knuth's _TAoCP_, 3rd ed, volume 2, pg232. + This alters the value at m, s, and increments count. +*/ + +/* + These two functions are used by the Item_sum_variance and the + Item_variance_field classes, which are unrelated, and each need to calculate + variance. The difference between the two classes is that the first is used + for a mundane SELECT, while the latter is used in a GROUPing SELECT. +*/ +static void variance_fp_recurrence_next(double *m, double *s, ulonglong *count, double nr) +{ + *count += 1; + + if (*count == 1) + { + *m= nr; + *s= 0; + } + else + { + double m_kminusone= *m; + *m= m_kminusone + (nr - m_kminusone) / (double) *count; + *s= *s + (nr - m_kminusone) * (nr - *m); + } +} + + +static double variance_fp_recurrence_result(double s, ulonglong count, bool is_sample_variance) +{ + if (count == 1) + return 0.0; + + if (is_sample_variance) + return s / (count - 1); + + /* else, is a population variance */ + return s / count; +} + + +Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item): + Item_sum_num(thd, item), + count(item->count), sample(item->sample), + prec_increment(item->prec_increment) +{ + recurrence_m= item->recurrence_m; + recurrence_s= item->recurrence_s; +} + + +void Item_sum_variance::fix_length_and_dec_double() +{ + DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double); + decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS); +} + + +void Item_sum_variance::fix_length_and_dec_decimal() +{ + DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double); + int precision= args[0]->decimal_precision() * 2 + prec_increment; + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS - 1); + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); +} + + +void Item_sum_variance::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_variance::fix_length_and_dec"); + maybe_null= null_value= 1; + prec_increment= current_thd->variables.div_precincrement; + + /* + According to the SQL2003 standard (Part 2, Foundations; sec 10.9, + aggregate function; paragraph 7h of Syntax Rules), "the declared + type of the result is an implementation-defined aproximate numeric + type. + */ + + args[0]->type_handler()->Item_sum_variance_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int)decimals)); + DBUG_VOID_RETURN; +} + + +Item *Item_sum_variance::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_variance(thd, this); +} + + +/** + Create a new field to match the type of value we're expected to yield. + If we're grouping, then we need some space to serialize variables into, to + pass around. +*/ +Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table) +{ + Field *field; + if (group) + { + /* + We must store both value and counter in the temporary table in one field. + The easiest way is to do this is to store both value in a string + and unpack on access. + */ + field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, + &name, &my_charset_bin); + } + else + field= new Field_double(max_length, maybe_null, &name, decimals, + TRUE); + + if (field != NULL) + field->init(table); + + return field; +} + + +void Item_sum_variance::clear() +{ + count= 0; +} + +bool Item_sum_variance::add() +{ + /* + Why use a temporary variable? We don't know if it is null until we + evaluate it, which has the side-effect of setting null_value . + */ + double nr= args[0]->val_real(); + + if (!args[0]->null_value) + variance_fp_recurrence_next(&recurrence_m, &recurrence_s, &count, nr); + return 0; +} + +double Item_sum_variance::val_real() +{ + DBUG_ASSERT(fixed == 1); + + /* + 'sample' is a 1/0 boolean value. If it is 1/true, id est this is a sample + variance call, then we should set nullness when the count of the items + is one or zero. If it's zero, i.e. a population variance, then we only + set nullness when the count is zero. + + Another way to read it is that 'sample' is the numerical threshhold, at and + below which a 'count' number of items is called NULL. + */ + DBUG_ASSERT((sample == 0) || (sample == 1)); + if (count <= sample) + { + null_value=1; + return 0.0; + } + + null_value=0; + return variance_fp_recurrence_result(recurrence_s, count, sample); +} + + +my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf) +{ + DBUG_ASSERT(fixed == 1); + return val_decimal_from_real(dec_buf); +} + + +void Item_sum_variance::reset_field() +{ + double nr; + uchar *res= result_field->ptr; + + nr= args[0]->val_real(); /* sets null_value as side-effect */ + + if (args[0]->null_value) + bzero(res,sizeof(double)*2+sizeof(longlong)); + else + { + /* Serialize format is (double)m, (double)s, (longlong)count */ + ulonglong tmp_count; + double tmp_s; + float8store(res, nr); /* recurrence variable m */ + tmp_s= 0.0; + float8store(res + sizeof(double), tmp_s); + tmp_count= 1; + int8store(res + sizeof(double)*2, tmp_count); + } +} + + +void Item_sum_variance::update_field() +{ + ulonglong field_count; + uchar *res=result_field->ptr; + + double nr= args[0]->val_real(); /* sets null_value as side-effect */ + + if (args[0]->null_value) + return; + + /* Serialize format is (double)m, (double)s, (longlong)count */ + double field_recurrence_m, field_recurrence_s; + float8get(field_recurrence_m, res); + float8get(field_recurrence_s, res + sizeof(double)); + field_count=sint8korr(res+sizeof(double)*2); + + variance_fp_recurrence_next(&field_recurrence_m, &field_recurrence_s, &field_count, nr); + + float8store(res, field_recurrence_m); + float8store(res + sizeof(double), field_recurrence_s); + res+= sizeof(double)*2; + int8store(res,field_count); +} + + +Item *Item_sum_variance::result_item(THD *thd, Field *field) +{ + return new (thd->mem_root) Item_variance_field(thd, this); +} + +/* min & max */ + +void Item_sum_hybrid::clear() +{ + DBUG_ENTER("Item_sum_hybrid::clear"); + value->clear(); + null_value= 1; + DBUG_VOID_RETURN; +} + + +bool +Item_sum_hybrid::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + DBUG_ASSERT(fixed == 1); + if (null_value) + return true; + bool retval= value->get_date(ltime, fuzzydate); + if ((null_value= value->null_value)) + DBUG_ASSERT(retval == true); + return retval; +} + + +void Item_sum_hybrid::direct_add(Item *item) +{ + DBUG_ENTER("Item_sum_hybrid::direct_add"); + DBUG_PRINT("info", ("item: %p", item)); + direct_added= TRUE; + direct_item= item; + DBUG_VOID_RETURN; +} + + +double Item_sum_hybrid::val_real() +{ + DBUG_ENTER("Item_sum_hybrid::val_real"); + DBUG_ASSERT(fixed == 1); + if (null_value) + DBUG_RETURN(0.0); + double retval= value->val_real(); + if ((null_value= value->null_value)) + DBUG_ASSERT(retval == 0.0); + DBUG_RETURN(retval); +} + +longlong Item_sum_hybrid::val_int() +{ + DBUG_ENTER("Item_sum_hybrid::val_int"); + DBUG_ASSERT(fixed == 1); + if (null_value) + DBUG_RETURN(0); + longlong retval= value->val_int(); + if ((null_value= value->null_value)) + DBUG_ASSERT(retval == 0); + DBUG_RETURN(retval); +} + + +my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val) +{ + DBUG_ENTER("Item_sum_hybrid::val_decimal"); + DBUG_ASSERT(fixed == 1); + if (null_value) + DBUG_RETURN(0); + my_decimal *retval= value->val_decimal(val); + if ((null_value= value->null_value)) + DBUG_ASSERT(retval == NULL); + DBUG_RETURN(retval); +} + + +String * +Item_sum_hybrid::val_str(String *str) +{ + DBUG_ENTER("Item_sum_hybrid::val_str"); + DBUG_ASSERT(fixed == 1); + if (null_value) + DBUG_RETURN(0); + String *retval= value->val_str(str); + if ((null_value= value->null_value)) + DBUG_ASSERT(retval == NULL); + DBUG_RETURN(retval); +} + + +void Item_sum_hybrid::cleanup() +{ + DBUG_ENTER("Item_sum_hybrid::cleanup"); + Item_sum::cleanup(); + if (cmp) + delete cmp; + cmp= 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() +{ + DBUG_ENTER("Item_sum_hybrid::no_rows_in_result"); + /* We may be called here twice in case of ref field in function */ + if (was_values) + { + was_values= FALSE; + was_null_value= value->null_value; + clear(); + } + DBUG_VOID_RETURN; +} + +void Item_sum_hybrid::restore_to_before_no_rows_in_result() +{ + if (!was_values) + { + was_values= TRUE; + null_value= value->null_value= was_null_value; + } +} + + +Item *Item_sum_min::copy_or_same(THD* thd) +{ + DBUG_ENTER("Item_sum_min::copy_or_same"); + Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this); + item->setup_hybrid(thd, args[0], value); + DBUG_RETURN(item); +} + + +bool Item_sum_min::add() +{ + Item *tmp_item; + DBUG_ENTER("Item_sum_min::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); + /* args[0] < value */ + arg_cache->cache_value(); + if (!arg_cache->null_value && + (null_value || cmp->compare() < 0)) + { + value->store(arg_cache); + value->cache_value(); + null_value= 0; + } + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); +} + + +Item *Item_sum_max::copy_or_same(THD* thd) +{ + Item_sum_max *item= new (thd->mem_root) Item_sum_max(thd, this); + item->setup_hybrid(thd, args[0], value); + return item; +} + + +bool Item_sum_max::add() +{ + Item *tmp_item; + DBUG_ENTER("Item_sum_max::add"); + DBUG_PRINT("enter", ("this: %p", this)); + + if (unlikely(direct_added)) + { + /* Change to use direct_item */ + tmp_item= arg_cache->get_item(); + arg_cache->store(direct_item); + } + /* args[0] > value */ + arg_cache->cache_value(); + DBUG_PRINT("info", ("null_value: %s", null_value ? "TRUE" : "FALSE")); + if (!arg_cache->null_value && + (null_value || cmp->compare() > 0)) + { + value->store(arg_cache); + value->cache_value(); + null_value= 0; + } + if (unlikely(direct_added)) + { + /* Restore original item */ + direct_added= FALSE; + arg_cache->store(tmp_item); + } + DBUG_RETURN(0); +} + + +/* bit_or and bit_and */ + +longlong Item_sum_bit::val_int() +{ + DBUG_ASSERT(fixed == 1); + return (longlong) bits; +} + + +void Item_sum_bit::clear() +{ + bits= reset_bits; + if (as_window_function) + clear_as_window(); +} + +Item *Item_sum_or::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_or(thd, this); +} + +bool Item_sum_bit::clear_as_window() +{ + memset(bit_counters, 0, sizeof(bit_counters)); + num_values_added= 0; + set_bits_from_counters(); + return 0; +} + +bool Item_sum_bit::remove_as_window(ulonglong value) +{ + DBUG_ASSERT(as_window_function); + if (num_values_added == 0) + return 0; // Nothing to remove. + + for (int i= 0; i < NUM_BIT_COUNTERS; i++) + { + if (!bit_counters[i]) + { + // Don't attempt to remove values that were never added. + DBUG_ASSERT((value & (1ULL << i)) == 0); + continue; + } + bit_counters[i]-= (value & (1ULL << i)) ? 1 : 0; + } + + // Prevent overflow; + num_values_added = std::min(num_values_added, num_values_added - 1); + set_bits_from_counters(); + return 0; +} + +bool Item_sum_bit::add_as_window(ulonglong value) +{ + DBUG_ASSERT(as_window_function); + for (int i= 0; i < NUM_BIT_COUNTERS; i++) + { + bit_counters[i]+= (value & (1ULL << i)) ? 1 : 0; + } + // Prevent overflow; + num_values_added = std::max(num_values_added, num_values_added + 1); + set_bits_from_counters(); + return 0; +} + +void Item_sum_or::set_bits_from_counters() +{ + ulonglong value= 0; + for (int i= 0; i < NUM_BIT_COUNTERS; i++) + { + value|= bit_counters[i] > 0 ? (1 << i) : 0; + } + bits= value | reset_bits; +} + +bool Item_sum_or::add() +{ + ulonglong value= (ulonglong) args[0]->val_int(); + if (!args[0]->null_value) + { + if (as_window_function) + return add_as_window(value); + bits|=value; + } + return 0; +} + +void Item_sum_xor::set_bits_from_counters() +{ + ulonglong value= 0; + for (int i= 0; i < NUM_BIT_COUNTERS; i++) + { + value|= (bit_counters[i] % 2) ? (1 << i) : 0; + } + bits= value ^ reset_bits; +} + +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) + { + if (as_window_function) + return add_as_window(value); + bits^=value; + } + return 0; +} + +void Item_sum_and::set_bits_from_counters() +{ + ulonglong value= 0; + if (!num_values_added) + { + bits= reset_bits; + return; + } + + for (int i= 0; i < NUM_BIT_COUNTERS; i++) + { + // We've only added values of 1 for this bit. + if (bit_counters[i] == num_values_added) + value|= (1ULL << i); + } + bits= value & reset_bits; +} +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(); + if (!args[0]->null_value) + { + if (as_window_function) + return add_as_window(value); + bits&=value; + } + return 0; +} + +/************************************************************************ +** reset result of a Item_sum with is saved in a tmp_table +*************************************************************************/ + +void Item_sum_num::reset_field() +{ + double nr= args[0]->val_real(); + uchar *res=result_field->ptr; + + if (maybe_null) + { + if (args[0]->null_value) + { + nr=0.0; + result_field->set_null(); + } + else + result_field->set_notnull(); + } + float8store(res,nr); +} + + +void Item_sum_hybrid::reset_field() +{ + Item *tmp_item, *arg0; + DBUG_ENTER("Item_sum_hybrid::reset_field"); + + arg0= args[0]; + if (unlikely(direct_added)) + { + /* Switch to use direct item */ + tmp_item= value->get_item(); + value->store(direct_item); + arg0= direct_item; + } + + switch(result_type()) { + case STRING_RESULT: + { + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff),result_field->charset()),*res; + + res= arg0->val_str(&tmp); + if (arg0->null_value) + { + result_field->set_null(); + result_field->reset(); + } + else + { + result_field->set_notnull(); + result_field->store(res->ptr(),res->length(),tmp.charset()); + } + break; + } + case INT_RESULT: + { + longlong nr= arg0->val_int(); + + if (maybe_null) + { + if (arg0->null_value) + { + nr=0; + result_field->set_null(); + } + else + result_field->set_notnull(); + } + DBUG_PRINT("info", ("nr: %lld", nr)); + result_field->store(nr, unsigned_flag); + break; + } + case REAL_RESULT: + { + double nr= arg0->val_real(); + + if (maybe_null) + { + if (arg0->null_value) + { + nr=0.0; + result_field->set_null(); + } + else + result_field->set_notnull(); + } + result_field->store(nr); + break; + } + case DECIMAL_RESULT: + { + my_decimal value_buff, *arg_dec= arg0->val_decimal(&value_buff); + + if (maybe_null) + { + if (arg0->null_value) + result_field->set_null(); + else + result_field->set_notnull(); + } + /* + We must store zero in the field as we will use the field value in + add() + */ + if (!arg_dec) // Null + arg_dec= &decimal_zero; + result_field->store_decimal(arg_dec); + break; + } + case ROW_RESULT: + case TIME_RESULT: + DBUG_ASSERT(0); + } + + if (unlikely(direct_added)) + { + direct_added= FALSE; + value->store(tmp_item); + } + DBUG_VOID_RETURN; +} + + +void Item_sum_sum::reset_field() +{ + my_bool null_flag; + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + if (result_type() == DECIMAL_RESULT) + { + my_decimal value, *arg_val; + if (unlikely(direct_added)) + arg_val= &direct_sum_decimal; + else + { + if (!(arg_val= args[0]->val_decimal(&value))) + arg_val= &decimal_zero; // Null + } + result_field->store_decimal(arg_val); + } + else + { + DBUG_ASSERT(result_type() == REAL_RESULT); + double nr= likely(!direct_added) ? args[0]->val_real() : direct_sum_real; + float8store(result_field->ptr, nr); + } + + if (unlikely(direct_added)) + { + direct_added= FALSE; + direct_reseted_field= TRUE; + null_flag= direct_sum_is_null; + } + else + null_flag= args[0]->null_value; + + if (null_flag) + result_field->set_null(); + else + result_field->set_notnull(); +} + + +void Item_sum_count::reset_field() +{ + DBUG_ENTER("Item_sum_count::reset_field"); + uchar *res=result_field->ptr; + longlong nr=0; + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + + if (unlikely(direct_counted)) + { + nr= direct_count; + direct_counted= FALSE; + direct_reseted_field= TRUE; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) + nr= 1; + DBUG_PRINT("info", ("nr: %lld", nr)); + int8store(res,nr); + DBUG_VOID_RETURN; +} + + +void Item_sum_avg::reset_field() +{ + uchar *res=result_field->ptr; + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + if (result_type() == DECIMAL_RESULT) + { + longlong tmp; + my_decimal value, *arg_dec= args[0]->val_decimal(&value); + if (args[0]->null_value) + { + arg_dec= &decimal_zero; + tmp= 0; + } + else + tmp= 1; + my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, res, f_precision, f_scale); + res+= dec_bin_size; + int8store(res, tmp); + } + else + { + double nr= args[0]->val_real(); + + if (args[0]->null_value) + bzero(res,sizeof(double)+sizeof(longlong)); + else + { + longlong tmp= 1; + float8store(res,nr); + res+=sizeof(double); + int8store(res,tmp); + } + } +} + + +void Item_sum_bit::reset_field() +{ + reset_and_add(); + int8store(result_field->ptr, bits); +} + +void Item_sum_bit::update_field() +{ + // We never call update_field when computing the function as a window + // function. Setting bits to a random value invalidates the bits counters and + // the result of the bit function becomes erroneous. + DBUG_ASSERT(!as_window_function); + uchar *res=result_field->ptr; + bits= uint8korr(res); + add(); + int8store(res, bits); +} + + +/** + calc next value and merge it with field_value. +*/ + +void Item_sum_sum::update_field() +{ + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + if (result_type() == DECIMAL_RESULT) + { + my_decimal value, *arg_val; + my_bool null_flag; + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + arg_val= &direct_sum_decimal; + null_flag= direct_sum_is_null; + } + else + { + arg_val= args[0]->val_decimal(&value); + null_flag= args[0]->null_value; + } + + if (!null_flag) + { + if (!result_field->is_null()) + { + my_decimal field_value; + my_decimal *field_val= result_field->val_decimal(&field_value); + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val); + result_field->store_decimal(dec_buffs); + } + else + { + result_field->store_decimal(arg_val); + result_field->set_notnull(); + } + } + } + else + { + double old_nr,nr; + uchar *res= result_field->ptr; + my_bool null_flag; + + float8get(old_nr,res); + if (unlikely(direct_added || direct_reseted_field)) + { + direct_added= direct_reseted_field= FALSE; + null_flag= direct_sum_is_null; + nr= direct_sum_real; + } + else + { + nr= args[0]->val_real(); + null_flag= args[0]->null_value; + } + if (!null_flag) + { + old_nr+=nr; + result_field->set_notnull(); + } + float8store(res,old_nr); + } +} + + +void Item_sum_count::update_field() +{ + DBUG_ENTER("Item_sum_count::update_field"); + longlong nr; + uchar *res=result_field->ptr; + + nr=sint8korr(res); + if (unlikely(direct_counted || direct_reseted_field)) + { + direct_counted= direct_reseted_field= FALSE; + nr+= direct_count; + } + else if (!args[0]->maybe_null || !args[0]->is_null()) + nr++; + DBUG_PRINT("info", ("nr: %lld", nr)); + int8store(res,nr); + DBUG_VOID_RETURN; +} + + +void Item_sum_avg::update_field() +{ + longlong field_count; + uchar *res=result_field->ptr; + + DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); + + if (result_type() == DECIMAL_RESULT) + { + my_decimal value, *arg_val= args[0]->val_decimal(&value); + if (!args[0]->null_value) + { + binary2my_decimal(E_DEC_FATAL_ERROR, res, + dec_buffs + 1, f_precision, f_scale); + field_count= sint8korr(res + dec_bin_size); + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, dec_buffs + 1); + my_decimal2binary(E_DEC_FATAL_ERROR, dec_buffs, + res, f_precision, f_scale); + res+= dec_bin_size; + field_count++; + int8store(res, field_count); + } + } + else + { + double nr; + + nr= args[0]->val_real(); + if (!args[0]->null_value) + { + double old_nr; + float8get(old_nr, res); + field_count= sint8korr(res + sizeof(double)); + old_nr+= nr; + float8store(res,old_nr); + res+= sizeof(double); + field_count++; + int8store(res, field_count); + } + } +} + + +Item *Item_sum_avg::result_item(THD *thd, Field *field) +{ + return + result_type() == DECIMAL_RESULT ? + (Item_avg_field*) new (thd->mem_root) Item_avg_field_decimal(thd, this) : + (Item_avg_field*) new (thd->mem_root) Item_avg_field_double(thd, this); +} + + +void Item_sum_hybrid::update_field() +{ + DBUG_ENTER("Item_sum_hybrid::update_field"); + Item *tmp_item; + if (unlikely(direct_added)) + { + tmp_item= args[0]; + args[0]= direct_item; + } + switch (result_type()) { + case STRING_RESULT: + min_max_update_str_field(); + break; + case INT_RESULT: + min_max_update_int_field(); + break; + case DECIMAL_RESULT: + min_max_update_decimal_field(); + break; + default: + min_max_update_real_field(); + } + if (unlikely(direct_added)) + { + direct_added= FALSE; + args[0]= tmp_item; + } + DBUG_VOID_RETURN; +} + + +void +Item_sum_hybrid::min_max_update_str_field() +{ + DBUG_ENTER("Item_sum_hybrid::min_max_update_str_field"); + DBUG_ASSERT(cmp); + String *res_str=args[0]->val_str(&cmp->value1); + + if (!args[0]->null_value) + { + result_field->val_str(&cmp->value2); + + if (result_field->is_null() || + (cmp_sign * sortcmp(res_str,&cmp->value2,collation.collation)) < 0) + result_field->store(res_str->ptr(),res_str->length(),res_str->charset()); + result_field->set_notnull(); + } + DBUG_VOID_RETURN; +} + + +void +Item_sum_hybrid::min_max_update_real_field() +{ + double nr,old_nr; + + DBUG_ENTER("Item_sum_hybrid::min_max_update_real_field"); + old_nr=result_field->val_real(); + nr= args[0]->val_real(); + if (!args[0]->null_value) + { + if (result_field->is_null(0) || + (cmp_sign > 0 ? old_nr > nr : old_nr < nr)) + old_nr=nr; + result_field->set_notnull(); + } + else if (result_field->is_null(0)) + result_field->set_null(); + result_field->store(old_nr); + DBUG_VOID_RETURN; +} + + +void +Item_sum_hybrid::min_max_update_int_field() +{ + longlong nr,old_nr; + + DBUG_ENTER("Item_sum_hybrid::min_max_update_int_field"); + old_nr=result_field->val_int(); + nr=args[0]->val_int(); + if (!args[0]->null_value) + { + if (result_field->is_null(0)) + old_nr=nr; + else + { + bool res=(unsigned_flag ? + (ulonglong) old_nr > (ulonglong) nr : + old_nr > nr); + /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ + if ((cmp_sign > 0) ^ (!res)) + old_nr=nr; + } + result_field->set_notnull(); + } + else if (result_field->is_null(0)) + result_field->set_null(); + DBUG_PRINT("info", ("nr: %lld", old_nr)); + result_field->store(old_nr, unsigned_flag); + DBUG_VOID_RETURN; +} + + +/** + @todo + optimize: do not get result_field in case of args[0] is NULL +*/ +void +Item_sum_hybrid::min_max_update_decimal_field() +{ + DBUG_ENTER("Item_sum_hybrid::min_max_update_decimal_field"); + my_decimal old_val, nr_val; + const my_decimal *old_nr; + const my_decimal *nr= args[0]->val_decimal(&nr_val); + if (!args[0]->null_value) + { + if (result_field->is_null(0)) + old_nr=nr; + else + { + old_nr= result_field->val_decimal(&old_val); + bool res= my_decimal_cmp(old_nr, nr) > 0; + /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ + if ((cmp_sign > 0) ^ (!res)) + old_nr=nr; + } + result_field->set_notnull(); + result_field->store_decimal(old_nr); + } + else if (result_field->is_null(0)) + result_field->set_null(); + DBUG_VOID_RETURN; +} + + +double Item_avg_field_double::val_real() +{ + // fix_fields() never calls for this Item + double nr; + longlong count; + uchar *res; + + float8get(nr,field->ptr); + res= (field->ptr+sizeof(double)); + count= sint8korr(res); + + if ((null_value= !count)) + return 0.0; + return nr/(double) count; +} + + +my_decimal *Item_avg_field_decimal::val_decimal(my_decimal *dec_buf) +{ + // fix_fields() never calls for this Item + longlong count= sint8korr(field->ptr + dec_bin_size); + if ((null_value= !count)) + return 0; + + my_decimal dec_count, dec_field; + binary2my_decimal(E_DEC_FATAL_ERROR, + field->ptr, &dec_field, f_precision, f_scale); + int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count); + my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, + &dec_field, &dec_count, prec_increment); + return dec_buf; +} + + +double Item_std_field::val_real() +{ + double nr; + // fix_fields() never calls for this Item + nr= Item_variance_field::val_real(); + DBUG_ASSERT(nr >= 0.0); + return sqrt(nr); +} + + +double Item_variance_field::val_real() +{ + // fix_fields() never calls for this Item + double recurrence_s; + ulonglong count; + float8get(recurrence_s, (field->ptr + sizeof(double))); + count=sint8korr(field->ptr+sizeof(double)*2); + + if ((null_value= (count <= sample))) + return 0.0; + + return variance_fp_recurrence_result(recurrence_s, count, sample); +} + + +/**************************************************************************** +** Functions to handle dynamic loadable aggregates +** Original source by: Alexis Mikhailov +** Adapted for UDAs by: Andreas F. Bobak . +** Rewritten by: Monty. +****************************************************************************/ + +#ifdef HAVE_DLOPEN + +void Item_udf_sum::clear() +{ + DBUG_ENTER("Item_udf_sum::clear"); + udf.clear(); + DBUG_VOID_RETURN; +} + +bool Item_udf_sum::add() +{ + my_bool tmp_null_value; + DBUG_ENTER("Item_udf_sum::add"); + udf.add(&tmp_null_value); + null_value= tmp_null_value; + 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(); +} + + +void Item_udf_sum::print(String *str, enum_query_type query_type) +{ + str->append(func_name()); + str->append('('); + for (uint i=0 ; i < arg_count ; i++) + { + if (i) + str->append(','); + args[i]->print(str, query_type); + } + str->append(')'); +} + + +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_real() +{ + my_bool tmp_null_value; + double res; + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_sum_udf_float::val"); + DBUG_PRINT("enter",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + res= udf.val(&tmp_null_value); + null_value= tmp_null_value; + DBUG_RETURN(res); +} + + +String *Item_sum_udf_float::val_str(String *str) +{ + return val_string_from_real(str); +} + + +my_decimal *Item_sum_udf_float::val_decimal(my_decimal *dec) +{ + return val_decimal_from_real(dec); +} + + +String *Item_sum_udf_decimal::val_str(String *str) +{ + return val_string_from_decimal(str); +} + + +double Item_sum_udf_decimal::val_real() +{ + return val_real_from_decimal(); +} + + +longlong Item_sum_udf_decimal::val_int() +{ + return val_int_from_decimal(); +} + + +my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) +{ + my_decimal *res; + my_bool tmp_null_value; + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_func_udf_decimal::val_decimal"); + DBUG_PRINT("enter",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + + res= udf.val_decimal(&tmp_null_value, dec_buf); + null_value= tmp_null_value; + DBUG_RETURN(res); +} + + +Item *Item_sum_udf_decimal::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_udf_decimal(thd, this); +} + + +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() +{ + my_bool tmp_null_value; + longlong res; + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_sum_udf_int::val_int"); + DBUG_PRINT("enter",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + res= udf.val_int(&tmp_null_value); + null_value= tmp_null_value; + DBUG_RETURN(res); +} + + +String *Item_sum_udf_int::val_str(String *str) +{ + return val_string_from_int(str); +} + +my_decimal *Item_sum_udf_int::val_decimal(my_decimal *dec) +{ + return val_decimal_from_int(dec); +} + + +/** Default max_length is max argument length. */ + +void Item_sum_udf_str::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_udf_str::fix_length_and_dec"); + max_length=0; + for (uint i = 0; i < arg_count; i++) + set_if_bigger(max_length,args[i]->max_length); + DBUG_VOID_RETURN; +} + + +Item *Item_sum_udf_str::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_udf_str(thd, this); +} + + +my_decimal *Item_sum_udf_str::val_decimal(my_decimal *dec) +{ + return val_decimal_from_string(dec); +} + +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; + DBUG_RETURN(res); +} + +#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 + Blobs doesn't work with DISTINCT or ORDER BY +*****************************************************************************/ + + + +/** + Compares the values for fields in expr list of GROUP_CONCAT. + @note + + GROUP_CONCAT([DISTINCT] expr [,expr ...] + [ORDER BY {unsigned_integer | col_name | expr} + [ASC | DESC] [,col_name ...]] + [SEPARATOR str_val]) + + @return + @retval -1 : key1 < key2 + @retval 0 : key1 = key2 + @retval 1 : key1 > key2 +*/ + +extern "C" +int group_concat_key_cmp_with_distinct(void* arg, const void* key1, + const void* key2) +{ + Item_func_group_concat *item_func= (Item_func_group_concat*)arg; + + for (uint i= 0; i < item_func->arg_count_field; i++) + { + Item *item= item_func->args[i]; + /* + If item is a const item then either get_tmp_table_field returns 0 + or it is an item over a const table. + */ + if (item->const_item()) + continue; + /* + 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 (!field) + continue; + + uint offset= (field->offset(field->table->record[0]) - + field->table->s->null_bytes); + int res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset); + if (res) + return res; + } + return 0; +} + + +/** + function of sort for syntax: GROUP_CONCAT(expr,... ORDER BY col,... ) +*/ + +extern "C" +int group_concat_key_cmp_with_order(void* arg, const void* key1, + const void* 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; + /* + If field_item is a const item then either get_tmp_table_field returns 0 + or it is an item over a const table. + */ + if (item->const_item()) + continue; + /* + If item is a const item then either get_tmp_table_field returns 0 + or it is an item over a const table. + */ + if (item->const_item()) + continue; + /* + 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 + + Note that for the case of ROLLUP, field may point to another table + tham grp_item->table. This is however ok as the table definitions are + the same. + */ + Field *field= item->get_tmp_table_field(); + if (!field) + continue; + + uint offset= (field->offset(field->table->record[0]) - + field->table->s->null_bytes); + int res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset); + if (res) + return ((*order_item)->direction == ORDER::ORDER_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 returned values are not the same we do the sort on. + */ + return 1; +} + + +/** + Append data from current leaf to item->result. +*/ + +extern "C" +int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), + void* item_arg) +{ + Item_func_group_concat *item= (Item_func_group_concat *) item_arg; + TABLE *table= item->table; + uint max_length= (uint)table->in_use->variables.group_concat_max_len; + String tmp((char *)table->record[1], table->s->reclength, + default_charset_info); + String tmp2; + uchar *key= (uchar *) key_arg; + String *result= &item->result; + Item **arg= item->args, **arg_end= item->args + item->arg_count_field; + uint old_length= result->length(); + + if (item->no_appended) + item->no_appended= FALSE; + else + result->append(*item->separator); + + tmp.length(0); + + for (; arg < arg_end; arg++) + { + String *res; + /* + 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 + We also can't use table->field array to access the fields + because it contains both order and arg list fields. + */ + if ((*arg)->const_item()) + res= (*arg)->val_str(&tmp); + else + { + Field *field= (*arg)->get_tmp_table_field(); + if (field) + { + uint offset= (field->offset(field->table->record[0]) - + table->s->null_bytes); + DBUG_ASSERT(offset < table->s->reclength); + res= field->val_str(&tmp, key + offset); + } + else + res= (*arg)->val_str(&tmp); + } + if (res) + result->append(*res); + } + + item->row_count++; + + /* stop if length of result more than max_length */ + if (result->length() > max_length) + { + CHARSET_INFO *cs= item->collation.collation; + const char *ptr= result->ptr(); + THD *thd= current_thd; + /* + It's ok to use item->result.length() as the fourth argument + as this is never used to limit the length of the data. + Cut is done with the third argument. + */ + uint add_length= Well_formed_prefix(cs, + ptr + old_length, + ptr + max_length, + result->length()).length(); + result->length(old_length + add_length); + item->warning_for_row= TRUE; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_CUT_VALUE_GROUP_CONCAT, + ER_THD(thd, ER_CUT_VALUE_GROUP_CONCAT), + item->row_count); + + /** + To avoid duplicated warnings in Item_func_group_concat::val_str() + */ + if (table && table->blob_storage) + table->blob_storage->set_truncated_value(false); + return 1; + } + return 0; +} + + +/** + Constructor of Item_func_group_concat. + + @param distinct_arg distinct + @param select_list list of expression for show values + @param order_list list of sort columns + @param separator_arg string value of separator. +*/ + +Item_func_group_concat:: +Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, + bool distinct_arg, List *select_list, + const SQL_I_List &order_list, + String *separator_arg) + :Item_sum(thd), tmp_table_param(0), separator(separator_arg), tree(0), + unique_filter(NULL), table(0), + order(0), context(context_arg), + arg_count_order(order_list.elements), + arg_count_field(select_list->elements), + row_count(0), + distinct(distinct_arg), + warning_for_row(FALSE), + force_copy_fields(0), original(0) +{ + Item *item_select; + Item **arg_ptr; + + quick_group= FALSE; + arg_count= arg_count_field + arg_count_order; + + /* + We need to allocate: + args - arg_count_field+arg_count_order + (for possible order items in temporary tables) + order - arg_count_order + */ + if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count * 2 + + sizeof(ORDER*)*arg_count_order))) + return; + + order= (ORDER**)(args + arg_count); + + /* fill args items of show and sort */ + List_iterator_fast li(*select_list); + + 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_list.first; + order_item != NULL; + order_item= order_item->next) + { + (*order_ptr++)= order_item; + *arg_ptr= *order_item->item; + order_item->item= arg_ptr++; + } + } + + /* orig_args is only used for print() */ + orig_args= (Item**) (order + arg_count_order); + memcpy(orig_args, args, sizeof(Item*) * arg_count); +} + + +Item_func_group_concat::Item_func_group_concat(THD *thd, + Item_func_group_concat *item) + :Item_sum(thd, item), + tmp_table_param(item->tmp_table_param), + separator(item->separator), + tree(item->tree), + unique_filter(item->unique_filter), + table(item->table), + context(item->context), + arg_count_order(item->arg_count_order), + arg_count_field(item->arg_count_field), + row_count(item->row_count), + distinct(item->distinct), + warning_for_row(item->warning_for_row), + always_null(item->always_null), + force_copy_fields(item->force_copy_fields), + original(item) +{ + quick_group= item->quick_group; + result.set_charset(collation.collation); + + /* + Since the ORDER structures pointed to by the elements of the 'order' array + may be modified in find_order_in_list() called from + Item_func_group_concat::setup(), create a copy of those structures so that + such modifications done in this object would not have any effect on the + object being copied. + */ + ORDER *tmp; + if (!(tmp= (ORDER *) thd->alloc(sizeof(ORDER *) * arg_count_order + + sizeof(ORDER) * arg_count_order))) + return; + order= (ORDER **)(tmp + arg_count_order); + for (uint i= 0; i < arg_count_order; i++, tmp++) + { + /* + Compiler generated copy constructor is used to + to copy all the members of ORDER struct. + It's also necessary to update ORDER::next pointer + so that it points to new ORDER element. + */ + new (tmp) st_order(*(item->order[i])); + tmp->next= (i + 1 == arg_count_order) ? NULL : (tmp + 1); + order[i]= tmp; + } +} + + +void Item_func_group_concat::cleanup() +{ + DBUG_ENTER("Item_func_group_concat::cleanup"); + Item_sum::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) + { + delete tmp_table_param; + tmp_table_param= 0; + if (table) + { + THD *thd= table->in_use; + if (table->blob_storage) + delete table->blob_storage; + free_tmp_table(thd, table); + table= 0; + if (tree) + { + delete_tree(tree, 0); + tree= 0; + } + if (unique_filter) + { + delete unique_filter; + unique_filter= NULL; + } + } + DBUG_ASSERT(tree == 0); + } + /* + As the ORDER structures pointed to by the elements of the + 'order' array may be modified in find_order_in_list() called + from Item_func_group_concat::setup() to point to runtime + created objects, we need to reset them back to the original + arguments of the function. + */ + ORDER **order_ptr= order; + for (uint i= 0; i < arg_count_order; i++) + { + (*order_ptr)->item= &args[arg_count_field + i]; + order_ptr++; + } + DBUG_VOID_RETURN; +} + + +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) + reset_tree(tree); + if (unique_filter) + unique_filter->reset(); + if (table && table->blob_storage) + table->blob_storage->reset(); + /* No need to reset the table as we never call write_row */ +} + + +bool Item_func_group_concat::add() +{ + if (always_null) + return 0; + copy_fields(tmp_table_param); + if (copy_funcs(tmp_table_param->items_to_copy, table->in_use)) + return TRUE; + + for (uint i= 0; i < arg_count_field; i++) + { + Item *show_item= args[i]; + if (show_item->const_item()) + continue; + + Field *field= show_item->get_tmp_table_field(); + if (field && field->is_null_in_record((const uchar*) table->record[0])) + return 0; // Skip row if it contains null + } + + null_value= FALSE; + bool row_eligible= TRUE; + + if (distinct) + { + /* Filter out duplicate rows. */ + uint count= unique_filter->elements_in_tree(); + unique_filter->unique_add(table->record[0] + table->s->null_bytes); + if (count == unique_filter->elements_in_tree()) + row_eligible= FALSE; + } + + TREE_ELEMENT *el= 0; // Only for safety + if (row_eligible && tree) + { + el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0, + tree->custom_arg); + /* check if there was enough memory to insert the row */ + if (!el) + return 1; + } + /* + 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 (row_eligible && !warning_for_row && + (!tree || (el->count == 1 && distinct && !arg_count_order))) + dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this); + + return 0; +} + + +bool +Item_func_group_concat::fix_fields(THD *thd, Item **ref) +{ + uint i; /* for loop variable */ + DBUG_ASSERT(fixed == 0); + + if (init_sum_func_check(thd)) + return TRUE; + + maybe_null= 1; + + /* + Fix fields for select list and ORDER clause + */ + + for (i=0 ; i < arg_count ; i++) + { + if ((!args[i]->fixed && + args[i]->fix_fields(thd, args + i)) || + args[i]->check_cols(1)) + return TRUE; + m_with_subquery|= args[i]->with_subquery(); + with_window_func|= args[i]->with_window_func; + } + + /* skip charset aggregation for order columns */ + if (agg_arg_charsets_for_string_result(collation, + args, arg_count - arg_count_order)) + return 1; + + result.set_charset(collation.collation); + result_field= 0; + null_value= 1; + max_length= (uint32)(thd->variables.group_concat_max_len + / collation.collation->mbminlen + * collation.collation->mbmaxlen); + + uint32 offset; + if (separator->needs_conversion(separator->length(), separator->charset(), + collation.collation, &offset)) + { + uint32 buflen= collation.collation->mbmaxlen * separator->length(); + uint errors, conv_length; + char *buf; + String *new_separator; + + if (!(buf= (char*) thd->stmt_arena->alloc(buflen)) || + !(new_separator= new(thd->stmt_arena->mem_root) + String(buf, buflen, collation.collation))) + return TRUE; + + conv_length= copy_and_convert(buf, buflen, collation.collation, + separator->ptr(), separator->length(), + separator->charset(), &errors); + new_separator->length(conv_length); + separator= new_separator; + } + + if (check_sum_func(thd, ref)) + return TRUE; + + fixed= 1; + return FALSE; +} + + +bool Item_func_group_concat::setup(THD *thd) +{ + List list; + SELECT_LEX *select_lex= thd->lex->current_select; + const bool order_or_distinct= MY_TEST(arg_count_order > 0 || distinct); + DBUG_ENTER("Item_func_group_concat::setup"); + + /* + Currently setup() can be called twice. Please add + assertion here when this is fixed. + */ + if (table || tree) + DBUG_RETURN(FALSE); + + if (!(tmp_table_param= new TMP_TABLE_PARAM)) + DBUG_RETURN(TRUE); + + /* Push all not constant fields to the list and create a temp table */ + always_null= 0; + for (uint i= 0; i < arg_count_field; i++) + { + Item *item= args[i]; + if (list.push_back(item, thd->mem_root)) + DBUG_RETURN(TRUE); + if (item->const_item()) + { + if (item->is_null()) + { + always_null= 1; + DBUG_RETURN(FALSE); + } + } + } + + List all_fields(list); + /* + Try to find every ORDER expression in the list of GROUP_CONCAT + arguments. If an expression is not found, prepend it to + "all_fields". The resulting field list is used as input to create + tmp table columns. + */ + if (arg_count_order) + { + uint n_elems= arg_count_order + all_fields.elements; + ref_pointer_array= static_cast(thd->alloc(sizeof(Item*) * n_elems)); + if (!ref_pointer_array) + DBUG_RETURN(TRUE); + memcpy(ref_pointer_array, args, arg_count * sizeof(Item*)); + if (setup_order(thd, Ref_ptr_array(ref_pointer_array, n_elems), + context->table_list, list, all_fields, *order)) + DBUG_RETURN(TRUE); + } + + count_field_types(select_lex, tmp_table_param, all_fields, 0); + tmp_table_param->force_copy_fields= force_copy_fields; + DBUG_ASSERT(table == 0); + if (order_or_distinct) + { + /* + Force the create_tmp_table() to convert BIT columns to INT + as we cannot compare two table records containg BIT fields + stored in the the tree used for distinct/order by. + Moreover we don't even save in the tree record null bits + where BIT fields store parts of their data. + */ + List_iterator_fast li(all_fields); + Item *item; + while ((item= li++)) + { + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field->type() == FIELD_TYPE_BIT) + item->marker= 4; + } + } + + /* + 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. + */ + if (!(table= create_tmp_table(thd, tmp_table_param, all_fields, + (ORDER*) 0, 0, TRUE, + (select_lex->options | + thd->variables.option_bits), + HA_POS_ERROR, (char*) ""))) + DBUG_RETURN(TRUE); + table->file->extra(HA_EXTRA_NO_ROWS); + table->no_rows= 1; + + /** + Initialize blob_storage if GROUP_CONCAT is used + with ORDER BY | DISTINCT and BLOB field count > 0. + */ + if (order_or_distinct && table->s->blob_fields) + table->blob_storage= new Blob_mem_storage(); + + /* + Need sorting or uniqueness: init tree and choose a function to sort. + Don't reserve space for NULLs: if any of gconcat arguments is NULL, + the row is not added to the result. + */ + uint tree_key_length= table->s->reclength - table->s->null_bytes; + + if (arg_count_order) + { + tree= &tree_base; + /* + Create a tree for sorting. The tree is used to sort (according to the + syntax of this function). If there is no ORDER BY clause, we don't + create this tree. + */ + init_tree(tree, (size_t)MY_MIN(thd->variables.max_heap_table_size, + thd->variables.sortbuff_size/16), 0, + tree_key_length, + group_concat_key_cmp_with_order, NULL, (void*) this, + MYF(MY_THREAD_SPECIFIC)); + } + + if (distinct) + unique_filter= new Unique(group_concat_key_cmp_with_distinct, + (void*)this, + tree_key_length, + ram_limitation(thd)); + + DBUG_RETURN(FALSE); +} + + +/* This is used by rollup to create a separate usable copy of the function */ + +void Item_func_group_concat::make_unique() +{ + tmp_table_param= 0; + table=0; + original= 0; + force_copy_fields= 1; + tree= 0; +} + + +String* Item_func_group_concat::val_str(String* str) +{ + DBUG_ASSERT(fixed == 1); + if (null_value) + return 0; + if (no_appended && tree) + /* Tree is used for sorting as in ORDER BY */ + tree_walk(tree, &dump_leaf_key, this, left_root_right); + + if (table && table->blob_storage && + table->blob_storage->is_truncated_value()) + { + warning_for_row= true; + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT), + row_count); + } + + return &result; +} + + +void Item_func_group_concat::print(String *str, enum_query_type query_type) +{ + str->append(STRING_WITH_LEN("group_concat(")); + if (distinct) + str->append(STRING_WITH_LEN("distinct ")); + for (uint i= 0; i < arg_count_field; i++) + { + if (i) + str->append(','); + orig_args[i]->print(str, query_type); + } + if (arg_count_order) + { + str->append(STRING_WITH_LEN(" order by ")); + for (uint i= 0 ; i < arg_count_order ; i++) + { + if (i) + str->append(','); + orig_args[i + arg_count_field]->print(str, query_type); + if (order[i]->direction == ORDER::ORDER_ASC) + str->append(STRING_WITH_LEN(" ASC")); + else + str->append(STRING_WITH_LEN(" DESC")); + } + } + str->append(STRING_WITH_LEN(" separator \'")); + str->append(*separator); + str->append(STRING_WITH_LEN("\')")); +} + + +Item_func_group_concat::~Item_func_group_concat() +{ + if (!original && unique_filter) + delete unique_filter; +} diff --git a/sql/item_sum.h b/sql/item_sum.h index fd6b20e2b81..cbf245f3cd3 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1747,6 +1747,16 @@ class Item_func_group_concat : public Item_sum bool always_null; bool force_copy_fields; bool no_appended; + /** Limits the rows in the result */ + Item *row_limit; + /** Skips a particular number of rows in from the result*/ + Item *offset_limit; + bool limit_clause; + /* copy of the offset limit */ + ulonglong copy_offset_limit; + /*copy of the row limit */ + ulonglong copy_row_limit; + /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -1763,7 +1773,8 @@ class Item_func_group_concat : public Item_sum public: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool is_distinct, List *is_select, - const SQL_I_List &is_order, String *is_separator); + const SQL_I_List &is_order, String *is_separator, + bool limit_clause, Item *row_limit, Item *offset_limit); Item_func_group_concat(THD *thd, Item_func_group_concat *item); ~Item_func_group_concat(); diff --git a/sql/item_sum.h.orig b/sql/item_sum.h.orig new file mode 100644 index 00000000000..fd6b20e2b81 --- /dev/null +++ b/sql/item_sum.h.orig @@ -0,0 +1,1821 @@ +#ifndef ITEM_SUM_INCLUDED +#define ITEM_SUM_INCLUDED +/* Copyright (c) 2000, 2013 Oracle and/or its affiliates. + Copyright (c) 2008, 2013 Monty Program Ab. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +/* classes for sum functions */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#include +#include "sql_udf.h" /* udf_handler */ + +class Item_sum; +class Aggregator_distinct; +class Aggregator_simple; + +/** + The abstract base class for the Aggregator_* classes. + It implements the data collection functions (setup/add/clear) + as either pass-through to the real functionality or + as collectors into an Unique (for distinct) structure. + + Note that update_field/reset_field are not in that + class, because they're simply not called when + GROUP BY/DISTINCT can be handled with help of index on grouped + fields (quick_group = 0); +*/ + +class Aggregator : public Sql_alloc +{ + friend class Item_sum; + friend class Item_sum_sum; + friend class Item_sum_count; + friend class Item_sum_avg; + + /* + All members are protected as this class is not usable outside of an + Item_sum descendant. + */ +protected: + /* the aggregate function class to act on */ + Item_sum *item_sum; + +public: + Aggregator (Item_sum *arg): item_sum(arg) {} + virtual ~Aggregator () {} /* Keep gcc happy */ + + enum Aggregator_type { SIMPLE_AGGREGATOR, DISTINCT_AGGREGATOR }; + virtual Aggregator_type Aggrtype() = 0; + + /** + Called before adding the first row. + Allocates and sets up the internal aggregation structures used, + e.g. the Unique instance used to calculate distinct. + */ + virtual bool setup(THD *) = 0; + + /** + Called when we need to wipe out all the data from the aggregator : + all the values acumulated and all the state. + Cleans up the internal structures and resets them to their initial state. + */ + virtual void clear() = 0; + + /** + Called when there's a new value to be aggregated. + Updates the internal state of the aggregator to reflect the new value. + */ + virtual bool add() = 0; + + /** + Called when there are no more data and the final value is to be retrieved. + Finalises the state of the aggregator, so the final result can be retrieved. + */ + virtual void endup() = 0; + + /** Decimal value of being-aggregated argument */ + virtual my_decimal *arg_val_decimal(my_decimal * value) = 0; + /** Floating point value of being-aggregated argument */ + virtual double arg_val_real() = 0; + /** + NULLness of being-aggregated argument. + + @param use_null_value Optimization: to determine if the argument is NULL + we must, in the general case, call is_null() on it, which itself might + call val_*() on it, which might be costly. If you just have called + arg_val*(), you can pass use_null_value=true; this way, arg_is_null() + might avoid is_null() and instead do a cheap read of the Item's null_value + (updated by arg_val*()). + */ + virtual bool arg_is_null(bool use_null_value) = 0; +}; + + +class st_select_lex; +class Window_spec; + +/** + Class Item_sum is the base class used for special expressions that SQL calls + 'set functions'. These expressions are formed with the help of aggregate + functions such as SUM, MAX, GROUP_CONCAT etc. + + GENERAL NOTES + + A set function cannot be used in certain positions where expressions are + accepted. There are some quite explicable restrictions for the usage of + set functions. + + In the query: + SELECT AVG(b) FROM t1 WHERE SUM(b) > 20 GROUP by a + the usage of the set function AVG(b) is legal, while the usage of SUM(b) + is illegal. A WHERE condition must contain expressions that can be + evaluated for each row of the table. Yet the expression SUM(b) can be + evaluated only for each group of rows with the same value of column a. + In the query: + SELECT AVG(b) FROM t1 WHERE c > 30 GROUP BY a HAVING SUM(b) > 20 + both set function expressions AVG(b) and SUM(b) are legal. + + We can say that in a query without nested selects an occurrence of a + set function in an expression of the SELECT list or/and in the HAVING + clause is legal, while in the WHERE clause it's illegal. + + The general rule to detect whether a set function is legal in a query with + nested subqueries is much more complicated. + + Consider the the following query: + SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL (SELECT t2.c FROM t2 WHERE SUM(t1.b) < t2.c). + The set function SUM(b) is used here in the WHERE clause of the subquery. + Nevertheless it is legal since it is under the HAVING clause of the query + to which this function relates. The expression SUM(t1.b) is evaluated + for each group defined in the main query, not for groups of the subquery. + + The problem of finding the query where to aggregate a particular + set function is not so simple as it seems to be. + + In the query: + SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL(SELECT t2.c FROM t2 GROUP BY t2.c + HAVING SUM(t1.a) < t2.c) + the set function can be evaluated for both outer and inner selects. + If we evaluate SUM(t1.a) for the outer query then we get the value of t1.a + multiplied by the cardinality of a group in table t1. In this case + in each correlated subquery SUM(t1.a) is used as a constant. But we also + can evaluate SUM(t1.a) for the inner query. In this case t1.a will be a + constant for each correlated subquery and summation is performed + for each group of table t2. + (Here it makes sense to remind that the query + SELECT c FROM t GROUP BY a HAVING SUM(1) < a + is quite legal in our SQL). + + So depending on what query we assign the set function to we + can get different result sets. + + The general rule to detect the query where a set function is to be + evaluated can be formulated as follows. + Consider a set function S(E) where E is an expression with occurrences + of column references C1, ..., CN. Resolve these column references against + subqueries that contain the set function S(E). Let Q be the innermost + subquery of those subqueries. (It should be noted here that S(E) + in no way can be evaluated in the subquery embedding the subquery Q, + otherwise S(E) would refer to at least one unbound column reference) + If S(E) is used in a construct of Q where set functions are allowed then + we evaluate S(E) in Q. + Otherwise we look for a innermost subquery containing S(E) of those where + usage of S(E) is allowed. + + Let's demonstrate how this rule is applied to the following queries. + + 1. SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL(SELECT t2.b FROM t2 GROUP BY t2.b + HAVING t2.b > ALL(SELECT t3.c FROM t3 GROUP BY t3.c + HAVING SUM(t1.a+t2.b) < t3.c)) + For this query the set function SUM(t1.a+t2.b) depends on t1.a and t2.b + with t1.a defined in the outermost query, and t2.b defined for its + subquery. The set function is in the HAVING clause of the subquery and can + be evaluated in this subquery. + + 2. SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL(SELECT t2.b FROM t2 + WHERE t2.b > ALL (SELECT t3.c FROM t3 GROUP BY t3.c + HAVING SUM(t1.a+t2.b) < t3.c)) + Here the set function SUM(t1.a+t2.b)is in the WHERE clause of the second + subquery - the most upper subquery where t1.a and t2.b are defined. + If we evaluate the function in this subquery we violate the context rules. + So we evaluate the function in the third subquery (over table t3) where it + is used under the HAVING clause. + + 3. SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL(SELECT t2.b FROM t2 + WHERE t2.b > ALL (SELECT t3.c FROM t3 + WHERE SUM(t1.a+t2.b) < t3.c)) + In this query evaluation of SUM(t1.a+t2.b) is not legal neither in the second + nor in the third subqueries. So this query is invalid. + + Mostly set functions cannot be nested. In the query + SELECT t1.a from t1 GROUP BY t1.a HAVING AVG(SUM(t1.b)) > 20 + the expression SUM(b) is not acceptable, though it is under a HAVING clause. + Yet it is acceptable in the query: + SELECT t.1 FROM t1 GROUP BY t1.a HAVING SUM(t1.b) > 20. + + An argument of a set function does not have to be a reference to a table + column as we saw it in examples above. This can be a more complex expression + SELECT t1.a FROM t1 GROUP BY t1.a HAVING SUM(t1.b+1) > 20. + The expression SUM(t1.b+1) has a very clear semantics in this context: + we sum up the values of t1.b+1 where t1.b varies for all values within a + group of rows that contain the same t1.a value. + + A set function for an outer query yields a constant within a subquery. So + the semantics of the query + SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a IN (SELECT t2.c FROM t2 GROUP BY t2.c + HAVING AVG(t2.c+SUM(t1.b)) > 20) + is still clear. For a group of the rows with the same t1.a values we + calculate the value of SUM(t1.b). This value 's' is substituted in the + the subquery: + SELECT t2.c FROM t2 GROUP BY t2.c HAVING AVG(t2.c+s) + than returns some result set. + + By the same reason the following query with a subquery + SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a IN (SELECT t2.c FROM t2 GROUP BY t2.c + HAVING AVG(SUM(t1.b)) > 20) + is also acceptable. + + IMPLEMENTATION NOTES + + Three methods were added to the class to check the constraints specified + in the previous section. These methods utilize several new members. + + The field 'nest_level' contains the number of the level for the subquery + containing the set function. The main SELECT is of level 0, its subqueries + are of levels 1, the subqueries of the latter are of level 2 and so on. + + The field 'aggr_level' is to contain the nest level of the subquery + where the set function is aggregated. + + The field 'max_arg_level' is for the maximun of the nest levels of the + unbound column references occurred in the set function. A column reference + is unbound within a set function if it is not bound by any subquery + used as a subexpression in this function. A column reference is bound by + a subquery if it is a reference to the column by which the aggregation + of some set function that is used in the subquery is calculated. + For the set function used in the query + SELECT t1.a FROM t1 GROUP BY t1.a + HAVING t1.a > ALL(SELECT t2.b FROM t2 GROUP BY t2.b + HAVING t2.b > ALL(SELECT t3.c FROM t3 GROUP BY t3.c + HAVING SUM(t1.a+t2.b) < t3.c)) + the value of max_arg_level is equal to 1 since t1.a is bound in the main + query, and t2.b is bound by the first subquery whose nest level is 1. + Obviously a set function cannot be aggregated in the subquery whose + nest level is less than max_arg_level. (Yet it can be aggregated in the + subqueries whose nest level is greater than max_arg_level.) + In the query + SELECT t.a FROM t1 HAVING AVG(t1.a+(SELECT MIN(t2.c) FROM t2)) + the value of the max_arg_level for the AVG set function is 0 since + the reference t2.c is bound in the subquery. + + The field 'max_sum_func_level' is to contain the maximum of the + nest levels of the set functions that are used as subexpressions of + the arguments of the given set function, but not aggregated in any + subquery within this set function. A nested set function s1 can be + used within set function s0 only if s1.max_sum_func_level < + s0.max_sum_func_level. Set function s1 is considered as nested + for set function s0 if s1 is not calculated in any subquery + within s0. + + A set function that is used as a subexpression in an argument of another + set function refers to the latter via the field 'in_sum_func'. + + The condition imposed on the usage of set functions are checked when + we traverse query subexpressions with the help of the recursive method + fix_fields. When we apply this method to an object of the class + Item_sum, first, on the descent, we call the method init_sum_func_check + that initialize members used at checking. Then, on the ascent, we + call the method check_sum_func that validates the set function usage + and reports an error if it is illegal. + The method register_sum_func serves to link the items for the set functions + that are aggregated in the embedding (sub)queries. Circular chains of such + functions are attached to the corresponding st_select_lex structures + through the field inner_sum_func_list. + + Exploiting the fact that the members mentioned above are used in one + recursive function we could have allocated them on the thread stack. + Yet we don't do it now. + + We assume that the nesting level of subquries does not exceed 127. + TODO: to catch queries where the limit is exceeded to make the + code clean here. + + @note + The implementation takes into account the used strategy: + - Items resolved at optimization phase return 0 from Item_sum::used_tables(). + - Items that depend on the number of join output records, but not columns of + any particular table (like COUNT(*)), returm 0 from Item_sum::used_tables(), + but still return false from Item_sum::const_item(). +*/ + +class Item_sum :public Item_func_or_sum +{ + friend class Aggregator_distinct; + friend class Aggregator_simple; + +protected: + /** + Aggregator class instance. Not set initially. Allocated only after + it is determined if the incoming data are already distinct. + */ + Aggregator *aggr; + +private: + /** + Used in making ROLLUP. Set for the ROLLUP copies of the original + Item_sum and passed to create_tmp_field() to cause it to work + over the temp table buffer that is referenced by + Item_result_field::result_field. + */ + bool force_copy_fields; + + /** + Indicates how the aggregate function was specified by the parser : + 1 if it was written as AGGREGATE(DISTINCT), + 0 if it was AGGREGATE() + */ + bool with_distinct; + + /* TRUE if this is aggregate function of a window function */ + bool window_func_sum_expr_flag; + +public: + + bool has_force_copy_fields() const { return force_copy_fields; } + bool has_with_distinct() const { return with_distinct; } + + enum Sumfunctype + { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC, + AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC, + VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC, + ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC, + CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC, + NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC, PERCENTILE_CONT_FUNC, + PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC + }; + + Item **ref_by; /* pointer to a ref to the object used to register it */ + Item_sum *next; /* next in the circular chain of registered objects */ + Item_sum *in_sum_func; /* embedding set function if any */ + st_select_lex * aggr_sel; /* select where the function is aggregated */ + int8 nest_level; /* number of the nesting level of the set function */ + int8 aggr_level; /* nesting level of the aggregating subquery */ + int8 max_arg_level; /* max level of unbound column references */ + int8 max_sum_func_level;/* max level of aggregation for embedded functions */ + bool quick_group; /* If incremental update of fields */ + /* + This list is used by the check for mixing non aggregated fields and + sum functions in the ONLY_FULL_GROUP_BY_MODE. We save all outer fields + directly or indirectly used under this function it as it's unclear + at the moment of fixing outer field whether it's aggregated or not. + */ + List outer_fields; + +protected: + /* + Copy of the arguments list to hold the original set of arguments. + Used in EXPLAIN EXTENDED instead of the current argument list because + the current argument list can be altered by usage of temporary tables. + */ + Item **orig_args, *tmp_orig_args[2]; + + static size_t ram_limitation(THD *thd); + +public: + + void mark_as_sum_func(); + Item_sum(THD *thd): Item_func_or_sum(thd), quick_group(1) + { + mark_as_sum_func(); + init_aggregator(); + } + Item_sum(THD *thd, Item *a): Item_func_or_sum(thd, a), quick_group(1), + orig_args(tmp_orig_args) + { + mark_as_sum_func(); + init_aggregator(); + } + Item_sum(THD *thd, Item *a, Item *b): Item_func_or_sum(thd, a, b), + quick_group(1), orig_args(tmp_orig_args) + { + mark_as_sum_func(); + init_aggregator(); + } + Item_sum(THD *thd, List &list); + //Copy constructor, need to perform subselects with temporary tables + Item_sum(THD *thd, Item_sum *item); + enum Type type() const { return SUM_FUNC_ITEM; } + virtual enum Sumfunctype sum_func () const=0; + bool is_aggr_sum_func() + { + switch (sum_func()) { + case COUNT_FUNC: + case COUNT_DISTINCT_FUNC: + case SUM_FUNC: + case SUM_DISTINCT_FUNC: + case AVG_FUNC: + case AVG_DISTINCT_FUNC: + case MIN_FUNC: + case MAX_FUNC: + case STD_FUNC: + case VARIANCE_FUNC: + case SUM_BIT_FUNC: + case UDF_SUM_FUNC: + case GROUP_CONCAT_FUNC: + return true; + default: + return false; + } + } + /** + Resets the aggregate value to its default and aggregates the current + value of its attribute(s). + */ + inline bool reset_and_add() + { + aggregator_clear(); + return aggregator_add(); + }; + + /* + Called when new group is started and results are being saved in + a temporary table. Similarly to reset_and_add() it resets the + value to its default and aggregates the value of its + attribute(s), but must also store it in result_field. + This set of methods (result_item(), reset_field, update_field()) of + Item_sum is used only if quick_group is not null. Otherwise + copy_or_same() is used to obtain a copy of this item. + */ + virtual void reset_field()=0; + /* + Called for each new value in the group, when temporary table is in use. + Similar to add(), but uses temporary table field to obtain current value, + Updated value is then saved in the field. + */ + virtual void update_field()=0; + virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } + virtual Item *result_item(THD *thd, Field *field); + + void update_used_tables (); + COND *build_equal_items(THD *thd, COND_EQUAL *inherited, + bool link_item_fields, + COND_EQUAL **cond_equal_ref) + { + /* + Item_sum (and derivants) of the original WHERE/HAVING clauses + should already be replaced to Item_aggregate_ref by the time when + build_equal_items() is called. See Item::split_sum_func2(). + */ + DBUG_ASSERT(0); + return Item::build_equal_items(thd, inherited, link_item_fields, + cond_equal_ref); + } + bool is_null() { return null_value; } + /** + make_const() + Called if we've managed to calculate the value of this Item in + opt_sum_query(), hence it can be considered constant at all subsequent + steps. + */ + void make_const () + { + used_tables_cache= 0; + const_item_cache= true; + } + void reset_forced_const() { const_item_cache= false; } + virtual bool const_during_execution() const { return false; } + virtual void print(String *str, enum_query_type query_type); + void fix_num_length_and_dec(); + + /** + Mark an aggregate as having no rows. + + This function is called by the execution engine to assign 'NO ROWS + FOUND' value to an aggregate item, when the underlying result set + has no rows. Such value, in a general case, may be different from + the default value of the item after 'clear()': e.g. a numeric item + may be initialized to 0 by clear() and to NULL by + no_rows_in_result(). + */ + virtual void no_rows_in_result() + { + set_aggregator(with_distinct ? + Aggregator::DISTINCT_AGGREGATOR : + Aggregator::SIMPLE_AGGREGATOR); + aggregator_clear(); + } + virtual void make_unique() { force_copy_fields= TRUE; } + Item *get_tmp_table_item(THD *thd); + Field *create_tmp_field(bool group, TABLE *table); + virtual bool collect_outer_ref_processor(void *param); + bool init_sum_func_check(THD *thd); + bool check_sum_func(THD *thd, Item **ref); + bool register_sum_func(THD *thd, Item **ref); + st_select_lex *depended_from() + { return (nest_level == aggr_level ? 0 : aggr_sel); } + + Item *get_arg(uint i) const { return args[i]; } + Item *set_arg(uint i, THD *thd, Item *new_val); + uint get_arg_count() const { return arg_count; } + virtual Item **get_args() { return fixed ? orig_args : args; } + + /* Initialization of distinct related members */ + void init_aggregator() + { + aggr= NULL; + with_distinct= FALSE; + force_copy_fields= FALSE; + } + + /** + Called to initialize the aggregator. + */ + + inline bool aggregator_setup(THD *thd) { return aggr->setup(thd); }; + + /** + Called to cleanup the aggregator. + */ + + inline void aggregator_clear() { aggr->clear(); } + + /** + Called to add value to the aggregator. + */ + + inline bool aggregator_add() { return aggr->add(); }; + + /* stores the declared DISTINCT flag (from the parser) */ + void set_distinct(bool distinct) + { + with_distinct= distinct; + quick_group= with_distinct ? 0 : 1; + } + + /* + Set the type of aggregation : DISTINCT or not. + + May be called multiple times. + */ + + int set_aggregator(Aggregator::Aggregator_type aggregator); + + virtual void clear()= 0; + virtual bool add()= 0; + virtual bool setup(THD *thd) { return false; } + + virtual bool supports_removal() const { return false; } + virtual void remove() { DBUG_ASSERT(0); } + + virtual void cleanup(); + bool check_vcol_func_processor(void *arg); + virtual void setup_window_func(THD *thd, Window_spec *window_spec) {} + void mark_as_window_func_sum_expr() { window_func_sum_expr_flag= true; } + bool is_window_func_sum_expr() { return window_func_sum_expr_flag; } + virtual void setup_caches(THD *thd) {}; +}; + + +class Unique; + + +/** + The distinct aggregator. + Implements AGGFN (DISTINCT ..) + Collects all the data into an Unique (similarly to what Item_sum + does currently when with_distinct=true) and then (if applicable) iterates over + the list of unique values and pumps them back into its object +*/ + +class Aggregator_distinct : public Aggregator +{ + friend class Item_sum_sum; + + /* + flag to prevent consecutive runs of endup(). Normally in endup there are + expensive calculations (like walking the distinct tree for example) + which we must do only once if there are no data changes. + We can re-use the data for the second and subsequent val_xxx() calls. + endup_done set to TRUE also means that the calculated values for + the aggregate functions are correct and don't need recalculation. + */ + bool endup_done; + + /* + Used depending on the type of the aggregate function and the presence of + blob columns in it: + - For COUNT(DISTINCT) and no blob fields this points to a real temporary + table. It's used as a hash table. + - For AVG/SUM(DISTINCT) or COUNT(DISTINCT) with blob fields only the + in-memory data structure of a temporary table is constructed. + It's used by the Field classes to transform data into row format. + */ + TABLE *table; + + /* + An array of field lengths on row allocated and used only for + COUNT(DISTINCT) with multiple columns and no blobs. Used in + Aggregator_distinct::composite_key_cmp (called from Unique to compare + nodes + */ + uint32 *field_lengths; + + /* + Used in conjunction with 'table' to support the access to Field classes + for COUNT(DISTINCT). Needed by copy_fields()/copy_funcs(). + */ + TMP_TABLE_PARAM *tmp_table_param; + + /* + If there are no blobs in the COUNT(DISTINCT) arguments, we can use a tree, + which is faster than heap table. In that case, we still use the table + to help get things set up, but we insert nothing in it. + For AVG/SUM(DISTINCT) we always use this tree (as it takes a single + argument) to get the distinct rows. + */ + Unique *tree; + + /* + The length of the temp table row. Must be a member of the class as it + gets passed down to simple_raw_key_cmp () as a compare function argument + to Unique. simple_raw_key_cmp () is used as a fast comparison function + when the entire row can be binary compared. + */ + uint tree_key_length; + + /* + Set to true if the result is known to be always NULL. + If set deactivates creation and usage of the temporary table (in the + 'table' member) and the Unique instance (in the 'tree' member) as well as + the calculation of the final value on the first call to + Item_[sum|avg|count]::val_xxx(). + */ + bool always_null; + + /** + When feeding back the data in endup() from Unique/temp table back to + Item_sum::add() methods we must read the data from Unique (and not + recalculate the functions that are given as arguments to the aggregate + function. + This flag is to tell the arg_*() methods to take the data from the Unique + instead of calling the relevant val_..() method. + */ + bool use_distinct_values; + +public: + Aggregator_distinct (Item_sum *sum) : + Aggregator(sum), table(NULL), tmp_table_param(NULL), tree(NULL), + always_null(false), use_distinct_values(false) {} + virtual ~Aggregator_distinct (); + Aggregator_type Aggrtype() { return DISTINCT_AGGREGATOR; } + + bool setup(THD *); + void clear(); + bool add(); + void endup(); + virtual my_decimal *arg_val_decimal(my_decimal * value); + virtual double arg_val_real(); + virtual bool arg_is_null(bool use_null_value); + + bool unique_walk_function(void *element); + bool unique_walk_function_for_count(void *element); + static int composite_key_cmp(void* arg, uchar* key1, uchar* key2); +}; + + +/** + The pass-through aggregator. + Implements AGGFN (DISTINCT ..) by knowing it gets distinct data on input. + So it just pumps them back to the Item_sum descendant class. +*/ +class Aggregator_simple : public Aggregator +{ +public: + + Aggregator_simple (Item_sum *sum) : + Aggregator(sum) {} + Aggregator_type Aggrtype() { return Aggregator::SIMPLE_AGGREGATOR; } + + bool setup(THD * thd) { return item_sum->setup(thd); } + void clear() { item_sum->clear(); } + bool add() { return item_sum->add(); } + void endup() {}; + virtual my_decimal *arg_val_decimal(my_decimal * value); + virtual double arg_val_real(); + virtual bool arg_is_null(bool use_null_value); +}; + + +class Item_sum_num :public Item_sum +{ +protected: + /* + val_xxx() functions may be called several times during the execution of a + query. Derived classes that require extensive calculation in val_xxx() + maintain cache of aggregate value. This variable governs the validity of + that cache. + */ + bool is_evaluated; +public: + Item_sum_num(THD *thd): Item_sum(thd), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, Item *item_par): + Item_sum(thd, item_par), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, Item *a, Item* b): + Item_sum(thd, a, b), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, List &list): + Item_sum(thd, list), is_evaluated(FALSE) {} + Item_sum_num(THD *thd, Item_sum_num *item): + Item_sum(thd, item),is_evaluated(item->is_evaluated) {} + bool fix_fields(THD *, Item **); + longlong val_int() { return val_int_from_real(); /* Real as default */ } + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + void reset_field(); +}; + + +class Item_sum_int :public Item_sum_num +{ +public: + Item_sum_int(THD *thd): Item_sum_num(thd) {} + Item_sum_int(THD *thd, Item *item_par): Item_sum_num(thd, item_par) {} + Item_sum_int(THD *thd, List &list): Item_sum_num(thd, list) {} + Item_sum_int(THD *thd, Item_sum_int *item) :Item_sum_num(thd, item) {} + double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + const Type_handler *type_handler() const { return &type_handler_longlong; } + void fix_length_and_dec() + { decimals=0; max_length=21; maybe_null=null_value=0; } +}; + + +class Item_sum_sum :public Item_sum_num, + public Type_handler_hybrid_field_type +{ +protected: + bool direct_added; + bool direct_reseted_field; + bool direct_sum_is_null; + double direct_sum_real; + double sum; + my_decimal direct_sum_decimal; + my_decimal dec_buffs[2]; + uint curr_dec_buff; + void fix_length_and_dec(); + +public: + Item_sum_sum(THD *thd, Item *item_par, bool distinct): + Item_sum_num(thd, item_par), direct_added(FALSE), + direct_reseted_field(FALSE) + { + set_distinct(distinct); + } + Item_sum_sum(THD *thd, Item_sum_sum *item); + enum Sumfunctype sum_func () const + { + return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC; + } + void cleanup(); + void direct_add(my_decimal *add_sum_decimal); + void direct_add(double add_sum_real, bool add_sum_is_null); + void clear(); + bool add(); + double val_real(); + longlong val_int(); + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); + void reset_field(); + void update_field(); + void no_rows_in_result() {} + const char *func_name() const + { + return has_with_distinct() ? "sum(distinct " : "sum("; + } + Item *copy_or_same(THD* thd); + void remove(); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + + bool supports_removal() const + { + return true; + } + +private: + void add_helper(bool perform_removal); + ulonglong count; +}; + + +class Item_sum_count :public Item_sum_int +{ + bool direct_counted; + bool direct_reseted_field; + longlong direct_count; + longlong count; + + friend class Aggregator_distinct; + + void clear(); + bool add(); + void cleanup(); + void remove(); + +public: + Item_sum_count(THD *thd, Item *item_par): + Item_sum_int(thd, item_par), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) + {} + + /** + Constructs an instance for COUNT(DISTINCT) + + @param list a list of the arguments to the aggregate function + + This constructor is called by the parser only for COUNT (DISTINCT). + */ + + Item_sum_count(THD *thd, List &list): + Item_sum_int(thd, list), direct_counted(FALSE), + direct_reseted_field(FALSE), count(0) + { + set_distinct(TRUE); + } + Item_sum_count(THD *thd, Item_sum_count *item): + Item_sum_int(thd, item), direct_counted(FALSE), + direct_reseted_field(FALSE), count(item->count) + {} + enum Sumfunctype sum_func () const + { + return has_with_distinct() ? COUNT_DISTINCT_FUNC : COUNT_FUNC; + } + void no_rows_in_result() { count=0; } + void make_const(longlong count_arg) + { + count=count_arg; + Item_sum::make_const(); + } + longlong val_int(); + void reset_field(); + void update_field(); + void direct_add(longlong add_count); + const char *func_name() const + { + return has_with_distinct() ? "count(distinct " : "count("; + } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + + bool supports_removal() const + { + return true; + } +}; + + +class Item_sum_avg :public Item_sum_sum +{ +public: + // TODO-cvicentiu given that Item_sum_sum now uses a counter of its own, in + // order to implement remove(), it is possible to remove this member. + ulonglong count; + uint prec_increment; + uint f_precision, f_scale, dec_bin_size; + + Item_sum_avg(THD *thd, Item *item_par, bool distinct): + Item_sum_sum(thd, item_par, distinct), count(0) + {} + Item_sum_avg(THD *thd, Item_sum_avg *item) + :Item_sum_sum(thd, item), count(item->count), + prec_increment(item->prec_increment) {} + + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); + void fix_length_and_dec(); + enum Sumfunctype sum_func () const + { + return has_with_distinct() ? AVG_DISTINCT_FUNC : AVG_FUNC; + } + void clear(); + bool add(); + void remove(); + double val_real(); + // In SPs we might force the "wrong" type with select into a declare variable + longlong val_int() { return val_int_from_real(); } + my_decimal *val_decimal(my_decimal *); + String *val_str(String *str); + void reset_field(); + void update_field(); + Item *result_item(THD *thd, Field *field); + void no_rows_in_result() {} + const char *func_name() const + { + return has_with_distinct() ? "avg(distinct " : "avg("; + } + Item *copy_or_same(THD* thd); + Field *create_tmp_field(bool group, TABLE *table); + void cleanup() + { + count= 0; + Item_sum_sum::cleanup(); + } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + + bool supports_removal() const + { + return true; + } +}; + + +/* + variance(a) = + + = sum (ai - avg(a))^2 / count(a) ) + = sum (ai^2 - 2*ai*avg(a) + avg(a)^2) / count(a) + = (sum(ai^2) - sum(2*ai*avg(a)) + sum(avg(a)^2))/count(a) = + = (sum(ai^2) - 2*avg(a)*sum(a) + count(a)*avg(a)^2)/count(a) = + = (sum(ai^2) - 2*sum(a)*sum(a)/count(a) + count(a)*sum(a)^2/count(a)^2 )/count(a) = + = (sum(ai^2) - 2*sum(a)^2/count(a) + sum(a)^2/count(a) )/count(a) = + = (sum(ai^2) - sum(a)^2/count(a))/count(a) + +But, this falls prey to catastrophic cancellation. Instead, use the recurrence formulas + + M_{1} = x_{1}, ~ M_{k} = M_{k-1} + (x_{k} - M_{k-1}) / k newline + S_{1} = 0, ~ S_{k} = S_{k-1} + (x_{k} - M_{k-1}) times (x_{k} - M_{k}) newline + for 2 <= k <= n newline + ital variance = S_{n} / (n-1) + +*/ + +class Item_sum_variance : public Item_sum_num +{ + void fix_length_and_dec(); + +public: + double recurrence_m, recurrence_s; /* Used in recurrence relation. */ + ulonglong count; + uint sample; + uint prec_increment; + + Item_sum_variance(THD *thd, Item *item_par, uint sample_arg): + Item_sum_num(thd, item_par), count(0), + sample(sample_arg) + {} + Item_sum_variance(THD *thd, Item_sum_variance *item); + enum Sumfunctype sum_func () const { return VARIANCE_FUNC; } + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); + void clear(); + bool add(); + double val_real(); + my_decimal *val_decimal(my_decimal *); + void reset_field(); + void update_field(); + Item *result_item(THD *thd, Field *field); + void no_rows_in_result() {} + const char *func_name() const + { return sample ? "var_samp(" : "variance("; } + Item *copy_or_same(THD* thd); + Field *create_tmp_field(bool group, TABLE *table); + const Type_handler *type_handler() const { return &type_handler_double; } + void cleanup() + { + count= 0; + Item_sum_num::cleanup(); + } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + +/* + standard_deviation(a) = sqrt(variance(a)) +*/ + +class Item_sum_std :public Item_sum_variance +{ + public: + Item_sum_std(THD *thd, Item *item_par, uint sample_arg): + Item_sum_variance(thd, item_par, sample_arg) {} + Item_sum_std(THD *thd, Item_sum_std *item) + :Item_sum_variance(thd, item) + {} + enum Sumfunctype sum_func () const { return STD_FUNC; } + double val_real(); + Item *result_item(THD *thd, Field *field); + const char *func_name() const { return "std("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + +// This class is a string or number function depending on num_func +class Arg_comparator; +class Item_cache; +class Item_sum_hybrid :public Item_sum, public Type_handler_hybrid_field_type +{ +protected: + bool direct_added; + Item *direct_item; + Item_cache *value, *arg_cache; + Arg_comparator *cmp; + int cmp_sign; + bool was_values; // Set if we have found at least one row (for max/min only) + bool was_null_value; + + public: + Item_sum_hybrid(THD *thd, Item *item_par,int sign): + Item_sum(thd, item_par), + Type_handler_hybrid_field_type(&type_handler_longlong), + direct_added(FALSE), value(0), arg_cache(0), cmp(0), + cmp_sign(sign), was_values(TRUE) + { collation.set(&my_charset_bin); } + Item_sum_hybrid(THD *thd, Item_sum_hybrid *item) + :Item_sum(thd, item), + Type_handler_hybrid_field_type(item), + direct_added(FALSE), value(item->value), arg_cache(0), + cmp_sign(item->cmp_sign), was_values(item->was_values) + { } + bool fix_fields(THD *, Item **); + void fix_length_and_dec(); + void setup_hybrid(THD *thd, Item *item, Item *value_arg); + void clear(); + void direct_add(Item *item); + double val_real(); + longlong val_int(); + my_decimal *val_decimal(my_decimal *); + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); + void reset_field(); + String *val_str(String *); + const Type_handler *real_type_handler() const + { + return get_arg(0)->real_type_handler(); + } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + TYPELIB *get_typelib() const { return args[0]->get_typelib(); } + void update_field(); + void min_max_update_str_field(); + void min_max_update_real_field(); + void min_max_update_int_field(); + void min_max_update_decimal_field(); + void cleanup(); + bool any_value() { return was_values; } + void no_rows_in_result(); + void restore_to_before_no_rows_in_result(); + Field *create_tmp_field(bool group, TABLE *table); + void setup_caches(THD *thd) { setup_hybrid(thd, arguments()[0], NULL); } +}; + + +class Item_sum_min :public Item_sum_hybrid +{ +public: + Item_sum_min(THD *thd, Item *item_par): Item_sum_hybrid(thd, item_par, 1) {} + Item_sum_min(THD *thd, Item_sum_min *item) :Item_sum_hybrid(thd, item) {} + enum Sumfunctype sum_func () const {return MIN_FUNC;} + + bool add(); + const char *func_name() const { return "min("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_sum_max :public Item_sum_hybrid +{ +public: + Item_sum_max(THD *thd, Item *item_par): Item_sum_hybrid(thd, item_par, -1) {} + Item_sum_max(THD *thd, Item_sum_max *item) :Item_sum_hybrid(thd, item) {} + enum Sumfunctype sum_func () const {return MAX_FUNC;} + + bool add(); + const char *func_name() const { return "max("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_sum_bit :public Item_sum_int +{ +public: + Item_sum_bit(THD *thd, Item *item_par, ulonglong reset_arg): + Item_sum_int(thd, item_par), reset_bits(reset_arg), bits(reset_arg), + as_window_function(FALSE), num_values_added(0) {} + Item_sum_bit(THD *thd, Item_sum_bit *item): + Item_sum_int(thd, item), reset_bits(item->reset_bits), bits(item->bits), + as_window_function(item->as_window_function), + num_values_added(item->num_values_added) + { + if (as_window_function) + memcpy(bit_counters, item->bit_counters, sizeof(bit_counters)); + } + enum Sumfunctype sum_func () const {return SUM_BIT_FUNC;} + void clear(); + longlong val_int(); + void reset_field(); + void update_field(); + void fix_length_and_dec() + { decimals= 0; max_length=21; unsigned_flag= 1; maybe_null= null_value= 0; } + void cleanup() + { + bits= reset_bits; + if (as_window_function) + clear_as_window(); + Item_sum_int::cleanup(); + } + void setup_window_func(THD *thd __attribute__((unused)), + Window_spec *window_spec __attribute__((unused))) + { + as_window_function= TRUE; + clear_as_window(); + } + void remove() + { + if (as_window_function) + { + remove_as_window(args[0]->val_int()); + return; + } + // Unless we're counting bits, we can not remove anything. + DBUG_ASSERT(0); + } + + bool supports_removal() const + { + return true; + } + +protected: + enum bit_counters { NUM_BIT_COUNTERS= 64 }; + ulonglong reset_bits,bits; + /* + Marks whether the function is to be computed as a window function. + */ + bool as_window_function; + // When used as an aggregate window function, we need to store + // this additional information. + ulonglong num_values_added; + ulonglong bit_counters[NUM_BIT_COUNTERS]; + bool add_as_window(ulonglong value); + bool remove_as_window(ulonglong value); + bool clear_as_window(); + virtual void set_bits_from_counters()= 0; +}; + + +class Item_sum_or :public Item_sum_bit +{ +public: + Item_sum_or(THD *thd, Item *item_par): Item_sum_bit(thd, item_par, 0) {} + Item_sum_or(THD *thd, Item_sum_or *item) :Item_sum_bit(thd, item) {} + bool add(); + const char *func_name() const { return "bit_or("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + +private: + void set_bits_from_counters(); +}; + + +class Item_sum_and :public Item_sum_bit +{ +public: + Item_sum_and(THD *thd, Item *item_par): + Item_sum_bit(thd, item_par, ULONGLONG_MAX) {} + Item_sum_and(THD *thd, Item_sum_and *item) :Item_sum_bit(thd, item) {} + bool add(); + const char *func_name() const { return "bit_and("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + +private: + void set_bits_from_counters(); +}; + +class Item_sum_xor :public Item_sum_bit +{ +public: + Item_sum_xor(THD *thd, Item *item_par): Item_sum_bit(thd, item_par, 0) {} + Item_sum_xor(THD *thd, Item_sum_xor *item) :Item_sum_bit(thd, item) {} + bool add(); + const char *func_name() const { return "bit_xor("; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + +private: + void set_bits_from_counters(); +}; + +class sp_head; +class sp_name; +class Query_arena; +struct st_sp_security_context; + +/* + Item_sum_sp handles STORED AGGREGATE FUNCTIONS + + Each Item_sum_sp represents a custom aggregate function. Inside the + function's body, we require at least one occurence of FETCH GROUP NEXT ROW + instruction. This cursor is what makes custom stored aggregates possible. + + During computation the function's add method is called. This in turn performs + an execution of the function. The function will execute from the current + function context (and instruction), if one exists, or from the start if not. + See Item_sp for more details. + + Upon encounter of FETCH GROUP NEXT ROW instruction, the function will pause + execution. We assume that the user has performed the necessary additions for + a row, between two encounters of FETCH GROUP NEXT ROW. + + Example: + create aggregate function f1(x INT) returns int + begin + declare continue handler for not found return s; + declare s int default 0 + loop + fetch group next row; + set s = s + x; + end loop; + end + + The function will always stop after an encounter of FETCH GROUP NEXT ROW, + except (!) on first encounter, as the value for the first row in the + group is already set in the argument x. This behaviour is done so when + a user writes a function, he should "logically" include FETCH GROUP NEXT ROW + before any "add" instructions in the stored function. This means however that + internally, the first occurence doesn't stop the function. See the + implementation of FETCH GROUP NEXT ROW for details as to how it happens. + + Either way, one should assume that after calling "Item_sum_sp::add()" that + the values for that particular row have been added to the aggregation. + + To produce values for val_xxx methods we need an extra syntactic construct. + We require a continue handler when "no more rows are available". val_xxx + methods force a function return by executing the function again, while + setting a server flag that no more rows have been found. This implies + that val_xxx methods should only be called once per group however. + + Example: + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ret_val; +*/ +class Item_sum_sp :public Item_sum, + public Item_sp +{ + private: + bool execute(); + +public: + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp); + + Item_sum_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name, + sp_head *sp, List &list); + + enum Sumfunctype sum_func () const + { + return SP_AGGREGATE_FUNC; + } + void fix_length_and_dec(); + bool fix_fields(THD *thd, Item **ref); + const char *func_name() const; + const Type_handler *type_handler() const; + bool add(); + + /* val_xx functions */ + longlong val_int() + { + if(execute()) + return 0; + return sp_result_field->val_int(); + } + + double val_real() + { + if(execute()) + return 0.0; + return sp_result_field->val_real(); + } + + my_decimal *val_decimal(my_decimal *dec_buf) + { + if(execute()) + return NULL; + return sp_result_field->val_decimal(dec_buf); + } + + String *val_str(String *str) + { + String buf; + char buff[20]; + buf.set(buff, 20, str->charset()); + buf.length(0); + if (execute()) + return NULL; + /* + result_field will set buf pointing to internal buffer + of the resul_field. Due to this it will change any time + when SP is executed. In order to prevent occasional + corruption of returned value, we make here a copy. + */ + sp_result_field->val_str(&buf); + str->copy(buf); + return str; + } + void reset_field(){DBUG_ASSERT(0);} + void update_field(){DBUG_ASSERT(0);} + void clear(); + void cleanup(); + inline Field *get_sp_result_field() + { + return sp_result_field; + } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + +/* Items to get the value of a stored sum function */ + +class Item_sum_field :public Item +{ +protected: + Field *field; +public: + Item_sum_field(THD *thd, Item_sum *item) + :Item(thd), field(item->result_field) + { + name= item->name; + maybe_null= true; + decimals= item->decimals; + max_length= item->max_length; + unsigned_flag= item->unsigned_flag; + fixed= true; + } + table_map used_tables() const { return (table_map) 1L; } + void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); } + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE); + } +}; + + +class Item_avg_field :public Item_sum_field +{ +protected: + uint prec_increment; +public: + Item_avg_field(THD *thd, Item_sum_avg *item) + :Item_sum_field(thd, item), prec_increment(item->prec_increment) + { } + enum Type type() const { return FIELD_AVG_ITEM; } + bool is_null() { update_null_value(); return null_value; } +}; + + +class Item_avg_field_double :public Item_avg_field +{ +public: + Item_avg_field_double(THD *thd, Item_sum_avg *item) + :Item_avg_field(thd, item) + { } + const Type_handler *type_handler() const { return &type_handler_double; } + longlong val_int() { return val_int_from_real(); } + my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); } + String *val_str(String *str) { return val_string_from_real(str); } + double val_real(); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_avg_field_decimal :public Item_avg_field +{ + uint f_precision, f_scale, dec_bin_size; +public: + Item_avg_field_decimal(THD *thd, Item_sum_avg *item) + :Item_avg_field(thd, item), + f_precision(item->f_precision), + f_scale(item->f_scale), + dec_bin_size(item->dec_bin_size) + { } + const Type_handler *type_handler() const { return &type_handler_newdecimal; } + double val_real() { return val_real_from_decimal(); } + longlong val_int() { return val_int_from_decimal(); } + String *val_str(String *str) { return val_string_from_decimal(str); } + my_decimal *val_decimal(my_decimal *); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_variance_field :public Item_sum_field +{ + uint sample; +public: + Item_variance_field(THD *thd, Item_sum_variance *item) + :Item_sum_field(thd, item), sample(item->sample) + { } + enum Type type() const {return FIELD_VARIANCE_ITEM; } + double val_real(); + longlong val_int() { return val_int_from_real(); } + String *val_str(String *str) + { return val_string_from_real(str); } + my_decimal *val_decimal(my_decimal *dec_buf) + { return val_decimal_from_real(dec_buf); } + bool is_null() { update_null_value(); return null_value; } + const Type_handler *type_handler() const { return &type_handler_double; } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_std_field :public Item_variance_field +{ +public: + Item_std_field(THD *thd, Item_sum_std *item) + :Item_variance_field(thd, item) + { } + enum Type type() const { return FIELD_STD_ITEM; } + double val_real(); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +/* + User defined aggregates +*/ + +#ifdef HAVE_DLOPEN + +class Item_udf_sum : public Item_sum +{ +protected: + udf_handler udf; + +public: + Item_udf_sum(THD *thd, udf_func *udf_arg): + Item_sum(thd), udf(udf_arg) + { quick_group=0; } + Item_udf_sum(THD *thd, udf_func *udf_arg, List &list): + Item_sum(thd, list), udf(udf_arg) + { quick_group=0;} + Item_udf_sum(THD *thd, Item_udf_sum *item) + :Item_sum(thd, item), udf(item->udf) + { udf.not_original= TRUE; } + const char *func_name() const { return udf.name(); } + bool fix_fields(THD *thd, Item **ref) + { + DBUG_ASSERT(fixed == 0); + + if (init_sum_func_check(thd)) + return TRUE; + + fixed= 1; + /* + We set const_item_cache to false in constructors. + It can be later changed to "true", in a Item_sum::make_const() call. + No make_const() calls should have happened so far. + */ + DBUG_ASSERT(!const_item_cache); + if (udf.fix_fields(thd, this, this->arg_count, this->args)) + return TRUE; + /** + The above call for udf.fix_fields() updates + the Used_tables_and_const_cache part of "this" as if it was a regular + non-aggregate UDF function and can change both const_item_cache and + used_tables_cache members. + - The used_tables_cache will be re-calculated in update_used_tables() + which is called from check_sum_func() below. So we don't care about + its current value. + - The const_item_cache must stay "false" until a Item_sum::make_const() + call happens, if ever. So we need to reset const_item_cache back to + "false" here. + */ + const_item_cache= false; + memcpy (orig_args, args, sizeof (Item *) * arg_count); + return check_sum_func(thd, ref); + } + enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } + virtual bool have_field_update(void) const { return 0; } + + void clear(); + bool add(); + void reset_field() {}; + void update_field() {}; + void cleanup(); + virtual void print(String *str, enum_query_type query_type); +}; + + +class Item_sum_udf_float :public Item_udf_sum +{ + public: + Item_sum_udf_float(THD *thd, udf_func *udf_arg): + Item_udf_sum(thd, udf_arg) {} + Item_sum_udf_float(THD *thd, udf_func *udf_arg, List &list): + Item_udf_sum(thd, udf_arg, list) {} + Item_sum_udf_float(THD *thd, Item_sum_udf_float *item) + :Item_udf_sum(thd, item) {} + longlong val_int() { return val_int_from_real(); } + double val_real(); + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + const Type_handler *type_handler() const { return &type_handler_double; } + void fix_length_and_dec() { fix_num_length_and_dec(); } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_sum_udf_int :public Item_udf_sum +{ +public: + Item_sum_udf_int(THD *thd, udf_func *udf_arg): + Item_udf_sum(thd, udf_arg) {} + Item_sum_udf_int(THD *thd, udf_func *udf_arg, List &list): + Item_udf_sum(thd, udf_arg, list) {} + Item_sum_udf_int(THD *thd, Item_sum_udf_int *item) + :Item_udf_sum(thd, item) {} + longlong val_int(); + double val_real() + { DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); } + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + const Type_handler *type_handler() const { return &type_handler_longlong; } + void fix_length_and_dec() { decimals=0; max_length=21; } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_sum_udf_str :public Item_udf_sum +{ +public: + Item_sum_udf_str(THD *thd, udf_func *udf_arg): + Item_udf_sum(thd, udf_arg) {} + Item_sum_udf_str(THD *thd, udf_func *udf_arg, List &list): + Item_udf_sum(thd, udf_arg, list) {} + Item_sum_udf_str(THD *thd, Item_sum_udf_str *item) + :Item_udf_sum(thd, item) {} + String *val_str(String *); + double val_real() + { + int err_not_used; + char *end_not_used; + String *res; + res=val_str(&str_value); + return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(), + &end_not_used, &err_not_used) : 0.0; + } + longlong val_int() + { + int err_not_used; + char *end; + String *res; + CHARSET_INFO *cs; + + if (!(res= val_str(&str_value))) + return 0; /* Null value */ + cs= res->charset(); + end= (char*) res->ptr()+res->length(); + return cs->cset->strtoll10(cs, res->ptr(), &end, &err_not_used); + } + my_decimal *val_decimal(my_decimal *dec); + const Type_handler *type_handler() const { return string_type_handler(); } + void fix_length_and_dec(); + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + + +class Item_sum_udf_decimal :public Item_udf_sum +{ +public: + Item_sum_udf_decimal(THD *thd, udf_func *udf_arg): + Item_udf_sum(thd, udf_arg) {} + Item_sum_udf_decimal(THD *thd, udf_func *udf_arg, List &list): + Item_udf_sum(thd, udf_arg, list) {} + Item_sum_udf_decimal(THD *thd, Item_sum_udf_decimal *item) + :Item_udf_sum(thd, item) {} + String *val_str(String *); + double val_real(); + longlong val_int(); + my_decimal *val_decimal(my_decimal *); + const Type_handler *type_handler() const { return &type_handler_newdecimal; } + void fix_length_and_dec() { fix_num_length_and_dec(); } + Item *copy_or_same(THD* thd); + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + +#else /* Dummy functions to get sql_yacc.cc compiled */ + +class Item_sum_udf_float :public Item_sum_num +{ + public: + Item_sum_udf_float(THD *thd, udf_func *udf_arg): + Item_sum_num(thd) {} + Item_sum_udf_float(THD *thd, udf_func *udf_arg, List &list): + Item_sum_num(thd) {} + Item_sum_udf_float(THD *thd, Item_sum_udf_float *item) + :Item_sum_num(thd, item) {} + enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } + double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; } + void clear() {} + bool add() { return 0; } + void update_field() {} +}; + + +class Item_sum_udf_int :public Item_sum_num +{ +public: + Item_sum_udf_int(THD *thd, udf_func *udf_arg): + Item_sum_num(thd) {} + Item_sum_udf_int(THD *thd, udf_func *udf_arg, List &list): + Item_sum_num(thd) {} + Item_sum_udf_int(THD *thd, Item_sum_udf_int *item) + :Item_sum_num(thd, item) {} + enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } + longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; } + double val_real() { DBUG_ASSERT(fixed == 1); return 0; } + void clear() {} + bool add() { return 0; } + void update_field() {} +}; + + +class Item_sum_udf_decimal :public Item_sum_num +{ + public: + Item_sum_udf_decimal(THD *thd, udf_func *udf_arg): + Item_sum_num(thd) {} + Item_sum_udf_decimal(THD *thd, udf_func *udf_arg, List &list): + Item_sum_num(thd) {} + Item_sum_udf_decimal(THD *thd, Item_sum_udf_float *item) + :Item_sum_num(thd, item) {} + enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } + double val_real() { DBUG_ASSERT(fixed == 1); return 0.0; } + my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; } + void clear() {} + bool add() { return 0; } + void update_field() {} +}; + + +class Item_sum_udf_str :public Item_sum_num +{ +public: + Item_sum_udf_str(THD *thd, udf_func *udf_arg): + Item_sum_num(thd) {} + Item_sum_udf_str(THD *thd, udf_func *udf_arg, List &list): + Item_sum_num(thd) {} + Item_sum_udf_str(THD *thd, Item_sum_udf_str *item) + :Item_sum_num(thd, item) {} + String *val_str(String *) + { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } + double val_real() { DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; } + longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } + void fix_length_and_dec() { maybe_null=1; max_length=0; } + enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } + void clear() {} + bool add() { return 0; } + void update_field() {} +}; + +#endif /* HAVE_DLOPEN */ + +C_MODE_START +int group_concat_key_cmp_with_distinct(void* arg, const void* key1, + const void* key2); +int group_concat_key_cmp_with_order(void* arg, const void* key1, + const void* key2); +int dump_leaf_key(void* key_arg, + element_count count __attribute__((unused)), + void* item_arg); +C_MODE_END + +class Item_func_group_concat : public Item_sum +{ + TMP_TABLE_PARAM *tmp_table_param; + String result; + String *separator; + TREE tree_base; + TREE *tree; + Item **ref_pointer_array; + + /** + If DISTINCT is used with this GROUP_CONCAT, this member is used to filter + out duplicates. + @see Item_func_group_concat::setup + @see Item_func_group_concat::add + @see Item_func_group_concat::clear + */ + Unique *unique_filter; + TABLE *table; + ORDER **order; + Name_resolution_context *context; + /** The number of ORDER BY items. */ + uint arg_count_order; + /** The number of selected items, aka the expr list. */ + uint arg_count_field; + uint row_count; + bool distinct; + bool warning_for_row; + bool always_null; + bool force_copy_fields; + bool no_appended; + /* + Following is 0 normal object and pointer to original one for copy + (to correctly free resources) + */ + Item_func_group_concat *original; + + friend int group_concat_key_cmp_with_distinct(void* arg, const void* key1, + const void* key2); + friend int group_concat_key_cmp_with_order(void* arg, const void* key1, + const void* key2); + friend int dump_leaf_key(void* key_arg, + element_count count __attribute__((unused)), + void* item_arg); +public: + Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, + bool is_distinct, List *is_select, + const SQL_I_List &is_order, String *is_separator); + + Item_func_group_concat(THD *thd, Item_func_group_concat *item); + ~Item_func_group_concat(); + void cleanup(); + + enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;} + const char *func_name() const { return "group_concat("; } + const Type_handler *type_handler() const + { + if (too_big_for_varchar()) + return &type_handler_blob; + return &type_handler_varchar; + } + void clear(); + bool add(); + void reset_field() { DBUG_ASSERT(0); } // not used + void update_field() { DBUG_ASSERT(0); } // not used + bool fix_fields(THD *,Item **); + bool setup(THD *thd); + void make_unique(); + double val_real() + { + int error; + const char *end; + String *res; + if (!(res= val_str(&str_value))) + return 0.0; + end= res->ptr() + res->length(); + return (my_strtod(res->ptr(), (char**) &end, &error)); + } + longlong val_int() + { + String *res; + char *end_ptr; + int error; + if (!(res= val_str(&str_value))) + return (longlong) 0; + end_ptr= (char*) res->ptr()+ res->length(); + return my_strtoll10(res->ptr(), &end_ptr, &error); + } + my_decimal *val_decimal(my_decimal *decimal_value) + { + return val_decimal_from_string(decimal_value); + } + String* val_str(String* str); + Item *copy_or_same(THD* thd); + void no_rows_in_result() {} + virtual void print(String *str, enum_query_type query_type); + virtual bool change_context_processor(void *cntx) + { context= (Name_resolution_context *)cntx; return FALSE; } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } +}; + +#endif /* ITEM_SUM_INCLUDED */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 9082bc684f4..516d009a69e 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7807,3 +7807,5 @@ ER_NOT_AGGREGATE_FUNCTION eng "Non-aggregate function contains aggregate specific instructions: (FETCH GROUP NEXT ROW)" ER_INVALID_AGGREGATE_FUNCTION eng "Aggregate specific instruction(FETCH GROUP NEXT ROW) missing from the aggregate function" +ER_INVALID_VALUE_TO_LIMIT + eng "Limit only accepts integer values" diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f073d4173ad..b2227bdbbbf 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1679,7 +1679,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -10754,16 +10754,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -11053,6 +11059,48 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + + + in_sum_expr: opt_all { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index fb14acac902..54c27783b0a 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1092,7 +1092,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type order_dir lock_option udf_type opt_local opt_no_write_to_binlog - opt_temporary all_or_any opt_distinct + opt_temporary all_or_any opt_distinct opt_glimit_clause opt_ignore_leaves fulltext_options union_option opt_not select_derived_init transaction_access_mode_types @@ -10571,16 +10571,22 @@ sum_expr: | GROUP_CONCAT_SYM '(' opt_distinct { Select->in_sum_expr++; } expr_list opt_gorder_clause - opt_gconcat_separator + opt_gconcat_separator opt_glimit_clause ')' { SELECT_LEX *sel= Select; sel->in_sum_expr--; $$= new (thd->mem_root) - Item_func_group_concat(thd, Lex->current_context(), $3, $5, - sel->gorder_list, $7); + Item_func_group_concat(thd, Lex->current_context(), + $3, $5, + sel->gorder_list, $7, $8, + sel->select_limit, + sel->offset_limit); if ($$ == NULL) MYSQL_YYABORT; + sel->select_limit= NULL; + sel->offset_limit= NULL; + sel->explicit_limit= 0; $5->empty(); sel->gorder_list.empty(); } @@ -10866,6 +10872,46 @@ gorder_list: { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } ; +opt_glimit_clause: + /* empty */ { $$ = 0; } + | glimit_clause { $$ = 1; } + ; + +glimit_clause_init: + LIMIT{} + ; + +glimit_clause: + glimit_clause_init glimit_options + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +glimit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + in_sum_expr: opt_all {