mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
70aa7424c4
(All commit emails since 4.0.1 checked) This had to be done now, before the 4.1 tree changes to much, to make it easy to propagate bug fixes to the 4.1 tree. BUILD/SETUP.sh: Added -DPEDANTIC_SAFEMALLOC as standard debug option Docs/manual.texi: Changes for new version. client/mysql.cc: Fixed default value for rehash cleanup client/mysqladmin.c: Cleanup client/mysqlbinlog.cc: cleanup client/mysqldump.c: Cleanup client/mysqlmanager-pwgen.c: Cleanup client/mysqlmanagerc.c: Cleanup client/mysqltest.c: Cleanup dbug/dbug.c: Cleanup extra/resolve_stack_dump.c: Cleanup & Simple optimizations include/ft_global.h: Cleanup include/my_alloc.h: Cleanup include/my_global.h: Cleanup include/my_sys.h: Cleanup include/myisam.h: Cleanup libmysql/libmysql.c: Cleanup libmysql/manager.c: Cleanup myisam/ft_boolean_search.c: Cleanup myisam/ft_dump.c: Change strcpy -> strmov myisam/ft_eval.c: Cleanup myisam/ft_nlq_search.c: Cleanup myisam/ft_test1.c: strncpy -> strnmov myisam/ft_update.c: Cleanup myisam/mi_static.c: Cleanup myisam/mi_test2.c: Cleanup myisam/mi_write.c: Cleanup mysys/mf_fn_ext.c: Cleanup mysys/mf_iocache.c: Cleanup mysys/mf_iocache2.c: Cleanup mysys/my_getopt.c: Cleanup mysys/my_read.c: Cleanup mysys/my_thr_init.c: Cleanup mysys/queues.c: Cleanup mysys/safemalloc.c: Cleanup sql/field.cc: Indentation cleanups sql/ha_berkeley.cc: Indentation cleanups sql/ha_myisam.cc: Cleanup sql/item.h: Indentation cleanups sql/item_cmpfunc.cc: Indentation cleanups sql/item_create.cc: cleanup sql/item_func.cc: Cleanup sql/item_func.h: Indentation cleanups sql/item_strfunc.cc: Indentation cleanups sql/item_sum.cc: Indentation cleanups sql/item_timefunc.cc: Indentation cleanups sql/lock.cc: Indentation cleanups sql/log.cc: Cleanup strnmov -> strmake sql/log_event.cc: Cleanup + optimizations Fixed memory leak Added missing pthread_mutex_unlock() (On error condition) sql/log_event.h: Indentation and comment cleanup Merged #ifdef's into common blocks for better readability sql/mini_client.cc: Indentation cleanup sql/mysql_priv.h: Cleanup Changed int function to bool sql/mysqld.cc: Indentation and comment cleanup sql/net_pkg.cc: Indentation cleanup sql/net_serv.cc: Changed int function -> bool sql/nt_servc.cc: Cleanup sql/opt_range.cc: Indentation cleanup sql/repl_failsafe.cc: Cleanup + simple optimization strnmov -> strmake sql/slave.cc: strnmov -> strmake Cleanups sql/slave.h: Cleanup sql/sql_acl.cc: Indentation and DBUG_PRINT cleanup Changed WITH MAX... to not use = sql/sql_base.cc: Indentation cleanup sql/sql_cache.cc: Indentation cleanup sql/sql_class.cc: Indentation cleanup sql/sql_class.h: Renamed some struct slots sql/sql_delete.cc: Indentation cleanup sql/sql_handler.cc: Indentation cleanup sql/sql_insert.cc: Use new slot names. sql/sql_lex.cc: Indentation cleanup sql/sql_lex.h: Indentation cleanup sql/sql_load.cc: Indentation cleanup sql/sql_parse.cc: Indentation cleanup Removed not used check from LOCK TABLES sql/sql_repl.cc: strnmov -> strmake sql/sql_repl.h: Removed test if file is included (We want to know if it's included twice to avoid this) sql/sql_select.cc: Indentation cleanup sql/sql_show.cc: Indentation cleanup sql/sql_string.cc: Indentation cleanup sql/sql_table.cc: Indentation cleanup sql/sql_union.cc: Use renamed struct slot sql/sql_update.cc: Indentation cleanup sql/sql_yacc.yy: Removed = after GRANT ... MAX_ to make the syntax uniform sql/table.cc: Indentation cleanup sql/table.h: Indentation cleanup sql/time.cc: Indentation cleanup sql/udf_example.cc: Indentation cleanup sql/unireg.cc: strnmov -> strmake tests/grant.pl: Added test for LOCK TABLES tools/mysqlmanager.c: Cleanup fopen() -> my_fopen() vio/viosocket.c: DBUG_PRINT cleanups vio/viosslfactories.c: Indentation cleanup Checking of results from malloc() Fixed possible memory leak BitKeeper/etc/ignore: Added scripts/mysql_secure_installation to the ignore list BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted
1212 lines
25 KiB
C++
1212 lines
25 KiB
C++
/* 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_fast<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;
|
|
if (unsigned_flag)
|
|
tmp_field->flags |= UNSIGNED_FLAG;
|
|
tmp_field->length=max_length;
|
|
tmp_field->decimals=decimals;
|
|
tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG :
|
|
result_type() == REAL_RESULT ? FIELD_TYPE_DOUBLE :
|
|
FIELD_TYPE_VAR_STRING);
|
|
}
|
|
tmp_field->table_name=(char*)"";
|
|
tmp_field->col_name=name;
|
|
}
|
|
|
|
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=20;
|
|
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;
|
|
unsigned_flag=item->unsigned_flag;
|
|
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;
|
|
switch (hybrid_type) {
|
|
case STRING_RESULT:
|
|
String *res; res=val_str(&str_value);
|
|
return res ? atof(res->c_ptr()) : 0.0;
|
|
case INT_RESULT:
|
|
if (unsigned_flag)
|
|
return ulonglong2double(sum_int);
|
|
return (double) sum_int;
|
|
case REAL_RESULT:
|
|
return sum;
|
|
}
|
|
return 0; // Keep compiler happy
|
|
}
|
|
|
|
longlong Item_sum_hybrid::val_int()
|
|
{
|
|
if (null_value)
|
|
return 0;
|
|
if (hybrid_type == INT_RESULT)
|
|
return sum_int;
|
|
return (longlong) Item_sum_hybrid::val();
|
|
}
|
|
|
|
|
|
String *
|
|
Item_sum_hybrid::val_str(String *str)
|
|
{
|
|
if (null_value)
|
|
return 0;
|
|
switch (hybrid_type) {
|
|
case STRING_RESULT:
|
|
return &value;
|
|
case REAL_RESULT:
|
|
str->set(sum,decimals);
|
|
break;
|
|
case INT_RESULT:
|
|
if (unsigned_flag)
|
|
str->set((ulonglong) sum_int);
|
|
else
|
|
str->set((longlong) sum_int);
|
|
break;
|
|
}
|
|
return str; // Keep compiler happy
|
|
}
|
|
|
|
bool Item_sum_min::add()
|
|
{
|
|
switch (hybrid_type) {
|
|
case STRING_RESULT:
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
case INT_RESULT:
|
|
{
|
|
longlong nr=args[0]->val_int();
|
|
if (!args[0]->null_value && (null_value ||
|
|
(unsigned_flag &&
|
|
(ulonglong) nr < (ulonglong) sum_int) ||
|
|
(!unsigned_flag && nr < sum_int)))
|
|
{
|
|
sum_int=nr;
|
|
null_value=0;
|
|
}
|
|
}
|
|
break;
|
|
case REAL_RESULT:
|
|
{
|
|
double nr=args[0]->val();
|
|
if (!args[0]->null_value && (null_value || nr < sum))
|
|
{
|
|
sum=nr;
|
|
null_value=0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool Item_sum_max::add()
|
|
{
|
|
switch (hybrid_type) {
|
|
case STRING_RESULT:
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
case INT_RESULT:
|
|
{
|
|
longlong nr=args[0]->val_int();
|
|
if (!args[0]->null_value && (null_value ||
|
|
(unsigned_flag &&
|
|
(ulonglong) nr > (ulonglong) sum_int) ||
|
|
(!unsigned_flag && nr > sum_int)))
|
|
{
|
|
sum_int=nr;
|
|
null_value=0;
|
|
}
|
|
}
|
|
break;
|
|
case REAL_RESULT:
|
|
{
|
|
double nr=args[0]->val();
|
|
if (!args[0]->null_value && (null_value || nr > sum))
|
|
{
|
|
sum=nr;
|
|
null_value=0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
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))
|
|
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(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"
|
|
|
|
static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
|
|
{
|
|
return memcmp(key1, key2, *(uint*) arg);
|
|
}
|
|
|
|
static int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
|
|
{
|
|
return my_sortcmp((char*) key1, (char*) key2, *(uint*) arg);
|
|
}
|
|
|
|
/*
|
|
Did not make this one static - at least gcc gets confused when
|
|
I try to declare a static function as a friend. If you can figure
|
|
out the syntax to make a static function a friend, make this one
|
|
static
|
|
*/
|
|
|
|
int composite_key_cmp(void* arg, byte* key1, byte* key2)
|
|
{
|
|
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
|
|
Field **field = item->table->field;
|
|
Field **field_end= field + item->table->fields;
|
|
uint32 *lengths=item->field_lengths;
|
|
for (; field < field_end; ++field)
|
|
{
|
|
Field* f = *field;
|
|
int len = *lengths++;
|
|
int res = f->key_cmp(key1, key2);
|
|
if (res)
|
|
return res;
|
|
key1 += len;
|
|
key2 += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
helper function for walking the tree when we dump it to MyISAM -
|
|
tree_walk will call it for each leaf
|
|
*/
|
|
|
|
int dump_leaf(byte* key, uint32 count __attribute__((unused)),
|
|
Item_sum_count_distinct* item)
|
|
{
|
|
byte* buf = item->table->record[0];
|
|
int error;
|
|
/*
|
|
The first item->rec_offset bytes are taken care of with
|
|
restore_record(table,2) in setup()
|
|
*/
|
|
memcpy(buf + item->rec_offset, key, item->tree.size_of_element);
|
|
if ((error = item->table->file->write_row(buf)))
|
|
{
|
|
if (error != HA_ERR_FOUND_DUPP_KEY &&
|
|
error != HA_ERR_FOUND_DUPP_UNIQUE)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
Item_sum_count_distinct::~Item_sum_count_distinct()
|
|
{
|
|
if (table)
|
|
free_tmp_table(current_thd, table);
|
|
delete tmp_table_param;
|
|
if (use_tree)
|
|
delete_tree(&tree);
|
|
}
|
|
|
|
|
|
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++)
|
|
{
|
|
Item *item=args[i];
|
|
if (list.push_back(item))
|
|
return 1; // End of memory
|
|
if (item->const_item())
|
|
{
|
|
(void) item->val_int();
|
|
if (item->null_value)
|
|
always_null=1;
|
|
}
|
|
}
|
|
if (always_null)
|
|
return 0;
|
|
count_field_types(tmp_table_param,list,0);
|
|
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->select->options | thd->options)))
|
|
return 1;
|
|
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
|
|
table->no_rows=1;
|
|
|
|
|
|
// no blobs, otherwise it would be MyISAM
|
|
if (table->db_type == DB_TYPE_HEAP)
|
|
{
|
|
qsort_cmp2 compare_key;
|
|
void* cmp_arg;
|
|
|
|
// to make things easier for dump_leaf if we ever have to dump to MyISAM
|
|
restore_record(table,2);
|
|
|
|
if (table->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
|
|
*/
|
|
Field* field = table->field[0];
|
|
switch(field->type())
|
|
{
|
|
/*
|
|
If we have a string, we must take care of charsets and case
|
|
sensitivity
|
|
*/
|
|
case FIELD_TYPE_STRING:
|
|
case FIELD_TYPE_VAR_STRING:
|
|
compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp:
|
|
simple_str_key_cmp);
|
|
break;
|
|
default:
|
|
/*
|
|
Since at this point we cannot have blobs anything else can
|
|
be compared with memcmp
|
|
*/
|
|
compare_key = (qsort_cmp2)simple_raw_key_cmp;
|
|
break;
|
|
}
|
|
key_length = field->pack_length();
|
|
cmp_arg = (void*) &key_length;
|
|
rec_offset = 1;
|
|
}
|
|
else // too bad, cannot cheat - there is more than one field
|
|
{
|
|
bool all_binary = 1;
|
|
Field** field, **field_end;
|
|
field_end = (field = table->field) + table->fields;
|
|
uint32 *lengths;
|
|
if (!(field_lengths=
|
|
(uint32*) thd->alloc(sizeof(uint32) * table->fields)))
|
|
return 1;
|
|
|
|
for (key_length = 0, lengths=field_lengths; field < field_end; ++field)
|
|
{
|
|
uint32 length= (*field)->pack_length();
|
|
key_length += length;
|
|
*lengths++ = length;
|
|
if (!(*field)->binary())
|
|
all_binary = 0; // Can't break loop here
|
|
}
|
|
rec_offset = table->reclength - key_length;
|
|
if (all_binary)
|
|
{
|
|
compare_key = (qsort_cmp2)simple_raw_key_cmp;
|
|
cmp_arg = (void*) &key_length;
|
|
}
|
|
else
|
|
{
|
|
compare_key = (qsort_cmp2) composite_key_cmp ;
|
|
cmp_arg = (void*) this;
|
|
}
|
|
}
|
|
|
|
init_tree(&tree, min(max_heap_table_size, sortbuff_size/16), 0,
|
|
key_length, compare_key, 0, NULL, cmp_arg);
|
|
use_tree = 1;
|
|
|
|
/*
|
|
The only time 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
|
|
*/
|
|
max_elements_in_tree = ((key_length) ? max_heap_table_size/key_length :
|
|
1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Item_sum_count_distinct::tree_to_myisam()
|
|
{
|
|
if (create_myisam_from_heap(table, tmp_table_param,
|
|
HA_ERR_RECORD_FILE_FULL, 1) ||
|
|
tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this,
|
|
left_root_right))
|
|
return 1;
|
|
delete_tree(&tree);
|
|
use_tree = 0;
|
|
return 0;
|
|
}
|
|
|
|
void Item_sum_count_distinct::reset()
|
|
{
|
|
if (use_tree)
|
|
reset_tree(&tree);
|
|
else if (table)
|
|
{
|
|
table->file->extra(HA_EXTRA_NO_CACHE);
|
|
table->file->delete_all_rows();
|
|
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
|
}
|
|
(void) add();
|
|
}
|
|
|
|
bool Item_sum_count_distinct::add()
|
|
{
|
|
int error;
|
|
if (always_null)
|
|
return 0;
|
|
copy_fields(tmp_table_param);
|
|
copy_funcs(tmp_table_param->funcs);
|
|
|
|
for (Field **field=table->field ; *field ; field++)
|
|
if ((*field)->is_real_null(0))
|
|
return 0; // Don't count NULL
|
|
|
|
if (use_tree)
|
|
{
|
|
/*
|
|
If the tree got too big, convert to MyISAM, otherwise insert into the
|
|
tree.
|
|
*/
|
|
if (tree.elements_in_tree > max_elements_in_tree)
|
|
{
|
|
if (tree_to_myisam())
|
|
return 1;
|
|
}
|
|
else if (!tree_insert(&tree, table->record[0] + rec_offset, 0))
|
|
return 1;
|
|
}
|
|
else 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);
|
|
if (use_tree)
|
|
return tree.elements_in_tree;
|
|
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::add");
|
|
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 */
|