summaryrefslogtreecommitdiff
path: root/sql/item_sum.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_sum.cc')
-rw-r--r--sql/item_sum.cc941
1 files changed, 941 insertions, 0 deletions
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
new file mode 100644
index 00000000000..408e5d941ae
--- /dev/null
+++ b/sql/item_sum.cc
@@ -0,0 +1,941 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* Sum functions (COUNT, MIN...) */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+
+
+Item_sum::Item_sum(List<Item> &list)
+{
+ arg_count=list.elements;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
+ {
+ uint i=0;
+ List_iterator<Item> li(list);
+ Item *item;
+
+ while ((item=li++))
+ {
+ args[i++]= item;
+ }
+ }
+ with_sum_func=1;
+ list.empty(); // Fields are used
+}
+
+
+void Item_sum::make_field(Send_field *tmp_field)
+{
+ if (args[0]->type() == Item::FIELD_ITEM && keep_field_type())
+ ((Item_field*) args[0])->field->make_field(tmp_field);
+ else
+ {
+ tmp_field->flags=0;
+ if (!maybe_null)
+ tmp_field->flags|= NOT_NULL_FLAG;
+ tmp_field->length=max_length;
+ tmp_field->decimals=decimals;
+ tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG :
+ result_type() == REAL_RESULT ? FIELD_TYPE_DOUBLE :
+ FIELD_TYPE_VAR_STRING);
+ }
+ tmp_field->table_name=(char*)"";
+ tmp_field->col_name=name;
+}
+
+void Item_sum::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (i)
+ str->append(',');
+ args[i]->print(str);
+ }
+ 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);
+}
+
+
+String *
+Item_sum_num::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+
+String *
+Item_sum_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ char buff[21];
+ uint length= (uint) (longlong10_to_str(nr,buff,-10)-buff);
+ str->copy(buff,length);
+ return str;
+}
+
+
+bool
+Item_sum_num::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+ thd->allow_sum_func=0; // No included group funcs
+ decimals=0;
+ maybe_null=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (args[i]->fix_fields(thd,tables))
+ return 1;
+ if (decimals < args[i]->decimals)
+ decimals=args[i]->decimals;
+ maybe_null |= args[i]->maybe_null;
+ }
+ result_field=0;
+ max_length=float_length(decimals);
+ null_value=1;
+ fix_length_and_dec();
+ thd->allow_sum_func=1; // Allow group functions
+ return 0;
+}
+
+
+bool
+Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ Item *item=args[0];
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+ thd->allow_sum_func=0; // No included group funcs
+ if (item->fix_fields(thd,tables))
+ return 1;
+ hybrid_type=item->result_type();
+ if (hybrid_type == INT_RESULT)
+ max_length=21;
+ else if (hybrid_type == REAL_RESULT)
+ max_length=float_length(decimals);
+ else
+ max_length=item->max_length;
+ decimals=item->decimals;
+ maybe_null=item->maybe_null;
+ binary=item->binary;
+ result_field=0;
+ null_value=1;
+ fix_length_and_dec();
+ thd->allow_sum_func=1; // Allow group functions
+ return 0;
+}
+
+
+/***********************************************************************
+** reset and add of sum_func
+***********************************************************************/
+
+void Item_sum_sum::reset()
+{
+ null_value=0; sum=0.0; Item_sum_sum::add();
+}
+
+bool Item_sum_sum::add()
+{
+ sum+=args[0]->val();
+ return 0;
+}
+
+double Item_sum_sum::val()
+{
+ return sum;
+}
+
+
+void Item_sum_count::reset()
+{
+ count=0; add();
+}
+
+bool Item_sum_count::add()
+{
+ if (!args[0]->maybe_null)
+ count++;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ count++;
+ }
+ return 0;
+}
+
+longlong Item_sum_count::val_int()
+{
+ return (longlong) count;
+}
+
+/*
+** Avgerage
+*/
+
+void Item_sum_avg::reset()
+{
+ sum=0.0; count=0; Item_sum_avg::add();
+}
+
+bool Item_sum_avg::add()
+{
+ double nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ sum+=nr;
+ count++;
+ }
+ return 0;
+}
+
+double Item_sum_avg::val()
+{
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return sum/ulonglong2double(count);
+}
+
+
+/*
+** Standard deviation
+*/
+
+void Item_sum_std::reset()
+{
+ sum=sum_sqr=0.0; count=0; (void) Item_sum_std::add();
+}
+
+bool Item_sum_std::add()
+{
+ double nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ sum+=nr;
+ sum_sqr+=nr*nr;
+ count++;
+ }
+ return 0;
+}
+
+double Item_sum_std::val()
+{
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ /* Avoid problems when the precision isn't good enough */
+ double tmp=ulonglong2double(count);
+ double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
+ return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+}
+
+
+void Item_sum_std::reset_field()
+{
+ double nr=args[0]->val();
+ char *res=result_field->ptr;
+
+ if (args[0]->null_value)
+ bzero(res,sizeof(double)*2+sizeof(longlong));
+ else
+ {
+ float8store(res,nr);
+ nr*=nr;
+ float8store(res+sizeof(double),nr);
+ longlong tmp=1;
+ int8store(res+sizeof(double)*2,tmp);
+ }
+}
+
+void Item_sum_std::update_field(int offset)
+{
+ double nr,old_nr,old_sqr;
+ longlong field_count;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ float8get(old_sqr,res+offset+sizeof(double));
+ field_count=sint8korr(res+offset+sizeof(double)*2);
+
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ old_nr+=nr;
+ old_sqr+=nr*nr;
+ field_count++;
+ }
+ float8store(res,old_nr);
+ float8store(res+sizeof(double),old_sqr);
+ int8store(res+sizeof(double)*2,field_count);
+}
+
+/* min & max */
+
+double Item_sum_hybrid::val()
+{
+ if (null_value)
+ return 0.0;
+ if (hybrid_type == STRING_RESULT)
+ {
+ String *res; res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+ }
+ return sum;
+}
+
+
+String *
+Item_sum_hybrid::val_str(String *str)
+{
+ if (null_value)
+ return 0;
+ if (hybrid_type == STRING_RESULT)
+ return &value;
+ str->set(sum,decimals);
+ return str;
+}
+
+
+bool Item_sum_min::add()
+{
+ if (hybrid_type != STRING_RESULT)
+ {
+ double nr=args[0]->val();
+ if (!args[0]->null_value && (null_value || nr < sum))
+ {
+ sum=nr;
+ null_value=0;
+ }
+ }
+ else
+ {
+ String *result=args[0]->val_str(&tmp_value);
+ if (!args[0]->null_value &&
+ (null_value ||
+ (binary ? stringcmp(&value,result) : sortcmp(&value,result)) > 0))
+ {
+ value.copy(*result);
+ null_value=0;
+ }
+ }
+ return 0;
+}
+
+
+bool Item_sum_max::add()
+{
+ if (hybrid_type != STRING_RESULT)
+ {
+ double nr=args[0]->val();
+ if (!args[0]->null_value && (null_value || nr > sum))
+ {
+ sum=nr;
+ null_value=0;
+ }
+ }
+ else
+ {
+ String *result=args[0]->val_str(&tmp_value);
+ if (!args[0]->null_value &&
+ (null_value ||
+ (binary ? stringcmp(&value,result) : sortcmp(&value,result)) < 0))
+ {
+ value.copy(*result);
+ null_value=0;
+ }
+ }
+ return 0;
+}
+
+
+/* bit_or and bit_and */
+
+longlong Item_sum_bit::val_int()
+{
+ return (longlong) bits;
+}
+
+void Item_sum_bit::reset()
+{
+ bits=reset_bits; add();
+}
+
+bool Item_sum_or::add()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (!args[0]->null_value)
+ bits|=value;
+ return 0;
+}
+
+bool Item_sum_and::add()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (!args[0]->null_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();
+ char *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()
+{
+ if (hybrid_type == STRING_RESULT)
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff)),*res;
+
+ res=args[0]->val_str(&tmp);
+ if (args[0]->null_value)
+ {
+ result_field->set_null();
+ result_field->reset();
+ }
+ else
+ {
+ result_field->set_notnull();
+ result_field->store(res->ptr(),res->length());
+ }
+ }
+ else if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=args[0]->val_int();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr);
+ }
+ else // REAL_RESULT
+ {
+ double nr=args[0]->val();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0.0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr);
+ }
+}
+
+
+void Item_sum_sum::reset_field()
+{
+ double nr=args[0]->val(); // Nulls also return 0
+ float8store(result_field->ptr,nr);
+ null_value=0;
+ result_field->set_notnull();
+}
+
+
+void Item_sum_count::reset_field()
+{
+ char *res=result_field->ptr;
+ longlong nr=0;
+
+ if (!args[0]->maybe_null)
+ nr=1;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ nr=1;
+ }
+ int8store(res,nr);
+}
+
+
+void Item_sum_avg::reset_field()
+{
+ double nr=args[0]->val();
+ char *res=result_field->ptr;
+
+ if (args[0]->null_value)
+ bzero(res,sizeof(double)+sizeof(longlong));
+ else
+ {
+ float8store(res,nr);
+ res+=sizeof(double);
+ longlong tmp=1;
+ int8store(res,tmp);
+ }
+}
+
+void Item_sum_bit::reset_field()
+{
+ char *res=result_field->ptr;
+ ulonglong nr=(ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+/*
+** calc next value and merge it with field_value
+*/
+
+void Item_sum_sum::update_field(int offset)
+{
+ double old_nr,nr;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ old_nr+=nr;
+ float8store(res,old_nr);
+}
+
+
+void Item_sum_count::update_field(int offset)
+{
+ longlong nr;
+ char *res=result_field->ptr;
+
+ nr=sint8korr(res+offset);
+ if (!args[0]->maybe_null)
+ nr++;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ nr++;
+ }
+ int8store(res,nr);
+}
+
+
+void Item_sum_avg::update_field(int offset)
+{
+ double nr,old_nr;
+ longlong field_count;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ field_count=sint8korr(res+offset+sizeof(double));
+
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ old_nr+=nr;
+ field_count++;
+ }
+ float8store(res,old_nr);
+ res+=sizeof(double);
+ int8store(res,field_count);
+}
+
+void Item_sum_hybrid::update_field(int offset)
+{
+ if (hybrid_type == STRING_RESULT)
+ min_max_update_str_field(offset);
+ else if (hybrid_type == INT_RESULT)
+ min_max_update_int_field(offset);
+ else
+ min_max_update_real_field(offset);
+}
+
+
+void
+Item_sum_hybrid::min_max_update_str_field(int offset)
+{
+ String *res_str=args[0]->val_str(&value);
+
+ if (args[0]->null_value)
+ result_field->copy_from_tmp(offset); // Use old value
+ else
+ {
+ res_str->strip_sp();
+ result_field->ptr+=offset; // Get old max/min
+ result_field->val_str(&tmp_value,&tmp_value);
+ result_field->ptr-=offset;
+
+ if (result_field->is_null() ||
+ (cmp_sign * (binary ? stringcmp(res_str,&tmp_value) :
+ sortcmp(res_str,&tmp_value)) < 0))
+ result_field->store(res_str->ptr(),res_str->length());
+ else
+ { // Use old value
+ char *res=result_field->ptr;
+ memcpy(res,res+offset,result_field->pack_length());
+ }
+ result_field->set_notnull();
+ }
+}
+
+
+void
+Item_sum_hybrid::min_max_update_real_field(int offset)
+{
+ double nr,old_nr;
+
+ result_field->ptr+=offset;
+ old_nr=result_field->val_real();
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ if (result_field->is_null(offset) ||
+ (cmp_sign > 0 ? old_nr > nr : old_nr < nr))
+ old_nr=nr;
+ result_field->set_notnull();
+ }
+ else if (result_field->is_null(offset))
+ result_field->set_null();
+ result_field->ptr-=offset;
+ result_field->store(old_nr);
+}
+
+
+void
+Item_sum_hybrid::min_max_update_int_field(int offset)
+{
+ longlong nr,old_nr;
+
+ result_field->ptr+=offset;
+ old_nr=result_field->val_int();
+ nr=args[0]->val_int();
+ if (!args[0]->null_value)
+ {
+ if (result_field->is_null(offset) ||
+ (cmp_sign > 0 ? old_nr > nr : old_nr < nr))
+ old_nr=nr;
+ result_field->set_notnull();
+ }
+ else if (result_field->is_null(offset))
+ result_field->set_null();
+ result_field->ptr-=offset;
+ result_field->store(old_nr);
+}
+
+
+void Item_sum_or::update_field(int offset)
+{
+ ulonglong nr;
+ char *res=result_field->ptr;
+
+ nr=uint8korr(res+offset);
+ nr|= (ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+
+void Item_sum_and::update_field(int offset)
+{
+ ulonglong nr;
+ char *res=result_field->ptr;
+
+ nr=uint8korr(res+offset);
+ nr&= (ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+
+Item_avg_field::Item_avg_field(Item_sum_avg *item)
+{
+ name=item->name;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ field=item->result_field;
+ maybe_null=1;
+}
+
+double Item_avg_field::val()
+{
+ double nr;
+ longlong count;
+ float8get(nr,field->ptr);
+ char *res=(field->ptr+sizeof(double));
+ count=sint8korr(res);
+
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return nr/(double) count;
+}
+
+String *Item_avg_field::val_str(String *str)
+{
+ double nr=Item_avg_field::val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+Item_std_field::Item_std_field(Item_sum_std *item)
+{
+ name=item->name;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ field=item->result_field;
+ maybe_null=1;
+}
+
+double Item_std_field::val()
+{
+ double sum,sum_sqr;
+ longlong count;
+ float8get(sum,field->ptr);
+ float8get(sum_sqr,(field->ptr+sizeof(double)));
+ count=sint8korr(field->ptr+sizeof(double)*2);
+
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ double tmp= (double) count;
+ double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
+ return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+}
+
+String *Item_std_field::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+/****************************************************************************
+** COUNT(DISTINCT ...)
+****************************************************************************/
+
+#include "sql_select.h"
+
+Item_sum_count_distinct::~Item_sum_count_distinct()
+{
+ if (table)
+ free_tmp_table(current_thd, table);
+ delete tmp_table_param;
+}
+
+
+bool Item_sum_count_distinct::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (Item_sum_num::fix_fields(thd,tables) ||
+ !(tmp_table_param= new TMP_TABLE_PARAM))
+ return 1;
+ return 0;
+}
+
+bool Item_sum_count_distinct::setup(THD *thd)
+{
+ List<Item> list;
+ /* Create a table with an unique key over all parameters */
+ for (uint i=0; i < arg_count ; i++)
+ if (list.push_back(args[i]))
+ return 1;
+ count_field_types(tmp_table_param,list);
+ if (table)
+ {
+ free_tmp_table(thd, table);
+ tmp_table_param->cleanup();
+ }
+ if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
+ 0, 0, current_lex->options | thd->options)))
+ return 1;
+ table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
+ return 0;
+}
+
+
+void Item_sum_count_distinct::reset()
+{
+ table->file->extra(HA_EXTRA_NO_CACHE);
+ table->file->delete_all_rows();
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ (void) add();
+}
+
+bool Item_sum_count_distinct::add()
+{
+ int error;
+ copy_fields(tmp_table_param);
+ copy_funcs(tmp_table_param->funcs);
+
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ if (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE)
+ {
+ if (create_myisam_from_heap(table, tmp_table_param, error,1))
+ return 1; // Not a table_is_full error
+ }
+ }
+ return 0;
+}
+
+longlong Item_sum_count_distinct::val_int()
+{
+ if (!table) // Empty query
+ return LL(0);
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ return table->file->records;
+}
+
+/****************************************************************************
+** Functions to handle dynamic loadable aggregates
+** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
+** Adapted for UDAs by: Andreas F. Bobak <bobak@relog.ch>.
+** Rewritten by: Monty.
+****************************************************************************/
+
+#ifdef HAVE_DLOPEN
+
+void Item_udf_sum::reset()
+{
+ DBUG_ENTER("Item_udf_sum::reset");
+ udf.reset(&null_value);
+ DBUG_VOID_RETURN;
+}
+
+bool Item_udf_sum::add()
+{
+ DBUG_ENTER("Item_udf_sum::reset");
+ udf.add(&null_value);
+ DBUG_RETURN(0);
+}
+
+double Item_sum_udf_float::val()
+{
+ DBUG_ENTER("Item_sum_udf_float::val");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val(&null_value));
+}
+
+String *Item_sum_udf_float::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+longlong Item_sum_udf_int::val_int()
+{
+ DBUG_ENTER("Item_sum_udf_int::val_int");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val_int(&null_value));
+}
+
+String *Item_sum_udf_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* 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;
+}
+
+String *Item_sum_udf_str::val_str(String *str)
+{
+ DBUG_ENTER("Item_sum_udf_str::str");
+ String *res=udf.val_str(str,&str_value);
+ null_value = !res;
+ DBUG_RETURN(res);
+}
+
+#endif /* HAVE_DLOPEN */