mariadb/sql/item_sum.cc
unknown 70aa7424c4 Big code cleanup/review before 4.0.2 release.
(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
2002-06-11 11:20:31 +03:00

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 */