diff options
Diffstat (limited to 'sql')
55 files changed, 5906 insertions, 990 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 3c520ac971c..498fb93abf3 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -58,7 +58,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ log_event.h sql_repl.h slave.h \ stacktrace.h sql_sort.h sql_cache.h set_var.h \ spatial.h gstream.h client_settings.h tzfile.h \ - tztime.h \ + tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ examples/ha_example.h examples/ha_archive.h \ @@ -95,7 +95,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ stacktrace.c repl_failsafe.h repl_failsafe.cc \ sql_olap.cc sql_view.cc \ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ - tztime.cc my_time.c \ + tztime.cc my_time.c my_decimal.cc\ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \ sp_cache.cc parse_file.cc sql_trigger.cc \ examples/ha_example.cc examples/ha_archive.cc \ diff --git a/sql/field.cc b/sql/field.cc index f95eaaba5df..ad66fec2fc2 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -52,6 +52,25 @@ const char field_separator=','; Static help functions *****************************************************************************/ +/* + Numeric fields base class constructor +*/ +Field_num::Field_num(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + struct st_table *table_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg) + :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg), + dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) +{ + if (zerofill) + flags|=ZEROFILL_FLAG; + if (unsigned_flag) + flags|=UNSIGNED_FLAG; +} + + void Field_num::prepend_zeros(String *value) { int diff; @@ -82,7 +101,7 @@ void Field_num::prepend_zeros(String *value) Make this multi-byte-character safe RETURN - 0 ok + 0 OK 1 error. A warning is pushed if field_name != 0 */ @@ -104,7 +123,7 @@ bool Field::check_int(const char *str, int length, const char *int_end, } end= str+length; if ((str= int_end) == end) - return 0; // ok; All digits was used + return 0; // OK; All digits was used /* Allow end .0000 */ if (*str == '.') @@ -125,6 +144,32 @@ bool Field::check_int(const char *str, int length, const char *int_end, } +/* + Process decimal library return codes and issue warnings for overflow and + truncation. + + SYNOPSIS + Field::check_overflow() + op_result decimal library return code (E_DEC_* see include/decimal.h) + + RETURN + 1 there was overflow + 0 no error or some other errors except overflow +*/ + +int Field::check_overflow(int op_result) +{ + if (op_result == E_DEC_OVERFLOW) + { + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + return 1; + } + else if (op_result == E_DEC_TRUNCATED) + set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + /* we return 1 only in case of EFL */ + return 0; +} + #ifdef NOT_USED static bool test_if_real(const char *str,int length, CHARSET_INFO *cs) { @@ -191,6 +236,10 @@ static Field::field_cast_enum field_cast_decimal[]= {Field::FIELD_CAST_DECIMAL, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +static Field::field_cast_enum field_cast_new_decimal[]= +{Field::FIELD_CAST_NEWDECIMAL, + Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, + Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_tiny[]= {Field::FIELD_CAST_TINY, Field::FIELD_CAST_SHORT, Field::FIELD_CAST_MEDIUM, Field::FIELD_CAST_LONG, @@ -283,6 +332,9 @@ static Field::field_cast_enum field_cast_varstring[]= static Field::field_cast_enum field_cast_blob[]= {Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; +static Field::field_cast_enum field_cast_bit[]= +{Field::FIELD_CAST_BIT, + Field::FIELD_CAST_STOP}; /* Geometrical, enum and set fields can be casted only to expressions */ @@ -302,7 +354,8 @@ static Field::field_cast_enum *field_cast_array[]= field_cast_timestamp, field_cast_year, field_cast_date, field_cast_newdate, field_cast_time, field_cast_datetime, field_cast_string, field_cast_varstring, field_cast_blob, - field_cast_geom, field_cast_enum, field_cast_set + field_cast_geom, field_cast_enum, field_cast_set, field_cast_bit, + field_cast_new_decimal }; @@ -398,6 +451,14 @@ bool Field::send_binary(Protocol *protocol) } +my_decimal *Field::val_decimal(my_decimal *decimal) +{ + /* This never have to be called */ + DBUG_ASSERT(0); + return 0; +} + + void Field_num::add_zerofill_and_unsigned(String &res) const { if (unsigned_flag) @@ -406,6 +467,7 @@ void Field_num::add_zerofill_and_unsigned(String &res) const res.append(" zerofill"); } + void Field::make_field(Send_field *field) { field->db_name= orig_table->s->table_cache_key; @@ -420,12 +482,155 @@ void Field::make_field(Send_field *field) } +/* + Conversion from decimal to longlong with checking overflow and + setting correct value (min/max) in case of overflow + + SYNOPSIS + Field::convert_decimal2longlong() + val value which have to be converted + unsigned_flag type of integer in which we convert val + err variable to pass error code + + RETURN + value converted from val +*/ +longlong Field::convert_decimal2longlong(const my_decimal *val, + bool unsigned_flag, int *err) +{ + longlong i; + if (unsigned_flag) + { + if (val->sign()) + { + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + i= 0; + *err= 1; + } + else if (check_overflow(my_decimal2int(E_DEC_ERROR & + ~E_DEC_OVERFLOW & ~E_DEC_TRUNCATED, + val, TRUE, &i))) + { + i= ~(longlong) 0; + *err= 1; + } + } + else if (check_overflow(my_decimal2int(E_DEC_ERROR & + ~E_DEC_OVERFLOW & ~E_DEC_TRUNCATED, + val, FALSE, &i))) + { + i= (val->sign() ? LONGLONG_MIN : LONGLONG_MAX); + *err= 1; + } + return i; +} + + +/* + Storing decimal in integer fields. + + SYNOPSIS + Field_num::store_decimal() + val value for storing + + NOTE + This method is used by all integer fields, real/decimal redefine it + + RETURN + 0 OK + != 0 error +*/ + +int Field_num::store_decimal(const my_decimal *val) +{ + int err= 0; + longlong i= convert_decimal2longlong(val, unsigned_flag, &err); + return test(err | store(i)); +} + + +/* + Return decimal value of integer field + + SYNOPSIS + Field_num::val_decimal() + decimal_value buffer for storing decimal value + + NOTE + This method is used by all integer fields, real/decimal redefine it + All longlong values fit in our decimal buffer which cal store 8*9=72 + digits of integer number + + RETURN + pointer to decimal buffer with value of field +*/ + +my_decimal* Field_num::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(result_type() == INT_RESULT); + longlong nr= val_int(); + if (!is_null()) + int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); + return decimal_value; +} + + +Field_str::Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + struct st_table *table_arg,CHARSET_INFO *charset) + :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg) +{ + field_charset=charset; + if (charset->state & MY_CS_BINSORT) + flags|=BINARY_FLAG; +} + + void Field_num::make_field(Send_field *field) { Field::make_field(field); field->decimals= dec; } +/* + Decimal representation of Field_str + + SYNOPSIS + Field_str::store_decimal() + d value for storing + + NOTE + Field_str is the base class for fields like Field_date, and some + similar. Some dates use fraction and also string value should be + converted to floating point value according our rules, so we use double + to store value of decimal in string + + RETURN + 0 OK + != 0 error +*/ + +int Field_str::store_decimal(const my_decimal *d) +{ + double val; + /* TODO: use decimal2string? */ + int err= check_overflow(my_decimal2double(E_DEC_FATAL_ERROR & + ~E_DEC_OVERFLOW, d, &val)); + return err | store(val); +} + + +my_decimal *Field_str::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(result_type() == INT_RESULT); + longlong nr= val_int(); + if (is_null()) + int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value); + return decimal_value; +} + uint Field::fill_cache_field(CACHE_FIELD *copy) { @@ -697,7 +902,7 @@ void Field_decimal::overflow(bool negative) int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); /* Convert character set if the old one is multi byte */ @@ -838,7 +1043,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) /* We only have to generate warnings if count_cuted_fields is set. This is to avoid extra checks of the number when they are not needed. - Even if this flag is not set, it's ok to increment warnings, if + Even if this flag is not set, it's OK to increment warnings, if it makes the code easer to read. */ @@ -1255,6 +1460,275 @@ void Field_decimal::sql_type(String &res) const /**************************************************************************** +** Field_new_decimal +****************************************************************************/ + +/* + Constructors of new decimal field. In case of using NOT_FIXED_DEC it try + to use maximally allowed length (DECIMAL_MAX_LENGTH) and number of digits + after decimal point maximally close to half of this range + (min(DECIMAL_MAX_LENGTH/2, NOT_FIXED_DEC-1)) +*/ +Field_new_decimal::Field_new_decimal(char *ptr_arg, + uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, + const char *field_name_arg, + struct st_table *table_arg, + uint8 dec_arg,bool zero_arg, + bool unsigned_arg) + :Field_num(ptr_arg, + (dec_arg == NOT_FIXED_DEC || len_arg > DECIMAL_MAX_LENGTH ? + DECIMAL_MAX_LENGTH : len_arg), + null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, + (dec_arg == NOT_FIXED_DEC ? + min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1) : + dec_arg), + zero_arg, unsigned_arg) +{ + bin_size= my_decimal_get_binary_size(field_length, dec); +} + + +Field_new_decimal::Field_new_decimal(uint32 len_arg, + bool maybe_null, + const char *name, + struct st_table *t_arg, + uint8 dec_arg) + :Field_num((char*) 0, + (dec_arg == NOT_FIXED_DEC|| len_arg > DECIMAL_MAX_LENGTH ? + DECIMAL_MAX_LENGTH : len_arg), + maybe_null ? (uchar*) "": 0, 0, + NONE, name, t_arg, + (dec_arg == NOT_FIXED_DEC ? + min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1) : + dec_arg), + 0, 0) +{ + bin_size= my_decimal_get_binary_size(field_length, dec); +} + + +void Field_new_decimal::reset(void) +{ + store_value(&decimal_zero); +} + + +/* + Generate max/min decimal value in case of overflow. + + SYNOPSIS + Field_new_decimal::set_value_on_overflow(); + decimal_value buffer for value + sign sign of value which caused overflow +*/ + +void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, + bool sign) +{ + DBUG_ENTER("Field_new_decimal::set_value_on_overflow"); + max_my_decimal(decimal_value, field_length, decimals()); + if (sign) + { + if (unsigned_flag) + my_decimal_set_zero(decimal_value); + else + decimal_value->sign(TRUE); + } + DBUG_VOID_RETURN; +} + + +/* + Store decimal value in the binary buffer + + SYNOPSIS + store_value(const my_decimal *decimal_value) + decimal_value my_decimal + + DESCRIPTION + checks if decimal_value fits into field size. + if it does, stores the decimal in the buffer using binary format. + Otherwise sets maximal number that can be stored in the field. +*/ + +bool Field_new_decimal::store_value(const my_decimal *decimal_value) +{ + DBUG_ENTER("Field_new_decimal::store_value"); + my_decimal *dec= (my_decimal*)decimal_value; + DBUG_EXECUTE("enter", print_decimal(dec);); + + /* check that we do not try to write negative value in unsigned field */ + if (unsigned_flag && decimal_value->sign()) + { + DBUG_PRINT("info", ("unsigned overflow")); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + dec= &decimal_zero; + } + DBUG_PRINT("info", ("saving with precision %d, scale: %d", + (int)field_length, (int)decimals())); + DBUG_EXECUTE("info", print_decimal(dec);); + + if (check_overflow(my_decimal2binary(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, + dec, ptr, + field_length, + decimals()))) + { + my_decimal buff; + DBUG_PRINT("info", ("overflow")); + set_value_on_overflow(&buff, dec->sign()); + my_decimal2binary(E_DEC_FATAL_ERROR, &buff, ptr, field_length, decimals()); + DBUG_EXECUTE("info", print_decimal_buff(&buff, ptr, bin_size);); + DBUG_RETURN(1); + } + DBUG_EXECUTE("info", print_decimal_buff(dec, ptr, bin_size);); + DBUG_RETURN(0); +} + + +int Field_new_decimal::store(const char *from, uint length, + CHARSET_INFO *charset) +{ + DBUG_ENTER("Field_new_decimal::store(char*)"); + int err; + my_decimal decimal_value; + switch ((err= str2my_decimal(E_DEC_FATAL_ERROR & + ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), + from, length, charset, &decimal_value))) + { + case E_DEC_TRUNCATED: + set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + break; + case E_DEC_OVERFLOW: + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_value_on_overflow(&decimal_value, decimal_value.sign()); + break; + case E_DEC_BAD_NUM: + push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, + ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + "decimal", from, field_name, + (ulong) table->in_use->row_count); + my_decimal_set_zero(&decimal_value); + break; + } + + DBUG_EXECUTE("info", print_decimal(&decimal_value);); + store_value(&decimal_value); + DBUG_RETURN(err); +} + + +int Field_new_decimal::store(double nr) +{ + my_decimal decimal_value; + int err= double2my_decimal(E_DEC_FATAL_ERROR & + ~E_DEC_OVERFLOW, nr, + &decimal_value); + /* + TODO: fix following when double2my_decimal when double2decimal + will return E_DEC_TRUNCATED always correctly + */ + if (!err) + { + double nr2; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &nr2); + if (nr2 != nr) + err= E_DEC_TRUNCATED; + } + if (check_overflow(err)) + set_value_on_overflow(&decimal_value, decimal_value.sign()); + store_value(&decimal_value); + return err; +} + + +int Field_new_decimal::store(longlong nr) +{ + my_decimal decimal_value; + int err; + if ((err= check_overflow(int2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, + nr, unsigned_flag, &decimal_value)))) + set_value_on_overflow(&decimal_value, decimal_value.sign()); + store_value(&decimal_value); + return err; +} + + +int Field_new_decimal::store_decimal(const my_decimal *decimal_value) +{ + return store_value(decimal_value); +} + + +double Field_new_decimal::val_real(void) +{ + double dbl; + my_decimal decimal_value; + my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl); + return dbl; +} + + +longlong Field_new_decimal::val_int(void) +{ + longlong i; + my_decimal decimal_value; + my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), + unsigned_flag, &i); + return i; +} + + +my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ENTER("Field_new_decimal::val_decimal"); + binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value, + field_length, + decimals()); + DBUG_EXECUTE("info", print_decimal_buff(decimal_value, ptr, bin_size);); + DBUG_RETURN(decimal_value); +} + + +String *Field_new_decimal::val_str(String *val_buffer, + String *val_ptr __attribute__((unused))) +{ + my_decimal decimal_value; + int fixed_precision= (zerofill ? + (field_length + (decimals() ? 1 : 0)) : + 0); + my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), + fixed_precision, decimals(), '0', + val_buffer); + return val_buffer; +} + + +int Field_new_decimal::cmp(const char *a,const char*b) +{ + return memcmp(a, b, bin_size); +} + + +void Field_new_decimal::sort_string(char *buff, + uint length __attribute__((unused))) +{ + memcpy(buff, ptr, bin_size); +} + + +void Field_new_decimal::sql_type(String &str) const +{ + CHARSET_INFO *cs= str.charset(); + str.length(cs->cset->snprintf(cs, (char*) str.ptr(), str.alloced_length(), + "decimal(%d,%d)", field_length, (int)dec)); + add_zerofill_and_unsigned(str); +} + +/**************************************************************************** ** tiny int ****************************************************************************/ @@ -2554,7 +3028,6 @@ int Field_float::store(longlong nr) return store((double)nr); } - double Field_float::val_real(void) { float j; @@ -2835,6 +3308,12 @@ int Field_double::store(longlong nr) return store((double)nr); } +int Field_real::store_decimal(const my_decimal *dm) +{ + double dbl; + my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl); + return store(dbl); +} double Field_double::val_real(void) { @@ -2865,6 +3344,13 @@ longlong Field_double::val_int(void) } +my_decimal *Field_real::val_decimal(my_decimal *decimal_value) +{ + double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value); + return decimal_value; +} + + String *Field_double::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { @@ -3032,7 +3518,7 @@ void Field_double::sql_type(String &res) const TIMESTAMP_OLD_FIELD - old timestamp, if there was not any fields with auto-set-on-update (or now() as default) in this table before, then this field has NOW() as default and is updated when row changes, else it is - field which has 0 as default value and is not automaitcally updated. + field which has 0 as default value and is not automatically updated. TIMESTAMP_DN_FIELD - field with NOW() as default but not set on update automatically (TIMESTAMP DEFAULT NOW()) TIMESTAMP_UN_FIELD - field which is set on update automatically but has not @@ -3094,7 +3580,7 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const return TIMESTAMP_AUTO_SET_ON_UPDATE; case TIMESTAMP_OLD_FIELD: /* - Altough we can have several such columns in legacy tables this + Although we can have several such columns in legacy tables this function should be called only for first of them (i.e. the one having auto-set property). */ @@ -3598,7 +4084,7 @@ String *Field_time::val_str(String *val_buffer, /* - Normally we would not consider 'time' as a vaild date, but we allow + Normally we would not consider 'time' as a valid date, but we allow get_date() here to be able to do things like DATE_FORMAT(time, "%l.%i %p") */ @@ -4319,7 +4805,7 @@ String *Field_datetime::val_str(String *val_buffer, longlongget(tmp,ptr); /* - Avoid problem with slow longlong aritmetic and sprintf + Avoid problem with slow longlong arithmetic and sprintf */ part1=(long) (tmp/LL(1000000)); @@ -4437,14 +4923,14 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { int error= 0; uint32 not_used; - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); uint copy_length; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); - /* Convert character set if nesessary */ + /* Convert character set if necessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { uint conv_errors; @@ -4542,6 +5028,16 @@ int Field_string::store(longlong nr) return Field_string::store(buff,(uint)l,cs); } +int Field_longstr::store_decimal(const my_decimal *d) +{ + uint buf_size= my_decimal_string_length(d); + char *buff= (char *)my_alloca(buf_size); + String str(buff, buf_size, &my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str); + int result= store(str.ptr(), str.length(), str.charset()); + my_afree(buff); + return result; +} double Field_string::val_real(void) { @@ -4561,6 +5057,14 @@ longlong Field_string::val_int(void) } +my_decimal *Field_longstr::val_decimal(my_decimal *decimal_value) +{ + str2my_decimal(E_DEC_FATAL_ERROR, ptr, field_length, charset(), + decimal_value); + return decimal_value; +} + + String *Field_string::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { @@ -4792,11 +5296,11 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { int error= 0; uint32 not_used, copy_length; - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); enum MYSQL_ERROR::enum_warning_level level= MYSQL_ERROR::WARN_LEVEL_WARN; - /* Convert character set if nesessary */ + /* Convert character set if necessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { uint conv_errors; @@ -4872,7 +5376,6 @@ longlong Field_varstring::val_int(void) &end_not_used, ¬_used); } - String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { @@ -5273,7 +5776,7 @@ Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint blob_pack_length, CHARSET_INFO *cs) - :Field_str(ptr_arg, (1L << min(blob_pack_length,3)*8)-1L, + :Field_longstr(ptr_arg, (1L << min(blob_pack_length,3)*8)-1L, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, cs), packlength(blob_pack_length) @@ -5396,12 +5899,12 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) else { bool was_conversion; - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); uint copy_length; uint32 not_used; - /* Convert character set if nesessary */ + /* Convert character set if necessary */ if ((was_conversion= String::needs_conversion(length, cs, field_charset, ¬_used))) { @@ -5415,7 +5918,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) copy_length= max_data_length(); /* - copy_length is ok as last argument to well_formed_len as this is never + copy_length is OK as last argument to well_formed_len as this is never used to limit the length of the data. The cut of long data is done with the 'min()' call below. */ @@ -5457,7 +5960,6 @@ int Field_blob::store(longlong nr) return Field_blob::store(value.ptr(), (uint) value.length(), cs); } - double Field_blob::val_real(void) { int not_used; @@ -5485,7 +5987,6 @@ longlong Field_blob::val_int(void) return my_strntoll(charset(),blob,length,10,NULL,¬_used); } - String *Field_blob::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { @@ -5998,10 +6499,10 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { int err= 0; uint32 not_used; - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); - /* Convert character set if nesessary */ + /* Convert character set if necessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { uint dummy_errors; @@ -6182,10 +6683,10 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) char *not_used; uint not_used2; uint32 not_used_offset; - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); - /* Convert character set if nesessary */ + /* Convert character set if necessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used_offset)) { uint dummy_errors; @@ -6444,6 +6945,14 @@ int Field_bit::store(longlong nr) } +int Field_bit::store_decimal(const my_decimal *val) +{ + int err= 0; + longlong i= convert_decimal2longlong(val, 1, &err); + return test(err | store(i)); +} + + double Field_bit::val_real(void) { return (double) Field_bit::val_int(); @@ -6487,6 +6996,13 @@ String *Field_bit::val_str(String *val_buffer, } +my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value) +{ + int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value); + return deciaml_value; +} + + int Field_bit::key_cmp(const byte *str, uint length) { if (bit_len) @@ -6593,6 +7109,9 @@ void create_field::create_length_to_internal_length(void) /* We need one extra byte to store the bits we save among the null bits */ key_length= pack_length+ test(length & 7); break; + case MYSQL_TYPE_NEWDECIMAL: + key_length= pack_length= my_decimal_get_binary_size(length, decimals); + break; default: key_length= pack_length= calc_pack_length(sql_type, length); break; @@ -6646,7 +7165,8 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case FIELD_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; case FIELD_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; case FIELD_TYPE_SET: - case FIELD_TYPE_ENUM: abort(); return 0; // This shouldn't happen + case FIELD_TYPE_ENUM: + case FIELD_TYPE_NEWDECIMAL: abort(); return 0; // This shouldn't happen case FIELD_TYPE_BIT: return length / 8; default: return 0; } @@ -6767,6 +7287,12 @@ Field *make_field(char *ptr, uint32 field_length, f_decimals(pack_flag), f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); + case FIELD_TYPE_NEWDECIMAL: + return new Field_new_decimal(ptr,field_length,null_pos,null_bit, + unireg_check, field_name, table, + f_decimals(pack_flag), + f_is_zerofill(pack_flag) != 0, + f_is_dec(pack_flag) == 0); case FIELD_TYPE_FLOAT: return new Field_float(ptr,field_length,null_pos,null_bit, unireg_check, field_name, table, @@ -6959,7 +7485,7 @@ create_field::create_field(Field *old_field,Field *orig_field) /* Produce warning or note about data saved into field - SYNOPSYS + SYNOPSIS set_warning() level - level of message (Note/Warning/Error) code - error code of message to be produced @@ -6991,7 +7517,7 @@ Field::set_warning(const uint level, const uint code, int cuted_increment) /* Produce warning or note about datetime string data saved into field - SYNOPSYS + SYNOPSIS set_datime_warning() level - level of message (Note/Warning/Error) code - error code of message to be produced @@ -7020,7 +7546,7 @@ Field::set_datetime_warning(const uint level, const uint code, /* Produce warning or note about integer datetime value saved into field - SYNOPSYS + SYNOPSIS set_warning() level - level of message (Note/Warning/Error) code - error code of message to be produced @@ -7052,7 +7578,7 @@ Field::set_datetime_warning(const uint level, const uint code, /* Produce warning or note about double datetime data saved into field - SYNOPSYS + SYNOPSIS set_warning() level - level of message (Note/Warning/Error) code - error code of message to be produced diff --git a/sql/field.h b/sql/field.h index 756fa713707..d0b43e77e5a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -80,7 +80,8 @@ public: FIELD_CAST_TIMESTAMP, FIELD_CAST_YEAR, FIELD_CAST_DATE, FIELD_CAST_NEWDATE, FIELD_CAST_TIME, FIELD_CAST_DATETIME, FIELD_CAST_STRING, FIELD_CAST_VARSTRING, FIELD_CAST_BLOB, - FIELD_CAST_GEOM, FIELD_CAST_ENUM, FIELD_CAST_SET, FIELD_CAST_BIT + FIELD_CAST_GEOM, FIELD_CAST_ENUM, FIELD_CAST_SET, FIELD_CAST_BIT, + FIELD_CAST_NEWDECIMAL }; utype unireg_check; @@ -96,9 +97,11 @@ public: virtual int store(const char *to,uint length,CHARSET_INFO *cs)=0; virtual int store(double nr)=0; virtual int store(longlong nr)=0; + virtual int store_decimal(const my_decimal *d)=0; virtual int store_time(TIME *ltime, timestamp_type t_type); virtual double val_real(void)=0; virtual longlong val_int(void)=0; + virtual my_decimal *val_decimal(my_decimal *); inline String *val_str(String *str) { return val_str(str, str); } /* val_str(buf1, buf2) gets two buffers and should use them as follows: @@ -287,10 +290,16 @@ public: int cuted_increment); void set_datetime_warning(const uint level, const uint code, double nr, timestamp_type ts_type); + int check_overflow(int op_result); virtual field_cast_enum field_cast_type()= 0; bool field_cast_compatible(field_cast_enum type); /* maximum possible display length */ virtual uint32 max_length()= 0; + /* length of field value symbolic representation (in bytes) */ + virtual uint32 representation_length() { return field_length; } + /* convert decimal to longlong with overflow check */ + longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, + int *err); friend bool reopen_table(THD *,struct st_table *,bool); friend int cre_myisam(my_string name, register TABLE *form, uint options, ulonglong auto_increment_value); @@ -317,16 +326,7 @@ public: uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg), - dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) - { - if (zerofill) - flags|=ZEROFILL_FLAG; - if (unsigned_flag) - flags|=UNSIGNED_FLAG; - } + uint8 dec_arg, bool zero_arg, bool unsigned_arg); Item_result result_type () const { return REAL_RESULT; } void prepend_zeros(String *value); void add_zerofill_and_unsigned(String &res) const; @@ -335,6 +335,8 @@ public: uint decimals() const { return (uint) dec; } uint size_of() const { return sizeof(*this); } bool eq_def(Field *field); + int store_decimal(const my_decimal *); + my_decimal *val_decimal(my_decimal *); }; @@ -345,18 +347,12 @@ public: Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, - struct st_table *table_arg,CHARSET_INFO *charset) - :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg) - { - field_charset=charset; - if (charset->state & MY_CS_BINSORT) - flags|=BINARY_FLAG; - } + struct st_table *table_arg, CHARSET_INFO *charset); Item_result result_type () const { return STRING_RESULT; } uint decimals() const { return NOT_FIXED_DEC; } int store(double nr); int store(longlong nr)=0; + int store_decimal(const my_decimal *); int store(const char *to,uint length,CHARSET_INFO *cs)=0; uint size_of() const { return sizeof(*this); } CHARSET_INFO *charset(void) const { return field_charset; } @@ -364,19 +360,54 @@ public: bool binary() const { return field_charset == &my_charset_bin; } uint32 max_length() { return field_length; } friend class create_field; + my_decimal *val_decimal(my_decimal *); +}; + +/* base class for Item_string, Item_valstring, Item_blob */ +class Field_longstr :public Field_str +{ +public: + Field_longstr(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + struct st_table *table_arg,CHARSET_INFO *charset) + :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, table_arg, charset) + {} + + my_decimal *val_decimal(my_decimal *); + int store_decimal(const my_decimal *d); +}; + +/* base class for float and double and decimal (old one) */ +class Field_real :public Field_num { +public: + + Field_real(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, utype unireg_check_arg, + const char *field_name_arg, + struct st_table *table_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg) + :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + field_name_arg, table_arg, dec_arg, zero_arg, unsigned_arg) + {} + + + int store_decimal(const my_decimal *); + my_decimal *val_decimal(my_decimal *); }; -class Field_decimal :public Field_num { +class Field_decimal :public Field_real { public: Field_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg, - dec_arg, zero_arg,unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, + dec_arg, zero_arg, unsigned_arg) {} enum_field_types type() const { return FIELD_TYPE_DECIMAL;} enum ha_base_keytype key_type() const @@ -398,6 +429,46 @@ public: }; +/* New decimal/numeric field which use fixed point arithmetic */ +class Field_new_decimal :public Field_num { +public: + uint bin_size; + Field_new_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, + uchar null_bit_arg, + enum utype unireg_check_arg, const char *field_name_arg, + struct st_table *table_arg, + uint8 dec_arg, bool zero_arg, bool unsigned_arg); + Field_new_decimal(uint32 len_arg, bool maybe_null_arg, + const char *field_name_arg, + struct st_table *table_arg, uint8 dec_arg); + enum_field_types type() const { return FIELD_TYPE_NEWDECIMAL;} + enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } + Item_result result_type () const { return DECIMAL_RESULT; } + void reset(void); + bool store_value(const my_decimal *decimal_value); + void set_value_on_overflow(my_decimal *decimal_value, bool sign); + int store(const char *to, uint length, CHARSET_INFO *charset); + int store(double nr); + int store(longlong nr); + int store_decimal(const my_decimal *); + double val_real(void); + longlong val_int(void); + my_decimal *val_decimal(my_decimal *); + String *val_str(String*, String *); + int cmp(const char *, const char*); + void sort_string(char *buff, uint length); + bool zero_pack() const { return 0; } + void sql_type(String &str) const; + uint32 max_length() + { return field_length + 1 + (dec ? 1 : 0) + (field_length == dec ? 1 : 0); } + uint32 representation_length() + { return field_length + 1 + (dec ? 1 : 0) + (field_length == dec ? 1 : 0); }; + field_cast_enum field_cast_type() { return FIELD_CAST_NEWDECIMAL; } + uint size_of() const { return sizeof(*this); } + uint32 pack_length() const { return (uint32) bin_size; } +}; + + class Field_tiny :public Field_num { public: Field_tiny(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, @@ -576,21 +647,22 @@ public: }; #endif -class Field_float :public Field_num { + +class Field_float :public Field_real { public: Field_float(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg, - dec_arg, zero_arg,unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, + dec_arg, zero_arg, unsigned_arg) {} Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, struct st_table *table_arg, uint8 dec_arg) - :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, - NONE, field_name_arg, table_arg,dec_arg,0,0) + :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, table_arg, dec_arg, 0, 0) {} enum_field_types type() const { return FIELD_TYPE_FLOAT;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } @@ -611,21 +683,21 @@ public: }; -class Field_double :public Field_num { +class Field_double :public Field_real { public: Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) - :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg, - dec_arg, zero_arg,unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, + dec_arg, zero_arg, unsigned_arg) {} Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, struct st_table *table_arg, uint8 dec_arg) - :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, - NONE, field_name_arg, table_arg,dec_arg,0,0) + :Field_real((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, table_arg, dec_arg, 0, 0) {} enum_field_types type() const { return FIELD_TYPE_DOUBLE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } @@ -646,7 +718,7 @@ public: }; -/* Everything saved in this will disapper. It will always return NULL */ +/* Everything saved in this will disappear. It will always return NULL */ class Field_null :public Field_str { static uchar null[1]; @@ -662,9 +734,11 @@ public: { null[0]=1; return 0; } int store(double nr) { null[0]=1; return 0; } int store(longlong nr) { null[0]=1; return 0; } + int store_decimal(const my_decimal *d) { null[0]=1; return 0; } void reset(void) {} double val_real(void) { return 0.0;} longlong val_int(void) { return 0;} + my_decimal *val_decimal(my_decimal *) { return 0; } String *val_str(String *value,String *value2) { value2->length(0); return value2;} int cmp(const char *a, const char *b) { return 0;} @@ -892,18 +966,18 @@ public: }; -class Field_string :public Field_str { +class Field_string :public Field_longstr { public: Field_string(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg,cs) {}; + :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, cs) {}; Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, table_arg, cs) {}; + struct st_table *table_arg, CHARSET_INFO *cs) + :Field_longstr((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, table_arg, cs) {}; enum_field_types type() const { @@ -942,7 +1016,7 @@ public: }; -class Field_varstring :public Field_str { +class Field_varstring :public Field_longstr { public: /* Store number of bytes used to store length (1 or 2) */ uint32 length_bytes; @@ -952,19 +1026,19 @@ public: uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, table_arg, cs), - length_bytes(length_bytes_arg) + :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, table_arg, cs), + length_bytes(length_bytes_arg) { if (table) table->s->varchar_fields++; } Field_varstring(uint32 len_arg,bool maybe_null_arg, - const char *field_name_arg, - struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, table_arg, cs), - length_bytes(len_arg < 256 ? 1 :2) + const char *field_name_arg, + struct st_table *table_arg, CHARSET_INFO *cs) + :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, table_arg, cs), + length_bytes(len_arg < 256 ? 1 :2) { if (table) table->s->varchar_fields++; @@ -1012,7 +1086,7 @@ public: }; -class Field_blob :public Field_str { +class Field_blob :public Field_longstr { protected: uint packlength; String value; // For temporaries @@ -1023,8 +1097,8 @@ public: CHARSET_INFO *cs); Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, struct st_table *table_arg, CHARSET_INFO *cs) - :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, table_arg, cs), + :Field_longstr((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, + NONE, field_name_arg, table_arg, cs), packlength(4) { flags|= BLOB_FLAG; @@ -1103,9 +1177,7 @@ public: bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } field_cast_enum field_cast_type() { return FIELD_CAST_BLOB; } - uint32 max_length(); -}; - + uint32 max_length();}; #ifdef HAVE_SPATIAL class Field_geom :public Field_blob { @@ -1130,7 +1202,7 @@ public: int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr) { return 1; } int store(longlong nr) { return 1; } - + int store_decimal(const my_decimal *) { return 1; } void get_key_image(char *buff,uint length,imagetype type); field_cast_enum field_cast_type() { return FIELD_CAST_GEOM; } }; @@ -1224,9 +1296,11 @@ public: int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr); + int store_decimal(const my_decimal *); double val_real(void); longlong val_int(void); String *val_str(String*, String *); + my_decimal *val_decimal(my_decimal *); int cmp(const char *a, const char *b) { return cmp_binary(a, b); } int key_cmp(const byte *a, const byte *b) diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 002f059f70b..57161a7063e 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -586,6 +586,9 @@ void field_conv(Field *to,Field *from) !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && to->real_type() != FIELD_TYPE_ENUM && to->real_type() != FIELD_TYPE_SET && + (to->real_type() != FIELD_TYPE_NEWDECIMAL || + (to->field_length == from->field_length && + (((Field_num*)to)->dec == ((Field_num*)from)->dec))) && from->charset() == to->charset() && to->table->s->db_low_byte_first == from->table->s->db_low_byte_first) { // Identical fields @@ -623,6 +626,11 @@ void field_conv(Field *to,Field *from) } else if (from->result_type() == REAL_RESULT) to->store(from->val_real()); + else if (from->result_type() == DECIMAL_RESULT) + { + my_decimal buff; + to->store_decimal(from->val_decimal(&buff)); + } else to->store(from->val_int()); } diff --git a/sql/filesort.cc b/sql/filesort.cc index 1665358dbf0..956ac2ef61b 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -703,6 +703,22 @@ static void make_sortkey(register SORTPARAM *param, #endif break; } + case DECIMAL_RESULT: + { + my_decimal dec_buf, *dec_val= item->val_decimal(&dec_buf); + if ((maybe_null=item->null_value)) + { + bzero((char*)to, sort_field->length+1); + to++; + break; + } + if ((maybe_null=item->maybe_null)) + *to++=1; + my_decimal2binary(E_DEC_FATAL_ERROR, dec_val, (byte*)to, + item->max_length - (item->decimals ? 1:0), + item->decimals); + break; + } case REAL_RESULT: { double value= item->val_real(); @@ -1212,6 +1228,12 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) sortorder->length=4; #endif break; + case DECIMAL_RESULT: + sortorder->length= + my_decimal_get_binary_size(sortorder->item->max_length - + (sortorder->item->decimals ? 1 : 0), + sortorder->item->decimals); + break; case REAL_RESULT: sortorder->length=sizeof(double); break; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index be493138fd0..052ee0643ee 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2159,6 +2159,8 @@ get_innobase_type_from_mysql_type( } else { return(DATA_MYSQL); } + case FIELD_TYPE_NEWDECIMAL: + return(DATA_BINARY); case FIELD_TYPE_LONG: case FIELD_TYPE_LONGLONG: case FIELD_TYPE_TINY: diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 9631b78bca3..ad7e775aacf 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -727,7 +727,7 @@ int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt) if ((error= mi_assign_to_key_cache(file, map, new_key_cache))) { - char buf[80]; + char buf[STRING_BUFFER_USUAL_SIZE]; my_snprintf(buf, sizeof(buf), "Failed to flush to index file (errno: %d)", error); errmsg= buf; diff --git a/sql/item.cc b/sql/item.cc index 30e03916a74..17ded05766f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -44,6 +44,34 @@ void item_init(void) item_user_lock_init(); } + +/* +TODO: make this functions class dependent +*/ +bool Item::val_bool() +{ + switch(result_type()) + { + case INT_RESULT: + return val_int(); + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } +} + + Item::Item(): name(0), orig_name(0), name_length(0), fixed(0), collation(&my_charset_bin, DERIVATION_COERCIBLE) @@ -72,7 +100,7 @@ Item::Item(): } /* - Constructor used by Item_field, Item_*_ref & agregate (sum) functions. + Constructor used by Item_field, Item_*_ref & aggregate (sum) functions. Used for duplicating lists in processing queries with temporary tables */ @@ -148,7 +176,7 @@ bool Item::cleanup_processor(byte *arg) void Item::rename(char *new_name) { /* - we can compare pointers to names here, bacause if name was not changed, + we can compare pointers to names here, because if name was not changed, pointer will be same */ if (!orig_name && new_name != name) @@ -411,6 +439,55 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) } +double Item_splocal::val_real() +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + double ret= it->val_real(); + Item::null_value= it->null_value; + return ret; +} + + +longlong Item_splocal::val_int() +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + longlong ret= it->val_int(); + Item::null_value= it->null_value; + return ret; +} + + +String *Item_splocal::val_str(String *sp) +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + String *ret= it->val_str(sp); + Item::null_value= it->null_value; + return ret; +} + + +my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + Item *it= this_item(); + my_decimal value, *val= it->val_decimal(&value); + Item::null_value= it->null_value; + return val; +} + + +bool Item_splocal::is_null() +{ + Item *it= this_item(); + bool ret= it->is_null(); + Item::null_value= it->null_value; + return ret; +} + + Item * Item_splocal::this_item() { @@ -438,12 +515,38 @@ Item_splocal::type() const } +bool Item_splocal::fix_fields(THD *, struct st_table_list *, Item **) +{ + Item *it= this_item(); + DBUG_ASSERT(it->fixed); + max_length= it->max_length; + decimals= it->decimals; + fixed= 1; + return FALSE; +} + + +void Item_splocal::cleanup() +{ + fixed= 0; +} + + +void Item_splocal::print(String *str) +{ + str->reserve(m_name.length+8); + str->append(m_name.str, m_name.length); + str->append('@'); + str->qs_append(m_offset); +} + + /* Aggregate two collations together taking into account their coercibility (aka derivation): - 0 == DERIVATION_EXPLICIT - an explicitely written COLLATE clause + 0 == DERIVATION_EXPLICIT - an explicitly written COLLATE clause 1 == DERIVATION_NONE - a mix of two different collations 2 == DERIVATION_IMPLICIT - a column 3 == DERIVATION_COERCIBLE - a string constant @@ -482,7 +585,7 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags) /* We do allow to use binary strings (like BLOBS) together with character strings. - Binaries have more precedance than a character + Binaries have more precedence than a character string of the same derivation. */ if (collation == &my_charset_bin) @@ -634,8 +737,8 @@ void Item_field::set_field(Field *field_par) { field=result_field=field_par; // for easy coding with fields maybe_null=field->maybe_null(); - max_length=field_par->field_length; decimals= field->decimals(); + max_length= field_par->representation_length(); table_name= *field_par->table_name; field_name= field_par->field_name; db_name= field_par->table->s->db; @@ -743,6 +846,7 @@ String *Item_field::val_str(String *str) return field->val_str(str,&str_value); } + double Item_field::val_real() { DBUG_ASSERT(fixed == 1); @@ -751,6 +855,7 @@ double Item_field::val_real() return field->val_real(); } + longlong Item_field::val_int() { DBUG_ASSERT(fixed == 1); @@ -760,6 +865,14 @@ longlong Item_field::val_int() } +my_decimal *Item_field::val_decimal(my_decimal *decimal_value) +{ + if ((null_value= field->is_null())) + return 0; + return field->val_decimal(decimal_value); +} + + String *Item_field::str_result(String *str) { if ((null_value=result_field->is_null())) @@ -814,6 +927,40 @@ longlong Item_field::val_int_result() } +my_decimal *Item_field::val_decimal_result(my_decimal *decimal_value) +{ + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_decimal(decimal_value); +} + + +bool Item_field::val_bool_result() +{ + if ((null_value= result_field->is_null())) + return FALSE; + switch (result_field->result_type()) + { + case INT_RESULT: + return result_field->val_int(); + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= result_field->val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return result_field->val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } +} + + bool Item_field::eq(const Item *item, bool binary_cmp) const { if (item->type() != FIELD_ITEM) @@ -872,10 +1019,17 @@ Item_int::Item_int(const char *str_arg, uint length) value= my_strtoll10(str_arg, &end_ptr, &error); max_length= (uint) (end_ptr - str_arg); name= (char*) str_arg; + unsigned_flag= value > 0; fixed= 1; } +my_decimal *Item_int::val_decimal(my_decimal *decimal_value) +{ + int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_value); + return decimal_value; +} + String *Item_int::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor @@ -916,7 +1070,98 @@ void Item_uint::print(String *str) } -String *Item_real::val_str(String *str) +Item_decimal::Item_decimal(const char *str_arg, uint length, + CHARSET_INFO *charset) +{ + str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value); + name= (char*) str_arg; + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + +Item_decimal::Item_decimal(longlong val, bool unsig) +{ + int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(double val, int precision, int scale) +{ + double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, + uint decimal_par, uint length) +{ + my_decimal2decimal(val_arg, &decimal_value); + name= (char*) str; + decimals= (uint8) decimal_par; + max_length= length; + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(my_decimal *value_par) +{ + my_decimal2decimal(value_par, &decimal_value); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(value_par); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +Item_decimal::Item_decimal(const char *bin, int precision, int scale) +{ + binary2my_decimal(E_DEC_FATAL_ERROR, bin, &decimal_value, precision, scale); + decimals= (uint8) decimal_value.frac; + max_length= my_decimal_max_length(&decimal_value); + fixed= 1; + unsigned_flag= !decimal_value.sign(); +} + + +longlong Item_decimal::val_int() +{ + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &result); + return result; +} + +double Item_decimal::val_real() +{ + double result; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); + return result; +} + +String *Item_decimal::val_str(String *result) +{ + result->set_charset(&my_charset_bin); + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result); + return result; +} + +void Item_decimal::print(String *str) +{ + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value); + str->append(str_value); +} + + +String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); @@ -925,6 +1170,15 @@ String *Item_real::val_str(String *str) } +my_decimal *Item_float::val_decimal(my_decimal *decimal_value) +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value); + return (decimal_value); +} + + void Item_string::print(String *str) { str->append('_'); @@ -934,8 +1188,20 @@ void Item_string::print(String *str) str->append('\''); } + +my_decimal *Item_string::val_decimal(my_decimal *decimal_value) +{ + /* following assert is redundant, because fixed=1 assigned in constructor */ + DBUG_ASSERT(fixed == 1); + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); + return (decimal_value); +} + + bool Item_null::eq(const Item *item, bool binary_cmp) const { return item->type() == type(); } + + double Item_null::val_real() { // following assert is redundant, because fixed=1 assigned in constructor @@ -959,6 +1225,11 @@ String *Item_null::val_str(String *str) return 0; } +my_decimal *Item_null::val_decimal(my_decimal *decimal_value) +{ + return 0; +} + Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs) { @@ -1005,7 +1276,6 @@ void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); /* These are cleared after each execution by reset() method */ - max_length= 0; null_value= 1; /* Because of NULL and string values we need to set max_length for each new @@ -1042,6 +1312,32 @@ void Item_param::set_double(double d) /* + Set decimal parameter value from string. + + SYNOPSIS + set_decimal() + str - character string + length - string length + + NOTE + as we use character strings to send decimal values in + binary protocol, we use str2my_decimal to convert it to + internal decimal value. +*/ +void Item_param::set_decimal(const char *str, ulong length) +{ + DBUG_ENTER("Item_param::set_decimal"); + + str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value); + state= DECIMAL_VALUE; + decimals= decimal_value.frac; + max_length= decimal_value.intg + decimals + 2; + maybe_null= 0; + DBUG_VOID_RETURN; +} + + +/* Set parameter value from TIME value. SYNOPSIS @@ -1094,6 +1390,7 @@ bool Item_param::set_str(const char *str, ulong length) &dummy_errors)) DBUG_RETURN(TRUE); state= STRING_VALUE; + max_length= length; maybe_null= 0; /* max_length and decimals are set after charset conversion */ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ @@ -1133,7 +1430,7 @@ bool Item_param::set_longdata(const char *str, ulong length) RETURN 0 OK - 1 Out of memort + 1 Out of memory */ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) @@ -1179,6 +1476,15 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) DBUG_RETURN(1); break; } + case DECIMAL_RESULT: + { + const my_decimal *ent_value= (const my_decimal *)entry->value; + my_decimal2decimal(ent_value, &decimal_value); + state= DECIMAL_VALUE; + decimals= ent_value->frac; + max_length= ent_value->intg + decimals + 2; + break; + } default: DBUG_ASSERT(0); set_null(); @@ -1210,7 +1516,7 @@ void Item_param::reset() str_value.length(0); str_value_ptr.length(0); /* - We must prevent all charset conversions untill data has been written + We must prevent all charset conversions until data has been written to the binary log. */ str_value.set_charset(&my_charset_bin); @@ -1238,6 +1544,8 @@ int Item_param::save_in_field(Field *field, bool no_conversions) return field->store(value.integer); case REAL_VALUE: return field->store(value.real); + case DECIMAL_VALUE: + return field->store_decimal(&decimal_value); case TIME_VALUE: field->store_time(&value.time, value.time.time_type); return 0; @@ -1288,6 +1596,12 @@ double Item_param::val_real() return value.real; case INT_VALUE: return (double) value.integer; + case DECIMAL_VALUE: + { + double result; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); + return result; + } case STRING_VALUE: case LONG_DATA_VALUE: { @@ -1318,6 +1632,12 @@ longlong Item_param::val_int() return (longlong) (value.real + (value.real > 0 ? 0.5 : -0.5)); case INT_VALUE: return value.integer; + case DECIMAL_VALUE: + { + longlong i; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i); + return i; + } case STRING_VALUE: case LONG_DATA_VALUE: { @@ -1336,6 +1656,36 @@ longlong Item_param::val_int() } +my_decimal *Item_param::val_decimal(my_decimal *dec) +{ + switch (state) { + case DECIMAL_VALUE: + return &decimal_value; + case REAL_VALUE: + double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec); + return dec; + case INT_VALUE: + int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec); + return dec; + case STRING_VALUE: + case LONG_DATA_VALUE: + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, dec); + return dec; + case TIME_VALUE: + { + longlong i= (longlong) TIME_to_ulonglong(&value.time); + int2my_decimal(E_DEC_FATAL_ERROR, i, 0, dec); + return dec; + } + case NULL_VALUE: + return 0; + default: + DBUG_ASSERT(0); + } + return 0; +} + + String *Item_param::val_str(String* str) { switch (state) { @@ -1348,6 +1698,11 @@ String *Item_param::val_str(String* str) case INT_VALUE: str->set(value.integer, &my_charset_bin); return str; + case DECIMAL_VALUE: + if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, + 0, 0, 0, str) <= 1) + return str; + return NULL; case TIME_VALUE: { if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) @@ -1380,6 +1735,11 @@ const String *Item_param::query_val_str(String* str) const case REAL_VALUE: str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); break; + case DECIMAL_VALUE: + if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, + 0, 0, 0, str) > 1) + return &my_null_string; + break; case TIME_VALUE: { char *buf, *ptr; @@ -1474,7 +1834,7 @@ void Item_param::print(String *str) } else { - char buffer[80]; + char buffer[STRING_BUFFER_USUAL_SIZE]; String tmp(buffer, sizeof(buffer), &my_charset_bin); const String *res; res= query_val_str(&tmp); @@ -1505,6 +1865,17 @@ String *Item_copy_string::val_str(String *str) } +my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) +{ + // Item_copy_string is used without fix_fields call + if (null_value) + return 0; + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); + return (decimal_value); +} + + + int Item_copy_string::save_in_field(Field *field, bool no_conversions) { if (null_value) @@ -1548,6 +1919,24 @@ longlong Item_ref_null_helper::val_int() } +my_decimal *Item_ref_null_helper::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + my_decimal *val= (*ref)->val_decimal_result(decimal_value); + owner->was_null|= null_value= (*ref)->null_value; + return val; +} + + +bool Item_ref_null_helper::val_bool() +{ + DBUG_ASSERT(fixed == 1); + bool val= (*ref)->val_bool_result(); + owner->was_null|= null_value= (*ref)->null_value; + return val; +} + + String* Item_ref_null_helper::val_str(String* s) { DBUG_ASSERT(fixed == 1); @@ -1931,7 +2320,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) { if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) - return TRUE; /* Some error occured (e.g. ambigous names). */ + return TRUE; /* Some error occurred (e.g. ambiguous names). */ if (ref != not_found_item) { DBUG_ASSERT(*ref && (*ref)->fixed); @@ -1995,7 +2384,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() */ - if (rf->fix_fields(thd, tables, reference) || rf->check_cols(1)) + if (!rf->fixed && + rf->fix_fields(thd, tables, reference) || rf->check_cols(1)) return TRUE; mark_as_dependent(thd, last, current_sel, rf); @@ -2016,7 +2406,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) rf is Item_ref => never substitute other items (in this case) during fix_fields() => we can use rf after fix_fields() */ - return rf->fix_fields(thd, tables, reference) || rf->check_cols(1); + return (!rf->fixed && + rf->fix_fields(thd, tables, reference) || rf->check_cols(1)); } } } @@ -2089,7 +2480,7 @@ void Item_field::cleanup() Item_ident::cleanup(); /* Even if this object was created by direct link to field in setup_wild() - it will be linked correctly next tyme by name of field and table alias. + it will be linked correctly next time by name of field and table alias. I.e. we can drop 'field'. */ field= result_field= 0; @@ -2263,9 +2654,17 @@ void Item_empty_string::make_field(Send_field *tmp_field) enum_field_types Item::field_type() const { - return ((result_type() == STRING_RESULT) ? MYSQL_TYPE_VARCHAR : - (result_type() == INT_RESULT) ? FIELD_TYPE_LONGLONG : - FIELD_TYPE_DOUBLE); + switch (result_type()) + { + case STRING_RESULT: return MYSQL_TYPE_VARCHAR; + case INT_RESULT: return FIELD_TYPE_LONGLONG; + case DECIMAL_RESULT: return FIELD_TYPE_NEWDECIMAL; + case REAL_RESULT: return FIELD_TYPE_DOUBLE; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + return FIELD_TYPE_VAR_STRING; + }; } @@ -2318,6 +2717,11 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table) case MYSQL_TYPE_DECIMAL: return new Field_decimal((char*) 0, max_length, null_ptr, 0, Field::NONE, name, table, decimals, 0, unsigned_flag); + case MYSQL_TYPE_NEWDECIMAL: + return new Field_new_decimal((char*) 0, max_length - (decimals?1:0), + null_ptr, 0, + Field::NONE, name, table, decimals, 0, + unsigned_flag); case MYSQL_TYPE_TINY: return new Field_tiny((char*) 0, max_length, null_ptr, 0, Field::NONE, name, table, 0, unsigned_flag); @@ -2453,7 +2857,7 @@ int Item_null::save_in_field(Field *field, bool no_conversions) field Field where we want to store NULL RETURN VALUES - 0 ok + 0 OK 1 Field doesn't support NULL values */ @@ -2492,6 +2896,15 @@ int Item::save_in_field(Field *field, bool no_conversions) field->set_notnull(); error=field->store(nr); } + else if (result_type() == DECIMAL_RESULT) + { + my_decimal decimal_value; + my_decimal *value= val_decimal(&decimal_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store_decimal(value); + } else { longlong nr=val_int(); @@ -2533,9 +2946,18 @@ int Item_int::save_in_field(Field *field, bool no_conversions) return field->store(nr); } + +int Item_decimal::save_in_field(Field *field, bool no_conversions) +{ + field->set_notnull(); + return field->store_decimal(&decimal_value); +} + + Item_num *Item_uint::neg() { - return new Item_real(name, - ((double) value), 0, max_length); + Item_decimal *item= new Item_decimal(value, 0); + return item->neg(); } @@ -2544,7 +2966,7 @@ Item_num *Item_uint::neg() value is not a true double value (overflow) */ -Item_real::Item_real(const char *str_arg, uint length) +Item_float::Item_float(const char *str_arg, uint length) { int error; char *end_not_used; @@ -2566,7 +2988,7 @@ Item_real::Item_real(const char *str_arg, uint length) } -int Item_real::save_in_field(Field *field, bool no_conversions) +int Item_float::save_in_field(Field *field, bool no_conversions) { double nr= val_real(); if (null_value) @@ -2576,7 +2998,7 @@ int Item_real::save_in_field(Field *field, bool no_conversions) } -void Item_real::print(String *str) +void Item_float::print(String *str) { if (presentation) { @@ -2639,6 +3061,16 @@ longlong Item_hex_string::val_int() } +my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value) +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + ulonglong value= (ulonglong)val_int(); + int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value); + return (decimal_value); +} + + int Item_hex_string::save_in_field(Field *field, bool no_conversions) { int error; @@ -2728,6 +3160,7 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_BIT: + case MYSQL_TYPE_NEWDECIMAL: { String *res; if ((res=val_str(buffer))) @@ -2818,6 +3251,20 @@ bool Item_field::send(Protocol *protocol, String *buffer) } +Item_ref::Item_ref(Item **item, const char *table_name_par, + const char *field_name_par) + :Item_ident(NullS, table_name_par, field_name_par), result_field(0), + ref(item) +{ + /* + This constructor used to create some internals references over fixed items + */ + DBUG_ASSERT(ref); + if (*ref) + set_properties(); +} + + /* Resolve the name of a reference to a column reference. @@ -2888,7 +3335,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) Item **group_by_ref= NULL; if (!(ref= resolve_ref_in_select_and_group(thd, this, current_sel))) - return TRUE; /* Some error occured (e.g. ambigous names). */ + return TRUE; /* Some error occurred (e.g. ambiguous names). */ if (ref == not_found_item) /* This reference was not resolved. */ { @@ -2918,7 +3365,7 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) { if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) - return TRUE; /* Some error occured (e.g. ambigous names). */ + return TRUE; /* Some error occurred (e.g. ambiguous names). */ if (ref != not_found_item) { DBUG_ASSERT(*ref && (*ref)->fixed); @@ -3145,6 +3592,106 @@ String *Item_ref::str_result(String* str) } +my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value) +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_decimal(decimal_value); + } + return val_decimal(decimal_value); +} + + +bool Item_ref::val_bool_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + switch (result_field->result_type()) + { + case INT_RESULT: + return result_field->val_int(); + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *val= result_field->val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return 0; + } + case REAL_RESULT: + case STRING_RESULT: + return result_field->val_real() != 0.0; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + } + return val_bool(); +} + + +double Item_ref::val_real() +{ + DBUG_ASSERT(fixed); + double tmp=(*ref)->val_result(); + null_value=(*ref)->null_value; + return tmp; +} + + +longlong Item_ref::val_int() +{ + DBUG_ASSERT(fixed); + longlong tmp=(*ref)->val_int_result(); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_ref::val_bool() +{ + DBUG_ASSERT(fixed); + bool tmp= (*ref)->val_bool_result(); + null_value= (*ref)->null_value; + return tmp; +} + + +String *Item_ref::val_str(String* tmp) +{ + DBUG_ASSERT(fixed); + tmp=(*ref)->str_result(tmp); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_ref::is_null() +{ + DBUG_ASSERT(fixed); + (void) (*ref)->val_int_result(); + return (*ref)->null_value; +} + + +bool Item_ref::get_date(TIME *ltime,uint fuzzydate) +{ + return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); +} + + +my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) +{ + my_decimal *val= (*ref)->val_decimal(decimal_value); + null_value= (*ref)->null_value; + return val; +} + + void Item_ref_null_helper::print(String *str) { str->append("<ref_null_helper>(", 18); @@ -3156,6 +3703,59 @@ void Item_ref_null_helper::print(String *str) } +double Item_direct_ref::val_real() +{ + double tmp=(*ref)->val_real(); + null_value=(*ref)->null_value; + return tmp; +} + + +longlong Item_direct_ref::val_int() +{ + longlong tmp=(*ref)->val_int(); + null_value=(*ref)->null_value; + return tmp; +} + + +String *Item_direct_ref::val_str(String* tmp) +{ + tmp=(*ref)->val_str(tmp); + null_value=(*ref)->null_value; + return tmp; +} + + +my_decimal *Item_direct_ref::val_decimal(my_decimal *decimal_value) +{ + my_decimal *tmp= (*ref)->val_decimal(decimal_value); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_direct_ref::val_bool() +{ + bool tmp= (*ref)->val_bool(); + null_value=(*ref)->null_value; + return tmp; +} + + +bool Item_direct_ref::is_null() +{ + (void) (*ref)->val_int(); + return (*ref)->null_value; +} + + +bool Item_direct_ref::get_date(TIME *ltime,uint fuzzydate) +{ + return (null_value=(*ref)->get_date(ltime,fuzzydate)); +} + + void Item_null_helper::print(String *str) { str->append("<null_helper>(", 14); @@ -3380,6 +3980,9 @@ Item_result item_cmp_type(Item_result a,Item_result b) return INT_RESULT; else if (a == ROW_RESULT || b == ROW_RESULT) return ROW_RESULT; + if ((a == INT_RESULT || a == DECIMAL_RESULT) && + (b == INT_RESULT || b == DECIMAL_RESULT)) + return DECIMAL_RESULT; return REAL_RESULT; } @@ -3394,7 +3997,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) item->result_type()); char *name=item->name; // Alloced by sql_alloc - if (res_type == STRING_RESULT) + switch (res_type) + { + case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),&my_charset_bin),*result; @@ -3407,22 +4012,40 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) char *tmp_str= sql_strmake(result->ptr(), length); new_item= new Item_string(name, tmp_str, length, result->charset()); } + break; } - else if (res_type == INT_RESULT) + case INT_RESULT: { longlong result=item->val_int(); uint length=item->max_length; bool null_value=item->null_value; new_item= (null_value ? (Item*) new Item_null(name) : (Item*) new Item_int(name, result, length)); + break; } - else + case REAL_RESULT: { // It must REAL_RESULT double result= item->val_real(); uint length=item->max_length,decimals=item->decimals; bool null_value=item->null_value; new_item= (null_value ? (Item*) new Item_null(name) : (Item*) - new Item_real(name, result, decimals, length)); + new Item_float(name, result, decimals, length)); + break; + } + case DECIMAL_RESULT: + { + my_decimal decimal_value; + my_decimal *result= item->val_decimal(&decimal_value); + uint length= item->max_length, decimals= item->decimals; + bool null_value= item->null_value; + new_item= (null_value ? + (Item*) new Item_null(name) : + (Item*) new Item_decimal(name, result, length, decimals)); + break; + } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } if (new_item) thd->change_item_tree(ref, new_item); @@ -3453,6 +4076,16 @@ bool field_is_equal_to_item(Field *field,Item *item) } if (res_type == INT_RESULT) return 1; // Both where of type int + if (res_type == DECIMAL_RESULT) + { + my_decimal item_buf, *item_val, + field_buf, *field_val; + item_val= item->val_decimal(&item_buf); + if (item->null_value) + return 1; // This must be true + field_val= field->val_decimal(&field_buf); + return !my_decimal_cmp(item_val, field_val); + } double result= item->val_real(); if (item->null_value) return 1; @@ -3467,6 +4100,8 @@ Item_cache* Item_cache::get_cache(Item_result type) return new Item_cache_int(); case REAL_RESULT: return new Item_cache_real(); + case DECIMAL_RESULT: + return new Item_cache_decimal(); case STRING_RESULT: return new Item_cache_str(); case ROW_RESULT: @@ -3494,6 +4129,23 @@ void Item_cache_int::store(Item *item) { value= item->val_int_result(); null_value= item->null_value; + unsigned_flag= item->unsigned_flag; +} + + +String *Item_cache_int::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + str->set(value, default_charset()); + return str; +} + + +my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); + return decimal_val; } @@ -3504,6 +4156,69 @@ void Item_cache_real::store(Item *item) } +longlong Item_cache_real::val_int() +{ + DBUG_ASSERT(fixed == 1); + return (longlong) (value+(value > 0 ? 0.5 : -0.5)); +} + + +String* Item_cache_real::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + str->set(value, decimals, default_charset()); + return str; +} + + +my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); + return decimal_val; +} + + +void Item_cache_decimal::store(Item *item) +{ + my_decimal *val= item->val_decimal_result(&decimal_value); + if (val != &decimal_value) + my_decimal2decimal(val, &decimal_value); + null_value= item->null_value; +} + +double Item_cache_decimal::val_real() +{ + DBUG_ASSERT(fixed); + double res; + my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); + return res; +} + +longlong Item_cache_decimal::val_int() +{ + DBUG_ASSERT(fixed); + longlong res; + my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); + return res; +} + +String* Item_cache_decimal::val_str(String *str) +{ + DBUG_ASSERT(fixed); + my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, + &decimal_value); + my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); + return str; +} + +my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed); + return &decimal_value; +} + + void Item_cache_str::store(Item *item) { value_buff.set(buffer, sizeof(buffer), item->collation.collation); @@ -3525,7 +4240,6 @@ void Item_cache_str::store(Item *item) } } - double Item_cache_str::val_real() { DBUG_ASSERT(fixed == 1); @@ -3549,6 +4263,16 @@ longlong Item_cache_str::val_int() return (longlong)0; } +my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) +{ + DBUG_ASSERT(fixed == 1); + if (value) + string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); + else + decimal_val= 0; + return decimal_val; +} + bool Item_cache_row::allocate(uint num) { @@ -3701,18 +4425,18 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item, TABLE *table) /* - STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT + STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT DECIMAL_RESULT ROW_RESULT should never appear in Item_type_holder::join_types, but it is included in following table just to make table full (there DBUG_ASSERT in function to catch ROW_RESULT) */ -static Item_result type_convertor[4][4]= -{{STRING_RESULT, STRING_RESULT, STRING_RESULT, ROW_RESULT}, - {STRING_RESULT, REAL_RESULT, REAL_RESULT, ROW_RESULT}, - {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT}, - {ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT}}; - +static Item_result type_convertor[5][5]= +{{STRING_RESULT, STRING_RESULT, STRING_RESULT, ROW_RESULT, STRING_RESULT}, + {STRING_RESULT, REAL_RESULT, REAL_RESULT, ROW_RESULT, REAL_RESULT}, + {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT}, + {ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT, ROW_RESULT}, + {STRING_RESULT, REAL_RESULT, DECIMAL_RESULT, ROW_RESULT, DECIMAL_RESULT}}; /* Values of 'from' field can be stored in 'to' field. @@ -3730,6 +4454,12 @@ static Item_result type_convertor[4][4]= inline bool is_attr_compatible(Item *from, Item *to) { return ((to->max_length >= from->max_length) && + ((to->result_type() != DECIMAL_RESULT && + to->result_type() != REAL_RESULT && + to->result_type() != INT_RESULT) || + (to->decimals >= from->decimals) && + ((to->max_length - to->decimals) >= + (from->max_length - from->decimals))) && (to->maybe_null || !from->maybe_null) && (to->result_type() != STRING_RESULT || from->result_type() != STRING_RESULT || @@ -3778,6 +4508,9 @@ bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table) if (use_new_field || (new_result_type != item_type) || (new_length > max_length) || (!maybe_null && item->maybe_null) || + ((new_result_type == REAL_RESULT || new_result_type == DECIMAL_RESULT) && + (decimals < item->decimals || + (max_length - decimals) < (new_length - item->decimals))) || (item_type == STRING_RESULT && collation.collation != item->collation.collation)) { @@ -3805,8 +4538,20 @@ bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table) return 1; } - max_length= max(max_length, new_length); - decimals= max(decimals, item->decimals); + if (new_result_type == DECIMAL_RESULT) + { + int intp1= new_length - item->decimals; + int intp2= max_length - decimals; + max_length= max(intp1, intp2); + decimals= max(decimals, item->decimals); + /* can't be overflow because it work only for decimals (no strings) */ + max_length+= decimals; + } + else + { + max_length= max(max_length, new_length); + decimals= max(decimals, item->decimals); + } maybe_null|= item->maybe_null; item_type= new_result_type; } @@ -3823,6 +4568,7 @@ uint32 Item_type_holder::real_length(Item *item) switch (item->result_type()) { case STRING_RESULT: + case DECIMAL_RESULT: return item->max_length; case REAL_RESULT: return 53; @@ -3848,6 +4594,11 @@ longlong Item_type_holder::val_int() return 0; } +my_decimal *Item_type_holder::val_decimal(my_decimal *) +{ + DBUG_ASSERT(0); // should never be called + return 0; +} String *Item_type_holder::val_str(String*) { diff --git a/sql/item.h b/sql/item.h index 8209c566025..ccbc00470e5 100644 --- a/sql/item.h +++ b/sql/item.h @@ -27,7 +27,7 @@ class Item_field; /* "Declared Type Collation" - A combination of collation and its deriviation. + A combination of collation and its derivation. */ enum Derivation @@ -45,7 +45,7 @@ enum Derivation MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value (i.e. constant). MY_COLL_ALLOW_CONV - allow any kind of conversion - (combintion of the above two) + (combination of the above two) MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE (e.g. when aggregating for comparison) MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV @@ -130,7 +130,7 @@ public: PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, - PARAM_ITEM, TRIGGER_FIELD_ITEM}; + PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -156,11 +156,11 @@ public: // alloc & destruct is done as start of select using sql_alloc Item(); /* - Constructor used by Item_field, Item_ref & agregate (sum) functions. + Constructor used by Item_field, Item_ref & aggregate (sum) functions. Used for duplicating lists in processing queries with temporary tables Also it used for Item_cond_and/Item_cond_or for creating - top AND/OR ctructure of WHERE clause to protect it of + top AND/OR structure of WHERE clause to protect it of optimisation changes in prepared statements */ Item(THD *thd, Item *item); @@ -193,40 +193,107 @@ public: virtual enum_field_types field_type() const; virtual enum Type type() const =0; /* valXXX methods must return NULL or 0 or 0.0 if null_value is set. */ + /* + Return double precision floating point representation of item. + + SYNOPSIS + val_real() + + RETURN + In case of NULL value return 0.0 and set null_value flag to TRUE. + If value is not null null_value flag will be reset to FALSE. + */ virtual double val_real()=0; + /* + Return integer representation of item. + + SYNOPSIS + val_int() + + RETURN + In case of NULL value return 0 and set null_value flag to TRUE. + If value is not null null_value flag will be reset to FALSE. + */ virtual longlong val_int()=0; /* Return string representation of this item object. - The argument to val_str() is an allocated buffer this or any - nested Item object can use to store return value of this method. - This buffer should only be used if the item itself doesn't have an - own String buffer. In case when the item maintains it's own string - buffer, it's preferrable to return it instead to minimize number of - mallocs/memcpys. - The caller of this method can modify returned string, but only in - case when it was allocated on heap, (is_alloced() is true). This - allows the caller to efficiently use a buffer allocated by a child - without having to allocate a buffer of it's own. The buffer, given - to val_str() as agrument, belongs to the caller and is later used - by the caller at it's own choosing. - A few implications from the above: - - unless you return a string object which only points to your buffer - but doesn't manages it you should be ready that it will be - modified. - - even for not allocated strings (is_alloced() == false) the caller - can change charset (see Item_func_{typecast/binary}. XXX: is this - a bug? - - still you should try to minimize data copying and return internal - object whenever possible. + SYNOPSIS + val_str() + str an allocated buffer this or any nested Item object can use to + store return value of this method. + + NOTE + Buffer passed via argument should only be used if the item itself + doesn't have an own String buffer. In case when the item maintains + it's own string buffer, it's preferable to return it instead to + minimize number of mallocs/memcpys. + The caller of this method can modify returned string, but only in case + when it was allocated on heap, (is_alloced() is true). This allows + the caller to efficiently use a buffer allocated by a child without + having to allocate a buffer of it's own. The buffer, given to + val_str() as argument, belongs to the caller and is later used by the + caller at it's own choosing. + A few implications from the above: + - unless you return a string object which only points to your buffer + but doesn't manages it you should be ready that it will be + modified. + - even for not allocated strings (is_alloced() == false) the caller + can change charset (see Item_func_{typecast/binary}. XXX: is this + a bug? + - still you should try to minimize data copying and return internal + object whenever possible. + + RETURN + In case of NULL value return 0 (NULL pointer) and set null_value flag + to TRUE. + If value is not null null_value flag will be reset to FALSE. + */ + virtual String *val_str(String *str)=0; + /* + Return decimal representation of item with fixed point. + + SYNOPSIS + val_decimal() + decimal_buffer buffer which can be used by Item for returning value + (but can be not) + + NOTE + Returned value should not be changed if it is not the same which was + passed via argument. + + RETURN + Return pointer on my_decimal (it can be other then passed via argument) + if value is not NULL (null_value flag will be reset to FALSE). + In case of NULL value it return 0 pointer and set null_value flag + to TRUE. + */ + virtual my_decimal *val_decimal(my_decimal *decimal_buffer)= 0; + /* + Return boolean value of item. + + RETURN + FALSE value is false or NULL + TRUE value is true (not equal to 0) */ - virtual String *val_str(String*)=0; + bool val_bool(); virtual Field *get_tmp_table_field() { return 0; } virtual Field *tmp_table_field(TABLE *t_arg) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } + + /* + *result* family of methods is analog of *val* family (see above) but + return value of result_field of item if it is present. If Item have not + result field, it return val(). This methods set null_value flag in same + way as *val* methods do it. + */ virtual double val_result() { return val_real(); } virtual longlong val_int_result() { return val_int(); } virtual String *str_result(String* tmp) { return val_str(tmp); } + virtual my_decimal *val_decimal_result(my_decimal *val) + { return val_decimal(val); } + virtual bool val_bool_result() { return val_bool(); } + /* bit map of tables used by item */ virtual table_map used_tables() const { return (table_map) 0L; } /* @@ -287,14 +354,14 @@ public: virtual void top_level_item() {} /* set field of temporary table for Item which can be switched on temporary - table during query processing (groupping and so on) + table during query processing (grouping and so on) */ virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } virtual bool is_bool_func() { return 0; } virtual void save_in_result_field(bool no_conversions) {} /* - set value of aggegate function in case of no rows for groupping were found + set value of aggregate function in case of no rows for grouping were found */ virtual void no_rows_in_result() {} virtual Item *copy_or_same(THD *thd) { return this; } @@ -368,6 +435,9 @@ public: Item *this_item(); Item *this_const_item() const; + bool fix_fields(THD *, struct st_table_list *, Item **); + void cleanup(); + inline uint get_offset() { return m_offset; @@ -377,37 +447,12 @@ public: // the item in the frame enum Type type() const; - inline double val_real() - { - Item *it= this_item(); - double ret= it->val_real(); - Item::null_value= it->null_value; - return ret; - } - - inline longlong val_int() - { - Item *it= this_item(); - longlong ret= it->val_int(); - Item::null_value= it->null_value; - return ret; - } - - inline String *val_str(String *sp) - { - Item *it= this_item(); - String *ret= it->val_str(sp); - Item::null_value= it->null_value; - return ret; - } - - inline bool is_null() - { - Item *it= this_item(); - bool ret= it->is_null(); - Item::null_value= it->null_value; - return ret; - } + double val_real(); + longlong val_int(); + String *val_str(String *sp); + my_decimal *val_decimal(my_decimal *); + inline bool is_null(); + void print(String *str); inline void make_field(Send_field *field) { @@ -432,14 +477,6 @@ public: return this_item()->save_in_field(field, no_conversions); } - void print(String *str) - { - str->reserve(m_name.length+8); - str->append(m_name.str, m_name.length); - str->append('@'); - str->qs_append(m_offset); - } - inline bool send(Protocol *protocol, String *str) { return this_item()->send(protocol, str); @@ -544,10 +581,13 @@ public: bool eq(const Item *item, bool binary_cmp) const; double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal *); String *val_str(String*); double val_result(); longlong val_int_result(); String *str_result(String* tmp); + my_decimal *val_decimal_result(my_decimal *); + bool val_bool_result(); bool send(Protocol *protocol, String *str_arg); void reset_field(Field *f); bool fix_fields(THD *, struct st_table_list *, Item **); @@ -600,12 +640,13 @@ public: double val_real(); longlong val_int(); String *val_str(String *str); + my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); int save_safe_in_field(Field *field); bool send(Protocol *protocol, String *str); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_NULL; } - // to prevent drop fixed flag (no need parent cleanup call) + /* to prevent drop fixed flag (no need parent cleanup call) */ void cleanup() {} bool basic_const_item() const { return 1; } Item *new_item() { return new Item_null(name); } @@ -623,7 +664,8 @@ public: enum enum_item_param_state { NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE, - STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE + STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE, + DECIMAL_VALUE } state; /* @@ -637,6 +679,7 @@ public: Can not be declared inside the union as it's not a POD type. */ String str_value_ptr; + my_decimal decimal_value; union { longlong integer; @@ -688,6 +731,7 @@ public: double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal*); String *val_str(String*); bool get_time(TIME *tm); bool get_date(TIME *tm, uint fuzzydate); @@ -696,6 +740,7 @@ public: void set_null(); void set_int(longlong i, uint32 max_length_arg); void set_double(double i); + void set_decimal(const char *str, ulong length); bool set_str(const char *str, ulong length); bool set_longdata(const char *str, ulong length); void set_time(TIME *tm, timestamp_type type, uint32 max_length_arg); @@ -746,6 +791,7 @@ public: enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } + my_decimal *val_decimal(my_decimal *); String *val_str(String*); int save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } @@ -784,14 +830,52 @@ public: }; -class Item_real :public Item_num +/* decimal (fixed point) constant */ +class Item_decimal :public Item_num +{ +protected: + my_decimal decimal_value; +public: + Item_decimal(const char *str_arg, uint length, CHARSET_INFO *charset); + Item_decimal(const char *str, const my_decimal *val_arg, + uint decimal_par, uint length); + Item_decimal(my_decimal *value_par); + Item_decimal(longlong val, bool unsig); + Item_decimal(double val, int precision, int scale); + Item_decimal(const char *bin, int precision, int scale); + + enum Type type() const { return DECIMAL_ITEM; } + enum Item_result result_type () const { return DECIMAL_RESULT; } + enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + longlong val_int(); + double val_real(); + String *val_str(String*); + my_decimal *val_decimal(my_decimal *val) { return &decimal_value; } + int save_in_field(Field *field, bool no_conversions); + bool basic_const_item() const { return 1; } + Item *new_item() + { + return new Item_decimal(name, &decimal_value, decimals, max_length); + } + // to prevent drop fixed flag (no need parent cleanup call) + void cleanup() {} + void print(String *str); + Item_num *neg() + { + my_decimal_neg(&decimal_value); + unsigned_flag= !decimal_value.sign(); + return this; + } +}; + +class Item_float :public Item_num { char *presentation; public: double value; // Item_real() :value(0) {} - Item_real(const char *str_arg, uint length); - Item_real(const char *str,double val_arg,uint decimal_par,uint length) + Item_float(const char *str_arg, uint length); + Item_float(const char *str,double val_arg,uint decimal_par,uint length) :value(val_arg) { presentation= name=(char*) str; @@ -799,7 +883,8 @@ public: max_length=length; fixed= 1; } - Item_real(double value_par) :presentation(0), value(value_par) { fixed= 1; } + Item_float(double value_par) :presentation(0), value(value_par) { fixed= 1; } + int save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } @@ -810,37 +895,29 @@ public: return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } String *val_str(String*); + my_decimal *val_decimal(my_decimal *); bool basic_const_item() const { return 1; } // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} - Item *new_item() { return new Item_real(name,value,decimals,max_length); } + Item *new_item() + { return new Item_float(name, value, decimals, max_length); } Item_num *neg() { value= -value; return this; } void print(String *str); }; -class Item_static_real_func :public Item_real +class Item_static_float_func :public Item_float { const char *func_name; public: - Item_static_real_func(const char *str, double val_arg, uint decimal_par, + Item_static_float_func(const char *str, double val_arg, uint decimal_par, uint length) - :Item_real(NullS, val_arg, decimal_par, length), func_name(str) + :Item_float(NullS, val_arg, decimal_par, length), func_name(str) {} void print(String *str) { str->append(func_name); } }; -class Item_float :public Item_real -{ -public: - Item_float(const char *str,uint length) :Item_real(str,length) - { - decimals=NOT_FIXED_DEC; - max_length=DBL_DIG+8; - } -}; - class Item_string :public Item { public: @@ -894,6 +971,7 @@ public: DBUG_ASSERT(fixed == 1); return (String*) &str_value; } + my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } @@ -932,7 +1010,7 @@ class Item_datetime :public Item_string { public: Item_datetime(const char *item_name): Item_string(item_name,"",0, - &my_charset_bin) + &my_charset_bin) { max_length=19;} enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } }; @@ -971,6 +1049,7 @@ public: longlong val_int(); bool basic_const_item() const { return 1; } String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } + my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } @@ -1033,54 +1112,25 @@ public: TODO we probably fix a superset of problems like in BUG#6658. Check this with Bar, and if we have a more broader set of problems like this. */ - Item_ref(Item **item, const char *table_name_par, const char *field_name_par) - :Item_ident(NullS, table_name_par, field_name_par), result_field(0), ref(item) - { - DBUG_ASSERT(item); - if (*item) - set_properties(); - } + Item_ref(Item **item, const char *table_name_par, const char *field_name_par); /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) :Item_ident(thd, item), result_field(item->result_field), ref(item->ref) {} enum Type type() const { return REF_ITEM; } bool eq(const Item *item, bool binary_cmp) const { return ref && (*ref)->eq(item, binary_cmp); } - double val_real() - { - DBUG_ASSERT(fixed); - double tmp=(*ref)->val_result(); - null_value=(*ref)->null_value; - return tmp; - } - longlong val_int() - { - DBUG_ASSERT(fixed); - longlong tmp=(*ref)->val_int_result(); - null_value=(*ref)->null_value; - return tmp; - } - String *val_str(String* tmp) - { - DBUG_ASSERT(fixed); - tmp=(*ref)->str_result(tmp); - null_value=(*ref)->null_value; - return tmp; - } - bool is_null() - { - DBUG_ASSERT(fixed); - (void) (*ref)->val_int_result(); - return (*ref)->null_value; - } - bool get_date(TIME *ltime,uint fuzzydate) - { - DBUG_ASSERT(fixed); - return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); - } + double val_real(); + longlong val_int(); + my_decimal *val_decimal(my_decimal *); + bool val_bool(); + String *val_str(String* tmp); + bool is_null(); + bool get_date(TIME *ltime,uint fuzzydate); double val_result(); longlong val_int_result(); String *str_result(String* tmp); + my_decimal *val_decimal_result(my_decimal *); + bool val_bool_result(); bool send(Protocol *prot, String *tmp); void make_field(Send_field *field) { (*ref)->make_field(field); } bool fix_fields(THD *, struct st_table_list *, Item **); @@ -1121,33 +1171,13 @@ public: /* Constructor need to process subselect with temporary tables (see Item) */ Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {} - double val_real() - { - double tmp=(*ref)->val_real(); - null_value=(*ref)->null_value; - return tmp; - } - longlong val_int() - { - longlong tmp=(*ref)->val_int(); - null_value=(*ref)->null_value; - return tmp; - } - String *val_str(String* tmp) - { - tmp=(*ref)->val_str(tmp); - null_value=(*ref)->null_value; - return tmp; - } - bool is_null() - { - (void) (*ref)->val_int(); - return (*ref)->null_value; - } - bool get_date(TIME *ltime,uint fuzzydate) - { - return (null_value=(*ref)->get_date(ltime,fuzzydate)); - } + double val_real(); + longlong val_int(); + String *val_str(String* tmp); + my_decimal *val_decimal(my_decimal *); + bool val_bool(); + bool is_null(); + bool get_date(TIME *ltime,uint fuzzydate); }; @@ -1164,6 +1194,8 @@ public: double val_real(); longlong val_int(); String* val_str(String* s); + my_decimal *val_decimal(my_decimal *); + bool val_bool(); bool get_date(TIME *ltime, uint fuzzydate); void print(String *str); }; @@ -1174,7 +1206,8 @@ class Item_null_helper :public Item_ref_null_helper public: Item_null_helper(Item_in_subselect* master, Item *item, const char *table_name_par, const char *field_name_par) - :Item_ref_null_helper(master, &item, table_name_par, field_name_par), + :Item_ref_null_helper(master, (store= 0, &store), table_name_par, + field_name_par), store(item) { ref= &store; } void print(String *str); @@ -1243,6 +1276,7 @@ public: return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),10, (char**) 0,&err); } String *val_str(String*); + my_decimal *val_decimal(my_decimal *); void make_field(Send_field *field) { item->make_field(field); } void copy(); int save_in_field(Field *field, bool no_conversions); @@ -1291,6 +1325,15 @@ public: }; +class Item_decimal_buff :public Item_buff +{ + Item *item; + my_decimal value; +public: + Item_decimal_buff(Item *item_par); + bool cmp(void); +}; + class Item_field_buff :public Item_buff { char *buff; @@ -1453,24 +1496,23 @@ public: void print(String *str); }; + class Item_cache_int: public Item_cache { +protected: longlong value; public: Item_cache_int(): Item_cache(), value(0) {} - + void store(Item *item); double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } - String* val_str(String *str) - { - DBUG_ASSERT(fixed == 1); - str->set(value, default_charset()); - return str; - } + String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return INT_RESULT; } }; + class Item_cache_real: public Item_cache { double value; @@ -1479,30 +1521,41 @@ public: void store(Item *item); double val_real() { DBUG_ASSERT(fixed == 1); return value; } - longlong val_int() - { - DBUG_ASSERT(fixed == 1); - return (longlong) (value+(value > 0 ? 0.5 : -0.5)); - } - String* val_str(String *str) - { - str->set(value, decimals, default_charset()); - return str; - } + longlong val_int(); + String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return REAL_RESULT; } }; + +class Item_cache_decimal: public Item_cache +{ +protected: + my_decimal decimal_value; +public: + Item_cache_decimal(): Item_cache() {} + + void store(Item *item); + double val_real(); + longlong val_int(); + String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); + enum Item_result result_type() const { return DECIMAL_RESULT; } +}; + + class Item_cache_str: public Item_cache { - char buffer[80]; + char buffer[STRING_BUFFER_USUAL_SIZE]; String *value, value_buff; public: Item_cache_str(): Item_cache(), value(0) { } - + void store(Item *item); double val_real(); longlong val_int(); String* val_str(String *) { DBUG_ASSERT(fixed == 1); return value; } + my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return STRING_RESULT; } CHARSET_INFO *charset() const { return value->charset(); }; }; @@ -1547,6 +1600,12 @@ public: illegal_method_call((const char*)"val_str"); return 0; }; + my_decimal *val_decimal(my_decimal *val) + { + illegal_method_call((const char*)"val_decimal"); + return 0; + }; + enum Item_result result_type() const { return ROW_RESULT; } uint cols() { return item_count; } @@ -1585,6 +1644,7 @@ public: enum Type type() const { return TYPE_HOLDER; } double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal *); String *val_str(String*); bool join_types(THD *thd, Item *, TABLE *); Field *example() { return field_example; } diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 66de26dba9a..7c77f7fa3bc 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -28,11 +28,21 @@ Item_buff *new_Item_buff(Item *item) if (item->type() == Item::FIELD_ITEM && !(((Item_field *) item)->field->flags & BLOB_FLAG)) return new Item_field_buff((Item_field *) item); - if (item->result_type() == STRING_RESULT) + switch (item->result_type()) + { + case STRING_RESULT: return new Item_str_buff((Item_field *) item); - if (item->result_type() == INT_RESULT) + case INT_RESULT: return new Item_int_buff((Item_field *) item); - return new Item_real_buff(item); + case REAL_RESULT: + return new Item_real_buff(item); + case DECIMAL_RESULT: + return new Item_decimal_buff(item); + case ROW_RESULT: + default: + DBUG_ASSERT(0); + return 0; + } } Item_buff::~Item_buff() {} @@ -107,6 +117,27 @@ bool Item_field_buff::cmp(void) } +Item_decimal_buff::Item_decimal_buff(Item *it) + :item(it) +{ + my_decimal_set_zero(&value); +} + + +bool Item_decimal_buff::cmp() +{ + my_decimal tmp; + my_decimal *ptmp= item->val_decimal(&tmp); + if (null_value != item->null_value || my_decimal_cmp(&value, ptmp) == 0) + { + null_value= item->null_value; + my_decimal2decimal(ptmp, &value); + return TRUE; + } + return FALSE; +} + + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1079e31e23a..71a4e4dda53 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -31,6 +31,8 @@ static Item_result item_store_type(Item_result a,Item_result b) return STRING_RESULT; else if (a == REAL_RESULT || b == REAL_RESULT) return REAL_RESULT; + else if (a == DECIMAL_RESULT || b == DECIMAL_RESULT) + return DECIMAL_RESULT; else return INT_RESULT; } @@ -51,7 +53,8 @@ static void agg_cmp_type(Item_result *type, Item **items, uint nitems) type[0]= item_cmp_type(type[0], items[i]->result_type()); } -static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) +static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, + const char *fname) { my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), c1.collation->name,c1.derivation_name(), @@ -104,7 +107,7 @@ Item_bool_func2* Le_creator::create(Item *a, Item *b) const longlong Item_func_not::val_int() { DBUG_ASSERT(fixed == 1); - double value= args[0]->val_real(); + bool value= args[0]->val_bool(); null_value=args[0]->null_value; return ((!null_value && value == 0) ? 1 : 0); } @@ -116,11 +119,11 @@ longlong Item_func_not::val_int() longlong Item_func_not_all::val_int() { DBUG_ASSERT(fixed == 1); - double value= args[0]->val_real(); + bool value= args[0]->val_bool(); /* - return TRUE if there was records in underlaying select in max/min - optimisation (ALL subquery) + return TRUE if there was records in underlying select in max/min + optimization (ALL subquery) */ if (empty_underlying_subquery()) return 1; @@ -147,7 +150,7 @@ void Item_func_not_all::print(String *str) /* Special NOP (No OPeration) for ALL subquery it is like Item_func_not_all - (return TRUE if underlaying sudquery do not return rows) but if subquery + (return TRUE if underlying subquery do not return rows) but if subquery returns some rows it return same value as argument (TRUE/FALSE). */ @@ -157,8 +160,8 @@ longlong Item_func_nop_all::val_int() longlong value= args[0]->val_int(); /* - return FALSE if there was records in underlaying select in max/min - optimisation (SAME/ANY subquery) + return FALSE if there was records in underlying select in max/min + optimization (SAME/ANY subquery) */ if (empty_underlying_subquery()) return 0; @@ -270,7 +273,9 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) owner= item; func= comparator_matrix[type] [test(owner->functype() == Item_func::EQUAL_FUNC)]; - if (type == ROW_RESULT) + switch(type) + { + case ROW_RESULT: { uint n= (*a)->cols(); if (n != (*b)->cols()) @@ -290,8 +295,9 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) } comparators[i].set_cmp_func(owner, (*a)->addr(i), (*b)->addr(i)); } + break; } - else if (type == STRING_RESULT) + case STRING_RESULT: { /* We must set cmp_charset here as we may be called from for an automatic @@ -315,7 +321,7 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) func= &Arg_comparator::compare_e_binary_string; /* - As this is binary comparsion, mark all fields that they can't be + As this is binary compassion, mark all fields that they can't be transformed. Otherwise we would get into trouble with comparisons like: WHERE col= 'j' AND col LIKE BINARY 'j' @@ -325,14 +331,16 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) (*a)->transform(&Item::set_no_const_sub, (byte*) 0); (*b)->transform(&Item::set_no_const_sub, (byte*) 0); } + break; } - else if (type == INT_RESULT) + case INT_RESULT: { if (func == &Arg_comparator::compare_int_signed) { if ((*a)->unsigned_flag) - func= ((*b)->unsigned_flag)? &Arg_comparator::compare_int_unsigned : - &Arg_comparator::compare_int_unsigned_signed; + func= (((*b)->unsigned_flag)? + &Arg_comparator::compare_int_unsigned : + &Arg_comparator::compare_int_unsigned_signed); else if ((*b)->unsigned_flag) func= &Arg_comparator::compare_int_signed_unsigned; } @@ -341,6 +349,13 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) func= &Arg_comparator::compare_e_int_diff_signedness; } + break; + } + case DECIMAL_RESULT: + case REAL_RESULT: + break; + default: + DBUG_ASSERT(0); } return 0; } @@ -434,6 +449,24 @@ int Arg_comparator::compare_real() return -1; } +int Arg_comparator::compare_decimal() +{ + my_decimal value1; + my_decimal *val1= (*a)->val_decimal(&value1); + if (!(*a)->null_value) + { + my_decimal value2; + my_decimal *val2= (*b)->val_decimal(&value2); + if (!(*b)->null_value) + { + owner->null_value= 0; + return my_decimal_cmp(val1, val2); + } + } + owner->null_value= 1; + return -1; +} + int Arg_comparator::compare_e_real() { double val1= (*a)->val_real(); @@ -443,6 +476,16 @@ int Arg_comparator::compare_e_real() return test(val1 == val2); } +int Arg_comparator::compare_e_decimal() +{ + my_decimal value1, value2; + my_decimal *val1= (*a)->val_decimal(&value1); + my_decimal *val2= (*b)->val_decimal(&value2); + if ((*a)->null_value || (*b)->null_value) + return test((*a)->null_value && (*b)->null_value); + return test(my_decimal_cmp(val1, val2) == 0); +} + int Arg_comparator::compare_int_signed() { longlong val1= (*a)->val_int(); @@ -776,6 +819,8 @@ longlong Item_func_strcmp::val_int() void Item_func_interval::fix_length_and_dec() { + use_decimal_comparison= (row->el(0)->result_type() == DECIMAL_RESULT) || + (row->el(0)->result_type() == INT_RESULT); if (row->cols() > 8) { bool consts=1; @@ -786,10 +831,41 @@ void Item_func_interval::fix_length_and_dec() } if (consts && - (intervals=(double*) sql_alloc(sizeof(double)*(row->cols()-1)))) + (intervals= + (interval_range*) sql_alloc(sizeof(interval_range)*(row->cols()-1)))) { - for (uint i=1 ; i < row->cols(); i++) - intervals[i-1]= row->el(i)->val_real(); + if (use_decimal_comparison) + { + for (uint i=1 ; i < row->cols(); i++) + { + Item *el= row->el(i); + interval_range *range= intervals + (i-1); + if ((el->result_type() == DECIMAL_RESULT) || + (el->result_type() == INT_RESULT)) + { + range->type= DECIMAL_RESULT; + range->dec.init(); + my_decimal *dec= el->val_decimal(&range->dec); + if (dec != &range->dec) + { + range->dec= *dec; + range->dec.fix_buffer_pointer(); + } + } + else + { + range->type= REAL_RESULT; + range->dbl= el->val_real(); + } + } + } + else + { + for (uint i=1 ; i < row->cols(); i++) + { + intervals[i-1].dbl= row->el(i)->val_real(); + } + } } } maybe_null= 0; @@ -812,6 +888,11 @@ longlong Item_func_interval::val_int() { DBUG_ASSERT(fixed == 1); double value= row->el(0)->val_real(); + my_decimal dec_buf, *dec= NULL; + if (use_decimal_comparison) + { + dec= row->el(0)->val_decimal(&dec_buf); + } uint i; if (row->el(0)->null_value) @@ -824,18 +905,37 @@ longlong Item_func_interval::val_int() while (start != end) { uint mid= (start + end + 1) / 2; - if (intervals[mid] <= value) + interval_range *range= intervals + mid; + my_bool cmp_result; + if (dec && range->type == DECIMAL_RESULT) + cmp_result= my_decimal_cmp(&range->dec, dec) <= 0; + else + cmp_result= (range->dbl <= value); + if (cmp_result) start= mid; else end= mid - 1; } - return (value < intervals[start]) ? 0 : start + 1; + interval_range *range= intervals+start; + return ((dec && range->type == DECIMAL_RESULT) ? + my_decimal_cmp(dec, &range->dec) < 0 : + value < range->dbl) ? 0 : start + 1; } for (i=1 ; i < row->cols() ; i++) { - if (row->el(i)->val_real() > value) - return i-1; + Item *el= row->el(i); + if (use_decimal_comparison && + ((el->result_type() == DECIMAL_RESULT) || + (el->result_type() == INT_RESULT))) + { + my_decimal e_dec_buf, *e_dec= row->el(i)->val_decimal(&e_dec_buf); + if (my_decimal_cmp(e_dec, dec) > 0) + return i-1; + } + else + if (row->el(i)->val_real() > value) + return i-1; } return i-1; } @@ -847,7 +947,7 @@ void Item_func_between::fix_length_and_dec() /* As some compare functions are generated after sql_yacc, - we have to check for out of memory conditons here + we have to check for out of memory conditions here */ if (!args[0] || !args[1] || !args[2]) return; @@ -857,7 +957,7 @@ void Item_func_between::fix_length_and_dec() return; /* - Make a special case of compare with date/time and longlong fields. + Make a special ease of compare with date/time and longlong fields. They are compared as integers, so for const item this time-consuming conversion can be done only once, not for every single comparison */ @@ -926,6 +1026,24 @@ longlong Item_func_between::val_int() null_value= value >= a; } } + else if (cmp_type == DECIMAL_RESULT) + { + my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf), + a_buf, *a_dec, b_buf, *b_dec; + if ((null_value=args[0]->null_value)) + return 0; /* purecov: inspected */ + a_dec= args[1]->val_decimal(&a_buf); + b_dec= args[2]->val_decimal(&b_buf); + if (!args[1]->null_value && !args[2]->null_value) + return (my_decimal_cmp(dec, a_dec)>=0) && (my_decimal_cmp(dec, b_dec)<=0); + + if (args[1]->null_value && args[2]->null_value) + null_value=1; + else if (args[1]->null_value) + null_value= (my_decimal_cmp(dec, b_dec) <= 0); + else + null_value= (my_decimal_cmp(dec, a_dec) >= 0); + } else { double value= args[0]->val_real(),a,b; @@ -965,14 +1083,26 @@ void Item_func_ifnull::fix_length_and_dec() { maybe_null=args[1]->maybe_null; - max_length=max(args[0]->max_length,args[1]->max_length); - decimals=max(args[0]->decimals,args[1]->decimals); + decimals= max(args[0]->decimals, args[1]->decimals); + max_length= (max(args[0]->max_length - args[0]->decimals, + args[1]->max_length - args[1]->decimals) + + decimals); agg_result_type(&cached_result_type, args, 2); - if (cached_result_type == STRING_RESULT) + switch (cached_result_type) + { + case STRING_RESULT: agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV); - else if (cached_result_type != REAL_RESULT) + break; + case DECIMAL_RESULT: + case REAL_RESULT: + break; + case INT_RESULT: decimals= 0; - + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } cached_field_type= args[0]->field_type(); if (cached_field_type != args[1]->field_type()) cached_field_type= Item_func::field_type(); @@ -1020,6 +1150,24 @@ Item_func_ifnull::val_int() return value; } + +my_decimal *Item_func_ifnull::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + my_decimal *value= args[0]->val_decimal(decimal_value); + if (!args[0]->null_value) + { + null_value= 0; + return value; + } + value= args[1]->val_decimal(decimal_value); + if ((null_value= args[1]->null_value)) + return 0; + return value; +} + + + String * Item_func_ifnull::val_str(String *str) { @@ -1043,8 +1191,10 @@ void Item_func_if::fix_length_and_dec() { maybe_null=args[1]->maybe_null || args[2]->maybe_null; - max_length=max(args[1]->max_length,args[2]->max_length); - decimals=max(args[1]->decimals,args[2]->decimals); + decimals= max(args[1]->decimals, args[2]->decimals); + max_length= (max(args[1]->max_length - args[1]->decimals, + args[2]->max_length - args[2]->decimals) + + decimals); enum Item_result arg1_type=args[1]->result_type(); enum Item_result arg2_type=args[2]->result_type(); bool null1=args[1]->const_item() && args[1]->null_value; @@ -1080,7 +1230,7 @@ double Item_func_if::val_real() { DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_int() ? args[1] : args[2]; + Item *arg= args[0]->val_bool() ? args[1] : args[2]; double value= arg->val_real(); null_value=arg->null_value; return value; @@ -1090,7 +1240,7 @@ longlong Item_func_if::val_int() { DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_int() ? args[1] : args[2]; + Item *arg= args[0]->val_bool() ? args[1] : args[2]; longlong value=arg->val_int(); null_value=arg->null_value; return value; @@ -1100,7 +1250,7 @@ String * Item_func_if::val_str(String *str) { DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_int() ? args[1] : args[2]; + Item *arg= args[0]->val_bool() ? args[1] : args[2]; String *res=arg->val_str(str); if (res) res->set_charset(collation.collation); @@ -1109,6 +1259,17 @@ Item_func_if::val_str(String *str) } +my_decimal * +Item_func_if::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + Item *arg= args[0]->val_bool() ? args[1] : args[2]; + my_decimal *value= arg->val_decimal(decimal_value); + null_value= arg->null_value; + return value; +} + + void Item_func_nullif::fix_length_and_dec() { @@ -1125,6 +1286,7 @@ Item_func_nullif::fix_length_and_dec() } } + /* nullif () returns NULL if arguments are equal, else it returns the first argument. @@ -1178,6 +1340,22 @@ Item_func_nullif::val_str(String *str) } +my_decimal * +Item_func_nullif::val_decimal(my_decimal * decimal_value) +{ + DBUG_ASSERT(fixed == 1); + my_decimal *res; + if (!cmp.compare()) + { + null_value=1; + return 0; + } + res= args[0]->val_decimal(decimal_value); + null_value= args[0]->null_value; + return res; +} + + bool Item_func_nullif::is_null() { @@ -1193,14 +1371,16 @@ Item_func_nullif::is_null() Item *Item_func_case::find_item(String *str) { - String *first_expr_str,*tmp; + String *first_expr_str, *tmp; + my_decimal *first_expr_dec, first_expr_dec_val; longlong first_expr_int; double first_expr_real; - + /* These will be initialized later */ LINT_INIT(first_expr_str); LINT_INIT(first_expr_int); LINT_INIT(first_expr_real); + LINT_INIT(first_expr_dec); if (first_expr_num != -1) { @@ -1221,9 +1401,14 @@ Item *Item_func_case::find_item(String *str) if (args[first_expr_num]->null_value) return else_expr_num != -1 ? args[else_expr_num] : 0; break; + case DECIMAL_RESULT: + first_expr_dec= args[first_expr_num]->val_decimal(&first_expr_dec_val); + if (args[first_expr_num]->null_value) + return else_expr_num != -1 ? args[else_expr_num] : 0; + break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -1235,7 +1420,7 @@ Item *Item_func_case::find_item(String *str) if (first_expr_num == -1) { // No expression between CASE and the first WHEN - if (args[i]->val_int()) + if (args[i]->val_bool()) return args[i+1]; continue; } @@ -1253,9 +1438,16 @@ Item *Item_func_case::find_item(String *str) if (args[i]->val_real() == first_expr_real && !args[i]->null_value) return args[i+1]; break; + case DECIMAL_RESULT: + { + my_decimal value; + if (my_decimal_cmp(args[i]->val_decimal(&value), first_expr_dec) == 0) + return args[i+1]; + break; + } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -1320,6 +1512,27 @@ double Item_func_case::val_real() return res; } + +my_decimal *Item_func_case::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + char buff[MAX_FIELD_WIDTH]; + String dummy_str(buff, sizeof(buff), default_charset()); + Item *item= find_item(&dummy_str); + my_decimal *res; + + if (!item) + { + null_value=1; + return 0; + } + + res= item->val_decimal(decimal_value); + null_value= item->null_value; + return res; +} + + void Item_func_case::fix_length_and_dec() { Item **agg; @@ -1358,7 +1571,7 @@ void Item_func_case::fix_length_and_dec() agg_cmp_type(&cmp_type, agg, nagg); if ((cmp_type == STRING_RESULT) && agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV)) - return; + return; } if (else_expr_num == -1 || args[else_expr_num]->maybe_null) @@ -1453,20 +1666,45 @@ double Item_func_coalesce::val_real() } +my_decimal *Item_func_coalesce::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + null_value= 0; + for (uint i= 0; i < arg_count; i++) + { + my_decimal *res= args[i]->val_decimal(decimal_value); + if (!args[i]->null_value) + return res; + } + null_value=1; + return 0; +} + + void Item_func_coalesce::fix_length_and_dec() { - max_length= 0; - decimals= 0; agg_result_type(&cached_result_type, args, arg_count); - for (uint i=0 ; i < arg_count ; i++) + switch (cached_result_type) { - set_if_bigger(max_length,args[i]->max_length); - set_if_bigger(decimals,args[i]->decimals); - } - if (cached_result_type == STRING_RESULT) + case STRING_RESULT: + count_only_length(); + decimals= NOT_FIXED_DEC; agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV); - else if (cached_result_type != REAL_RESULT) + break; + case DECIMAL_RESULT: + count_decimal_length(); + break; + case REAL_RESULT: + count_real_length(); + break; + case INT_RESULT: + count_only_length(); decimals= 0; + break; + case ROW_RESULT: + defaullt: + DBUG_ASSERT(0); + } } /**************************************************************************** @@ -1483,11 +1721,24 @@ static int cmp_double(void *cmp_arg, double *a,double *b) return *a < *b ? -1 : *a == *b ? 0 : 1; } -static int cmp_row(void *cmp_arg, cmp_item_row* a, cmp_item_row* b) +static int cmp_row(void *cmp_arg, cmp_item_row *a, cmp_item_row *b) { return a->compare(b); } + +static int cmp_decimal(void *cmp_arg, my_decimal *a, my_decimal *b) +{ + /* + We need call of fixing buffer pointer, because fast sort just copy + decimal buffers in memory and pointers left pointing on old buffer place + */ + a->fix_buffer_pointer(); + b->fix_buffer_pointer(); + return my_decimal_cmp(a, b); +} + + int in_vector::find(Item *item) { byte *result=get_value(item); @@ -1612,17 +1863,47 @@ byte *in_double::get_value(Item *item) return (byte*) &tmp; } -cmp_item* cmp_item::get_comparator(Item *item) + +in_decimal::in_decimal(uint elements) + :in_vector(elements, sizeof(my_decimal),(qsort2_cmp) cmp_decimal, 0) +{} + + +void in_decimal::set(uint pos, Item *item) +{ + /* as far as 'item' is constant, we can store reference on my_decimal */ + my_decimal *dec= ((my_decimal *)base) + pos; + dec->len= DECIMAL_BUFF_LENGTH; + dec->fix_buffer_pointer(); + my_decimal *res= item->val_decimal(dec); + if (res != dec) + my_decimal2decimal(res, dec); +} + + +byte *in_decimal::get_value(Item *item) +{ + my_decimal *result= item->val_decimal(&val); + if (item->null_value) + return 0; + return (byte *)result; +} + + +cmp_item* cmp_item::get_comparator(Item_result type, + CHARSET_INFO *cs) { - switch (item->result_type()) { + switch (type) { case STRING_RESULT: - return new cmp_item_sort_string(item->collation.collation); + return new cmp_item_sort_string(cs); case INT_RESULT: return new cmp_item_int; case REAL_RESULT: return new cmp_item_real; case ROW_RESULT: return new cmp_item_row; + case DECIMAL_RESULT: + return new cmp_item_decimal; default: DBUG_ASSERT(0); break; @@ -1681,7 +1962,9 @@ void cmp_item_row::store_value(Item *item) for (uint i=0; i < n; i++) { if (!comparators[i]) - if (!(comparators[i]= cmp_item::get_comparator(item->el(i)))) + if (!(comparators[i]= + cmp_item::get_comparator(item->el(i)->result_type(), + item->el(i)->collation.collation))) break; // new failed comparators[i]->store_value(item->el(i)); item->null_value|= item->el(i)->null_value; @@ -1752,6 +2035,36 @@ int cmp_item_row::compare(cmp_item *c) } +void cmp_item_decimal::store_value(Item *item) +{ + my_decimal *val= item->val_decimal(&value); + if (val != &value) + my_decimal2decimal(val, &value); +} + + +int cmp_item_decimal::cmp(Item *arg) +{ + my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); + if (arg->null_value) + return 1; + return my_decimal_cmp(&value, tmp); +} + + +int cmp_item_decimal::compare(cmp_item *c) +{ + cmp_item_decimal *cmp= (cmp_item_decimal *)c; + return my_decimal_cmp(&value, &cmp->value); +} + + +cmp_item* cmp_item_decimal::make_same() +{ + return new cmp_item_decimal(); +} + + bool Item_func_in::nulls_in_row() { Item **arg,**arg_end; @@ -1807,6 +2120,9 @@ void Item_func_in::fix_length_and_dec() case ROW_RESULT: array= new in_row(arg_count-1, args[0]); break; + case DECIMAL_RESULT: + array= new in_decimal(arg_count - 1); + break; default: DBUG_ASSERT(0); return; @@ -1828,7 +2144,7 @@ void Item_func_in::fix_length_and_dec() } else { - in_item= cmp_item::get_comparator(args[0]); + in_item= cmp_item::get_comparator(cmp_type, cmp_collation.collation); if (cmp_type == STRING_RESULT) in_item->cmp_charset= cmp_collation.collation; } @@ -2003,7 +2319,7 @@ bool Item_cond::walk(Item_processor processor, byte *arg) DESCRIPTION The function recursively applies the transform method with the - same transformer to each member item of the codition list. + same transformer to each member item of the condition list. If the call of the method for a member item returns a new item the old item is substituted for a new one. After this the transform method is applied to the root node @@ -2088,7 +2404,6 @@ void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, ref_pointer_array[el]= item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); - ref_pointer_array[el]= item; thd->change_item_tree(ref, new_item); } item->update_used_tables(); @@ -2157,7 +2472,7 @@ void Item_cond::neg_arguments(THD *thd) /* - Evalution of AND(expr, expr, expr ...) + Evaluation of AND(expr, expr, expr ...) NOTES: abort_if_null is set for AND expressions for which we don't care if the @@ -2182,7 +2497,7 @@ longlong Item_cond_and::val_int() null_value= 0; while ((item=li++)) { - if (item->val_int() == 0) + if (!item->val_bool()) { if (abort_on_null || !(null_value= item->null_value)) return 0; // return FALSE @@ -2200,7 +2515,7 @@ longlong Item_cond_or::val_int() null_value=0; while ((item=li++)) { - if (item->val_int() != 0) + if (item->val_bool()) { null_value=0; return 1; @@ -2439,7 +2754,7 @@ Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) DBUG_ASSERT(fixed == 0); if ((!args[0]->fixed && args[0]->fix_fields(thd, tables, args)) || args[0]->check_cols(1) || - (!args[1]->fixed && + (!args[1]->fixed && args[1]->fix_fields(thd,tables, args + 1)) || args[1]->check_cols(1)) return TRUE; /* purecov: inspected */ with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func; @@ -2813,7 +3128,7 @@ longlong Item_cond_xor::val_int() /* Apply NOT transformation to the item and return a new one. - SYNPOSIS + SYNOPSIS neg_transformer() thd thread handler @@ -2991,10 +3306,10 @@ uint Item_equal::members() SYNOPSIS contains() - field field whose occurence is to be checked + field field whose occurrence is to be checked DESCRIPTION - The function checks whether field is occured in the Item_equal object + The function checks whether field is occurred in the Item_equal object RETURN VALUES 1 if nultiple equality contains a reference to field @@ -3022,7 +3337,7 @@ bool Item_equal::contains(Field *field) item multiple equality whose members are to be joined DESCRIPTION - The function actually merges two multiple equalitis. + The function actually merges two multiple equalities. After this operation the Item_equal object additionally contains the field items of another item of the type Item_equal. If the optional constant items are not equal the cond_false flag is @@ -3159,7 +3474,8 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= const_item ? const_item : get_first(); - eval_item= cmp_item::get_comparator(item); + eval_item= cmp_item::get_comparator(item->result_type(), + item->collation.collation); if (item->result_type() == STRING_RESULT) eval_item->cmp_charset= cmp_collation.collation; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 181f1312d46..717bcbca7d5 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -67,6 +67,7 @@ public: int compare_string(); // compare args[0] & args[1] int compare_binary_string(); // compare args[0] & args[1] int compare_real(); // compare args[0] & args[1] + int compare_decimal(); // compare args[0] & args[1] int compare_int_signed(); // compare args[0] & args[1] int compare_int_signed_unsigned(); int compare_int_unsigned_signed(); @@ -75,11 +76,12 @@ public: int compare_e_string(); // compare args[0] & args[1] int compare_e_binary_string(); // compare args[0] & args[1] int compare_e_real(); // compare args[0] & args[1] + int compare_e_decimal(); // compare args[0] & args[1] int compare_e_int(); // compare args[0] & args[1] int compare_e_int_diff_signedness(); int compare_e_row(); // compare args[0] & args[1] - static arg_cmp_func comparator_matrix [4][2]; + static arg_cmp_func comparator_matrix [5][2]; friend class Item_func; }; @@ -269,7 +271,7 @@ public: class Item_func_not_all :public Item_func_not { - /* allow to check presence od values in max/min optimisation */ + /* allow to check presence of values in max/min optimization */ Item_sum_hybrid *test_sum_item; Item_maxmin_subselect *test_sub_item; @@ -421,10 +423,18 @@ public: }; +struct interval_range +{ + Item_result type; + double dbl; + my_decimal dec; +}; + class Item_func_interval :public Item_int_func { Item_row *row; - double *intervals; + my_bool use_decimal_comparison; + interval_range *intervals; public: Item_func_interval(Item_row *a) :Item_int_func(a),row(a),intervals(0) @@ -437,24 +447,43 @@ public: }; -class Item_func_ifnull :public Item_func +class Item_func_coalesce :public Item_func { +protected: enum Item_result cached_result_type; + Item_func_coalesce(Item *a, Item *b) + :Item_func(a, b), cached_result_type(INT_RESULT) + {} +public: + Item_func_coalesce(List<Item> &list) + :Item_func(list),cached_result_type(INT_RESULT) + {} + double val_real(); + longlong val_int(); + String *val_str(String *); + my_decimal *val_decimal(my_decimal *); + void fix_length_and_dec(); + enum Item_result result_type () const { return cached_result_type; } + const char *func_name() const { return "coalesce"; } + table_map not_null_tables() const { return 0; } +}; + + +class Item_func_ifnull :public Item_func_coalesce +{ +protected: enum_field_types cached_field_type; bool field_type_defined; public: - Item_func_ifnull(Item *a,Item *b) - :Item_func(a,b), cached_result_type(INT_RESULT) - {} + Item_func_ifnull(Item *a, Item *b) :Item_func_coalesce(a,b) {} double val_real(); longlong val_int(); String *val_str(String *str); - enum Item_result result_type () const { return cached_result_type; } + my_decimal *val_decimal(my_decimal *); enum_field_types field_type() const; void fix_length_and_dec(); const char *func_name() const { return "ifnull"; } Field *tmp_table_field(TABLE *table); - table_map not_null_tables() const { return 0; } }; @@ -468,6 +497,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *str); + my_decimal *val_decimal(my_decimal *); enum Item_result result_type () const { return cached_result_type; } bool fix_fields(THD *thd,struct st_table_list *tlist, Item **ref) { @@ -491,6 +521,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *str); + my_decimal *val_decimal(my_decimal *); enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "nullif"; } @@ -500,23 +531,6 @@ public: }; -class Item_func_coalesce :public Item_func -{ - enum Item_result cached_result_type; -public: - Item_func_coalesce(List<Item> &list) - :Item_func(list),cached_result_type(INT_RESULT) - {} - double val_real(); - longlong val_int(); - String *val_str(String *); - void fix_length_and_dec(); - enum Item_result result_type () const { return cached_result_type; } - const char *func_name() const { return "coalesce"; } - table_map not_null_tables() const { return 0; } -}; - - class Item_func_case :public Item_func { int first_expr_num, else_expr_num; @@ -529,7 +543,7 @@ public: Item_func_case(List<Item> &list, Item *first_expr_arg, Item *else_expr_arg) :Item_func(), first_expr_num(-1), else_expr_num(-1), cached_result_type(INT_RESULT) - { + { ncases= list.elements; if (first_expr_arg) { @@ -546,6 +560,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *); + my_decimal *val_decimal(my_decimal *); void fix_length_and_dec(); table_map not_null_tables() const { return 0; } enum Item_result result_type () const { return cached_result_type; } @@ -586,7 +601,7 @@ public: class in_string :public in_vector { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmp; public: in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs); @@ -613,6 +628,16 @@ public: byte *get_value(Item *item); }; +class in_decimal :public in_vector +{ + my_decimal val; +public: + in_decimal(uint elements); + void set(uint pos, Item *item); + byte *get_value(Item *item); +}; + + /* ** Classes for easy comparing of non const items */ @@ -627,7 +652,7 @@ public: virtual int cmp(Item *item)= 0; // for optimized IN with row virtual int compare(cmp_item *item)= 0; - static cmp_item* get_comparator(Item *); + static cmp_item* get_comparator(Item_result type, CHARSET_INFO *cs); virtual cmp_item *make_same()= 0; virtual void store_value_by_template(cmp_item *tmpl, Item *item) { @@ -648,7 +673,7 @@ public: class cmp_item_sort_string :public cmp_item_string { protected: - char value_buff[80]; + char value_buff[STRING_BUFFER_USUAL_SIZE]; String value; public: cmp_item_sort_string(CHARSET_INFO *cs): @@ -660,7 +685,7 @@ public: } int cmp(Item *arg) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff, sizeof(buff), cmp_charset), *res; if (!(res= arg->val_str(&tmp))) return 1; /* Can't be right */ @@ -714,6 +739,18 @@ public: cmp_item *make_same(); }; + +class cmp_item_decimal :public cmp_item +{ + my_decimal value; +public: + void store_value(Item *item); + int cmp(Item *arg); + int compare(cmp_item *c); + cmp_item *make_same(); +}; + + class cmp_item_row :public cmp_item { cmp_item **comparators; @@ -996,26 +1033,26 @@ public: /* - The class Item_equal is used to represent conjuctions of equality + The class Item_equal is used to represent conjunctions of equality predicates of the form field1 = field2, and field=const in where conditions and on expressions. All equality predicates of the form field1=field2 contained in a - conjuction are substituted for a sequence of items of this class. - An item of this class Item_equal(f1,f2,...fk) respresents a + conjunction are substituted for a sequence of items of this class. + An item of this class Item_equal(f1,f2,...fk) represents a multiple equality f1=f2=...=fk. - If a conjuction contains predicates f1=f2 and f2=f3, a new item of + If a conjunction contains predicates f1=f2 and f2=f3, a new item of this class is created Item_equal(f1,f2,f3) representing the multiple equality f1=f2=f3 that substitutes the above equality predicates in - the conjuction. - A conjuction of the predicates f2=f1 and f3=f1 and f3=f2 will be + the conjunction. + A conjunction of the predicates f2=f1 and f3=f1 and f3=f2 will be substituted for the item representing the same multiple equality f1=f2=f3. - An item Item_equal(f1,f2) can appear instead of a conjuction of + An item Item_equal(f1,f2) can appear instead of a conjunction of f2=f1 and f1=f2, or instead of just the predicate f1=f2. - An item of the class Item_equal inherites equalities from outer + An item of the class Item_equal inherits equalities from outer conjunctive levels. Suppose we have a where condition of the following form: @@ -1026,7 +1063,7 @@ public: f1=f3 will be substituted for Item_equal(f1,f2,f3,f4,f5); An object of the class Item_equal can contain an optional constant - item c. Thenit represents a multiple equality of the form + item c. Then it represents a multiple equality of the form c=f1=...=fk. Objects of the class Item_equal are used for the following: @@ -1044,7 +1081,7 @@ public: 3. An object Item_equal(t1.f1,...,tk.fk) is used to optimize the selected execution plan for the query: if table ti is accessed before the table tj then in any predicate P in the where condition - the occurence of tj.fj is substituted for ti.fi. This can allow + the occurrence of tj.fj is substituted for ti.fi. This can allow an evaluation of the predicate at an earlier step. When feature 1 is supported they say that join transitive closure @@ -1175,7 +1212,7 @@ public: /* - XOR is Item_cond, not an Item_int_func bevause we could like to + XOR is Item_cond, not an Item_int_func because we could like to optimize (a XOR b) later on. It's low prio, though */ @@ -1192,7 +1229,7 @@ public: }; -/* Some usefull inline functions */ +/* Some useful inline functions */ inline Item *and_conds(Item *a, Item *b) { diff --git a/sql/item_create.cc b/sql/item_create.cc index 9fb44658dd5..6bd5c0c9a52 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -289,7 +289,7 @@ Item *create_func_period_diff(Item* a, Item *b) Item *create_func_pi(void) { - return new Item_static_real_func("pi()", M_PI, 6, 8); + return new Item_static_float_func("pi()", M_PI, 6, 8); } Item *create_func_pow(Item* a, Item *b) @@ -454,7 +454,7 @@ Item *create_load_file(Item* a) } -Item *create_func_cast(Item *a, Cast_target cast_type, int len, +Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, CHARSET_INFO *cs) { Item *res; @@ -467,6 +467,9 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, case ITEM_CAST_DATE: res= new Item_date_typecast(a); break; case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; + case ITEM_CAST_DECIMAL: + res= new Item_decimal_typecast(a, (len>0) ? len : 10, dec ? dec : 2); + break; case ITEM_CAST_CHAR: res= new Item_char_typecast(a, len, cs ? cs : current_thd->variables.collation_connection); diff --git a/sql/item_create.h b/sql/item_create.h index 1be33fef257..0a9af144ec0 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -28,7 +28,8 @@ Item *create_func_bit_length(Item* a); Item *create_func_coercibility(Item* a); Item *create_func_ceiling(Item* a); Item *create_func_char_length(Item* a); -Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs); +Item *create_func_cast(Item *a, Cast_target cast_type, int len, int dec, + CHARSET_INFO *cs); Item *create_func_connection_id(void); Item *create_func_conv(Item* a, Item *b, Item *c); Item *create_func_cos(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index e1d81b2a37d..68654338cf6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -194,6 +194,11 @@ bool Item_func::agg_arg_charsets(DTCollation &coll, } if ((*arg)->type() == FIELD_ITEM) ((Item_field *)(*arg))->no_const_subst= 1; + /* + We do not check conv->fixed, because Item_func_conv_charset which can + be return by safe_charset_converter can't be fixed at creation, also + it do not need tables (second argument) for name resolving + */ *arg= conv; conv->fix_fields(thd, 0, arg); } @@ -414,7 +419,6 @@ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, ref_pointer_array[el]= item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); - ref_pointer_array[el]= item; thd->change_item_tree(arg, new_item); } } @@ -519,15 +523,26 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) case STRING_RESULT: res= make_string_field(t_arg); break; + case DECIMAL_RESULT: + res= new Field_new_decimal(max_length + (decimals?1:0), maybe_null, + name, t_arg, decimals); + break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } return res; } +my_decimal *Item_func::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + int2my_decimal(E_DEC_FATAL_ERROR, val_int(), unsigned_flag, decimal_value); + return decimal_value; +} + String *Item_real_func::val_str(String *str) { @@ -540,38 +555,97 @@ String *Item_real_func::val_str(String *str) } -String *Item_num_func::val_str(String *str) +void Item_func::fix_num_length_and_dec() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) + decimals= 0; + for (uint i=0 ; i < arg_count ; i++) { - longlong nr=val_int(); - if (null_value) - return 0; /* purecov: inspected */ - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + set_if_bigger(decimals, args[i]->decimals); } - else + max_length= float_length(decimals); +} + + +void Item_func_numhybrid::fix_num_length_and_dec() +{} + + +/* + Set max_length/decimals of function if function is fixed point and + result length/precision depends on argument ones + + SYNOPSIS + Item_func::count_decimal_length() +*/ + +void Item_func::count_decimal_length() +{ + uint32 length= 0; + decimals= 0; + for (uint i=0 ; i < arg_count ; i++) { - double nr= val_real(); - if (null_value) - return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + set_if_bigger(decimals, args[i]->decimals); + set_if_bigger(length, (args[i]->max_length - args[i]->decimals)); } - return str; + max_length= length; + length+= decimals; + if (length < max_length) // If previous operation gave overflow + max_length= UINT_MAX32; + else + max_length= length; } -void Item_func::fix_num_length_and_dec() +/* + Set max_length of if it is maximum length of its arguments + + SYNOPSIS + Item_func::count_only_length() +*/ + +void Item_func::count_only_length() { - decimals=0; + max_length= 0; + for (uint i=0 ; i < arg_count ; i++) + set_if_bigger(max_length, args[i]->max_length); +} + + +/* + Set max_length/decimals of function if function is floating point and + result length/precision depends on argument ones + + SYNOPSIS + Item_func::count_real_length() +*/ + +void Item_func::count_real_length() +{ + uint32 length= 0; + decimals= 0; + max_length= 0; for (uint i=0 ; i < arg_count ; i++) - set_if_bigger(decimals,args[i]->decimals); - max_length=float_length(decimals); + { + if (decimals != NOT_FIXED_DEC) + { + set_if_bigger(decimals, args[i]->decimals); + set_if_bigger(length, (args[i]->max_length - args[i]->decimals)); + } + set_if_bigger(max_length, args[i]->max_length); + } + if (decimals != NOT_FIXED_DEC) + { + max_length= length; + length+= decimals; + if (length < max_length) // If previous operation gave overflow + max_length= UINT_MAX32; + else + max_length= length; + } } + + void Item_func::signal_divide_by_null() { THD *thd= current_thd; @@ -602,45 +676,221 @@ String *Item_int_func::val_str(String *str) return str; } + /* - Change from REAL_RESULT (default) to INT_RESULT if both arguments are - integers + Check arguments here to determine result's type for function with two + arguments. + + SYNOPSIS + Item_num_op::find_num_type() */ void Item_num_op::find_num_type(void) { - if (args[0]->result_type() == INT_RESULT && - args[1]->result_type() == INT_RESULT) + DBUG_ENTER("Item_num_op::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + DBUG_ASSERT(arg_count == 2); + Item_result r0= args[0]->result_type(); + Item_result r1= args[1]->result_type(); + + if (r0 == REAL_RESULT || r1 == REAL_RESULT || + r0 == STRING_RESULT || r1 ==STRING_RESULT) + { + count_real_length(); + max_length= float_length(decimals); + hybrid_type= REAL_RESULT; + } + else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT) { + hybrid_type= DECIMAL_RESULT; + result_precision(); + } + else if (r0 == INT_RESULT && r1 == INT_RESULT) + { + decimals= 0; hybrid_type=INT_RESULT; unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; + result_precision(); } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + DBUG_VOID_RETURN; } -String *Item_num_op::val_str(String *str) + +/* + Set result type of function if it (type) is depends only on first argument + + SYNOPSIS + Item_func_num1::find_num_type() +*/ +void Item_func_num1::find_num_type() +{ + DBUG_ENTER("Item_func_num1::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + switch(hybrid_type= args[0]->result_type()) + { + case INT_RESULT: + unsigned_flag=args[0]->unsigned_flag; + hybrid_type= INT_RESULT; + break; + case STRING_RESULT: + case REAL_RESULT: + hybrid_type= REAL_RESULT; + max_length= float_length(decimals); + break; + case DECIMAL_RESULT: + hybrid_type= DECIMAL_RESULT; + break; + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + DBUG_VOID_RETURN; +} + + +void Item_func_num1::fix_num_length_and_dec() +{ + decimals= args[0]->decimals; + max_length= args[0]->max_length; +} + + +void Item_func_numhybrid::fix_length_and_dec() +{ + fix_num_length_and_dec(); + find_num_type(); +} + + +String *Item_func_numhybrid::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) + switch (hybrid_type) { - longlong nr=val_int(); + case DECIMAL_RESULT: + { + my_decimal decimal_value, *val; + if (!(val= decimal_op(&decimal_value))) + return 0; + my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); + my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); + break; + } + case INT_RESULT: + { + longlong nr= int_op(); if (null_value) return 0; /* purecov: inspected */ if (!unsigned_flag) str->set(nr,&my_charset_bin); else str->set((ulonglong) nr,&my_charset_bin); + break; } - else + case REAL_RESULT: { - double nr= val_real(); + double nr=real_op(); if (null_value) return 0; /* purecov: inspected */ str->set(nr,decimals,&my_charset_bin); + break; + } + default: + DBUG_ASSERT(0); } return str; } +double Item_func_numhybrid::val_real() +{ + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) + { + case DECIMAL_RESULT: + { + my_decimal decimal_value, *val; + if (!(val= decimal_op(&decimal_value))) + return 0.0; + double result; + my_decimal2double(E_DEC_FATAL_ERROR, val, &result); + return result; + } + case INT_RESULT: + return (double)int_op(); + case REAL_RESULT: + return real_op(); + default: + DBUG_ASSERT(0); + } + return 0.0; +} + + +longlong Item_func_numhybrid::val_int() +{ + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) + { + case DECIMAL_RESULT: + { + my_decimal decimal_value, *val; + if (!(val= decimal_op(&decimal_value))) + return 0; + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); + return result; + } + case INT_RESULT: + return int_op(); + case REAL_RESULT: + return (longlong)real_op(); + default: + DBUG_ASSERT(0); + } + return 0; +} + + +my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) +{ + my_decimal *val= decimal_value; + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) + { + case DECIMAL_RESULT: + val= decimal_op(decimal_value); + break; + case INT_RESULT: + { + longlong result= int_op(); + int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value); + break; + } + case REAL_RESULT: + { + double result= int_op(); + double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value); + break; + } + case STRING_RESULT: + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + return val; +} + + void Item_func_signed::print(String *str) { str->append("cast(", 5); @@ -659,26 +909,83 @@ void Item_func_unsigned::print(String *str) } -double Item_func_plus::val_real() +String *Item_decimal_typecast::val_str(String *str) +{ + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + my_decimal_round(E_DEC_FATAL_ERROR, tmp, decimals, FALSE, &tmp_buf); + my_decimal2string(E_DEC_FATAL_ERROR, &tmp_buf, 0, 0, 0, str); + return str; +} + + +double Item_decimal_typecast::val_real() +{ + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + double res; + my_decimal2double(E_DEC_FATAL_ERROR, tmp, &res); + return res; +} + + +longlong Item_decimal_typecast::val_int() +{ + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + longlong res; + my_decimal2int(E_DEC_FATAL_ERROR, tmp, unsigned_flag, &res); + return res; +} + + +my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec) +{ + my_decimal tmp_buf, *tmp= args[0]->val_decimal(&tmp_buf); + my_decimal_round(E_DEC_FATAL_ERROR, tmp, decimals, FALSE, dec); + return dec; +} + + +double Item_func_plus::real_op() { - DBUG_ASSERT(fixed == 1); double value= args[0]->val_real() + args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; return value; } -longlong Item_func_plus::val_int() + +longlong Item_func_plus::int_op() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int()+args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; - return value; - } - return (longlong) Item_func_plus::val_real(); + longlong value=args[0]->val_int()+args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; +} + + +my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + my_decimal value2, *val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value) || + my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, val1, val2) > 1) + return 0; + return decimal_value; +} + +/* + Set precision of results for additive operations (+ and -) + + SYNOPSIS + Item_func_additive_op::result_precision() +*/ +void Item_func_additive_op::result_precision() +{ + decimals= max(args[0]->decimals, args[1]->decimals); + max_length= (max(args[0]->max_length - args[0]->decimals, + args[1]->max_length - args[1]->decimals) + + decimals + 1); } @@ -696,53 +1003,80 @@ void Item_func_minus::fix_length_and_dec() } -double Item_func_minus::val_real() +double Item_func_minus::real_op() { - DBUG_ASSERT(fixed == 1); double value= args[0]->val_real() - args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; return value; } -longlong Item_func_minus::val_int() + +longlong Item_func_minus::int_op() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int() - args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; - return value; - } - return (longlong) Item_func_minus::val_real(); + longlong value=args[0]->val_int() - args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; } -double Item_func_mul::val_real() +my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + my_decimal value2, *val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value) || + my_decimal_sub(E_DEC_FATAL_ERROR, decimal_value, val1, val2) > 1) + return 0; + return decimal_value; +} + + +double Item_func_mul::real_op() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real() * args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0.0; /* purecov: inspected */ + return 0.0; return value; } -longlong Item_func_mul::val_int() + +longlong Item_func_mul::int_op() { DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int()*args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; /* purecov: inspected */ - return value; - } - return (longlong) Item_func_mul::val_real(); + longlong value=args[0]->val_int()*args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; +} + + +my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + my_decimal value2, *val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value) || + my_decimal_mul(E_DEC_FATAL_ERROR, decimal_value, val1, val2) > 1) + return 0; + return decimal_value; +} + + +void Item_func_mul::result_precision() +{ + decimals= args[0]->decimals + args[1]->decimals; + max_length= ((args[0]->max_length - args[0]->decimals) + + (args[1]->max_length - args[1]->decimals) + + decimals); } -double Item_func_div::val_real() +double Item_func_div::real_op() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); @@ -758,34 +1092,66 @@ double Item_func_div::val_real() } -longlong Item_func_div::val_int() +my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) + my_decimal value1, *val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + my_decimal value2, *val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value)) + return 0; + switch (my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, + val1, val2, DECIMAL_DIV_SCALE_INCREASE)) { - longlong value=args[0]->val_int(); - longlong val2=args[1]->val_int(); - if ((null_value= args[0]->null_value || args[1]->null_value)) - return 0; - if (val2 == 0) - { - signal_divide_by_null(); - return 0; - } - return value/val2; + case E_DEC_TRUNCATED: + case E_DEC_OK: + return decimal_value; + case E_DEC_DIV_ZERO: + signal_divide_by_null(); + default: + return 0; } - return (longlong) Item_func_div::val_real(); +} + + +void Item_func_div::result_precision() +{ + decimals= (args[0]->decimals + args[0]->decimals + + DECIMAL_DIV_SCALE_INCREASE); + max_length= ((args[0]->max_length - args[0]->decimals) + + (args[1]->max_length - args[1]->decimals) + + decimals); } void Item_func_div::fix_length_and_dec() { - decimals=max(args[0]->decimals,args[1]->decimals)+2; - set_if_smaller(decimals, NOT_FIXED_DEC); - max_length=args[0]->max_length - args[0]->decimals + decimals; - uint tmp=float_length(decimals); - set_if_smaller(max_length,tmp); - maybe_null=1; + DBUG_ENTER("Item_func_div::fix_length_and_dec"); + Item_num_op::fix_length_and_dec(); + switch(hybrid_type) + { + case REAL_RESULT: + { + decimals=max(args[0]->decimals,args[1]->decimals)+2; + set_if_smaller(decimals, NOT_FIXED_DEC); + max_length=args[0]->max_length - args[0]->decimals + decimals; + uint tmp=float_length(decimals); + set_if_smaller(max_length,tmp); + break; + } + case INT_RESULT: + hybrid_type= DECIMAL_RESULT; + DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); + result_precision(); + break; + case DECIMAL_RESULT: + result_precision(); + break; + default: + DBUG_ASSERT(0); + } + maybe_null= 1; // devision by zero + DBUG_VOID_RETURN; } @@ -808,15 +1174,43 @@ longlong Item_func_int_div::val_int() } +String *Item_func_int_div::val_str(String*str) +{ + longlong nr= val_int(); + if (null_value) + return 0; /* purecov: inspected */ + if (!unsigned_flag) + str->set(nr,&my_charset_bin); + else + str->set((ulonglong) nr,&my_charset_bin); + return str; +} + + void Item_func_int_div::fix_length_and_dec() { - find_num_type(); max_length=args[0]->max_length - args[0]->decimals; maybe_null=1; + unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; } -double Item_func_mod::val_real() +longlong Item_func_mod::int_op() +{ + DBUG_ASSERT(fixed == 1); + longlong value= args[0]->val_int(); + longlong val2= args[1]->val_int(); + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0; /* purecov: inspected */ + if (val2 == 0) + { + signal_divide_by_null(); + return 0; + } + return value % val2; +} + +double Item_func_mod::real_op() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); @@ -831,67 +1225,81 @@ double Item_func_mod::val_real() return fmod(value,val2); } -longlong Item_func_mod::val_int() + +my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); - longlong value= args[0]->val_int(); - longlong val2= args[1]->val_int(); - if ((null_value= args[0]->null_value || args[1]->null_value)) - return 0; /* purecov: inspected */ - if (val2 == 0) + my_decimal value1, *val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + my_decimal value2, *val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value)) + return 0; + switch (my_decimal_mod(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, + val1, val2)) { + case E_DEC_TRUNCATED: + case E_DEC_OK: + return decimal_value; + case E_DEC_DIV_ZERO: signal_divide_by_null(); + default: return 0; } - return value % val2; } -void Item_func_mod::fix_length_and_dec() + +void Item_func_mod::result_precision() { - Item_num_op::fix_length_and_dec(); + decimals= max(args[0]->decimals, args[1]->decimals); + max_length= max(args[0]->max_length, args[1]->max_length); } -double Item_func_neg::val_real() +double Item_func_neg::real_op() { - DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - null_value=args[0]->null_value; + null_value= args[0]->null_value; return -value; } -longlong Item_func_neg::val_int() +longlong Item_func_neg::int_op() { - DBUG_ASSERT(fixed == 1); - longlong value=args[0]->val_int(); - null_value=args[0]->null_value; + longlong value= args[0]->val_int(); + null_value= args[0]->null_value; return -value; } -void Item_func_neg::fix_length_and_dec() +my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) { - enum Item_result arg_result= args[0]->result_type(); - enum Item::Type arg_type= args[0]->type(); - decimals=args[0]->decimals; - max_length=args[0]->max_length; - hybrid_type= REAL_RESULT; - - /* - We need to account for added '-' in the following cases: - A) argument is a real or integer positive constant - in this case - argument's max_length is set to actual number of bytes occupied, and not - maximum number of bytes real or integer may require. Note that all - constants are non negative so we don't need to account for removed '-'. - B) argument returns a string. - */ - if (arg_result == STRING_RESULT || - (arg_type == REAL_ITEM && ((Item_real*)args[0])->value >= 0) || - (arg_type == INT_ITEM && ((Item_int*)args[0])->value > 0)) - max_length++; + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= args[0]->null_value)) + { + my_decimal2decimal(value, decimal_value); + my_decimal_neg(decimal_value); + } + return decimal_value; +} + + +void Item_func_neg::fix_num_length_and_dec() +{ + decimals= args[0]->decimals; + /* 1 add because sign can appear */ + max_length= args[0]->max_length + 1; + unsigned_flag= 0; +} - if (args[0]->result_type() == INT_RESULT) + +void Item_func_signproc::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_signproc::fix_length_and_dec"); + Item_func_num1::fix_length_and_dec(); + if (hybrid_type == INT_RESULT && + args[0]->type() == INT_ITEM && + ((ulonglong) ((Item_uint*) args[0])->value >= + (ulonglong) LONGLONG_MIN)) { /* If this is in integer context keep the context as integer @@ -903,42 +1311,47 @@ void Item_func_neg::fix_length_and_dec() (This is needed because the lex parser doesn't anymore handle signed integers) */ - if (args[0]->type() != INT_ITEM || - ((ulonglong) ((Item_uint*) args[0])->value <= - (ulonglong) LONGLONG_MIN)) - hybrid_type= INT_RESULT; + hybrid_type= DECIMAL_RESULT; + DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); } + DBUG_VOID_RETURN; } -double Item_func_abs::val_real() +double Item_func_abs::real_op() { - DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); - null_value=args[0]->null_value; + null_value= args[0]->null_value; return fabs(value); } -longlong Item_func_abs::val_int() +longlong Item_func_abs::int_op() { - DBUG_ASSERT(fixed == 1); - longlong value=args[0]->val_int(); - null_value=args[0]->null_value; + longlong value= args[0]->val_int(); + null_value= args[0]->null_value; return value >= 0 ? value : -value; } -void Item_func_abs::fix_length_and_dec() +my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) { - decimals=args[0]->decimals; - max_length=args[0]->max_length; - hybrid_type= REAL_RESULT; - if (args[0]->result_type() == INT_RESULT) + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= args[0]->null_value)) { - hybrid_type= INT_RESULT; - unsigned_flag= 1; + my_decimal2decimal(value, decimal_value); + if (decimal_value->sign()) + my_decimal_neg(decimal_value); } + return decimal_value; +} + + +void Item_func_abs::fix_length_and_dec() +{ + Item_func_num1::fix_length_and_dec(); + if (hybrid_type == INT_RESULT) + unsigned_flag= 1; } @@ -1139,42 +1552,148 @@ void Item_func_integer::fix_length_and_dec() decimals=0; } -longlong Item_func_ceiling::val_int() +void Item_func_int_val::fix_num_length_and_dec() { - DBUG_ASSERT(fixed == 1); - double value= args[0]->val_real(); - null_value=args[0]->null_value; + max_length= args[0]->max_length - (args[0]->decimals ? + args[0]->decimals + 1 : + 0) + 2; + uint tmp= float_length(decimals); + set_if_smaller(max_length,tmp); + decimals= 0; +} + + +void Item_func_int_val::find_num_type() +{ + DBUG_ENTER("Item_func_int_val::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + switch(hybrid_type= args[0]->result_type()) + { + case STRING_RESULT: + case REAL_RESULT: + hybrid_type= REAL_RESULT; + max_length= float_length(decimals); + break; + case INT_RESULT: + case DECIMAL_RESULT: + /* + -2 because in most high position can't be used any digit for longlong + and one position for increasing value during operation + */ + if ((args[0]->max_length - args[0]->decimals) >= + (DECIMAL_LONGLONG_DIGITS - 2)) + { + hybrid_type= DECIMAL_RESULT; + } + else + { + unsigned_flag= args[0]->unsigned_flag; + hybrid_type= INT_RESULT; + } + break; + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + + DBUG_VOID_RETURN; +} + + +longlong Item_func_ceiling::int_op() +{ + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ + volatile double value= args[0]->val_real(); + null_value= args[0]->null_value; return (longlong) ceil(value); } -longlong Item_func_floor::val_int() + +double Item_func_ceiling::real_op() { - DBUG_ASSERT(fixed == 1); - // the volatile's for BUG #3051 to calm optimizer down (because of gcc's bug) + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ volatile double value= args[0]->val_real(); - null_value=args[0]->null_value; + null_value= args[0]->null_value; + return ceil(value); +} + + +my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + if ((null_value= args[0]->null_value)) + return 0; + if (my_decimal_ceiling(E_DEC_FATAL_ERROR, value, decimal_value) > 1) + return 0; + return decimal_value; +} + + +longlong Item_func_floor::int_op() +{ + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ + volatile double value= args[0]->val_real(); + null_value= args[0]->null_value; return (longlong) floor(value); } -void Item_func_round::fix_length_and_dec() + +double Item_func_floor::real_op() { - max_length=args[0]->max_length; - decimals=args[0]->decimals; + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ + volatile double value= args[0]->val_real(); + null_value= args[0]->null_value; + return floor(value); +} + + +my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + if ((null_value= args[0]->null_value)) + return 0; + if (my_decimal_floor(E_DEC_FATAL_ERROR, value, decimal_value) > 1) + return 0; + return decimal_value; +} + + +void Item_func_round::fix_num_length_and_dec() +{ + max_length= args[0]->max_length; + decimals= NOT_FIXED_DEC; if (args[1]->const_item()) { int tmp=(int) args[1]->val_int(); if (tmp < 0) decimals=0; else - decimals=min(tmp,NOT_FIXED_DEC); + decimals=min(tmp, NOT_FIXED_DEC); } } -double Item_func_round::val_real() +double Item_func_round::real_op() { - DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); int dec=(int) args[1]->val_int(); + if (dec > 0) + decimals= dec; // to get correct output uint abs_dec=abs(dec); double tmp; /* @@ -1201,6 +1720,58 @@ double Item_func_round::val_real() } +longlong Item_func_round::int_op() +{ + longlong value= args[0]->val_int(); + int dec=(int) args[1]->val_int(); + decimals= 0; + uint abs_dec; + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0; + if (dec >= 0) + return value; // integer have not digits after point + + abs_dec= -dec; + double tmp; + /* + tmp2 is here to avoid return the value with 80 bit precision + This will fix that the test round(0.1,1) = round(0.1,1) is true + */ + volatile double tmp2; + + tmp= (abs_dec < array_elements(log_10) ? + log_10[abs_dec] : pow(10.0, (double) abs_dec)); + + if (truncate) + { + if (unsigned_flag) + tmp2= floor(((double)((ulonglong)value))/tmp)*tmp; + else if (value >= 0) + tmp2= floor(((double)value)/tmp)*tmp; + else + tmp2= ceil(((double)value)/tmp)*tmp; + } + else + tmp2= rint(((double)value)/tmp)*tmp; + return (longlong)tmp2; +} + + +my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + int dec=(int) args[1]->val_int(); + if (dec > 0) + decimals= dec; // to get correct output + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0; + if (my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate, + decimal_value) > 1) + return 0; + return decimal_value; +} + + bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables, Item **ref) { @@ -1291,10 +1862,8 @@ void Item_func_min_max::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) { - if (max_length < args[i]->max_length) - max_length=args[i]->max_length; - if (decimals < args[i]->decimals) - decimals=args[i]->decimals; + set_if_bigger(max_length, args[i]->max_length); + set_if_bigger(decimals, args[i]->decimals); if (!args[i]->maybe_null) maybe_null=0; cmp_type=item_cmp_type(cmp_type,args[i]->result_type()); @@ -1319,6 +1888,14 @@ String *Item_func_min_max::val_str(String *str) str->set((ulonglong) nr,&my_charset_bin); return str; } + case DECIMAL_RESULT: + { + my_decimal dec_buf, *dec_val= val_decimal(&dec_buf); + if (null_value) + return 0; + my_decimal2string(E_DEC_FATAL_ERROR, dec_val, 0, 0, 0, str); + return str; + } case REAL_RESULT: { double nr= val_real(); @@ -1357,7 +1934,7 @@ String *Item_func_min_max::val_str(String *str) } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); return 0; } @@ -1410,6 +1987,40 @@ longlong Item_func_min_max::val_int() return value; } + +my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) +{ + DBUG_ASSERT(fixed == 1); + my_decimal tmp_buf, *tmp, *res= NULL; + null_value=1; + for (uint i=0; i < arg_count ; i++) + { + if (null_value) + { + res= args[i]->val_decimal(dec); + null_value= args[i]->null_value; + } + else + { + tmp= args[i]->val_decimal(&tmp_buf); + if (args[i]->null_value) + continue; + if ((my_decimal_cmp(tmp, res) * cmp_sign) < 0) + { + if (tmp == &tmp_buf) + { + my_decimal2decimal(tmp, dec); + res= dec; + } + else + res= tmp; + } + } + } + return res; +} + + longlong Item_func_length::val_int() { DBUG_ASSERT(fixed == 1); @@ -1533,6 +2144,21 @@ longlong Item_func_field::val_int() return (longlong) (i); } } + else if (cmp_type == DECIMAL_RESULT) + { + my_decimal dec_arg_buf, *dec_arg, + dec_buf, *dec= args[0]->val_decimal(&dec_buf); + if (args[0]->is_null()) + return 0; + for (uint i=1; i < arg_count; i++) + { + dec_arg= args[i]->val_decimal(&dec_arg_buf); + if (args[i]->is_null()) + continue; + if (!my_decimal_cmp(dec_arg, dec)) + return (longlong) (i); + } + } else { double val= args[0]->val_real(); @@ -1776,7 +2402,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, arg != arg_end ; arg++,i++) { - if (!(*arg)->fixed && + if (!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg)) DBUG_RETURN(1); // we can't assign 'item' before, because fix_fields() can change arg @@ -1792,7 +2418,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, Moreover, some arguments can represent a numeric input which doesn't effect the result character set and collation. There is no a general rule for UDF. Everything depends on - the particular user definted function. + the particular user defined function. */ if (item->collation.collation->state & MY_CS_BINSORT) func->collation.set(&my_charset_bin); @@ -1803,7 +2429,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, const_item_cache&=item->const_item(); f_args.arg_type[i]=item->result_type(); } - //TODO: why all folowing memory is not allocated with 1 call of sql_alloc? + //TODO: why all following memory is not allocated with 1 call of sql_alloc? if (!(buffers=new String[arg_count]) || !(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) || !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) || @@ -1903,6 +2529,7 @@ bool udf_handler::get_arguments() f_args.args[i]=0; switch (f_args.arg_type[i]) { case STRING_RESULT: + case DECIMAL_RESULT: { String *res=args[i]->val_str(&buffers[str_count++]); if (!(args[i]->null_value)) @@ -1930,7 +2557,7 @@ bool udf_handler::get_arguments() break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -1975,6 +2602,31 @@ String *udf_handler::val_str(String *str,String *save_str) } +my_decimal *udf_handler::val_decimal(my_bool *null_value, my_decimal *dec_buf) +{ + char buf[DECIMAL_MAX_STR_LENGTH+1], *end; + ulong res_length= DECIMAL_MAX_STR_LENGTH; + + if (get_arguments()) + { + *null_value=1; + return 0; + } + char *(*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)= + (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)) + u_d->func; + + char *res= func(&initid, &f_args, buf, &res_length, &is_null, &error); + if (is_null || error) + { + *null_value= 1; + return 0; + } + buf[res_length]= 0; + str2my_decimal(E_DEC_FATAL_ERROR, buf, dec_buf, &end); + return dec_buf; +} + double Item_func_udf_float::val_real() { @@ -2021,6 +2673,59 @@ String *Item_func_udf_int::val_str(String *str) return str; } + +longlong Item_func_udf_decimal::val_int() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0; + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); + return result; +} + + +double Item_func_udf_decimal::val_real() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0.0; + double result; + my_decimal2double(E_DEC_FATAL_ERROR, dec, &result); + return result; +} + + +my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf) +{ + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_func_udf_decimal::val_decimal"); + DBUG_PRINT("info",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + + DBUG_RETURN(udf.val_decimal(&null_value, dec_buf)); +} + + +String *Item_func_udf_decimal::val_str(String *str) +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0; + if (str->length() < DECIMAL_MAX_STR_LENGTH) + str->length(DECIMAL_MAX_STR_LENGTH); + my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); + my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, '0', str); + return str; +} + + +void Item_func_udf_decimal::fix_length_and_dec() +{ + fix_num_length_and_dec(); +} + + /* Default max_length is max argument length */ void Item_func_udf_str::fix_length_and_dec() @@ -2417,7 +3122,7 @@ longlong Item_func_benchmark::val_int() break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); return 0; } @@ -2583,6 +3288,8 @@ bool Item_func_set_user_var::update_hash(void *ptr, uint length, entry->value[length]= 0; // Store end \0 } memcpy(entry->value,ptr,length); + if (type == DECIMAL_RESULT) + ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; entry->type=type; entry->collation.set(cs, dv); @@ -2598,7 +3305,7 @@ bool Item_func_set_user_var::update_hash(void *ptr, uint length, /* Get the value of a variable as a double */ -double user_var_entry::val(my_bool *null_value) +double user_var_entry::val_real(my_bool *null_value) { if ((*null_value= (value == 0))) return 0.0; @@ -2608,6 +3315,12 @@ double user_var_entry::val(my_bool *null_value) return *(double*) value; case INT_RESULT: return (double) *(longlong*) value; + case DECIMAL_RESULT: + { + double result; + my_decimal2double(E_DEC_FATAL_ERROR, (my_decimal *)value, &result); + return result; + } case STRING_RESULT: return my_atof(value); // This is null terminated case ROW_RESULT: @@ -2630,6 +3343,12 @@ longlong user_var_entry::val_int(my_bool *null_value) return (longlong) *(double*) value; case INT_RESULT: return *(longlong*) value; + case DECIMAL_RESULT: + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, (my_decimal *)value, 1, &result); + return result; + } case STRING_RESULT: { int error; @@ -2658,6 +3377,9 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, case INT_RESULT: str->set(*(longlong*) value, &my_charset_bin); break; + case DECIMAL_RESULT: + my_decimal2string(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, 0, 0, str); + break; case STRING_RESULT: if (str->copy(value, length, collation.collation)) str= 0; // EOM error @@ -2668,16 +3390,43 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, return(str); } +/* Get the value of a variable as a decimal */ + +my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) +{ + if ((*null_value= (value == 0))) + return 0; + + switch (type) { + case REAL_RESULT: + double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val); + break; + case INT_RESULT: + int2my_decimal(E_DEC_FATAL_ERROR, *(longlong*) value, 0, val); + break; + case DECIMAL_RESULT: + val= (my_decimal *)value; + break; + case STRING_RESULT: + str2my_decimal(E_DEC_FATAL_ERROR, value, length, collation.collation, val); + break; + case ROW_RESULT: + DBUG_ASSERT(1); // Impossible + break; + } + return(val); +} + /* This functions is invoked on SET @variable or @variable:= expression. - Evaluete (and check expression), store results. + Evaluate (and check expression), store results. SYNOPSYS Item_func_set_user_var::check() NOTES - For now it always return OK. All problem with value evalueting - will be catched by thd->net.report_error check in sql_set_variables(). + For now it always return OK. All problem with value evaluating + will be caught by thd->net.report_error check in sql_set_variables(). RETURN FALSE OK. @@ -2704,9 +3453,14 @@ Item_func_set_user_var::check() save_result.vstr= args[0]->val_str(&value); break; } + case DECIMAL_RESULT: + { + save_result.vdec= args[0]->val_decimal(&decimal_buff); + break; + } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -2725,7 +3479,7 @@ Item_func_set_user_var::check() the value method used by the user RETURN - 0 Ok + 0 OK 1 EOM Error */ @@ -2762,9 +3516,20 @@ Item_func_set_user_var::update() args[0]->collation.derivation); break; } + case DECIMAL_RESULT: + { + if (!save_result.vdec) // Null value + res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, + DERIVATION_NONE); + else + res= update_hash((void*) save_result.vdec, + sizeof(my_decimal), DECIMAL_RESULT, + &my_charset_bin, DERIVATION_NONE); + break; + } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -2777,7 +3542,7 @@ double Item_func_set_user_var::val_real() DBUG_ASSERT(fixed == 1); check(); update(); // Store expression - return entry->val(&null_value); + return entry->val_real(&null_value); } longlong Item_func_set_user_var::val_int() @@ -2797,6 +3562,15 @@ String *Item_func_set_user_var::val_str(String *str) } +my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed == 1); + check(); + update(); // Store expression + return entry->val_decimal(&null_value, val); +} + + void Item_func_set_user_var::print(String *str) { str->append("(@", 2); @@ -2833,7 +3607,16 @@ double Item_func_get_user_var::val_real() DBUG_ASSERT(fixed == 1); if (!var_entry) return 0.0; // No such variable - return (var_entry->val(&null_value)); + return (var_entry->val_real(&null_value)); +} + + +my_decimal *Item_func_get_user_var::val_decimal(my_decimal *dec) +{ + DBUG_ASSERT(fixed == 1); + if (!var_entry) + return 0; + return var_entry->val_decimal(&null_value, dec); } @@ -2864,7 +3647,7 @@ longlong Item_func_get_user_var::val_int() RETURN 0 OK - 1 Failed to put appropiate record into binary log + 1 Failed to put appropriate record into binary log */ @@ -2888,8 +3671,8 @@ int get_var_with_binlog(THD *thd, LEX_STRING &name, that it gets into the binlog (if it didn't, the slave could be influenced by a variable of the same name previously set by another thread). - We create it like if it had been explicitely set with SET before. - The 'new' mimicks what sql_yacc.yy does when 'SET @a=10;'. + We create it like if it had been explicitly set with SET before. + The 'new' mimics what sql_yacc.yy does when 'SET @a=10;'. sql_set_variables() is what is called from 'case SQLCOM_SET_OPTION' in dispatch_command()). Instead of building a one-element list to pass to sql_set_variables(), we could instead manually call check() and update(); @@ -2920,7 +3703,7 @@ int get_var_with_binlog(THD *thd, LEX_STRING &name, uint size; /* First we need to store value of var_entry, when the next situation - appers: + appears: > set @a:=1; > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); We have to write to binlog value @a= 1; @@ -2982,7 +3765,13 @@ void Item_func_get_user_var::fix_length_and_dec() case STRING_RESULT: max_length= MAX_BLOB_WIDTH; break; + case DECIMAL_RESULT: + max_length= DECIMAL_MAX_LENGTH; + decimals= min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1); + break; case ROW_RESULT: // Keep compiler happy + default: + DBUG_ASSERT(0); break; } } @@ -3539,7 +4328,7 @@ const char * Item_func_sp::func_name() const { THD *thd= current_thd; - /* Calculate length to avoud reallocation of string for sure */ + /* Calculate length to avoid reallocation of string for sure */ uint len= ((m_name->m_db.length + m_name->m_name.length)*2 + //characters*quoting 2 + // ` and ` @@ -3590,7 +4379,7 @@ Item_func_sp::execute(Item **itp) #endif /* - We don't need to surpress senfing of ok packet here (by setting + We don't need to suppress sending of OK packet here (by setting thd->net.no_send_ok to true), because we are not allowing statements in functions now. */ @@ -3663,9 +4452,13 @@ Item_func_sp::fix_length_and_dec() decimals= 0; max_length= 21; break; + case DECIMAL_RESULT: + // TODO: where to find real precision and scale? + decimals= min(DECIMAL_MAX_LENGTH / 2, NOT_FIXED_DEC - 1); + max_length= DECIMAL_MAX_LENGTH; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } diff --git a/sql/item_func.h b/sql/item_func.h index a3618cca23e..20f70fce575 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -133,7 +133,10 @@ public: void print(String *str); void print_op(String *str); void print_args(String *str, uint from); - void fix_num_length_and_dec(); + virtual void fix_num_length_and_dec(); + void count_only_length(); + void count_real_length(); + void count_decimal_length(); inline bool get_arg0_date(TIME *ltime, uint fuzzy_date) { return (null_value=args[0]->get_date(ltime, fuzzy_date)); @@ -148,7 +151,9 @@ public: Field *tmp_table_field() { return result_field; } Field *tmp_table_field(TABLE *t_arg); Item *get_tmp_table_item(THD *thd); - + + my_decimal *val_decimal(my_decimal *); + bool agg_arg_collations(DTCollation &c, Item **items, uint nitems, uint flags= 0); bool agg_arg_collations_for_comparison(DTCollation &c, @@ -172,49 +177,70 @@ public: longlong val_int() { DBUG_ASSERT(fixed == 1); return (longlong) val_real(); } enum Item_result result_type () const { return REAL_RESULT; } - void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } + void fix_length_and_dec() + { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } }; -class Item_num_func :public Item_func +class Item_func_numhybrid: public Item_func { - protected: +protected: Item_result hybrid_type; public: - Item_num_func(Item *a) :Item_func(a),hybrid_type(REAL_RESULT) {} - Item_num_func(Item *a,Item *b) :Item_func(a,b),hybrid_type(REAL_RESULT) {} - String *val_str(String*str); - longlong val_int() - { DBUG_ASSERT(fixed == 1); return (longlong) val_real(); } + Item_func_numhybrid(Item *a) :Item_func(a),hybrid_type(REAL_RESULT) + {} + Item_func_numhybrid(Item *a,Item *b) + :Item_func(a,b),hybrid_type(REAL_RESULT) + {} + enum Item_result result_type () const { return hybrid_type; } - void fix_length_and_dec() { fix_num_length_and_dec(); } + void fix_length_and_dec(); + void fix_num_length_and_dec(); + virtual void find_num_type()= 0; /* To be called from fix_length_and_dec */ + + double val_real(); + longlong val_int(); + my_decimal *val_decimal(my_decimal *); + String *val_str(String*str); + + virtual longlong int_op()= 0; + virtual double real_op()= 0; + virtual my_decimal *decimal_op(my_decimal *)= 0; bool is_null() { (void) val_real(); return null_value; } }; +/* function where type of result detected by first argument */ +class Item_func_num1: public Item_func_numhybrid +{ +public: + Item_func_num1(Item *a) :Item_func_numhybrid(a) {} + Item_func_num1(Item *a, Item *b) :Item_func_numhybrid(a, b) {} -class Item_num_op :public Item_func + void fix_num_length_and_dec(); + void find_num_type(); +}; + + +/* Base class for operations like '+', '-', '*' */ +class Item_num_op :public Item_func_numhybrid { - protected: - Item_result hybrid_type; public: - Item_num_op(Item *a,Item *b) :Item_func(a,b),hybrid_type(REAL_RESULT) {} - String *val_str(String*str); + Item_num_op(Item *a,Item *b) :Item_func_numhybrid(a, b) {} + virtual void result_precision()= 0; void print(String *str) { print_op(str); } - enum Item_result result_type () const { return hybrid_type; } - void fix_length_and_dec() { fix_num_length_and_dec(); find_num_type(); } - void find_num_type(void); - bool is_null() { (void) val_real(); return null_value; } + void find_num_type(); }; class Item_int_func :public Item_func { public: - Item_int_func() :Item_func() { max_length=21; } - Item_int_func(Item *a) :Item_func(a) { max_length=21; } - Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; } - Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } - Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } + Item_int_func() :Item_func() { max_length= 21; } + Item_int_func(Item *a) :Item_func(a) { max_length= 21; } + Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length= 21; } + Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) + { max_length= 21; } + Item_int_func(List<Item> &list) :Item_func(list) { max_length= 21; } Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item) {} double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } String *val_str(String*str); @@ -257,22 +283,51 @@ public: }; -class Item_func_plus :public Item_num_op +class Item_decimal_typecast :public Item_func { + my_decimal decimal_value; public: - Item_func_plus(Item *a,Item *b) :Item_num_op(a,b) {} - const char *func_name() const { return "+"; } + Item_decimal_typecast(Item *a, int len, int dec) :Item_func(a) + { + max_length= len + 2; + decimals= dec; + } + String *val_str(String *str); double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal*); + enum Item_result result_type () const { return DECIMAL_RESULT; } + enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; } + void fix_length_and_dec() {}; +}; + + +class Item_func_additive_op :public Item_num_op +{ +public: + Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {} + void result_precision(); +}; + + +class Item_func_plus :public Item_func_additive_op +{ +public: + Item_func_plus(Item *a,Item *b) :Item_func_additive_op(a,b) {} + const char *func_name() const { return "+"; } + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); }; -class Item_func_minus :public Item_num_op +class Item_func_minus :public Item_func_additive_op { public: - Item_func_minus(Item *a,Item *b) :Item_num_op(a,b) {} + Item_func_minus(Item *a,Item *b) :Item_func_additive_op(a,b) {} const char *func_name() const { return "-"; } - double val_real(); - longlong val_int(); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); void fix_length_and_dec(); }; @@ -282,8 +337,10 @@ class Item_func_mul :public Item_num_op public: Item_func_mul(Item *a,Item *b) :Item_num_op(a,b) {} const char *func_name() const { return "*"; } - double val_real(); - longlong val_int(); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); + void result_precision(); }; @@ -291,22 +348,27 @@ class Item_func_div :public Item_num_op { public: Item_func_div(Item *a,Item *b) :Item_num_op(a,b) {} - double val_real(); - longlong val_int(); + longlong int_op() { DBUG_ASSERT(0); } + double real_op(); + my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "/"; } void fix_length_and_dec(); + void result_precision(); }; -class Item_func_int_div :public Item_num_op +class Item_func_int_div :public Item_func { public: - Item_func_int_div(Item *a,Item *b) :Item_num_op(a,b) - { hybrid_type=INT_RESULT; } + Item_func_int_div(Item *a,Item *b) :Item_func(a,b) + {} double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } longlong val_int(); + String *val_str(String*str); const char *func_name() const { return "DIV"; } void fix_length_and_dec(); + void print(String *str) { print_op(str); } + enum Item_result result_type () const { return INT_RESULT; } }; @@ -314,37 +376,47 @@ class Item_func_mod :public Item_num_op { public: Item_func_mod(Item *a,Item *b) :Item_num_op(a,b) {} - double val_real(); - longlong val_int(); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "%"; } + void result_precision(); +}; + + +class Item_func_signproc :public Item_func_num1 +{ +public: + Item_func_signproc(Item *a) :Item_func_num1(a) {} + Item_func_signproc(Item *a, Item *b) :Item_func_num1(a, b) {} void fix_length_and_dec(); }; -class Item_func_neg :public Item_num_func +class Item_func_neg :public Item_func_signproc { public: - Item_func_neg(Item *a) :Item_num_func(a) {} - double val_real(); - longlong val_int(); + Item_func_neg(Item *a) :Item_func_signproc(a) {} + double real_op(); + longlong int_op(); + my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "-"; } - void fix_length_and_dec(); + void fix_num_length_and_dec(); }; -class Item_func_abs :public Item_num_func +class Item_func_abs :public Item_func_num1 { public: - Item_func_abs(Item *a) :Item_num_func(a) {} + Item_func_abs(Item *a) :Item_func_num1(a) {} + double real_op(); + longlong int_op(); + my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "abs"; } - double val_real(); - longlong val_int(); - enum Item_result result_type () const - { return args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT; } void fix_length_and_dec(); }; -// A class to handle logaritmic and trigometric functions +// A class to handle logarithmic and trigonometric functions class Item_dec_func :public Item_real_func { @@ -491,34 +563,49 @@ public: }; -class Item_func_ceiling :public Item_func_integer +class Item_func_int_val :public Item_func_num1 +{ +public: + Item_func_int_val(Item *a) :Item_func_num1(a) {} + void fix_num_length_and_dec(); + void find_num_type(); +}; + + +class Item_func_ceiling :public Item_func_int_val { - Item_func_ceiling(); /* Never called */ public: - Item_func_ceiling(Item *a) :Item_func_integer(a) {} + Item_func_ceiling(Item *a) :Item_func_int_val(a) {} const char *func_name() const { return "ceiling"; } - longlong val_int(); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); }; -class Item_func_floor :public Item_func_integer + +class Item_func_floor :public Item_func_int_val { public: - Item_func_floor(Item *a) :Item_func_integer(a) {} + Item_func_floor(Item *a) :Item_func_int_val(a) {} const char *func_name() const { return "floor"; } - longlong val_int(); + longlong int_op(); + double real_op(); + my_decimal *decimal_op(my_decimal *); }; /* This handles round and truncate */ -class Item_func_round :public Item_real_func +class Item_func_round :public Item_func_num1 { bool truncate; public: - Item_func_round(Item *a,Item *b,bool trunc_arg) - :Item_real_func(a,b),truncate(trunc_arg) {} + Item_func_round(Item *a, Item *b, bool trunc_arg) + :Item_func_num1(a,b), truncate(trunc_arg) {} const char *func_name() const { return truncate ? "truncate" : "round"; } - double val_real(); - void fix_length_and_dec(); + double real_op(); + longlong int_op(); + my_decimal *decimal_op(my_decimal *); + void fix_num_length_and_dec(); }; @@ -554,7 +641,8 @@ class Item_func_units :public Item_real_func :Item_real_func(a),name(name_arg),mul(mul_arg),add(add_arg) {} double val_real(); const char *func_name() const { return name; } - void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } + void fix_length_and_dec() + { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } }; @@ -569,6 +657,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *); + my_decimal *val_decimal(my_decimal *); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } table_map not_null_tables() const { return 0; } @@ -755,7 +844,11 @@ public: Item_func_last_insert_id(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "last_insert_id"; } - void fix_length_and_dec() { if (arg_count) max_length= args[0]->max_length; } + void fix_length_and_dec() + { + if (arg_count) + max_length= args[0]->max_length; + } }; class Item_func_benchmark :public Item_int_func @@ -809,6 +902,14 @@ class Item_func_udf_float :public Item_udf_func DBUG_ASSERT(fixed == 1); return (longlong) Item_func_udf_float::val_real(); } + my_decimal *val_decimal(my_decimal *dec_buf) + { + double res=val_real(); + if (null_value) + return NULL; + double2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf); + return dec_buf; + } double val_real(); String *val_str(String *str); void fix_length_and_dec() { fix_num_length_and_dec(); } @@ -825,7 +926,22 @@ public: double val_real() { return (double) Item_func_udf_int::val_int(); } String *val_str(String *str); enum Item_result result_type () const { return INT_RESULT; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { decimals= 0; max_length= 21; } +}; + + +class Item_func_udf_decimal :public Item_udf_func +{ +public: + Item_func_udf_decimal(udf_func *udf_arg) :Item_udf_func(udf_arg) {} + Item_func_udf_decimal(udf_func *udf_arg, List<Item> &list) + :Item_udf_func(udf_arg,list) {} + longlong val_int(); + double val_real(); + my_decimal *val_decimal(my_decimal *); + String *val_str(String *str); + enum Item_result result_type () const { return DECIMAL_RESULT; } + void fix_length_and_dec(); }; @@ -852,6 +968,14 @@ public: return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10, (char**) 0, &err_not_used) : (longlong) 0; } + my_decimal *val_decimal(my_decimal *dec_buf) + { + String *res=val_str(&str_value); + if (!res) + return NULL; + string2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf); + return dec_buf; + } enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); }; @@ -876,6 +1000,15 @@ public: }; +class Item_func_udf_decimal :public Item_int_func +{ +public: + Item_func_udf_decimal(udf_func *udf_arg) :Item_int_func() {} + Item_func_udf_decimal(udf_func *udf_arg, List<Item> &list) :Item_int_func(list) {} + my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; } +}; + + class Item_func_udf_str :public Item_func { public: @@ -934,7 +1067,7 @@ class Item_master_pos_wait :public Item_int_func }; -/* Handling of user definiable variables */ +/* Handling of user definable variables */ class user_var_entry; @@ -945,11 +1078,13 @@ class Item_func_set_user_var :public Item_func user_var_entry *entry; char buffer[MAX_FIELD_WIDTH]; String value; + my_decimal decimal_buff; union { longlong vint; double vreal; String *vstr; + my_decimal *vdec; } save_result; String save_buff; @@ -961,6 +1096,7 @@ public: double val_real(); longlong val_int(); String *val_str(String *str); + my_decimal *val_decimal(my_decimal *); bool update_hash(void *ptr, uint length, enum Item_result type, CHARSET_INFO *cs, Derivation dv); bool check(); @@ -986,6 +1122,7 @@ public: LEX_STRING get_name() { return name; } double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal*); String *val_str(String* str); void fix_length_and_dec(); void print(String *str); @@ -1095,7 +1232,8 @@ public: enum Cast_target { ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, - ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR + ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR, + ITEM_CAST_DECIMAL }; @@ -1161,6 +1299,22 @@ public: return d; } + my_decimal *val_decimal(my_decimal *dec_buf) + { + Item *it; + my_decimal *result; + + if (execute(&it)) + { + null_value= 1; + return NULL; + } + result= it->val_decimal(dec_buf); + null_value= it->null_value; + return result; + } + + String *val_str(String *str) { Item *it; diff --git a/sql/item_row.cc b/sql/item_row.cc index a5a430785d6..08c682afa85 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -100,10 +100,9 @@ void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array, (item->used_tables() && item->type() != REF_ITEM)) { uint el= fields.elements; - ref_pointer_array[el]=*arg; + ref_pointer_array[el]= *arg; Item *new_item= new Item_ref(ref_pointer_array + el, 0, (*arg)->name); fields.push_front(*arg); - ref_pointer_array[el]= *arg; thd->change_item_tree(arg, new_item); } } diff --git a/sql/item_row.h b/sql/item_row.h index 64bd5cbbb44..e6b23eebb49 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -56,6 +56,11 @@ public: illegal_method_call((const char*)"val_str"); return 0; }; + my_decimal *val_decimal(my_decimal *) + { + illegal_method_call((const char*)"val_decimal"); + return 0; + }; bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); table_map used_tables() const { return used_tables_cache; }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e3f1053dc73..55185a5f75b 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -49,6 +49,8 @@ static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, uint nr_of_decimals(const char *str) { + if (strchr(str,'e') || strchr(str,'E')) + return NOT_FIXED_DEC; if ((str=strchr(str,'.'))) { const char *start= ++str; @@ -1784,10 +1786,9 @@ void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array, (item->used_tables() && item->type() != REF_ITEM)) { uint el= fields.elements; - ref_pointer_array[el]=item; + ref_pointer_array[el]= item; Item *new_item= new Item_ref(ref_pointer_array + el, 0, item->name); fields.push_front(item); - ref_pointer_array[el]= item; thd->change_item_tree(&item, new_item); } Item_str_func::split_sum_func(thd, ref_pointer_array, fields); @@ -1803,7 +1804,7 @@ void Item_func_make_set::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) max_length+=args[i]->max_length; - + used_tables_cache|= item->used_tables(); not_null_tables_cache&= item->not_null_tables(); const_item_cache&= item->const_item(); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 97c42c3abf6..dc50c9a4ccd 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -397,8 +397,7 @@ public: bool fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) { DBUG_ASSERT(fixed == 0); - return (!item->fixed && - item->fix_fields(thd, tlist, &item) || + return ((!item->fixed && item->fix_fields(thd, tlist, &item)) || item->check_cols(1) || Item_func::fix_fields(thd, tlist, ref)); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index b5cb01494fa..8d392232f02 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -143,7 +143,7 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) res= engine->prepare(); - // all transformetion is done (used by prepared statements) + // all transformation is done (used by prepared statements) changed= 1; if (!res) @@ -246,7 +246,7 @@ void Item_subselect::update_used_tables() { if (!engine->uncacheable()) { - // did all used tables become ststic? + // did all used tables become static? if (!(used_tables_cache & ~engine->upper_select_const_tables())) const_item_cache= 1; } @@ -292,7 +292,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, const_item_cache= parent->get_const_item_cache(); /* - this subquery alwais creates during preparation, so we can assign + this subquery always creates during preparation, so we can assign thd here */ thd= thd_param; @@ -356,7 +356,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) !(select_lex->item_list.head()->type() == FIELD_ITEM || select_lex->item_list.head()->type() == REF_ITEM) && /* - switch off this optimisation for prepare statement, + switch off this optimization for prepare statement, because we do not rollback this changes TODO: make rollback for it, or special name resolving mode in 5.0. */ @@ -374,7 +374,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) } substitution= select_lex->item_list.head(); /* - as far as we moved content to upper leven, field which depend of + as far as we moved content to upper level, field which depend of 'upper' select is not really dependent => we remove this dependence */ substitution->walk(&Item::remove_dependence_processor, @@ -494,7 +494,7 @@ longlong Item_singlerow_subselect::val_int() } } -String *Item_singlerow_subselect::val_str (String *str) +String *Item_singlerow_subselect::val_str(String *str) { if (!exec() && !value->null_value) { @@ -509,10 +509,41 @@ String *Item_singlerow_subselect::val_str (String *str) } +my_decimal *Item_singlerow_subselect::val_decimal(my_decimal *decimal_value) +{ + if (!exec() && !value->null_value) + { + null_value= 0; + return value->val_decimal(decimal_value); + } + else + { + reset(); + return 0; + } +} + + +bool Item_singlerow_subselect::val_bool() +{ + if (!exec() && !value->null_value) + { + null_value= 0; + return value->val_bool(); + } + else + { + reset(); + return 0; + } +} + + Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex): Item_subselect() { DBUG_ENTER("Item_exists_subselect::Item_exists_subselect"); + bool val_bool(); init(select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; null_value= 0; //can't be NULL @@ -622,6 +653,32 @@ String *Item_exists_subselect::val_str(String *str) return str; } + +my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + if (exec()) + { + reset(); + return 0; + } + int2my_decimal(E_DEC_FATAL_ERROR, value, 0, decimal_value); + return decimal_value; +} + + +bool Item_exists_subselect::val_bool() +{ + DBUG_ASSERT(fixed == 1); + if (exec()) + { + reset(); + return 0; + } + return value; +} + + double Item_in_subselect::val_real() { DBUG_ASSERT(fixed == 1); @@ -752,7 +809,8 @@ Item_in_subselect::single_value_transformer(JOIN *join, /* Item_sum_(max|min) can't substitute other item => we can use 0 as - reference + reference, also Item_sum_(max|min) can't be fixed after creation, so + we do not check item->fixed */ if (item->fix_fields(thd, join->tables_list, 0)) goto err; @@ -774,7 +832,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, // left expression belong to outer select SELECT_LEX *current= thd->lex->current_select, *up; thd->lex->current_select= up= current->return_after_parsing(); - if (!left_expr->fixed && + if (!left_expr->fixed && left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) { thd->lex->current_select= current; @@ -803,7 +861,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, thd->lex->current_select= current; /* - As far as Item_ref_in_optimizer do not substitude itself on fix_fields + As far as Item_ref_in_optimizer do not substitute itself on fix_fields we can use same item for all selects. */ expr= new Item_direct_ref((Item**)optimizer->get_cache(), @@ -840,6 +898,10 @@ Item_in_subselect::single_value_transformer(JOIN *join, */ select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; + /* + we do not check join->having->fixed, because Item_and (from and_items) + or comparison function (from func->create) can't be fixed after creation + */ tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) @@ -871,6 +933,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, new Item_cond_and(having, join->having) : having); select_lex->having_fix_field= 1; + /* + we do not check join->having->fixed, because Item_and (from + and_items) or comparison function (from func->create) can't be + fixed after creation + */ tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) @@ -887,6 +954,10 @@ Item_in_subselect::single_value_transformer(JOIN *join, argument (reference) to fix_fields() */ select_lex->where= join->conds= and_items(join->conds, item); + /* + we do not check join->conds->fixed, because Item_and can't be fixed + after creation + */ if (join->conds->fix_fields(thd, join->tables_list, 0)) goto err; } @@ -908,6 +979,10 @@ Item_in_subselect::single_value_transformer(JOIN *join, item= new Item_cond_or(new Item_func_isnull(left_expr), item); select_lex->having= join->having= item; select_lex->having_fix_field= 1; + /* + we do not check join->having->fixed, because comparison function + (from func->create) can't be fixed after creation + */ tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) @@ -982,7 +1057,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) goto err; } - // we will refer to apper level cache array => we have to save it in PS + // we will refer to upper level cache array => we have to save it in PS optimizer->keep_top_level_cache(); thd->lex->current_select= current; @@ -1000,9 +1075,9 @@ Item_in_subselect::row_value_transformer(JOIN *join) check_cols(left_expr->el(i)->cols())) goto err; Item *func= new Item_ref_null_helper(this, - select_lex->ref_pointer_array+i, - (char *) "<no matter>", - (char *) "<list ref>"); + select_lex->ref_pointer_array+i, + (char *) "<no matter>", + (char *) "<list ref>"); func= eq_creator.create(new Item_direct_ref((*optimizer->get_cache())-> addr(i), @@ -1023,6 +1098,10 @@ Item_in_subselect::row_value_transformer(JOIN *join) */ select_lex->having= join->having= and_items(join->having, item); select_lex->having_fix_field= 1; + /* + join->having can't be fixed after creation, so we do not check + join->having->fixed + */ if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; @@ -1038,6 +1117,10 @@ Item_in_subselect::row_value_transformer(JOIN *join) argument (reference) to fix_fields() */ select_lex->where= join->conds= and_items(join->conds, item); + /* + join->conds can't be fixed after creation, so we do not check + join->conds->fixed + */ if (join->conds->fix_fields(thd, join->tables_list, 0)) goto err; } @@ -1533,7 +1616,7 @@ void subselect_indexsubquery_engine::print(String *str) str->append(" on ", 4); str->append(key_info->name); if (check_null) - str->append(" chicking NULL", 14); + str->append(" checking NULL", 14); if (cond) { str->append(" where ", 7); @@ -1545,7 +1628,7 @@ void subselect_indexsubquery_engine::print(String *str) /* change select_result object of engine - SINOPSYS + SYNOPSIS subselect_single_select_engine::change_result() si new subselect Item res new select_result object @@ -1567,7 +1650,7 @@ bool subselect_single_select_engine::change_result(Item_subselect *si, /* change select_result object of engine - SINOPSYS + SYNOPSIS subselect_single_select_engine::change_result() si new subselect Item res new select_result object @@ -1590,7 +1673,7 @@ bool subselect_union_engine::change_result(Item_subselect *si, /* change select_result emulation, never should be called - SINOPSYS + SYNOPSIS subselect_single_select_engine::change_result() si new subselect Item res new select_result object @@ -1611,7 +1694,7 @@ bool subselect_uniquesubquery_engine::change_result(Item_subselect *si, /* Report about presence of tables in subquery - SINOPSYS + SYNOPSIS subselect_single_select_engine::no_tables() RETURN @@ -1627,7 +1710,7 @@ bool subselect_single_select_engine::no_tables() /* Report about presence of tables in subquery - SINOPSYS + SYNOPSIS subselect_union_engine::no_tables() RETURN @@ -1648,7 +1731,7 @@ bool subselect_union_engine::no_tables() /* Report about presence of tables in subquery - SINOPSYS + SYNOPSIS subselect_uniquesubquery_engine::no_tables() RETURN diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 53fe21a9bb6..4661fae81da 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -142,6 +142,8 @@ public: double val_real(); longlong val_int (); String *val_str (String *); + my_decimal *val_decimal(my_decimal *); + bool val_bool(); enum Item_result result_type() const; void fix_length_and_dec(); @@ -155,7 +157,7 @@ public: friend class select_singlerow_subselect; }; -/* used in static ALL/ANY optimisation */ +/* used in static ALL/ANY optimization */ class select_max_min_finder_subselect; class Item_maxmin_subselect :public Item_singlerow_subselect { @@ -193,6 +195,8 @@ public: longlong val_int(); double val_real(); String *val_str(String*); + my_decimal *val_decimal(my_decimal *); + bool val_bool(); void fix_length_and_dec(); void print(String *str); @@ -294,7 +298,7 @@ public: virtual int prepare()= 0; virtual void fix_length_and_dec(Item_cache** row)= 0; virtual int exec()= 0; - virtual uint cols()= 0; /* return number of columnss in select */ + virtual uint cols()= 0; /* return number of columns in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ enum Item_result type() { return res_type; } virtual void exclude()= 0; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 29837c3afbd..2ecc1eb083c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -124,6 +124,16 @@ Item *Item_sum::get_tmp_table_item(THD *thd) return sum_item; } + +my_decimal *Item_sum::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + DBUG_ASSERT(decimal_value); + int2my_decimal(E_DEC_FATAL_ERROR, val_int(), unsigned_flag, decimal_value); + return decimal_value; +} + + bool Item_sum::walk (Item_processor processor, byte *argument) { if (arg_count) @@ -139,6 +149,32 @@ bool Item_sum::walk (Item_processor processor, byte *argument) } +Field *Item_sum::create_tmp_field(bool group, TABLE *table, + uint convert_blob_length) +{ + switch (result_type()) { + case REAL_RESULT: + return new Field_double(max_length,maybe_null,name,table,decimals); + case INT_RESULT: + return new Field_longlong(max_length,maybe_null,name,table,unsigned_flag); + case STRING_RESULT: + if (max_length > 255 && convert_blob_length) + return new Field_varstring(convert_blob_length, maybe_null, + name, table, + collation.collation); + return make_string_field(table); + case DECIMAL_RESULT: + return new Field_new_decimal(max_length - (decimals?1:0), + maybe_null, name, table, decimals); + case ROW_RESULT: + default: + // This case should never be choosen + DBUG_ASSERT(0); + return 0; + } +} + + String * Item_sum_num::val_str(String *str) { @@ -151,6 +187,17 @@ Item_sum_num::val_str(String *str) } +my_decimal *Item_sum_num::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + double nr= val_real(); + if (null_value) + return 0; + double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); + return (decimal_value); +} + + String * Item_sum_int::val_str(String *str) { @@ -184,8 +231,7 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { if (args[i]->fix_fields(thd, tables, args + i) || args[i]->check_cols(1)) return TRUE; - if (decimals < args[i]->decimals) - decimals=args[i]->decimals; + set_if_bigger(decimals, args[i]->decimals); maybe_null |= args[i]->maybe_null; } result_field=0; @@ -198,6 +244,29 @@ Item_sum_num::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } +Item_sum_hybrid::Item_sum_hybrid(THD *thd, Item_sum_hybrid *item) + :Item_sum(thd, item), value(item->value), hybrid_type(item->hybrid_type), + hybrid_field_type(item->hybrid_field_type), cmp_sign(item->cmp_sign), + used_table_cache(item->used_table_cache), was_values(item->was_values) +{ + switch (hybrid_type) + { + case INT_RESULT: + sum_int= item->sum_int; + break; + case DECIMAL_RESULT: + my_decimal2decimal(&item->sum_dec, &sum_dec); + break; + case REAL_RESULT: + sum= item->sum; + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + collation.set(item->collation); +} + bool Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) { @@ -217,20 +286,29 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) item->fix_fields(thd, tables, args) || (item= args[0])->check_cols(1)) return TRUE; + decimals=item->decimals; - 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 + switch (hybrid_type= item->result_type()) { - max_length=item->max_length; - } - decimals=item->decimals; + case INT_RESULT: + max_length= 20; + sum_int= 0; + break; + case DECIMAL_RESULT: + max_length= item->max_length; + my_decimal_set_zero(&sum_dec); + break; + case REAL_RESULT: + max_length= float_length(decimals); + sum= 0.0; + break; + case STRING_RESULT: + max_length= item->max_length; + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + }; /* MIN/MAX can return NULL for empty set indepedent of the used column */ maybe_null= 1; unsigned_flag=item->unsigned_flag; @@ -252,6 +330,19 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ** reset and add of sum_func ***********************************************************************/ +Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) + :Item_sum_num(thd, item), hybrid_type(item->hybrid_type), + curr_dec_buff(item->curr_dec_buff) +{ + if (hybrid_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); @@ -260,30 +351,128 @@ Item *Item_sum_sum::copy_or_same(THD* thd) void Item_sum_sum::clear() { - null_value=1; sum=0.0; + DBUG_ENTER("Item_sum_sum::clear"); + null_value=1; + if (hybrid_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() +{ + DBUG_ENTER("Item_sum_sum::fix_length_and_dec"); + maybe_null=null_value=1; + decimals= args[0]->decimals; + switch (args[0]->result_type()) + { + case REAL_RESULT: + case STRING_RESULT: + hybrid_type= REAL_RESULT; + sum= 0.0; + break; + case INT_RESULT: + case DECIMAL_RESULT: + /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ + max_length= min(args[0]->max_length + DECIMAL_LONGLONG_DIGITS, + DECIMAL_MAX_LENGTH); + curr_dec_buff= 0; + hybrid_type= DECIMAL_RESULT; + my_decimal_set_zero(dec_buffs); + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info", ("Type: %s (%d, %d)", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"), + max_length, + (int)decimals)); + DBUG_VOID_RETURN; } bool Item_sum_sum::add() { - sum+= args[0]->val_real(); - if (!args[0]->null_value) - null_value= 0; - return 0; + DBUG_ENTER("Item_sum_sum::add"); + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *val= args[0]->val_decimal(&value); + if (!args[0]->null_value) + { + 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= 0; + } + } + else + { + sum+= args[0]->val_real(); + if (!args[0]->null_value) + null_value= 0; + } + DBUG_RETURN(0); +} + + +longlong Item_sum_sum::val_int() +{ + DBUG_ASSERT(fixed == 1); + if (hybrid_type == DECIMAL_RESULT) + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag, + &result); + return result; + } + return Item_sum_num::val_int(); } double Item_sum_sum::val_real() { DBUG_ASSERT(fixed == 1); + if (hybrid_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 (hybrid_type == DECIMAL_RESULT) + { + if (null_value) + return NULL; + my_decimal_round(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, decimals, + FALSE, dec_buffs + curr_dec_buff); + my_decimal2string(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, + 0, 0, 0, str); + return str; + } + return Item_sum_num::val_str(str); +} + + +my_decimal *Item_sum_sum::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(hybrid_type == DECIMAL_RESULT); + return(dec_buffs + curr_dec_buff); +} + /* Item_sum_sum_distinct */ Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item) - :Item_sum_num(item), sum(0.0), tree(0) + :Item_sum_sum(item), tree(0) { /* quick_group is an optimizer hint, which means that GROUP BY can be @@ -297,12 +486,24 @@ Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item) Item_sum_sum_distinct::Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *original) - :Item_sum_num(thd, original), sum(0.0), tree(0) + :Item_sum_sum(thd, original), tree(0), dec_bin_buff(original->dec_bin_buff) { quick_group= 0; } +void Item_sum_sum_distinct::fix_length_and_dec() +{ + Item_sum_sum::fix_length_and_dec(); + if (hybrid_type == DECIMAL_RESULT) + { + dec_bin_buff= (byte *) + sql_alloc(my_decimal_get_binary_size(args[0]->max_length, + args[0]->decimals)); + } +} + + Item * Item_sum_sum_distinct::copy_or_same(THD *thd) { @@ -320,10 +521,11 @@ C_MODE_END bool Item_sum_sum_distinct::setup(THD *thd) { + DBUG_ENTER("Item_sum_sum_distinct::setup"); SELECT_LEX *select_lex= thd->lex->current_select; /* what does it mean??? */ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE) - return 1; + DBUG_RETURN(1); DBUG_ASSERT(tree == 0); /* setup can not be called twice */ @@ -337,17 +539,25 @@ bool Item_sum_sum_distinct::setup(THD *thd) TODO: if underlying item result fits in 4 bytes we can take advantage of it and have tree of long/ulong. It gives 10% performance boost */ - static uint key_length= sizeof(double); - tree= new Unique(simple_raw_key_cmp, &key_length, key_length, + uint *key_length_ptr= (uint *)thd->alloc(sizeof(uint)); + *key_length_ptr= ((hybrid_type == DECIMAL_RESULT) ? + my_decimal_get_binary_size(args[0]->max_length, + args[0]->decimals) : + sizeof(double)); + tree= new Unique(simple_raw_key_cmp, key_length_ptr, *key_length_ptr, thd->variables.max_heap_table_size); - return tree == 0; + DBUG_PRINT("info", ("tree 0x%lx, key length %d", (ulong)tree, + *key_length_ptr)); + DBUG_RETURN(tree == 0); } void Item_sum_sum_distinct::clear() { + DBUG_ENTER("Item_sum_sum_distinct::clear"); DBUG_ASSERT(tree); /* we always have a tree */ null_value= 1; tree->reset(); + DBUG_VOID_RETURN; } void Item_sum_sum_distinct::cleanup() @@ -360,25 +570,71 @@ void Item_sum_sum_distinct::cleanup() bool Item_sum_sum_distinct::add() { - /* args[0]->val_real() may reset args[0]->null_value */ - double val= args[0]->val_real(); - if (!args[0]->null_value) + DBUG_ENTER("Item_sum_sum_distinct::add"); + if (hybrid_type == DECIMAL_RESULT) { - DBUG_ASSERT(tree); - null_value= 0; - if (val) - return tree->unique_add(&val); + my_decimal value, *val= args[0]->val_decimal(&value); + if (!args[0]->null_value) + { + DBUG_ASSERT(tree); + null_value= 0; + my_decimal2binary(E_DEC_FATAL_ERROR, val, dec_bin_buff, + args[0]->max_length, args[0]->decimals); + DBUG_RETURN(tree->unique_add(dec_bin_buff)); + } } - return 0; + else + { + /* args[0]->val() may reset args[0]->null_value */ + double val= args[0]->val_real(); + if (!args[0]->null_value) + { + DBUG_ASSERT(tree); + null_value= 0; + DBUG_PRINT("info", ("real: %lg, tree 0x%lx", val, (ulong)tree)); + if (val) + DBUG_RETURN(tree->unique_add(&val)); + } + else + DBUG_PRINT("info", ("real: NULL")); + } + DBUG_RETURN(0); +} + + +void Item_sum_sum_distinct::add_real(double val) +{ + DBUG_ENTER("Item_sum_sum_distinct::add_real"); + sum+= val; + DBUG_PRINT("info", ("sum %lg, val %lg", sum, val)); + DBUG_VOID_RETURN; +} + + +void Item_sum_sum_distinct::add_decimal(byte *val) +{ + binary2my_decimal(E_DEC_FATAL_ERROR, val, &tmp_dec, + args[0]->max_length, args[0]->decimals); + my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1), + &tmp_dec, dec_buffs + curr_dec_buff); + curr_dec_buff^= 1; } C_MODE_START -static int sum_sum_distinct(void *element, element_count num_of_dups, - void *item_sum_sum_distinct) +static int sum_sum_distinct_real(void *element, element_count num_of_dups, + void *item_sum_sum_distinct) +{ + ((Item_sum_sum_distinct *) + (item_sum_sum_distinct))->add_real(* (double *) element); + return 0; +} + +static int sum_sum_distinct_decimal(void *element, element_count num_of_dups, + void *item_sum_sum_distinct) { ((Item_sum_sum_distinct *) - (item_sum_sum_distinct))->add(* (double *) element); + (item_sum_sum_distinct))->add_decimal((byte *)element); return 0; } @@ -386,16 +642,86 @@ C_MODE_END double Item_sum_sum_distinct::val_real() { + DBUG_ENTER("Item_sum_sum_distinct::val"); /* We don't have a tree only if 'setup()' hasn't been called; this is the case of sql_select.cc:return_zero_rows. */ - sum= 0.0; - if (tree) - tree->walk(sum_sum_distinct, (void *) this); - return sum; + if (hybrid_type == DECIMAL_RESULT) + { + /* Item_sum_sum_distinct::val_decimal do not use argument */ + my_decimal *val= val_decimal(0); + if (!null_value) + my_decimal2double(E_DEC_FATAL_ERROR, val, &sum); + } + else + { + sum= 0.0; + DBUG_PRINT("info", ("tree 0x%lx", (ulong)tree)); + if (tree) + tree->walk(sum_sum_distinct_real, (void *) this); + } + DBUG_RETURN(sum); } + +my_decimal *Item_sum_sum_distinct::val_decimal(my_decimal *fake) +{ + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal_set_zero(dec_buffs); + curr_dec_buff= 0; + if (tree) + tree->walk(sum_sum_distinct_decimal, (void *)this); + } + else + { + double real= val_real(); + double2my_decimal(E_DEC_FATAL_ERROR, real, dec_buffs + curr_dec_buff); + } + return(dec_buffs + curr_dec_buff); +} + + +longlong Item_sum_sum_distinct::val_int() +{ + longlong i; + if (hybrid_type == DECIMAL_RESULT) + { + /* Item_sum_sum_distinct::val_decimal do not use argument */ + my_decimal *val= val_decimal(0); + if (!null_value) + my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &i); + } + else + i= (longlong) val_real(); + return i; +} + + +String *Item_sum_sum_distinct::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + if (hybrid_type == DECIMAL_RESULT) + { + /* Item_sum_sum_distinct::val_decimal do not use argument */ + my_decimal *val= val_decimal(0); + if (null_value) + return 0; + my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); + my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); + } + else + { + double nr= val_real(); + if (null_value) + return 0; + str->set(nr, decimals, &my_charset_bin); + } + return str; +} + + /* end of Item_sum_sum_distinct */ Item *Item_sum_count::copy_or_same(THD* thd) @@ -442,6 +768,20 @@ void Item_sum_count::cleanup() /* Avgerage */ +void Item_sum_avg::fix_length_and_dec() +{ + Item_sum_sum::fix_length_and_dec(); + maybe_null=null_value=1; + decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC); + if (hybrid_type == DECIMAL_RESULT) + { + f_scale= args[0]->decimals; + max_length= DECIMAL_MAX_LENGTH + (f_scale ? 1 : 0); + f_precision= DECIMAL_MAX_LENGTH; + dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); + } +} + Item *Item_sum_avg::copy_or_same(THD* thd) { @@ -449,21 +789,43 @@ Item *Item_sum_avg::copy_or_same(THD* thd) } +Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table, + uint convert_blob_len) +{ + if (hybrid_type == DECIMAL_RESULT) + { + if (group) + return new Field_string(dec_bin_size + sizeof(longlong), + 0, name, table, &my_charset_bin); + else + return new Field_new_decimal(f_precision, + maybe_null, name, table, f_scale); + } + else + { + if (group) + return new Field_string(sizeof(double)+sizeof(longlong), + 0, name,table,&my_charset_bin); + else + return new Field_double(max_length, maybe_null, name, table, decimals); + } +} + + void Item_sum_avg::clear() { - sum=0.0; count=0; + Item_sum_sum::clear(); + count=0; } bool Item_sum_avg::add() { - double nr= args[0]->val_real(); + if (Item_sum_sum::add()) + return TRUE; if (!args[0]->null_value) - { - sum+=nr; count++; - } - return 0; + return FALSE; } double Item_sum_avg::val_real() @@ -474,11 +836,46 @@ double Item_sum_avg::val_real() null_value=1; return 0.0; } - null_value=0; - return sum/ulonglong2double(count); + return Item_sum_sum::val_real() / ulonglong2double(count); } +my_decimal *Item_sum_avg::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed == 1); + if (!count) + { + null_value=1; + return NULL; + } + my_decimal sum, cnt; + const my_decimal *sum_dec= Item_sum_sum::val_decimal(&sum); + int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt); + my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, 4); + return val; +} + + +String *Item_sum_avg::val_str(String *str) +{ + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *dec_val= val_decimal(&value); + if (null_value) + return NULL; + my_decimal_round(E_DEC_FATAL_ERROR, dec_val, decimals, FALSE, &value); + my_decimal2string(E_DEC_FATAL_ERROR, &value, 0, 0, 0, str); + return str; + } + double nr= val_real(); + if (null_value) + return NULL; + str->set(nr, decimals, &my_charset_bin); + return str; +} + + + /* Standard deviation */ @@ -500,26 +897,140 @@ Item *Item_sum_std::copy_or_same(THD* thd) Variance */ + +Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item): + Item_sum_num(thd, item), hybrid_type(item->hybrid_type), + cur_dec(item->cur_dec), count(item->count) +{ + if (hybrid_type == DECIMAL_RESULT) + { + memcpy(dec_sum, item->dec_sum, sizeof(item->dec_sum)); + memcpy(dec_sqr, item->dec_sqr, sizeof(item->dec_sqr)); + for (int i=0; i<2; i++) + { + dec_sum[i].fix_buffer_pointer(); + dec_sqr[i].fix_buffer_pointer(); + } + } + else + { + sum= item->sum; + sum_sqr= item->sum_sqr; + } +} + + +void Item_sum_variance::fix_length_and_dec() +{ + DBUG_ENTER("Item_sum_sum::fix_length_and_dec"); + maybe_null=null_value=1; + decimals= args[0]->decimals + 4; + switch (args[0]->result_type()) + { + case REAL_RESULT: + case STRING_RESULT: + hybrid_type= REAL_RESULT; + sum= 0.0; + break; + case INT_RESULT: + case DECIMAL_RESULT: + /* SUM result can't be longer than length(arg)*2 + digits_after_the_point_to_add*/ + max_length= args[0]->max_length*2 + 4; + cur_dec= 0; + hybrid_type= DECIMAL_RESULT; + my_decimal_set_zero(dec_sum); + my_decimal_set_zero(dec_sqr); + f_scale0= args[0]->decimals; + f_precision0= DECIMAL_MAX_LENGTH / 2; + f_scale1= min(f_scale0 * 2, NOT_FIXED_DEC - 1); + f_precision1= DECIMAL_MAX_LENGTH; + dec_bin_size0= my_decimal_get_binary_size(f_precision0, f_scale0); + dec_bin_size1= my_decimal_get_binary_size(f_precision1, f_scale1); + break; + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info", ("Type: %s (%d, %d)", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"), + 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); } +Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table, + uint convert_blob_len) +{ + if (hybrid_type == DECIMAL_RESULT) + { + if (group) + return new Field_string(dec_bin_size0+dec_bin_size1+sizeof(longlong), + 0, name,table,&my_charset_bin); + else + return new Field_new_decimal(DECIMAL_MAX_LENGTH, + maybe_null, name, table, f_scale1 + 4); + } + else + { + if (group) + return new Field_string(sizeof(double)*2+sizeof(longlong), + 0, name,table,&my_charset_bin); + else + return new Field_double(max_length, maybe_null,name,table,decimals); + } +} + + void Item_sum_variance::clear() { - sum=sum_sqr=0.0; + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal_set_zero(dec_sum); + my_decimal_set_zero(dec_sqr); + cur_dec= 0; + } + else + sum=sum_sqr=0.0; count=0; } bool Item_sum_variance::add() { - double nr= args[0]->val_real(); - if (!args[0]->null_value) + if (hybrid_type == DECIMAL_RESULT) { - sum+=nr; - sum_sqr+=nr*nr; - count++; + my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf); + my_decimal sqr_buf; + if (!args[0]->null_value) + { + count++; + int next_dec= cur_dec ^ 1; + my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, dec, dec); + my_decimal_add(E_DEC_FATAL_ERROR, dec_sqr+next_dec, + dec_sqr+cur_dec, &sqr_buf); + my_decimal_add(E_DEC_FATAL_ERROR, dec_sum+next_dec, + dec_sum+cur_dec, dec); + cur_dec= next_dec; + } + } + else + { + double nr= args[0]->val_real(); + if (!args[0]->null_value) + { + sum+=nr; + sum_sqr+=nr*nr; + count++; + } } return 0; } @@ -533,16 +1044,77 @@ double Item_sum_variance::val_real() return 0.0; } null_value=0; + if (hybrid_type == DECIMAL_RESULT) + { + double result; + my_decimal dec_buf, *dec= Item_sum_variance::val_decimal(&dec_buf); + my_decimal2double(E_DEC_FATAL_ERROR, dec, &result); + return result; + } /* 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 : tmp2; } + +my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf) +{ + DBUG_ASSERT(fixed ==1 ); + if (hybrid_type == REAL_RESULT) + { + double result= Item_sum_variance::val_real(); + if (null_value) + return 0; + double2my_decimal(E_DEC_FATAL_ERROR, result, dec_buf); + return dec_buf; + } + if (!count) + { + null_value= 1; + return 0; + } + null_value= 0; + my_decimal count_buf, sum_sqr_buf; + int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &count_buf); + my_decimal_mul(E_DEC_FATAL_ERROR, &sum_sqr_buf, + dec_sum+cur_dec, dec_sum+cur_dec); + my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &sum_sqr_buf, &count_buf, 2); + my_decimal_sub(E_DEC_FATAL_ERROR, &sum_sqr_buf, dec_sqr+cur_dec, dec_buf); + my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &sum_sqr_buf, &count_buf, 2); + return dec_buf; +} + void Item_sum_variance::reset_field() { - double nr= args[0]->val_real(); char *res=result_field->ptr; + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *arg_dec= args[0]->val_decimal(&value); + if (args[0]->null_value) + { + my_decimal2binary(E_DEC_FATAL_ERROR, &decimal_zero, + res, f_precision0, f_scale0); + my_decimal2binary(E_DEC_FATAL_ERROR, &decimal_zero, + res+dec_bin_size0, f_precision1, f_scale1); + res+= dec_bin_size0 + dec_bin_size1; + longlong tmp=0; + int8store(res,tmp); + } + else + { + my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, + res, f_precision0, f_scale0); + my_decimal_mul(E_DEC_FATAL_ERROR, dec_sum, arg_dec, arg_dec); + my_decimal2binary(E_DEC_FATAL_ERROR, dec_sum, + res+dec_bin_size0, f_precision1, f_scale1); + res+= dec_bin_size0 + dec_bin_size1; + longlong tmp=1; + int8store(res,tmp); + } + return; + } + double nr= args[0]->val_real(); if (args[0]->null_value) bzero(res,sizeof(double)*2+sizeof(longlong)); @@ -558,10 +1130,33 @@ void Item_sum_variance::reset_field() void Item_sum_variance::update_field() { - double nr,old_nr,old_sqr; longlong field_count; char *res=result_field->ptr; + if (hybrid_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_sum+1, f_precision0, f_scale0); + binary2my_decimal(E_DEC_FATAL_ERROR, res+dec_bin_size0, + dec_sqr+1, f_precision1, f_scale1); + field_count= sint8korr(res + (dec_bin_size0 + dec_bin_size1)); + my_decimal_add(E_DEC_FATAL_ERROR, dec_sum, arg_val, dec_sum+1); + my_decimal_mul(E_DEC_FATAL_ERROR, dec_sum+1, arg_val, arg_val); + my_decimal_add(E_DEC_FATAL_ERROR, dec_sqr, dec_sqr+1, dec_sum+1); + field_count++; + my_decimal2binary(E_DEC_FATAL_ERROR, dec_sum, + res, f_precision0, f_scale0); + my_decimal2binary(E_DEC_FATAL_ERROR, dec_sqr, + res+dec_bin_size0, f_precision1, f_scale1); + res+= dec_bin_size0 + dec_bin_size1; + int8store(res, field_count); + } + return; + } + double nr,old_nr,old_sqr; float8get(old_nr, res); float8get(old_sqr, res+sizeof(double)); field_count=sint8korr(res+sizeof(double)*2); @@ -582,9 +1177,20 @@ void Item_sum_variance::update_field() void Item_sum_hybrid::clear() { - sum= 0.0; - sum_int= 0; - value.length(0); + switch (hybrid_type) + { + case INT_RESULT: + sum_int= 0; + break; + case DECIMAL_RESULT: + my_decimal_set_zero(&sum_dec); + break; + case REAL_RESULT: + sum= 0.0; + break; + default: + value.length(0); + } null_value= 1; } @@ -606,6 +1212,9 @@ double Item_sum_hybrid::val_real() if (unsigned_flag) return ulonglong2double(sum_int); return (double) sum_int; + case DECIMAL_RESULT: + my_decimal2double(E_DEC_FATAL_ERROR, &sum_dec, &sum); + return sum; case REAL_RESULT: return sum; case ROW_RESULT: @@ -622,9 +1231,47 @@ longlong Item_sum_hybrid::val_int() DBUG_ASSERT(fixed == 1); if (null_value) return 0; - if (hybrid_type == INT_RESULT) + switch (hybrid_type) + { + case INT_RESULT: + return sum_int; + case DECIMAL_RESULT: + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, &sum_dec, unsigned_flag, &result); return sum_int; - return (longlong) Item_sum_hybrid::val_real(); + } + default: + return (longlong) Item_sum_hybrid::val_real(); + } +} + + +my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed == 1); + if (null_value) + return 0; + switch (hybrid_type) { + case STRING_RESULT: + string2my_decimal(E_DEC_FATAL_ERROR, &value, val); + break; + case REAL_RESULT: + double2my_decimal(E_DEC_FATAL_ERROR, sum, val); + break; + case DECIMAL_RESULT: + val= &sum_dec; + break; + case INT_RESULT: + int2my_decimal(E_DEC_FATAL_ERROR, sum_int, unsigned_flag, val); + break; + case ROW_RESULT: + default: + // This case should never be choosen + DBUG_ASSERT(0); + break; + } + return val; // Keep compiler happy } @@ -640,6 +1287,9 @@ Item_sum_hybrid::val_str(String *str) case REAL_RESULT: str->set(sum,decimals, &my_charset_bin); break; + case DECIMAL_RESULT: + my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str); + return str; case INT_RESULT: if (unsigned_flag) str->set((ulonglong) sum_int, &my_charset_bin); @@ -713,6 +1363,17 @@ bool Item_sum_min::add() } } break; + case DECIMAL_RESULT: + { + my_decimal value, *val= args[0]->val_decimal(&value); + if (!args[0]->null_value && + (null_value || (my_decimal_cmp(&sum_dec, val) > 0))) + { + my_decimal2decimal(val, &sum_dec); + null_value= 0; + } + } + break; case REAL_RESULT: { double nr= args[0]->val_real(); @@ -766,6 +1427,17 @@ bool Item_sum_max::add() } } break; + case DECIMAL_RESULT: + { + my_decimal value, *val= args[0]->val_decimal(&value); + if (!args[0]->null_value && + (null_value || (my_decimal_cmp(val, &sum_dec) > 0))) + { + my_decimal2decimal(val, &sum_dec); + null_value= 0; + } + } + break; case REAL_RESULT: { double nr= args[0]->val_real(); @@ -867,7 +1539,9 @@ void Item_sum_num::reset_field() void Item_sum_hybrid::reset_field() { - if (hybrid_type == STRING_RESULT) + switch(hybrid_type) + { + case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),result_field->charset()),*res; @@ -883,8 +1557,9 @@ void Item_sum_hybrid::reset_field() result_field->set_notnull(); result_field->store(res->ptr(),res->length(),tmp.charset()); } + break; } - else if (hybrid_type == INT_RESULT) + case INT_RESULT: { longlong nr=args[0]->val_int(); @@ -899,8 +1574,9 @@ void Item_sum_hybrid::reset_field() result_field->set_notnull(); } result_field->store(nr); + break; } - else // REAL_RESULT + case REAL_RESULT: { double nr= args[0]->val_real(); @@ -915,14 +1591,46 @@ void Item_sum_hybrid::reset_field() result_field->set_notnull(); } result_field->store(nr); + break; + } + case DECIMAL_RESULT: + { + my_decimal value, *arg_dec= args[0]->val_decimal(&value); + + if (maybe_null) + { + if (args[0]->null_value) + result_field->set_null(); + else + result_field->set_notnull(); + } + if (!args[0]->null_value) + result_field->store_decimal(arg_dec); + break; + } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } } void Item_sum_sum::reset_field() { - double nr= args[0]->val_real(); // Nulls also return 0 - float8store(result_field->ptr,nr); + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *arg_val= args[0]->val_decimal(&value); + if (args[0]->null_value) + result_field->reset(); + else + result_field->store_decimal(arg_val); + } + else + { + DBUG_ASSERT(hybrid_type == REAL_RESULT); + double nr= args[0]->val_real(); // Nulls also return 0 + float8store(result_field->ptr, nr); + } if (args[0]->null_value) result_field->set_null(); else @@ -949,17 +1657,40 @@ void Item_sum_count::reset_field() void Item_sum_avg::reset_field() { - double nr= args[0]->val_real(); char *res=result_field->ptr; - - if (args[0]->null_value) - bzero(res,sizeof(double)+sizeof(longlong)); + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *arg_dec= args[0]->val_decimal(&value); + if (args[0]->null_value) + { + my_decimal2binary(E_DEC_FATAL_ERROR, &decimal_zero, + res, f_precision, f_scale); + res+= dec_bin_size; + longlong tmp=0; + int8store(res,tmp); + } + else + { + my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, + res, f_precision, f_scale); + res+= dec_bin_size; + longlong tmp=1; + int8store(res,tmp); + } + } else { - float8store(res,nr); - res+=sizeof(double); - longlong tmp=1; - int8store(res,tmp); + double nr= args[0]->val_real(); + + if (args[0]->null_value) + bzero(res,sizeof(double)+sizeof(longlong)); + else + { + float8store(res,nr); + res+=sizeof(double); + longlong tmp=1; + int8store(res,tmp); + } } } @@ -983,17 +1714,39 @@ void Item_sum_bit::update_field() void Item_sum_sum::update_field() { - double old_nr,nr; - char *res=result_field->ptr; - - float8get(old_nr,res); - nr= args[0]->val_real(); - if (!args[0]->null_value) + if (hybrid_type == DECIMAL_RESULT) { - old_nr+=nr; - result_field->set_notnull(); + my_decimal value, *arg_val= args[0]->val_decimal(&value); + if (!args[0]->null_value) + { + if (!result_field->is_null()) + { + my_decimal field_value, + *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; + char *res=result_field->ptr; + + float8get(old_nr,res); + nr= args[0]->val_real(); + if (!args[0]->null_value) + { + old_nr+=nr; + result_field->set_notnull(); + } + float8store(res,old_nr); } - float8store(res,old_nr); } @@ -1017,32 +1770,59 @@ void Item_sum_count::update_field() void Item_sum_avg::update_field() { - double nr,old_nr; longlong field_count; char *res=result_field->ptr; + if (hybrid_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); + field_count++; + my_decimal2binary(E_DEC_FATAL_ERROR, dec_buffs, + res, f_precision, f_scale); + res+= dec_bin_size; + int8store(res, field_count); + } + } + else + { + double nr, old_nr; - float8get(old_nr,res); - field_count=sint8korr(res+sizeof(double)); + float8get(old_nr, res); + field_count= sint8korr(res + sizeof(double)); - nr= args[0]->val_real(); - if (!args[0]->null_value) - { - old_nr+=nr; - field_count++; + nr= args[0]->val_real(); + if (!args[0]->null_value) + { + old_nr+= nr; + field_count++; + } + float8store(res,old_nr); + res+= sizeof(double); + int8store(res, field_count); } - float8store(res,old_nr); - res+=sizeof(double); - int8store(res,field_count); } void Item_sum_hybrid::update_field() { - if (hybrid_type == STRING_RESULT) + switch (hybrid_type) + { + case STRING_RESULT: min_max_update_str_field(); - else if (hybrid_type == INT_RESULT) + break; + case INT_RESULT: min_max_update_int_field(); - else + break; + case DECIMAL_RESULT: + min_max_update_decimal_field(); + break; + default: min_max_update_real_field(); + } } @@ -1112,41 +1892,119 @@ Item_sum_hybrid::min_max_update_int_field() } -Item_avg_field::Item_avg_field(Item_sum_avg *item) +void +Item_sum_hybrid::min_max_update_decimal_field() +{ + /* TODO: optimize: do not get result_field in case of args[0] is NULL */ + my_decimal old_val, nr_val; + const my_decimal *old_nr= result_field->val_decimal(&old_val); + 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 + { + 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(); + } + else if (result_field->is_null(0)) + result_field->set_null(); + result_field->store_decimal(old_nr); +} + + +Item_avg_field::Item_avg_field(Item_result res_type, Item_sum_avg *item) { name=item->name; decimals=item->decimals; max_length=item->max_length; field=item->result_field; maybe_null=1; + hybrid_type= res_type; + if (hybrid_type == DECIMAL_RESULT) + { + f_scale= item->f_scale; + f_precision= item->f_precision; + dec_bin_size= item->dec_bin_size; + } } double Item_avg_field::val_real() { // fix_fields() never calls for this Item - double nr; - longlong count; - float8get(nr,field->ptr); - char *res=(field->ptr+sizeof(double)); - count=sint8korr(res); - - if (!count) + if (hybrid_type == DECIMAL_RESULT) { - null_value=1; - return 0.0; + my_decimal value, *dec_val= val_decimal(&value); + if (null_value) + return 0.0; + double d; + my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &d); + return d; } - null_value=0; - return nr/(double) count; + else + { + 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; + } +} + +longlong Item_avg_field::val_int() +{ + return (longlong)val_real(); +} + + +my_decimal *Item_avg_field::val_decimal(my_decimal * val) +{ + // fix_fields() never calls for this Item + longlong count= sint8korr(field->ptr + dec_bin_size); + if ((null_value= !count)) + return NULL; + + 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, val, &dec_field, &dec_count, 4); + return val; } + String *Item_avg_field::val_str(String *str) { // fix_fields() never calls for this Item - double nr= Item_avg_field::val_real(); - if (null_value) - return 0; - str->set(nr,decimals, &my_charset_bin); + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal value, *dec_val= val_decimal(&value); + if (null_value) + return NULL; + my_decimal_round(E_DEC_FATAL_ERROR, dec_val, decimals, FALSE, &value); + my_decimal2string(E_DEC_FATAL_ERROR, &value, 0, 0, 0, str); + } + else + { + double nr= Item_avg_field::val_real(); + if (null_value) + return 0; + str->set(nr, decimals, &my_charset_bin); + } return str; } @@ -1169,11 +2027,29 @@ Item_variance_field::Item_variance_field(Item_sum_variance *item) max_length=item->max_length; field=item->result_field; maybe_null=1; + if ((hybrid_type= item->hybrid_type) == DECIMAL_RESULT) + { + f_scale0= item->f_scale0; + f_precision0= item->f_precision0; + dec_bin_size0= item->dec_bin_size0; + f_scale1= item->f_scale1; + f_precision1= item->f_precision1; + dec_bin_size1= item->dec_bin_size1; + } } double Item_variance_field::val_real() { // fix_fields() never calls for this Item + if (hybrid_type == DECIMAL_RESULT) + { + my_decimal dec_buf, *dec_val= val_decimal(&dec_buf); + if (null_value) + return 0.0; + double d; + my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &d); + return d; + } double sum,sum_sqr; longlong count; float8get(sum,field->ptr); @@ -1201,6 +2077,28 @@ String *Item_variance_field::val_str(String *str) return str; } + +my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf) +{ + // fix_fields() never calls for this Item + longlong count= sint8korr(field->ptr+dec_bin_size0+dec_bin_size1); + if ((null_value= !count)) + return 0; + + my_decimal dec_count, dec_sum, dec_sqr, tmp; + int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count); + binary2my_decimal(E_DEC_FATAL_ERROR, field->ptr, + &dec_sum, f_precision0, f_scale0); + binary2my_decimal(E_DEC_FATAL_ERROR, field->ptr+dec_bin_size0, + &dec_sqr, f_precision1, f_scale1); + my_decimal_mul(E_DEC_FATAL_ERROR, &tmp, &dec_sum, &dec_sum); + my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &tmp, &dec_count, 2); + my_decimal_sub(E_DEC_FATAL_ERROR, &dec_sum, &dec_sqr, dec_buf); + my_decimal_div(E_DEC_FATAL_ERROR, dec_buf, &dec_sum, &dec_count, 2); + return dec_buf; +} + + /**************************************************************************** ** COUNT(DISTINCT ...) ****************************************************************************/ @@ -1597,6 +2495,58 @@ Item *Item_sum_udf_int::copy_or_same(THD* thd) } +String *Item_sum_udf_decimal::val_str(String *str) +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0; + if (str->length() < DECIMAL_MAX_STR_LENGTH) + str->length(DECIMAL_MAX_STR_LENGTH); + my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); + my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, '0', str); + return str; +} + + +double Item_sum_udf_decimal::val_real() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0.0; + double result; + my_decimal2double(E_DEC_FATAL_ERROR, dec, &result); + return result; +} + + +longlong Item_sum_udf_decimal::val_int() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0; + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); + return result; +} + + +my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf) +{ + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_func_udf_decimal::val_decimal"); + DBUG_PRINT("info",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + + DBUG_RETURN(udf.val_decimal(&null_value, dec_buf)); +} + + +Item *Item_sum_udf_decimal::copy_or_same(THD* thd) +{ + return new (thd->mem_root) Item_sum_udf_decimal(thd, this); +} + + longlong Item_sum_udf_int::val_int() { DBUG_ASSERT(fixed == 1); @@ -2037,7 +2987,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) for (i=0 ; i < arg_count ; i++) { - if ((!args[i]->fixed && + if ((!args[i]->fixed && args[i]->fix_fields(thd, tables, args + i)) || args[i]->check_cols(1)) return TRUE; diff --git a/sql/item_sum.h b/sql/item_sum.h index 7866a9ae913..d759f5607c3 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -78,6 +78,7 @@ public: virtual void update_field()=0; virtual bool keep_field_type(void) const { return 0; } virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } + my_decimal *val_decimal(my_decimal *); virtual const char *func_name() const { return "?"; } virtual Item *result_item(Field *field) { return new Item_field(field);} @@ -92,6 +93,9 @@ public: virtual bool setup(THD *thd) {return 0;} virtual void make_unique() {} Item *get_tmp_table_item(THD *thd); + virtual int scale() { return decimals; } + virtual Field *create_tmp_field(bool group, TABLE *table, + uint convert_blob_length); bool walk (Item_processor processor, byte *argument); }; @@ -112,6 +116,7 @@ public: return (longlong) val_real(); /* Real as default */ } String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); void reset_field(); }; @@ -132,17 +137,24 @@ public: class Item_sum_sum :public Item_sum_num { +protected: + Item_result hybrid_type; double sum; - void fix_length_and_dec() { maybe_null=null_value=1; } + my_decimal dec_buffs[2]; + uint curr_dec_buff; + void fix_length_and_dec(); - public: - Item_sum_sum(Item *item_par) :Item_sum_num(item_par),sum(0.0) {} - Item_sum_sum(THD *thd, Item_sum_sum *item) - :Item_sum_num(thd, item), sum(item->sum) {} +public: + Item_sum_sum(Item *item_par) :Item_sum_num(item_par) {} + Item_sum_sum(THD *thd, Item_sum_sum *item); enum Sumfunctype sum_func () const {return SUM_FUNC;} void clear(); bool add(); double val_real(); + longlong val_int(); + String *val_str(String*str); + my_decimal *val_decimal(my_decimal *); + enum Item_result result_type () const { return hybrid_type; } void reset_field(); void update_field(); void no_rows_in_result() {} @@ -159,29 +171,35 @@ class Item_sum_sum :public Item_sum_num class Unique; -class Item_sum_sum_distinct :public Item_sum_num +class Item_sum_sum_distinct :public Item_sum_sum { - double sum; Unique *tree; + byte *dec_bin_buff; + my_decimal tmp_dec; private: Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item); public: Item_sum_sum_distinct(Item *item_par); ~Item_sum_sum_distinct() {} - + bool setup(THD *thd); void clear(); void cleanup(); bool add(); double val_real(); + my_decimal *val_decimal(my_decimal *); + longlong val_int(); + String *val_str(String *str); - inline void add(double val) { sum+= val; } + void add_real(double val); + void add_decimal(byte *val); enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; } void reset_field() {} // not used void update_field() {} // not used const char *func_name() const { return "sum_distinct"; } Item *copy_or_same(THD* thd); virtual void no_rows_in_result() {} + void fix_length_and_dec(); }; @@ -304,44 +322,51 @@ class Item_avg_field :public Item_result_field { public: Field *field; - Item_avg_field(Item_sum_avg *item); + Item_result hybrid_type; + uint f_precision, f_scale; + uint dec_bin_size; + Item_avg_field(Item_result res_type, Item_sum_avg *item); enum Type type() const { return FIELD_AVG_ITEM; } double val_real(); - longlong val_int() - { /* can't be fix_fields()ed */ return (longlong) val_real(); } + longlong val_int(); + my_decimal *val_decimal(my_decimal *); bool is_null() { (void) val_int(); return null_value; } String *val_str(String*); - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + enum_field_types field_type() const + { + return hybrid_type == DECIMAL_RESULT ? + MYSQL_TYPE_NEWDECIMAL : MYSQL_TYPE_DOUBLE; + } void fix_length_and_dec() {} + enum Item_result result_type () const { return hybrid_type; } }; -class Item_sum_avg :public Item_sum_num +class Item_sum_avg :public Item_sum_sum { - void fix_length_and_dec() - { - decimals=min(decimals+4, NOT_FIXED_DEC); - maybe_null=1; - } - - double sum; +public: ulonglong count; + uint f_precision, f_scale; + uint dec_bin_size; - public: - Item_sum_avg(Item *item_par) :Item_sum_num(item_par), sum(0.0), count(0) {} + Item_sum_avg(Item *item_par) :Item_sum_sum(item_par), count(0) {} Item_sum_avg(THD *thd, Item_sum_avg *item) - :Item_sum_num(thd, item), sum(item->sum), count(item->count) {} + :Item_sum_sum(thd, item), count(item->count) {} + void fix_length_and_dec(); enum Sumfunctype sum_func () const {return AVG_FUNC;} void clear(); bool add(); double val_real(); + my_decimal *val_decimal(my_decimal *); + String *val_str(String *str); void reset_field(); void update_field(); Item *result_item(Field *field) - { return new Item_avg_field(this); } + { return new Item_avg_field(hybrid_type, this); } void no_rows_in_result() {} const char *func_name() const { return "avg"; } Item *copy_or_same(THD* thd); + Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); }; class Item_sum_variance; @@ -350,15 +375,25 @@ class Item_variance_field :public Item_result_field { public: Field *field; + Item_result hybrid_type; + uint f_precision0, f_scale0; + uint f_precision1, f_scale1; + uint dec_bin_size0, dec_bin_size1; Item_variance_field(Item_sum_variance *item); enum Type type() const {return FIELD_VARIANCE_ITEM; } double val_real(); longlong val_int() { /* can't be fix_fields()ed */ return (longlong) val_real(); } String *val_str(String*); + my_decimal *val_decimal(my_decimal *); bool is_null() { (void) val_int(); return null_value; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + enum_field_types field_type() const + { + return hybrid_type == DECIMAL_RESULT ? + MYSQL_TYPE_NEWDECIMAL : MYSQL_TYPE_DOUBLE; + } void fix_length_and_dec() {} + enum Item_result result_type () const { return hybrid_type; } }; @@ -376,23 +411,27 @@ public: class Item_sum_variance : public Item_sum_num { + void fix_length_and_dec(); + +public: + Item_result hybrid_type; double sum, sum_sqr; + my_decimal dec_sum[2], dec_sqr[2]; + int cur_dec; ulonglong count; - void fix_length_and_dec() - { - decimals=min(decimals+4, NOT_FIXED_DEC); - maybe_null=1; - } + uint f_precision0, f_scale0; + uint f_precision1, f_scale1; + uint dec_bin_size0, dec_bin_size1; - public: - Item_sum_variance(Item *item_par) :Item_sum_num(item_par),count(0) {} - Item_sum_variance(THD *thd, Item_sum_variance *item): - Item_sum_num(thd, item), sum(item->sum), sum_sqr(item->sum_sqr), - count(item->count) {} + Item_sum_variance(Item *item_par) :Item_sum_num(item_par), hybrid_type(REAL_RESULT), + cur_dec(0),count(0) + {} + Item_sum_variance(THD *thd, Item_sum_variance *item); enum Sumfunctype sum_func () const { return VARIANCE_FUNC; } void clear(); bool add(); double val_real(); + my_decimal *val_decimal(my_decimal *); void reset_field(); void update_field(); Item *result_item(Field *field) @@ -400,6 +439,8 @@ class Item_sum_variance : public Item_sum_num void no_rows_in_result() {} const char *func_name() const { return "variance"; } Item *copy_or_same(THD* thd); + Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); + enum Item_result result_type () const { return hybrid_type; } }; class Item_sum_std; @@ -410,6 +451,7 @@ public: Item_std_field(Item_sum_std *item); enum Type type() const { return FIELD_STD_ITEM; } double val_real(); + enum Item_result result_type () const { return REAL_RESULT; } }; /* @@ -429,16 +471,18 @@ class Item_sum_std :public Item_sum_variance { return new Item_std_field(this); } const char *func_name() const { return "std"; } Item *copy_or_same(THD* thd); + enum Item_result result_type () const { return REAL_RESULT; } }; // This class is a string or number function depending on num_func class Item_sum_hybrid :public Item_sum { - protected: +protected: String value,tmp_value; double sum; longlong sum_int; + my_decimal sum_dec; Item_result hybrid_type; enum_field_types hybrid_field_type; int cmp_sign; @@ -449,15 +493,10 @@ class Item_sum_hybrid :public Item_sum Item_sum_hybrid(Item *item_par,int sign) :Item_sum(item_par), sum(0.0), sum_int(0), hybrid_type(INT_RESULT), hybrid_field_type(FIELD_TYPE_LONGLONG), - cmp_sign(sign), used_table_cache(~(table_map) 0), was_values(TRUE) - { collation.set(&my_charset_bin); } - Item_sum_hybrid(THD *thd, Item_sum_hybrid *item): - Item_sum(thd, item), value(item->value), - sum(item->sum), sum_int(item->sum_int), hybrid_type(item->hybrid_type), - hybrid_field_type(item->hybrid_field_type),cmp_sign(item->cmp_sign), - used_table_cache(item->used_table_cache), + cmp_sign(sign), used_table_cache(~(table_map) 0), was_values(TRUE) - { collation.set(item->collation); } + { collation.set(&my_charset_bin); } + Item_sum_hybrid(THD *thd, Item_sum_hybrid *item); bool fix_fields(THD *, TABLE_LIST *, Item **); table_map used_tables() const { return used_table_cache; } bool const_item() const { return !used_table_cache; } @@ -465,6 +504,7 @@ class Item_sum_hybrid :public Item_sum void clear(); double val_real(); longlong val_int(); + my_decimal *val_decimal(my_decimal *); void reset_field(); String *val_str(String *); void make_const() { used_table_cache=0; } @@ -475,6 +515,7 @@ class Item_sum_hybrid :public Item_sum 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(); @@ -523,7 +564,7 @@ public: void reset_field(); void update_field(); void fix_length_and_dec() - { decimals=0; max_length=21; unsigned_flag=1; maybe_null=null_value=0; } + { decimals= 0; max_length=21; unsigned_flag= 1; maybe_null= null_value= 0; } }; @@ -661,6 +702,23 @@ public: Item *copy_or_same(THD* thd); }; +class Item_sum_udf_decimal :public Item_udf_sum +{ +public: + Item_sum_udf_decimal(udf_func *udf_arg) :Item_udf_sum(udf_arg) {} + Item_sum_udf_decimal(udf_func *udf_arg, List<Item> &list) + :Item_udf_sum(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 *); + enum Item_result result_type () const { return DECIMAL_RESULT; } + void fix_length_and_dec() { fix_num_length_and_dec(); } + Item *copy_or_same(THD* thd); +}; + #else /* Dummy functions to get sql_yacc.cc compiled */ class Item_sum_udf_float :public Item_sum_num @@ -694,6 +752,22 @@ public: }; +class Item_sum_udf_decimal :public Item_sum_num +{ + public: + Item_sum_udf_decimal(udf_func *udf_arg) :Item_sum_num() {} + Item_sum_udf_decimal(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {} + 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: diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 917018463e5..ab511ae2883 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -315,7 +315,9 @@ public: }; -/* This can't be a Item_str_func, because the val() functions are special */ +/* + This can't be a Item_str_func, because the val_real() functions are special +*/ class Item_date :public Item_func { diff --git a/sql/item_uniq.cc b/sql/item_uniq.cc index 88e0cbbc0e6..c1a19d71d04 100644 --- a/sql/item_uniq.cc +++ b/sql/item_uniq.cc @@ -20,3 +20,9 @@ #endif #include "mysql_priv.h" + +Field *Item_sum_unique_users::create_tmp_field(bool group, TABLE *table, + uint convert_blob_length) +{ + return new Field_long(9,maybe_null,name,table,1); +} diff --git a/sql/item_uniq.h b/sql/item_uniq.h index e74c09ca3c4..602474f7581 100644 --- a/sql/item_uniq.h +++ b/sql/item_uniq.h @@ -57,4 +57,5 @@ public: return new Item_sum_unique_users(thd, this); } void print(String *str) { str->append("0.0", 3); } + Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); }; diff --git a/sql/log_event.cc b/sql/log_event.cc index d09b2b3dc03..d2a0e8642f9 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3099,6 +3099,16 @@ void User_var_log_event::pack_info(Protocol* protocol) buf= my_malloc(val_offset + 22, MYF(MY_WME)); event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf; break; + case DECIMAL_RESULT: + { + buf= my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH, MYF(MY_WME)); + String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin); + my_decimal dec; + binary2my_decimal(E_DEC_FATAL_ERROR, val+2, &dec, val[0], val[1]); + my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str); + event_len= str.length() + val_offset; + break; + } case STRING_RESULT: /* 15 is for 'COLLATE' and other chars */ buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME)); @@ -3167,7 +3177,7 @@ bool User_var_log_event::write(IO_CACHE* file) char buf[UV_NAME_LEN_SIZE]; char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE]; - char buf2[8], *pos= buf2; + char buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2; uint buf1_length; ulong event_length; @@ -3182,8 +3192,6 @@ bool User_var_log_event::write(IO_CACHE* file) { buf1[1]= type; int4store(buf1 + 2, charset_number); - int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len); - buf1_length= 10; switch (type) { case REAL_RESULT: @@ -3192,6 +3200,16 @@ bool User_var_log_event::write(IO_CACHE* file) case INT_RESULT: int8store(buf2, *(longlong*) val); break; + case DECIMAL_RESULT: + { + my_decimal *dec= (my_decimal *)val; + dec->fix_buffer_pointer(); + buf2[0]= (char)(dec->intg + dec->frac); + buf2[1]= (char)dec->frac; + decimal2bin((decimal*)val, buf2+2, buf2[0], buf2[1]); + val_len= decimal_bin_size(buf2[0], buf2[1]) + 2; + break; + } case STRING_RESULT: pos= val; break; @@ -3200,6 +3218,8 @@ bool User_var_log_event::write(IO_CACHE* file) DBUG_ASSERT(1); return 0; } + int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len); + buf1_length= 10; } /* Length of the whole event */ @@ -3247,6 +3267,23 @@ void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* las longlong10_to_str(uint8korr(val), int_buf, -10); fprintf(file, ":=%s;\n", int_buf); break; + case DECIMAL_RESULT: + { + char str_buf[200]; + int str_len= sizeof(str_buf) - 1; + int precision= (int)val[0]; + int scale= (int)val[1]; + decimal_digit dec_buf[10]; + decimal dec; + dec.len= 10; + dec.buf= dec_buf; + + bin2decimal(val+2, &dec, precision, scale); + decimal2string(&dec, str_buf, &str_len, 0, 0, 0); + str_buf[str_len]= 0; + fprintf(file, "%s",str_buf); + break; + } case STRING_RESULT: { /* @@ -3323,7 +3360,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) switch (type) { case REAL_RESULT: float8get(real_val, val); - it= new Item_real(real_val); + it= new Item_float(real_val); val= (char*) &real_val; // Pointer to value in native format val_len= 8; break; @@ -3333,6 +3370,14 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli) val= (char*) &int_val; // Pointer to value in native format val_len= 8; break; + case DECIMAL_RESULT: + { + Item_decimal *dec= new Item_decimal(val+2, val[0], val[1]); + it= dec; + val= (char *)dec->val_decimal(NULL); + val_len= sizeof(my_decimal); + break; + } case STRING_RESULT: it= new Item_string(val, val_len, charset); break; diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc new file mode 100644 index 00000000000..eafcd2eaaf3 --- /dev/null +++ b/sql/my_decimal.cc @@ -0,0 +1,212 @@ +/* Copyright (C) 2004 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 */ +#include "mysql_priv.h" + +#ifndef MYSQL_CLIENT +/* + report result of decimal operation + + SYNOPSIS + decimal_operation_results() + result decimal library return code (E_DEC_* see include/decimal.h) + + return + result +*/ +int decimal_operation_results(int result) +{ + switch (result) + { + case E_DEC_OK: + break; +//TODO: fix error messages + case E_DEC_TRUNCATED: + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED), + "", (long)-1); + break; + case E_DEC_OVERFLOW: + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_WARN_DATA_OUT_OF_RANGE, + ER(ER_WARN_DATA_OUT_OF_RANGE), + "", (long)-1); + break; + case E_DEC_DIV_ZERO: + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO)); + break; + case E_DEC_BAD_NUM: + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, + ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + "decimal", "", "", (long)-1); + break; + case E_DEC_OOM: + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + break; + default: + DBUG_ASSERT(0); + } + return result; +} + + +/* + Converting decimal to string + + SYNOPSIS + my_decimal2string() + + return + E_DEC_OK + E_DEC_TRUNCATED + E_DEC_OVERFLOW + E_DEC_OOM +*/ + +int my_decimal2string(uint mask, const my_decimal *d, + int fixed_prec, int fixed_dec, + char filler, String *str) +{ + int length= (fixed_prec ? (fixed_prec + 1) : my_decimal_string_length(d)); + int result; + if (str->alloc(length)) + return check_result(mask, E_DEC_OOM); + char *sptr= (char *)str->ptr(); + int res= decimal2string((decimal *)d, sptr, + &length, fixed_prec, fixed_dec, + filler); + result= check_result(mask, res); + str->length(length); + return result; +} + + +/* + Convert from decimal to binary representation + + SYNOPSIS + my_decimal2binary() + mask error processing mask + d number for conversion + bin pointer to buffer where to write result + prec overall number of decimal digits + scale number of decimal digits after decimal point + + NOTE + Before conversion we round number if it need but produce truncation + error in this case + + RETURN + E_DEC_OK + E_DEC_TRUNCATED + E_DEC_OVERFLOW +*/ + +int my_decimal2binary(uint mask, const my_decimal *d, byte *bin, int prec, + int scale) +{ + int err1= E_DEC_OK, err2; + my_decimal rounded; + my_decimal2decimal(d, &rounded); + decimal_optimize_fraction(&rounded); + if (scale < rounded.frac) + { + err1= E_DEC_TRUNCATED; + /* decimal_round can return only E_DEC_TRUNCATED */ + decimal_round(&rounded, &rounded, scale, HALF_UP); + } + err2= decimal2bin(&rounded, bin, prec, scale); + if (!err2) + err2= err1; + return check_result(mask, err2); +} + + +/* + Convert string for decimal when string can be in some multibyte charset + + SYNOPSIS + str2my_decimal() + mask error processing mask + from string to process + length length of given string + charset charset of given string + decimal_value buffer for result storing + + RESULT + E_DEC_OK + E_DEC_TRUNCATED + E_DEC_OVERFLOW + E_DEC_BAD_NUM + E_DEC_OOM +*/ +int str2my_decimal(uint mask, const char *from, uint length, + CHARSET_INFO *charset, my_decimal *decimal_value) +{ + char *end; + int err; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmp(buff, sizeof(buff), &my_charset_bin); + if (charset->mbminlen > 1) + { + uint dummy_errors; + tmp.copy(from, length, charset, &my_charset_latin1, &dummy_errors); + from= tmp.ptr(); + length= tmp.length(); + charset= &my_charset_bin; + } + my_decimal_set_zero(decimal_value); + err= string2decimal((char *)from, (decimal *)decimal_value, &end); + if (*end && !err) + err= E_DEC_TRUNCATED; + check_result(mask, err); + return err; +} + + +#ifndef DBUG_OFF +/* routines for debugging print */ + +/* print decimal */ +void +print_decimal(const my_decimal *dec) +{ + fprintf(DBUG_FILE, + "\nDecimal: sign: %d intg: %d frac: %d \n\ +%09d,%09d,%09d,%09d,%09d,%09d,%09d,%09d\n", + dec->sign(), dec->intg, dec->frac, + dec->buf[0], dec->buf[1], dec->buf[2], dec->buf[3], + dec->buf[4], dec->buf[5], dec->buf[6], dec->buf[7]); +} + + +/* print decimal with its binary representation */ +void +print_decimal_buff(const my_decimal *dec, const byte* ptr, int length) +{ + print_decimal(dec); + fprintf(DBUG_FILE, "Record: "); + for(int i= 0; i < length; i++) + { + fprintf(DBUG_FILE, "%02X ", (uint)((uchar *)ptr)[i]); + } + fprintf(DBUG_FILE, "\n"); +} +#endif + + +#endif /*MYSQL_CLIENT*/ diff --git a/sql/my_decimal.h b/sql/my_decimal.h new file mode 100644 index 00000000000..63d3fb7e2e5 --- /dev/null +++ b/sql/my_decimal.h @@ -0,0 +1,332 @@ +/* Copyright (C) 2004 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 */ + +/* + It is interface module to fixed precision decimals library. + + Most functions use 'uint mask' as parameter, if during operation error + which fit in this mask is detected then it will be processed automatically + here. (errors are E_DEC_* constants, see include/decimal.h) + + Most function are just inline wrappers around library calls +*/ + +#ifndef my_decimal_h +#define my_decimal_h + +C_MODE_START +#include <decimal.h> +C_MODE_END + +#define DECIMAL_LONGLONG_DIGITS 22 +#define DECIMAL_LONG_DIGITS 10 +#define DECIMAL_LONG3_DIGITS 8 + +/* number of digits on which we increase scale of devision result */ +#define DECIMAL_DIV_SCALE_INCREASE 5 + +/* maximum length of buffer in our big digits (uint32) */ +#define DECIMAL_BUFF_LENGTH 8 +/* + maximum guaranteed length of number in decimal digits (number of our + digits * number of decimal digits in one our big digit - number of decimal + digits in one our big digit decreased on 1 (because we always put decimal + point on the border of our big digits)) +*/ +#define DECIMAL_MAX_LENGTH ((8 * 9) - 8) +/* + maximum length of string representation (number of maximum decimal + digits + 1 position for sign + 1 position for decimal point) +*/ +#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_LENGTH + 2) +/* + maximum size of packet length +*/ +#define DECIMAL_MAX_FIELD_SIZE DECIMAL_MAX_LENGTH + + +inline uint my_decimal_size(uint precision, uint scale) +{ + /* + Always allocate more space to allow library to put decimal point + where it want + */ + return decimal_size(precision, scale) + 1; +} + + +/* my_decimal class limits 'decimal' type to what we need in MySQL */ +/* It internally all necessary space iside the instance so no extra */ +/* memory is needed. One should call fix_buffer_pointer() function */ +/* when he moves my_decimal objects in memory */ +class my_decimal :public decimal +{ + decimal_digit buffer[DECIMAL_BUFF_LENGTH]; + +public: + + void init() + { + len= DECIMAL_BUFF_LENGTH; + buf= buffer; +#if !defined(HAVE_purify) && !defined(DBUG_OFF) + for (uint i= 0; i < DECIMAL_BUFF_LENGTH; i++) + buffer[i]= i; +#endif + } + my_decimal() + { + init(); + } + void fix_buffer_pointer() { buf= buffer; } + + bool sign() const { return decimal::sign; } + void sign(bool s) { decimal::sign= s; } +}; + + +#ifndef DBUG_OFF +void print_decimal(const my_decimal *dec); +void print_decimal_buff(const my_decimal *dec, const byte* ptr, int length); +#endif + +#ifndef MYSQL_CLIENT +int decimal_operation_results(int result); +#else +inline int decimal_operation_results(int result) +{ + return result; +} +#endif /*MYSQL_CLIENT*/ + +inline int check_result(uint mask, int result) +{ + if (result & mask) + decimal_operation_results(result); + return result; +} + + +inline +int my_decimal_string_length(const my_decimal *d) +{ + return decimal_string_size(d); +} + + +inline +int my_decimal_max_length(const my_decimal *d) +{ + /* -1 because we do not count \0 */ + return decimal_string_size(d) - 1; +} + + +inline +int my_decimal_get_binary_size(uint precision, uint scale) +{ + return decimal_bin_size((int)precision, (int)scale); +} + + +inline +void my_decimal2decimal(const my_decimal *from, my_decimal *to) +{ + *to= *from; + to->fix_buffer_pointer(); +} + + +int my_decimal2binary(uint mask, const my_decimal *d, byte *bin, int prec, + int scale); + + +inline +int binary2my_decimal(uint mask, const byte *bin, my_decimal *d, int prec, + int scale) +{ + return check_result(mask, bin2decimal((char *)bin, (decimal *)d, prec, + scale)); +} + + +inline +int my_decimal_set_zero(my_decimal *d) +{ + decimal_make_zero(((decimal *)d)); + return 0; +} + + +inline +bool my_decimal_is_zero(const my_decimal *decimal_value) +{ + return decimal_is_zero((decimal *)decimal_value); +} + + +inline +int my_decimal_round(uint mask, const my_decimal *from, int scale, + bool truncate, my_decimal *to) +{ + return check_result(mask, decimal_round((decimal *)from, to, scale, + (truncate ? TRUNCATE : HALF_UP))); +} + + +inline +int my_decimal_floor(uint mask, const my_decimal *from, my_decimal *to) +{ + return check_result(mask, decimal_round((decimal *)from, to, 0, FLOOR)); +} + + +inline +int my_decimal_ceiling(uint mask, const my_decimal *from, my_decimal *to) +{ + return check_result(mask, decimal_round((decimal *)from, to, 0, CEILING)); +} + + +#ifndef MYSQL_CLIENT +int my_decimal2string(uint mask, const my_decimal *d, int fixed_prec, + int fixed_dec, char filler, String *str); +#endif + +inline +int my_decimal2int(uint mask, const my_decimal *d, my_bool unsigned_flag, + longlong *l) +{ + my_decimal rounded; + /* decimal_round can return only E_DEC_TRUNCATED */ + decimal_round((decimal*)d, &rounded, 0, HALF_UP); + return check_result(mask, (unsigned_flag ? + decimal2ulonglong(&rounded, (ulonglong *)l) : + decimal2longlong(&rounded, l))); +} + + +inline +int my_decimal2double(uint mask, const my_decimal *d, double *result) +{ + return check_result(mask, decimal2double((decimal *)d, result)); +} + + +inline +int str2my_decimal(uint mask, const char *str, my_decimal *d, + char **end= 0) +{ + /* set it to 0 to avoid junk in value in case of error of conversion */ + my_decimal_set_zero(d); + return check_result(mask, string2decimal((char *)str, (decimal *)d, end)); +} + + +int str2my_decimal(uint mask, const char *from, uint length, + CHARSET_INFO *charset, my_decimal *decimal_value); + + +#ifdef MYSQL_SERVER +inline +int string2my_decimal(uint mask, const String *str, my_decimal *d) +{ + return str2my_decimal(mask, str->ptr(), str->length(), str->charset(), d); +} +#endif + + +inline +int double2my_decimal(uint mask, double val, my_decimal *d) +{ + return check_result(mask, double2decimal(val, (decimal *)d)); +} + + +inline +int int2my_decimal(uint mask, longlong i, my_bool unsigned_flag, my_decimal *d) +{ + return check_result(mask, (unsigned_flag ? + ulonglong2decimal((ulonglong)i, d) : + longlong2decimal(i, d))); +} + + +inline +int my_decimal_neg(st_decimal *arg) +{ + decimal_neg(arg); + return 0; +} + + +inline +int my_decimal_add(uint mask, my_decimal *res, const my_decimal *a, + const my_decimal *b) +{ + return check_result(mask, decimal_add((decimal *)a, (decimal *)b, res)); +} + + +inline +int my_decimal_sub(uint mask, my_decimal *res, const my_decimal *a, + const my_decimal *b) +{ + return check_result(mask, decimal_sub((decimal *)a, (decimal *)b, res)); +} + + +inline +int my_decimal_mul(uint mask, my_decimal *res, const my_decimal *a, + const my_decimal *b) +{ + return check_result(mask, decimal_mul((decimal *)a, (decimal *)b, res)); +} + + +inline +int my_decimal_div(uint mask, my_decimal *res, const my_decimal *a, + const my_decimal *b, int div_scale_inc) +{ + return check_result(mask, decimal_div((decimal *)a, (decimal *)b, res, + div_scale_inc)); +} + + +inline +int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a, + const my_decimal *b) +{ + return check_result(mask, decimal_mod((decimal *)a, (decimal *)b, res)); +} + + +/* Returns -1 if a<b, 1 if a>b and 0 if a==b */ +inline +int my_decimal_cmp(const my_decimal *a, const my_decimal *b) +{ + return decimal_cmp((decimal *)a, (decimal *)b); +} + +inline +void max_my_decimal(my_decimal *to, int precision, int frac) +{ + DBUG_ASSERT(precision <= DECIMAL_MAX_LENGTH); + max_decimal(precision, frac, (decimal *)to); +} + +#endif /*my_decimal_h*/ + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2cabcb6d49c..fdcf061ab7a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -343,6 +343,8 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); #define WEEK_YEAR 2 #define WEEK_FIRST_WEEKDAY 4 +#define STRING_BUFFER_USUAL_SIZE 80 + enum enum_parsing_place { NO_MATTER, @@ -413,6 +415,7 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #include "sql_string.h" #include "sql_list.h" #include "sql_map.h" +#include "my_decimal.h" #include "handler.h" #include "parse_file.h" #include "table.h" @@ -421,6 +424,7 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #include "sql_udf.h" class user_var_entry; #include "item.h" +extern my_decimal decimal_zero; typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* sql_parse.cc */ void free_items(Item *item); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9400bedb585..668ef033414 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -261,11 +261,12 @@ bool opt_large_files= sizeof(my_off_t) > 4; bool opt_help= 0; bool opt_verbose= 0; -arg_cmp_func Arg_comparator::comparator_matrix[4][2] = +arg_cmp_func Arg_comparator::comparator_matrix[5][2] = {{&Arg_comparator::compare_string, &Arg_comparator::compare_e_string}, {&Arg_comparator::compare_real, &Arg_comparator::compare_e_real}, {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int}, - {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row}}; + {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row}, + {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}}; /* Global variables */ @@ -372,6 +373,7 @@ const char *sql_mode_str="OFF"; const char *in_left_expr_name= "<left expr>"; /* name of additional condition */ const char *in_additional_cond= "<IN COND>"; +my_decimal decimal_zero; /* classes for comparation parsing/processing */ Eq_creator eq_creator; Ne_creator ne_creator; @@ -2384,6 +2386,7 @@ static int init_common_variables(const char *conf_file_name, int argc, char **argv, const char **groups) { umask(((~my_umask) & 0666)); + my_decimal_set_zero(&decimal_zero); // set decimal_zero constant; tzset(); // Set tzname max_system_variables.pseudo_thread_id= (ulong)~0; diff --git a/sql/procedure.cc b/sql/procedure.cc index 420a4f6262b..a31b93da358 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -41,6 +41,34 @@ static struct st_procedure_def { { "analyse",proc_analyse_init } // Analyse a result }; + +my_decimal *Item_proc_string::val_decimal(my_decimal *decimal_value) +{ + if (null_value) + return 0; + string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); + return (decimal_value); +} + + +my_decimal *Item_proc_int::val_decimal(my_decimal *decimal_value) +{ + if (null_value) + return 0; + int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_value); + return (decimal_value); +} + + +my_decimal *Item_proc_real::val_decimal(my_decimal *decimal_value) +{ + if (null_value) + return 0; + double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value); + return (decimal_value); +} + + /***************************************************************************** ** Setup handling of procedure ** Return 0 if everything is ok diff --git a/sql/procedure.h b/sql/procedure.h index 33c1288c88e..4e5bf8a2f4b 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -71,6 +71,7 @@ public: s->set(value,decimals,default_charset()); return s; } + my_decimal *val_decimal(my_decimal *); unsigned int size_of() { return sizeof(*this);} }; @@ -89,6 +90,7 @@ public: double val_real() { return (double) value; } longlong val_int() { return value; } String *val_str(String *s) { s->set(value, default_charset()); return s; } + my_decimal *val_decimal(my_decimal *); unsigned int size_of() { return sizeof(*this);} }; @@ -122,6 +124,7 @@ public: { return null_value ? (String*) 0 : (String*) &str_value; } + my_decimal *val_decimal(my_decimal *); unsigned int size_of() { return sizeof(*this);} }; diff --git a/sql/protocol.cc b/sql/protocol.cc index d537f9cf829..7c56bb3fc7a 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -735,6 +735,7 @@ bool Protocol_simple::store(const char *from, uint length, DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || field_types[field_pos] == MYSQL_TYPE_BIT || + field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL || (field_types[field_pos] >= MYSQL_TYPE_ENUM && field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; @@ -751,6 +752,7 @@ bool Protocol_simple::store(const char *from, uint length, DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || field_types[field_pos] == MYSQL_TYPE_BIT || + field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL || (field_types[field_pos] >= MYSQL_TYPE_ENUM && field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); field_pos++; @@ -813,6 +815,26 @@ bool Protocol_simple::store_longlong(longlong from, bool unsigned_flag) } +bool Protocol_simple::store_decimal(const my_decimal *d) +{ +#ifndef DEBUG_OFF + DBUG_ASSERT(field_types == 0 || + field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); + field_pos++; +#endif + int buf_size= my_decimal_string_length(d); + char *buff= (char *)my_alloca(buf_size); + String str(buff, buf_size, &my_charset_bin); + if (my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str)) + { + my_afree(buff); + return TRUE; + } + my_afree(buff); + return net_store_data(str.ptr(), str.length()); +} + + bool Protocol_simple::store(float from, uint32 decimals, String *buffer) { #ifndef DEBUG_OFF @@ -1027,6 +1049,24 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag) return 0; } +bool Protocol_prep::store_decimal(const my_decimal *d) +{ +#ifndef DEBUG_OFF + DBUG_ASSERT(field_types == 0 || + field_types[field_pos] == MYSQL_TYPE_NEWDECIMAL); + field_pos++; +#endif + int buf_size= my_decimal_string_length(d); + char *buff= (char *)my_alloca(buf_size); + String str(buff, buf_size, &my_charset_bin); + if (my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str)) + { + my_afree(buff); + return TRUE; + } + my_afree(buff); + return store(str.ptr(), str.length(), str.charset()); +} bool Protocol_prep::store(float from, uint32 decimals, String *buffer) { diff --git a/sql/protocol.h b/sql/protocol.h index fddd3ceba94..ad2593d3ab7 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -82,6 +82,7 @@ public: virtual bool store_short(longlong from)=0; virtual bool store_long(longlong from)=0; virtual bool store_longlong(longlong from, bool unsigned_flag)=0; + virtual bool store_decimal(const my_decimal *)=0; virtual bool store(const char *from, uint length, CHARSET_INFO *cs)=0; virtual bool store(const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0; @@ -107,6 +108,7 @@ public: virtual bool store_short(longlong from); virtual bool store_long(longlong from); virtual bool store_longlong(longlong from, bool unsigned_flag); + virtual bool store_decimal(const my_decimal *); virtual bool store(const char *from, uint length, CHARSET_INFO *cs); virtual bool store(const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); @@ -137,6 +139,7 @@ public: virtual bool store_short(longlong from); virtual bool store_long(longlong from); virtual bool store_longlong(longlong from, bool unsigned_flag); + virtual bool store_decimal(const my_decimal *); virtual bool store(const char *from,uint length, CHARSET_INFO *cs); virtual bool store(const char *from, uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); diff --git a/sql/set_var.cc b/sql/set_var.cc index 04bb2c5e78f..6fb44ae3ea3 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1539,7 +1539,7 @@ byte *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type, bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; const char *value; String str(buff, sizeof(buff), system_charset_info), *res; @@ -1576,7 +1576,7 @@ err: bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) { bool not_used; - char buff[80], *error= 0; + char buff[STRING_BUFFER_USUAL_SIZE], *error= 0; uint error_len= 0; String str(buff, sizeof(buff), system_charset_info), *res; @@ -1787,7 +1787,7 @@ bool sys_var_thd_date_time_format::update(THD *thd, set_var *var) bool sys_var_thd_date_time_format::check(THD *thd, set_var *var) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String str(buff,sizeof(buff), system_charset_info), *res; DATE_TIME_FORMAT *format; @@ -1891,7 +1891,7 @@ bool sys_var_collation::check(THD *thd, set_var *var) if (var->value->result_type() == STRING_RESULT) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String str(buff,sizeof(buff), system_charset_info), *res; if (!(res=var->value->val_str(&str))) { @@ -1925,7 +1925,7 @@ bool sys_var_character_set::check(THD *thd, set_var *var) if (var->value->result_type() == STRING_RESULT) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; String str(buff,sizeof(buff), system_charset_info), *res; if (!(res=var->value->val_str(&str))) { @@ -2982,7 +2982,7 @@ int set_var::check(THD *thd) return 0; } - if ((!value->fixed && + if ((!value->fixed && value->fix_fields(thd, 0, &value)) || value->check_cols(1)) return -1; if (var->check_update_type(value->result_type())) @@ -3121,7 +3121,7 @@ int set_var_password::update(THD *thd) bool sys_var_thd_storage_engine::check(THD *thd, set_var *var) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; const char *value; String str(buff, sizeof(buff), &my_charset_latin1), *res; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index d52474998a8..5ccbd9c518a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -36,6 +36,8 @@ sp_map_result_type(enum enum_field_types type) case MYSQL_TYPE_INT24: return INT_RESULT; case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DECIMAL_RESULT; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: return REAL_RESULT; @@ -127,7 +129,7 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) else { DBUG_PRINT("info", ("INT_RESULT: %d", i)); - it= new Item_int(it->val_int()); + it= new Item_int(i); } break; } @@ -147,13 +149,80 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) uint8 decimals= it->decimals; uint32 max_length= it->max_length; DBUG_PRINT("info", ("REAL_RESULT: %g", d)); - it= new Item_real(it->val_real()); + it= new Item_float(d); it->decimals= decimals; it->max_length= max_length; } break; } - default: + case DECIMAL_RESULT: + { + switch (it->result_type()) + { + case DECIMAL_RESULT: + { + my_decimal value, *val= it->val_decimal(&value); + if (it->null_value) + it= new Item_null(); + else + it= new Item_decimal(val); + break; + } + case INT_RESULT: + { + longlong val= it->val_int(); + if (it->null_value) + it= new Item_null(); + else + it= new Item_decimal(val, (int)it->max_length, + (bool)it->unsigned_flag); + break; + } + case REAL_RESULT: + { + double val= it->val_real(); + if (it->null_value) + it= new Item_null(); + else + it= new Item_decimal(val, (int)it->max_length, + (int)it->decimals); + break; + } + case STRING_RESULT: + { + char buffer[MAX_FIELD_WIDTH]; + String tmp(buffer, sizeof(buffer), it->collation.collation); + String *val= it->val_str(&tmp); + if (it->null_value) + it= new Item_null(); + else + it= new Item_decimal(val->ptr(), val->length(), val->charset()); + break; + } + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } +#ifndef DBUG_OFF + if (it->null_value) + { + DBUG_PRINT("info", ("DECIMAL_RESULT: null")); + } + else + { + my_decimal value, *val= it->val_decimal(&value); + int len; + char *buff= + (char *)my_alloca(len= my_decimal_string_length(val) + 3); + String str(buff, len, &my_charset_bin); + my_decimal2string(0, val, 0, 0, 0, &str); + DBUG_PRINT("info", ("DECIMAL_RESULT: %s", str.ptr())); + my_afree(buff); + } +#endif + break; + } + case STRING_RESULT: { char buffer[MAX_FIELD_WIDTH]; String tmp(buffer, sizeof(buffer), it->collation.collation); @@ -172,6 +241,9 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) } break; } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } } @@ -732,6 +804,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) static_cast<Item_func_get_user_var*>(fi); suv= new Item_func_set_user_var(guv->get_name(), item); + /* + we do not check suv->fixed, bacause it can't be fixed after + creation + */ suv->fix_fields(thd, NULL, &item); suv->fix_length_and_dec(); suv->check(); diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 609882b84c6..0c6c8c5aa70 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -249,14 +249,20 @@ sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars) it= new Item_int(s); break; case REAL_RESULT: - it= new Item_real(s, strlen(s)); + it= new Item_float(s, strlen(s)); break; - default: + case DECIMAL_RESULT: + it= new Item_decimal(s, strlen(s), thd->db_charset); + break; + case STRING_RESULT: { uint len= strlen(s); it= new Item_string(thd->strmake(s, len), len, thd->db_charset); break; } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } thd->spcont->set_item(pv->offset, it); } diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index b6bd49b1553..c2eea524cac 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -59,6 +59,11 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)), return compare_ulonglong(s,t); } +int compare_decimal2(int* len, const char *s, const char *t) +{ + return memcmp(s, t, *len); +} + Procedure * proc_analyse_init(THD *thd, ORDER *param, select_result *result, @@ -140,6 +145,8 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, } if (item->result_type() == REAL_RESULT) *f_info++ = new field_real(item, pc); + if (item->result_type() == DECIMAL_RESULT) + *f_info++= new field_decimal(item, pc); if (item->result_type() == STRING_RESULT) *f_info++ = new field_str(item, pc); } @@ -261,7 +268,7 @@ bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num) } else // ulonglong is as big as bigint in MySQL { - if ((check_ulonglong(num, info->integers) == REAL_NUM)) + if ((check_ulonglong(num, info->integers) == DECIMAL_NUM)) return 0; ev_info->ullval = (ulonglong) max(ev_info->ullval, info->ullval); ev_info->max_dval = (double) max(ev_info->max_dval, info->dval); @@ -449,6 +456,80 @@ void field_real::add() } // field_real::add +void field_decimal::add() +{ + my_decimal dec_buf, *dec= item->val_decimal(&dec_buf); + uint length, zero_count, decs; + TREE_ELEMENT *element; + + if (item->null_value) + { + nulls++; + return; + } + + length= my_decimal_string_length(dec); + + if (room_in_tree) + { + char buf[DECIMAL_MAX_FIELD_SIZE]; + my_decimal2binary(E_DEC_FATAL_ERROR, dec, buf, + item->max_length, item->decimals); + if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg))) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree); + } + /* + if element->count == 1, this element can be found only once from tree + if element->count == 2, or more, this element is already in tree + */ + else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) + { + room_in_tree = 0; // Remove tree, too many elements + delete_tree(&tree); + } + } + + if (!found) + { + found = 1; + min_arg = max_arg = sum[0] = *dec; + min_arg.fix_buffer_pointer(); + max_arg.fix_buffer_pointer(); + sum[0].fix_buffer_pointer(); + my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, dec, dec); + cur_sum= 0; + min_length = max_length = length; + } + else + { + int next_cur_sum= cur_sum ^ 1; + my_decimal sqr_buf; + + my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, dec); + my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, dec, dec); + my_decimal_add(E_DEC_FATAL_ERROR, + sum_sqr+next_cur_sum, sum_sqr+cur_sum, &sqr_buf); + cur_sum= next_cur_sum; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + if (my_decimal_cmp(dec, &min_arg) < 0) + { + min_arg= *dec; + min_arg.fix_buffer_pointer(); + } + if (my_decimal_cmp(dec, &max_arg) > 0) + { + max_arg= *dec; + max_arg.fix_buffer_pointer(); + } + } +} + + void field_longlong::add() { char buff[MAX_FIELD_WIDTH]; @@ -886,6 +967,70 @@ void field_ulonglong::get_opt_type(String *answer, } //field_ulonglong::get_opt_type +void field_decimal::get_opt_type(String *answer, + ha_rows total_rows __attribute__((unused))) +{ + my_decimal zero; + char buff[MAX_FIELD_WIDTH]; + + my_decimal_set_zero(&zero); + my_bool is_unsigned= (my_decimal_cmp(&zero, &min_arg) >= 0); + + sprintf(buff, "DECIMAL(%d, %d)", + (int)(max_length - (item->decimals ? 1 : 0)), item->decimals); + if (is_unsigned) + strcat(buff, " UNSIGNED"); + answer->append(buff, (uint) strlen(buff)); +} + + +String *field_decimal::get_min_arg(String *str) +{ + my_decimal2string(E_DEC_FATAL_ERROR, &min_arg, 0, 0, '0', str); + return str; +} + + +String *field_decimal::get_max_arg(String *str) +{ + my_decimal2string(E_DEC_FATAL_ERROR, &max_arg, 0, 0, '0', str); + return str; +} + + +String *field_decimal::avg(String *s, ha_rows rows) +{ + if (!(rows - nulls)) + { + s->set((double) 0.0, 1,my_thd_charset); + return s; + } + my_decimal num, avg_val; + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); + my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, 0); + my_decimal2string(E_DEC_FATAL_ERROR, &avg_val, 0, 0, '0', s); + return s; +} + + +String *field_decimal::std(String *s, ha_rows rows) +{ + if (!(rows - nulls)) + { + s->set((double) 0.0, 1,my_thd_charset); + return s; + } + my_decimal num, std_val, sum2, sum2d; + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); + my_decimal_mul(E_DEC_FATAL_ERROR, &sum2, sum+cur_sum, sum+cur_sum); + my_decimal_div(E_DEC_FATAL_ERROR, &std_val, &sum2, &num, 0); + my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &std_val); + my_decimal_div(E_DEC_FATAL_ERROR, &std_val, &sum2, &num, 0); + my_decimal2string(E_DEC_FATAL_ERROR, &std_val, 0, 0, '0', s); + return s; +} + + int collect_string(String *element, element_count count __attribute__((unused)), TREE_INFO *info) @@ -920,6 +1065,28 @@ int collect_real(double *element, element_count count __attribute__((unused)), } // collect_real +int collect_decimal(char *element, element_count count, + TREE_INFO *info) +{ + char buff[DECIMAL_MAX_STR_LENGTH]; + String s(buff, sizeof(buff),&my_charset_bin); + + if (info->found) + info->str->append(','); + else + info->found = 1; + my_decimal dec; + binary2my_decimal(E_DEC_FATAL_ERROR, element, &dec, + info->item->max_length, info->item->decimals); + + info->str->append('\''); + my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, '0', &s); + info->str->append(s); + info->str->append('\''); + return 0; +} + + int collect_longlong(longlong *element, element_count count __attribute__((unused)), TREE_INFO *info) @@ -1021,12 +1188,12 @@ uint check_ulonglong(const char *str, uint length) bigger = LONG_NUM; } else if (length > ulonglong_len) - return REAL_NUM; + return DECIMAL_NUM; else { cmp = ulonglong_str; smaller = LONG_NUM; - bigger = REAL_NUM; + bigger = DECIMAL_NUM; } while (*cmp && *cmp++ == *str++) ; return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger; diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 3d1cffecaef..a0f0df9b43b 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -61,6 +61,7 @@ int compare_longlong2(void* cmp_arg __attribute__((unused)), int compare_ulonglong(const ulonglong *s, const ulonglong *t); int compare_ulonglong2(void* cmp_arg __attribute__((unused)), const ulonglong *s, const ulonglong *t); +int compare_decimal2(int* len, const char *s, const char *t); Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list); void free_string(String*); @@ -143,6 +144,36 @@ public: }; +int collect_decimal(char *element, element_count count, + TREE_INFO *info); + +class field_decimal :public field_info +{ + my_decimal min_arg, max_arg; + my_decimal sum[2], sum_sqr[2]; + int cur_sum; + int bin_size; +public: + field_decimal(Item* a, analyse* b) :field_info(a,b) + { + bin_size= my_decimal_get_binary_size(a->max_length, a->decimals); + init_tree(&tree, 0, 0, bin_size, (qsort_cmp2)compare_decimal2, + 0, 0, (void *)&bin_size); + }; + + void add(); + void get_opt_type(String*, ha_rows); + String *get_min_arg(String *); + String *get_max_arg(String *); + String *avg(String *s, ha_rows rows); + friend int collect_decimal(char *element, element_count count, + TREE_INFO *info); + tree_walk_action collect_enum() + { return (tree_walk_action) collect_decimal; } + String *std(String *s, ha_rows rows); +}; + + int collect_real(double *element, element_count count, TREE_INFO *info); class field_real: public field_info diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0a57c0f6bc9..85e0d27160b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2169,6 +2169,8 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list, thd->change_item_tree(ref, item_ref); else if (item_ref) *ref= item_ref; + if (!(*ref)->fixed) + (*ref)->fix_fields(thd, 0, ref); } DBUG_RETURN((Field*) view_ref_found); } @@ -3389,7 +3391,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) if (cond_and->list.elements) { COND *on_expr= cond_and; - on_expr->fix_fields(thd, 0, &on_expr); + if (!on_expr->fixed) + on_expr->fix_fields(thd, 0, &on_expr); if (!embedded->outer_join) // Not left join { *conds= and_conds(*conds, cond_and); @@ -3398,7 +3401,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) thd->restore_backup_item_arena(arena, &backup); if (*conds && !(*conds)->fixed) { - if ((*conds)->fix_fields(thd, tables, conds)) + if (!(*conds)->fixed && + (*conds)->fix_fields(thd, tables, conds)) goto err_no_arena; } } @@ -3410,8 +3414,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) thd->restore_backup_item_arena(arena, &backup); if (embedded->on_expr && !embedded->on_expr->fixed) { - if (embedded->on_expr->fix_fields(thd, tables, - &embedded->on_expr)) + if (!embedded->on_expr->fixed && + embedded->on_expr->fix_fields(thd, tables, &embedded->on_expr)) goto err_no_arena; } } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 89442d157c6..32c9e2a50f7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1340,6 +1340,9 @@ bool select_max_min_finder_subselect::send_data(List<Item> &items) case STRING_RESULT: op= &select_max_min_finder_subselect::cmp_str; break; + case DECIMAL_RESULT: + op= &select_max_min_finder_subselect::cmp_decimal; + break; case ROW_RESULT: // This case should never be choosen DBUG_ASSERT(0); @@ -1381,6 +1384,26 @@ bool select_max_min_finder_subselect::cmp_int() val1 < val2); } +bool select_max_min_finder_subselect::cmp_decimal() +{ + String *val1, *val2, buf1, buf2; + Item *maxmin= ((Item_singlerow_subselect *)item)->el(0); + /* + as far as both operand is Item_cache buf1 & buf2 will not be used, + but added for safety + */ + my_decimal cval, *cvalue= cache->val_decimal(&cval); + my_decimal mval, *mvalue= maxmin->val_decimal(&mval); + if (fmax) + return (cache->null_value && !maxmin->null_value) || + (!cache->null_value && !maxmin->null_value && + my_decimal_cmp(cvalue, mvalue) > 0) ; + else + return (maxmin->null_value && !cache->null_value) || + (!cache->null_value && !maxmin->null_value && + my_decimal_cmp(cvalue,mvalue) < 0); +} + bool select_max_min_finder_subselect::cmp_str() { String *val1, *val2, buf1, buf2; @@ -1447,6 +1470,8 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) /* Item_func_set_user_var can't substitute something else on its place => 0 can be passed as last argument (reference on item) + Item_func_set_user_var can't be fixed after creation, so we do not + check xx->fixed */ xx->fix_fields(thd, (TABLE_LIST*) thd->lex->select_lex.table_list.first, 0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 29185dfbf7b..32a4093dee9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1519,6 +1519,7 @@ public: bool send_data(List<Item> &items); bool cmp_real(); bool cmp_int(); + bool cmp_decimal(); bool cmp_str(); }; @@ -1592,9 +1593,10 @@ class user_var_entry ulong length, update_query_id, used_query_id; Item_result type; - double val(my_bool *null_value); + double val_real(my_bool *null_value); longlong val_int(my_bool *null_value); String *val_str(my_bool *null_value, String *str, uint decimals); + my_decimal *val_decimal(my_bool *null_value, my_decimal *result); DTCollation collation; }; @@ -1623,9 +1625,11 @@ public: ~Unique(); inline bool unique_add(void *ptr) { + DBUG_ENTER("unique_add"); + DBUG_PRINT("info", ("tree %u - %u", tree.elements_in_tree, max_elements)); if (tree.elements_in_tree > max_elements && flush()) - return 1; - return !tree_insert(&tree, ptr, 0, tree.custom_arg); + DBUG_RETURN(1); + DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg)); } bool get(TABLE *table); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index c9c21d82568..dd2ac3c013b 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -409,8 +409,8 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, } tables->table=table; - if (cond && ((!cond->fixed && - cond->fix_fields(thd, tables, &cond)) || cond->check_cols(1))) + if (cond && ((!cond->fixed && + cond->fix_fields(thd, tables, &cond)) || cond->check_cols(1))) goto err0; table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it @@ -495,7 +495,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, for (key_len=0 ; (item=it_ke++) ; key_part++) { // 'item' can be changed by fix_fields() call - if ((!item->fixed && + if ((!item->fixed && item->fix_fields(thd, tables, it_ke.ref())) || (item= *it_ke.ref())->check_cols(1)) goto err; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index b8c77a822c4..78ce3c95d6b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -40,10 +40,6 @@ sys_var_long_ptr trg_new_row_fake_var(0, 0); #define yySkip() lex->ptr++ #define yyLength() ((uint) (lex->ptr - lex->tok_start)-1) -#if MYSQL_VERSION_ID < 32300 -#define FLOAT_NUM REAL_NUM -#endif - pthread_key(LEX*,THR_LEX); /* Longest standard keyword name */ @@ -437,12 +433,12 @@ inline static uint int_token(const char *str,uint length) else if (length < signed_longlong_len) return LONG_NUM; else if (length > signed_longlong_len) - return REAL_NUM; + return DECIMAL_NUM; else { cmp=signed_longlong_str+1; smaller=LONG_NUM; // If <= signed_longlong_str - bigger=REAL_NUM; + bigger=DECIMAL_NUM; } } else @@ -458,10 +454,10 @@ inline static uint int_token(const char *str,uint length) else if (length > longlong_len) { if (length > unsigned_longlong_len) - return REAL_NUM; + return DECIMAL_NUM; cmp=unsigned_longlong_str; smaller=ULONGLONG_NUM; - bigger=REAL_NUM; + bigger=DECIMAL_NUM; } else { @@ -799,7 +795,7 @@ int yylex(void *arg, void *yythd) return(FLOAT_NUM); } yylval->lex_str=get_token(lex,yyLength()); - return(REAL_NUM); + return(DECIMAL_NUM); case MY_LEX_HEX_NUMBER: // Found x'hexstring' yyGet(); // Skip ' diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5981a8b2d4d..e712cda8073 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2527,7 +2527,8 @@ mysql_execute_command(THD *thd) goto error; /* PURGE MASTER LOGS BEFORE 'data' */ it= (Item *)lex->value_list.head(); - if (it->check_cols(1) || it->fix_fields(lex->thd, 0, &it)) + if ((!it->fixed &&it->fix_fields(lex->thd, 0, &it)) || + it->check_cols(1)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE"); goto error; @@ -3746,7 +3747,7 @@ unsent_create_error: { Item *it= (Item *)lex->value_list.head(); - if (it->fix_fields(lex->thd, 0, &it) || it->check_cols(1)) + if ((!it->fixed && it->fix_fields(lex->thd, 0, &it)) || it->check_cols(1)) { my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), MYF(0)); @@ -5070,21 +5071,19 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, break; case FIELD_TYPE_NULL: break; - case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_NEWDECIMAL: if (!length) { - if ((new_field->length= new_field->decimals)) - new_field->length++; - else + if (!(new_field->length= new_field->decimals)) new_field->length= 10; // Default length for DECIMAL } - if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument - { - new_field->length+=sign_len; - if (new_field->decimals) - new_field->length++; - } - break; + new_field->pack_length= + my_decimal_get_binary_size(new_field->length, new_field->decimals); + if (new_field->length <= DECIMAL_MAX_LENGTH && + new_field->length >= new_field->decimals) + break; + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + DBUG_RETURN(1); case MYSQL_TYPE_VARCHAR: /* Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table @@ -5270,6 +5269,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->pack_length= (new_field->length + 7) / 8; break; } + case FIELD_TYPE_DECIMAL: + DBUG_ASSERT(0); /* Was obsolete */ } if (!(new_field->flags & BLOB_FLAG) && diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a71b8148f8e..6488820d2f9 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -332,6 +332,13 @@ static void set_param_double(Item_param *param, uchar **pos, ulong len) *pos+= 8; } +static void set_param_decimal(Item_param *param, uchar **pos, ulong len) +{ + ulong length= get_param_length(pos, len); + param->set_decimal((char*)*pos, length); + *pos+= len; +} + #ifndef EMBEDDED_LIBRARY /* @@ -508,6 +515,12 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, param->item_type= Item::REAL_ITEM; param->item_result_type= REAL_RESULT; break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + param->set_param_func= set_param_decimal; + param->item_type= Item::DECIMAL_ITEM; + param->item_result_type= DECIMAL_RESULT; + break; case MYSQL_TYPE_TIME: param->set_param_func= set_param_time; param->item_type= Item::STRING_ITEM; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 68438f7a785..6af257893d4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -542,6 +542,10 @@ JOIN::optimize() } else if ((conds=new Item_cond_and(conds,having))) { + /* + Item_cond_and can't be fixed after creation, so we do not check + conds->fixed + */ conds->fix_fields(thd, tables_list, &conds); conds->change_ref_to_fields(thd, tables_list); conds->top_level_item(); @@ -7213,7 +7217,8 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) if (conds) { conds= and_conds(conds, table->on_expr); - conds->fix_fields(join->thd, 0, &conds); + if (!conds->fixed) + conds->fix_fields(join->thd, 0, &conds); } else conds= table->on_expr; @@ -7432,6 +7437,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) 21)))) { cond=new_cond; + /* + Item_func_eq can't be fixed after creation so we do not check + cond->fixed, also it do not need tables so we use 0 as second + argument. + */ cond->fix_fields(thd, 0, &cond); } thd->insert_id(0); // Clear for next request @@ -7446,6 +7456,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) if ((new_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2)))) { cond=new_cond; + /* + Item_func_eq can't be fixed after creation so we do not check + cond->fixed, also it do not need tables so we use 0 as second + argument. + */ cond->fix_fields(thd, 0, &cond); } } @@ -7641,8 +7656,13 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, else new_field= item->make_string_field(table); break; - case ROW_RESULT: - default: + case DECIMAL_RESULT: + new_field= new Field_new_decimal(item->max_length - (item->decimals?1:0), + maybe_null, + item->name, table, item->decimals); + break; + case ROW_RESULT: + default: // This case should never be choosen DBUG_ASSERT(0); new_field= 0; // to satisfy compiler (uninitialized variable) @@ -7693,47 +7713,10 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, { Item_sum *item_sum=(Item_sum*) item; bool maybe_null=item_sum->maybe_null; - switch (item_sum->sum_func()) { - case Item_sum::AVG_FUNC: /* Place for sum & count */ - if (group) - return new Field_string(sizeof(double)+sizeof(longlong), - 0, item->name,table,&my_charset_bin); - else - return new Field_double(item_sum->max_length,maybe_null, - item->name, table, item_sum->decimals); - case Item_sum::VARIANCE_FUNC: /* Place for sum & count */ - case Item_sum::STD_FUNC: - if (group) - return new Field_string(sizeof(double)*2+sizeof(longlong), - 0, item->name,table,&my_charset_bin); - else - return new Field_double(item_sum->max_length, maybe_null, - item->name,table,item_sum->decimals); - case Item_sum::UNIQUE_USERS_FUNC: - return new Field_long(9,maybe_null,item->name,table,1); - default: - switch (item_sum->result_type()) { - case REAL_RESULT: - return new Field_double(item_sum->max_length,maybe_null, - item->name,table,item_sum->decimals); - case INT_RESULT: - return new Field_longlong(item_sum->max_length,maybe_null, - item->name,table,item->unsigned_flag); - case STRING_RESULT: - if (item_sum->max_length > 255 && convert_blob_length) - return new Field_varstring(convert_blob_length, maybe_null, - item->name, table, - item->collation.collation); - return item_sum->make_string_field(table); - case ROW_RESULT: - default: - // This case should never be choosen - DBUG_ASSERT(0); - thd->fatal_error(); - return 0; - } - } - /* We never come here */ + Field *result= item_sum->create_tmp_field(group, table, convert_blob_length); + if (!result) + thd->fatal_error(); + return result; } case Item::FIELD_ITEM: case Item::DEFAULT_VALUE_ITEM: @@ -7751,6 +7734,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::PROC_ITEM: case Item::INT_ITEM: case Item::REAL_ITEM: + case Item::DECIMAL_ITEM: case Item::STRING_ITEM: case Item::REF_ITEM: case Item::NULL_ITEM: diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 5abfe44f51b..62c214923f8 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2250,15 +2250,21 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[6]->store((const char*) pos, strlen((const char*) pos), cs); if (field->has_charset()) - table->field[8]->store((longlong) field->field_length/ + table->field[8]->store((longlong) field->representation_length()/ field->charset()->mbmaxlen); else - table->field[8]->store((longlong) field->field_length); - table->field[9]->store((longlong) field->field_length); + table->field[8]->store((longlong) field->representation_length()); + table->field[9]->store((longlong) field->representation_length()); { uint dec =field->decimals(); switch (field->type()) { + case FIELD_TYPE_NEWDECIMAL: + table->field[10]->store((longlong) field->field_length); + table->field[10]->set_notnull(); + table->field[11]->store((longlong) field->decimals()); + table->field[11]->set_notnull(); + break; case FIELD_TYPE_DECIMAL: { uint int_part=field->field_length - (dec ? dec + 1 : 0); @@ -2266,8 +2272,8 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[10]->set_notnull(); table->field[11]->store((longlong) field->decimals()); table->field[11]->set_notnull(); + break; } - break; case FIELD_TYPE_TINY: case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: @@ -2283,8 +2289,8 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[11]->store((longlong) dec); table->field[11]->set_notnull(); } + break; } - break; default: break; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 38f1e6e7250..d7cefb3dceb 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -777,6 +777,14 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, } sql_field->pack_flag= FIELDFLAG_NUMBER; break; + case FIELD_TYPE_NEWDECIMAL: + sql_field->pack_flag=(FIELDFLAG_NUMBER | + (sql_field->flags & UNSIGNED_FLAG ? 0 : + FIELDFLAG_DECIMAL) | + (sql_field->flags & ZEROFILL_FLAG ? + FIELDFLAG_ZEROFILL : 0) | + (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); + break; case FIELD_TYPE_TIMESTAMP: /* We should replace old TIMESTAMP fields with their newer analogs */ if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 5968d13efde..b9e837b0d64 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -176,7 +176,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) trg_field; trg_field= trg_field->next_trg_field) { trg_field->setup_field(thd, table, lex->trg_chistics.event); - if (trg_field->fix_fields(thd, (TABLE_LIST *)0, (Item **)0)) + if (!trg_field->fixed && + trg_field->fix_fields(thd, (TABLE_LIST *)0, (Item **)0)) return 1; } diff --git a/sql/sql_udf.h b/sql/sql_udf.h index d1f99a6d232..51ea6d4d627 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -103,6 +103,7 @@ class udf_handler :public Sql_alloc *null_value=0; return tmp; } + my_decimal *val_decimal(my_bool *null_value, my_decimal *dec_buf); void clear() { is_null= 0; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 82cc1394eaf..f9df1be2abd 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -501,7 +501,7 @@ int mysql_update(THD *thd, free_underlaid_joins(thd, select_lex); if (error < 0) { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, (ulong) thd->cuted_fields); thd->row_count_func= @@ -1383,7 +1383,7 @@ err: bool multi_update::send_eof() { - char buff[80]; + char buff[STRING_BUFFER_USUAL_SIZE]; thd->proc_info="updating reference tables"; /* Does updates for the last n - 1 tables, returns 0 if ok */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1402a85229b..012a64b36c2 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -239,6 +239,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURRENT_USER %token DATABASES %token DATA_SYM +%token DECIMAL_NUM %token DECLARE_SYM %token DEFAULT %token DELAYED_SYM @@ -382,7 +383,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RAID_CHUNKSIZE %token READ_SYM %token READS_SYM -%token REAL_NUM %token REDUNDANT_SYM %token REFERENCES %token REGEXP @@ -667,7 +667,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %right BINARY COLLATE_SYM %type <lex_str> - IDENT IDENT_QUOTED TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM + IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name @@ -2658,6 +2658,7 @@ udf_func_type: udf_type: STRING_SYM {$$ = (int) STRING_RESULT; } | REAL {$$ = (int) REAL_RESULT; } + | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; } | INT_SYM {$$ = (int) INT_RESULT; }; field_list: @@ -2838,11 +2839,11 @@ type: | MEDIUMTEXT opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } | LONGTEXT opt_binary { $$=FIELD_TYPE_LONG_BLOB; } | DECIMAL_SYM float_options field_options - { $$=FIELD_TYPE_DECIMAL;} + { $$=FIELD_TYPE_NEWDECIMAL;} | NUMERIC_SYM float_options field_options - { $$=FIELD_TYPE_DECIMAL;} + { $$=FIELD_TYPE_NEWDECIMAL;} | FIXED_SYM float_options field_options - { $$=FIELD_TYPE_DECIMAL;} + { $$=FIELD_TYPE_NEWDECIMAL;} | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary { $$=FIELD_TYPE_ENUM; } | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary @@ -2904,8 +2905,8 @@ real_type: float_options: - /* empty */ {} - | '(' NUM ')' { Lex->length=$2.str; } + /* empty */ { Lex->dec=Lex->length= (char*)0; } + | '(' NUM ')' { Lex->length=$2.str; Lex->dec= (char*)0; } | precision {}; precision: @@ -4187,13 +4188,15 @@ simple_expr: | ASCII_SYM '(' expr ')' { $$= new Item_func_ascii($3); } | BINARY simple_expr %prec NEG { - $$= create_func_cast($2, ITEM_CAST_CHAR, -1, &my_charset_bin); + $$= create_func_cast($2, ITEM_CAST_CHAR, -1, 0, &my_charset_bin); } | CAST_SYM '(' expr AS cast_type ')' { + LEX *lex= Lex; $$= create_func_cast($3, $5, - Lex->length ? atoi(Lex->length) : -1, - Lex->charset); + lex->length ? atoi(lex->length) : -1, + lex->dec ? atoi(lex->dec) : 0, + lex->charset); } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END { $$= new Item_func_case(* $4, $2, $5 ); } @@ -4201,6 +4204,7 @@ simple_expr: { $$= create_func_cast($3, $5, Lex->length ? atoi(Lex->length) : -1, + Lex->dec ? atoi(Lex->dec) : 0, Lex->charset); } | CONVERT_SYM '(' expr USING charset_name ')' @@ -4565,6 +4569,22 @@ simple_expr: $$ = new Item_sum_udf_int(udf); } break; + case DECIMAL_RESULT: + if (udf->type == UDFTYPE_FUNCTION) + { + if ($3 != NULL) + $$ = new Item_func_udf_decimal(udf, *$3); + else + $$ = new Item_func_udf_decimal(udf); + } + else + { + if ($3 != NULL) + $$ = new Item_sum_udf_decimal(udf, *$3); + else + $$ = new Item_sum_udf_decimal(udf); + } + break; default: YYABORT; } @@ -4814,16 +4834,17 @@ in_sum_expr: }; cast_type: - BINARY opt_len { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; } - | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; } + BINARY opt_len { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; } + | CHAR_SYM opt_len opt_binary { $$=ITEM_CAST_CHAR; Lex->dec= 0; } | NCHAR_SYM opt_len { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; } - | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->length= (char*)0; } - | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->length= (char*)0; } - | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->length= (char*)0; } - | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->length= (char*)0; } - | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->length= (char*)0; } - | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->length= (char*)0; } - | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->length= (char*)0; } + | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATE_SYM { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | TIME_SYM { $$=ITEM_CAST_TIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DATETIME { $$=ITEM_CAST_DATETIME; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; } + | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; } ; expr_list: @@ -5326,7 +5347,7 @@ ULONG_NUM: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | REAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } ; @@ -5334,7 +5355,7 @@ ulonglong_num: NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | REAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } ; @@ -6575,9 +6596,9 @@ NUM_literal: NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } | LONG_NUM { int error; $$ = new Item_int($1.str, (longlong) my_strtoll10($1.str, NULL, &error), $1.length); } | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } - | REAL_NUM + | DECIMAL_NUM { - $$= new Item_real($1.str, $1.length); + $$= new Item_decimal($1.str, $1.length, YYTHD->charset()); if (YYTHD->net.report_error) { YYABORT; |