diff options
Diffstat (limited to 'sql')
83 files changed, 7745 insertions, 1591 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/examples/ha_archive.cc b/sql/examples/ha_archive.cc index 436c72702a0..b33b5102e69 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -43,18 +43,20 @@ handle bulk inserts as well (that is if someone was trying to read at the same time since we would want to flush). - A "meta" file is kept. All this file does is contain information on - the number of rows. + A "meta" file is kept alongside the data file. This file serves two purpose. + The first purpose is to track the number of rows in the table. The second + purpose is to determine if the table was closed properly or not. When the + meta file is first opened it is marked as dirty. It is opened when the table + itself is opened for writing. When the table is closed the new count for rows + is written to the meta file and the file is marked as clean. If the meta file + is opened and it is marked as dirty, it is assumed that a crash occured. At + this point an error occurs and the user is told to rebuild the file. + A rebuild scans the rows and rewrites the meta file. If corruption is found + in the data file then the meta file is not repaired. - No attempts at durability are made. You can corrupt your data. A repair - method was added to repair the meta file that stores row information, - but if your data file gets corrupted I haven't solved that. I could - create a repair that would solve this, but do you want to take a - chance of loosing your data? + At some point a recovery method for such a drastic case needs to be divised. - Locks are row level, and you will get a consistant read. Transactions - will be added later (they are not that hard to add at this - stage). + Locks are row level, and you will get a consistant read. For performance as far as table scans go it is quite fast. I don't have good numbers but locally it has out performed both Innodb and MyISAM. For @@ -89,7 +91,6 @@ compression but may speed up ordered searches). Checkpoint the meta file to allow for faster rebuilds. Dirty open (right now the meta file is repaired if a crash occured). - Transactions. Option to allow for dirty reads, this would lower the sync calls, which would make inserts a lot faster, but would mean highly arbitrary reads. @@ -347,6 +348,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table) share->crashed= TRUE; else (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); + /* It is expensive to open and close the data files and since you can't have a gzip file that can be both read and written we keep a writer open @@ -393,7 +395,8 @@ int ha_archive::free_share(ARCHIVE_SHARE *share) (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE); if (gzclose(share->archive_write) == Z_ERRNO) rc= 1; - my_close(share->meta_file,MYF(0)); + if (my_close(share->meta_file, MYF(0))) + rc= 1; my_free((gptr) share, MYF(0)); } pthread_mutex_unlock(&archive_mutex); diff --git a/sql/field.cc b/sql/field.cc index aa2c5805bfe..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, @@ -261,6 +310,7 @@ static Field::field_cast_enum field_cast_date[]= Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; static Field::field_cast_enum field_cast_newdate[]= {Field::FIELD_CAST_NEWDATE, + Field::FIELD_CAST_DATE, Field::FIELD_CAST_DATETIME, Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING, Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP}; @@ -282,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 */ @@ -301,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 }; @@ -397,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) @@ -405,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; @@ -419,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) { @@ -696,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 */ @@ -837,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. */ @@ -1254,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 ****************************************************************************/ @@ -2553,7 +3028,6 @@ int Field_float::store(longlong nr) return store((double)nr); } - double Field_float::val_real(void) { float j; @@ -2834,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) { @@ -2864,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))) { @@ -3031,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 @@ -3093,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). */ @@ -3597,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") */ @@ -4318,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)); @@ -4436,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; @@ -4541,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) { @@ -4560,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) { @@ -4791,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; @@ -4871,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) { @@ -5272,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) @@ -5395,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))) { @@ -5414,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. */ @@ -5456,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; @@ -5484,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) { @@ -5997,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; @@ -6181,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; @@ -6443,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(); @@ -6486,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) @@ -6592,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; @@ -6645,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; } @@ -6766,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, @@ -6834,6 +7361,40 @@ Field *make_field(char *ptr, uint32 field_length, } +/* + Check if field_type is appropriate field type + to create field for tmp table using + item->tmp_table_field() method + + SYNOPSIS + field_types_to_be_kept() + field_type - field type + + NOTE + it is used in function get_holder_example_field() + from item.cc + + RETURN + 1 - can use item->tmp_table_field() method + 0 - can not use item->tmp_table_field() method + +*/ + +bool field_types_to_be_kept(enum_field_types field_type) +{ + switch (field_type) + { + case FIELD_TYPE_DATE: + case FIELD_TYPE_NEWDATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + return 1; + default: + return 0; + } +} + + /* Create a field suitable for create of table */ create_field::create_field(Field *old_field,Field *orig_field) @@ -6924,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 @@ -6956,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 @@ -6985,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 @@ -7017,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 af300c8d471..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) @@ -1333,6 +1407,7 @@ enum_field_types get_blob_type_from_length(ulong length); uint32 calc_pack_length(enum_field_types type,uint32 length); int set_field_to_null(Field *field); int set_field_to_null_with_conversions(Field *field, bool no_conversions); +bool field_types_to_be_kept(enum_field_types field_type); /* The following are for the interface with the .frm file 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_federated.cc b/sql/ha_federated.cc index 5185e0bbe9a..695c71677c0 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -586,16 +586,13 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) DBUG_RETURN(0); } -bool ha_federated::create_where_from_key( - String *to, - KEY *key_info, - const byte *key, - uint key_length - ) +bool ha_federated::create_where_from_key(String *to, KEY *key_info, + const byte *key, uint key_length) { uint second_loop= 0; KEY_PART_INFO *key_part; bool needs_quotes; + String tmp; DBUG_ENTER("ha_federated::create_where_from_key"); for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++) @@ -656,7 +653,9 @@ bool ha_federated::create_where_from_key( uint blob_length= uint2korr(key); key+= HA_KEY_BLOB_LENGTH; key_length-= HA_KEY_BLOB_LENGTH; - if (append_escaped(to, (char *)(key), blob_length)) + + tmp.set_quick((char*) key, blob_length, &my_charset_bin); + if (append_escaped(to, &tmp)) DBUG_RETURN(1); DBUG_PRINT("ha_federated::create_where_from_key", ("blob type %s", to->c_ptr_quick())); @@ -666,7 +665,8 @@ bool ha_federated::create_where_from_key( { length= uint2korr(key); key+= HA_KEY_BLOB_LENGTH; - if (append_escaped(to, (char *)(key), length)) + tmp.set_quick((char*) key, length, &my_charset_bin); + if (append_escaped(to, &tmp)) DBUG_RETURN(1); DBUG_PRINT("ha_federated::create_where_from_key", ("varchar type %s", to->c_ptr_quick())); @@ -680,7 +680,7 @@ bool ha_federated::create_where_from_key( res= field->val_str(&str, (char *)(key)); if (field->result_type() == STRING_RESULT) { - if (append_escaped(to, (char *) res->ptr(), res->length())) + if (append_escaped(to, res)) DBUG_RETURN(1); res= field->val_str(&str, (char *)(key)); @@ -1235,7 +1235,7 @@ int ha_federated::update_row( update_string.append(new_field_value); new_field_value.length(0); - if (x+1 < table->s->fields) + if ((uint) x+1 < table->s->fields) { update_string.append(", "); if (! has_a_primary_key) @@ -1311,7 +1311,7 @@ int ha_federated::delete_row(const byte * buf) delete_string.append(data_string); data_string.length(0); - if (x+1 < table->s->fields) + if ((uint) x+1 < table->s->fields) delete_string.append(" AND "); } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 32b203f7ad6..052ee0643ee 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -787,8 +787,9 @@ innobase_query_caching_of_table_permitted( char* full_name, /* in: concatenation of database name, the null character '\0', and the table name */ - uint full_name_len) /* in: length of the full name, i.e. + uint full_name_len, /* in: length of the full name, i.e. len(dbname) + len(tablename) + 1 */ + ulonglong *unused) /* unused for this engine */ { ibool is_autocommit; trx_t* trx; @@ -2158,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: @@ -4871,12 +4874,12 @@ ha_innobase::update_table_comment( dict_print_info_on_foreign_keys(FALSE, file, prebuilt->trx, prebuilt->table); flen = ftell(file); - if(length + flen + 3 > 64000) { + if (flen < 0) { + flen = 0; + } else if (length + flen + 3 > 64000) { flen = 64000 - 3 - length; } - ut_ad(flen > 0); - /* allocate buffer for the full string, and read the contents of the temporary file */ @@ -4940,12 +4943,12 @@ ha_innobase::get_foreign_key_create_info(void) prebuilt->trx->op_info = (char*)""; flen = ftell(file); - if(flen > 64000 - 1) { + if (flen < 0) { + flen = 0; + } else if(flen > 64000 - 1) { flen = 64000 - 1; } - ut_ad(flen >= 0); - /* allocate buffer for the string, and read the contents of the temporary file */ @@ -5546,12 +5549,12 @@ innodb_show_status( srv_printf_innodb_monitor(srv_monitor_file); flen = ftell(srv_monitor_file); os_file_set_eof(srv_monitor_file); - if(flen > 64000 - 1) { + if (flen < 0) { + flen = 0; + } else if (flen > 64000 - 1) { flen = 64000 - 1; } - ut_ad(flen > 0); - /* allocate buffer for the string, and read the contents of the temporary file */ @@ -5770,7 +5773,7 @@ ha_innobase::store_lock( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - if ((lock_type == TL_READ && thd->in_lock_tables) || + if ((lock_type == TL_READ && thd->in_lock_tables) || (lock_type == TL_READ_HIGH_PRIORITY && thd->in_lock_tables) || lock_type == TL_READ_WITH_SHARED_LOCKS || lock_type == TL_READ_NO_INSERT || @@ -5793,8 +5796,27 @@ ha_innobase::store_lock( unexpected if an obsolete consistent read view would be used. */ - prebuilt->select_lock_type = LOCK_S; - prebuilt->stored_select_lock_type = LOCK_S; + if (srv_locks_unsafe_for_binlog && + prebuilt->trx->isolation_level != TRX_ISO_SERIALIZABLE && + (lock_type == TL_READ || lock_type == TL_READ_NO_INSERT) && + thd->lex->sql_command != SQLCOM_SELECT && + thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + thd->lex->sql_command != SQLCOM_DELETE_MULTI ) { + + /* In case we have innobase_locks_unsafe_for_binlog + option set and isolation level of the transaction + is not set to serializable and MySQL is doing + INSERT INTO...SELECT without FOR UPDATE or IN + SHARE MODE we use consistent read for select. + Similarly, in case of DELETE...SELECT and + UPDATE...SELECT when these are not multi table.*/ + + prebuilt->select_lock_type = LOCK_NONE; + prebuilt->stored_select_lock_type = LOCK_NONE; + } else { + prebuilt->select_lock_type = LOCK_S; + prebuilt->stored_select_lock_type = LOCK_S; + } } else if (lock_type != TL_IGNORE) { @@ -6142,13 +6164,19 @@ innobase_get_at_most_n_mbchars( extern "C" { /********************************************************************** -This function returns true if SQL-query in the current thread +This function returns true if + +1) SQL-query in the current thread is either REPLACE or LOAD DATA INFILE REPLACE. + +2) SQL-query in the current thread +is INSERT ON DUPLICATE KEY UPDATE. + NOTE that /mysql/innobase/row/row0ins.c must contain the prototype for this function ! */ ibool -innobase_query_is_replace(void) +innobase_query_is_update(void) /*===========================*/ { THD* thd; @@ -6160,17 +6188,23 @@ innobase_query_is_replace(void) ( thd->lex->sql_command == SQLCOM_LOAD && thd->lex->duplicates == DUP_REPLACE )) { return true; - } else { - return false; } + + if ( thd->lex->sql_command == SQLCOM_INSERT && + thd->lex->duplicates == DUP_UPDATE ) { + return true; + } + + return false; } } /*********************************************************************** This function is used to prepare X/Open XA distributed transaction */ -int innobase_xa_prepare( -/*====================*/ +int +innobase_xa_prepare( +/*================*/ /* out: 0 or error number */ THD* thd, /* in: handle to the MySQL thread of the user whose XA transaction should be prepared */ @@ -6233,12 +6267,13 @@ int innobase_xa_prepare( /*********************************************************************** This function is used to recover X/Open XA distributed transactions */ -int innobase_xa_recover( +int +innobase_xa_recover( +/*================*/ /* out: number of prepared transactions stored in xid_list */ XID* xid_list, /* in/out: prepared transactions */ uint len) /* in: number of slots in xid_list */ -/*====================*/ { if (len == 0 || xid_list == NULL) { return 0; @@ -6251,8 +6286,9 @@ int innobase_xa_recover( This function is used to commit one X/Open XA distributed transaction which is in the prepared state */ -int innobase_commit_by_xid( -/*=======================*/ +int +innobase_commit_by_xid( +/*===================*/ /* out: 0 or error number */ XID* xid) /* in: X/Open XA Transaction Identification */ { @@ -6273,7 +6309,9 @@ int innobase_commit_by_xid( This function is used to rollback one X/Open XA distributed transaction which is in the prepared state */ -int innobase_rollback_by_xid( +int +innobase_rollback_by_xid( +/*=====================*/ /* out: 0 or error number */ XID *xid) /* in : X/Open XA Transaction Idenfification */ { @@ -6288,36 +6326,4 @@ int innobase_rollback_by_xid( } } -/*********************************************************************** -This function is used to test commit/rollback of XA transactions */ - -int innobase_xa_end( -/*================*/ - THD* thd) /* in: MySQL thread handle of the user for whom - transactions should be recovered */ -{ - DBUG_ENTER("innobase_xa_end"); - - XID trx_list[100]; - int trx_num, trx_num_max = 100; - int i; - XID xid; - - while((trx_num = innobase_xa_recover(trx_list, trx_num_max))) { - - for(i=0;i < trx_num; i++) { - xid = trx_list[i]; - - if ( i % 2) { - innobase_commit_by_xid(&xid); - } else { - innobase_rollback_by_xid(&xid); - } - } - } - - free(trx_list); - - DBUG_RETURN(0); -} #endif /* HAVE_INNOBASE_DB */ diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 7a48de52422..cca33cbbe1c 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -33,6 +33,10 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; +my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, + uint full_name_len, + ulonglong *unused); + /* The class defining a handle to an Innodb table */ class ha_innobase: public handler { @@ -173,6 +177,20 @@ class ha_innobase: public handler void init_table_handle_for_HANDLER(); ulonglong get_auto_increment(); uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; } + /* + ask handler about permission to cache table during query registration + */ + my_bool register_query_cache_table(THD *thd, char *table_key, + uint key_length, + qc_engine_callback *call_back, + ulonglong *engine_data) + { + *call_back= innobase_query_caching_of_table_permitted; + *engine_data= 0; + return innobase_query_caching_of_table_permitted(thd, table_key, + key_length, + engine_data); + } static char *get_mysql_bin_log_name(); static ulonglong get_mysql_bin_log_pos(); bool primary_key_is_clustered() { return true; } @@ -253,8 +271,6 @@ bool innodb_show_status(THD* thd); bool innodb_mutex_show_status(THD* thd); void innodb_export_status(void); -my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, - uint full_name_len); void innobase_release_temporary_latches(void* innobase_tid); void innobase_store_binlog_offset_and_flush_log(char *binlog_name,longlong offset); 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/ha_myisam.h b/sql/ha_myisam.h index d2fe36c8357..7e14a3b7941 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -88,8 +88,12 @@ class ha_myisam: public handler ft_handler->please->reinit_search(ft_handler); return 0; } - FT_INFO *ft_init_ext(uint flags, uint inx,const byte *key, uint keylen) - { return ft_init_search(flags,file,inx,(byte*) key,keylen, table->record[0]); } + FT_INFO *ft_init_ext(uint flags, uint inx,String *key) + { + return ft_init_search(flags,file,inx, + (byte *)key->ptr(), key->length(), key->charset(), + table->record[0]); + } int ft_read(byte *buf); int rnd_init(bool scan); int rnd_next(byte *buf); diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index f2b159353e3..f77136a548e 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -87,6 +87,12 @@ static int unpackfrm(const void **data, uint *len, static int ndb_get_table_statistics(Ndb*, const char *, struct Ndb_statistics *); +// Util thread variables +static pthread_t ndb_util_thread; +pthread_mutex_t LOCK_ndb_util_thread; +pthread_cond_t COND_ndb_util_thread; +extern "C" pthread_handler_decl(ndb_util_thread_func, arg); +ulong ndb_cache_check_time; /* Dummy buffer to read zero pack_length fields @@ -3053,9 +3059,13 @@ int ha_ndbcluster::reset() DBUG_RETURN(1); } +static const char *ha_ndb_bas_ext[]= { ha_ndb_ext, NullS }; -const char **ha_ndbcluster::bas_ext() const -{ static const char *ext[]= { ha_ndb_ext, NullS }; return ext; } +const char** +ha_ndbcluster::bas_ext() const +{ + return ha_ndb_bas_ext; +} /* @@ -3223,7 +3233,6 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) m_transaction_on= FALSE; else m_transaction_on= thd->variables.ndb_use_transactions; - // m_use_local_query_cache= thd->variables.ndb_use_local_query_cache; m_active_trans= thd->transaction.all.ndb_tid ? (NdbTransaction*)thd->transaction.all.ndb_tid: @@ -3650,6 +3659,52 @@ static int create_ndb_column(NDBCOL &col, Create a table in NDB Cluster */ +static void ndb_set_fragmentation(NDBTAB &tab, TABLE *form, uint pk_length) +{ + if (form->s->max_rows == 0) /* default setting, don't set fragmentation */ + return; + /** + * get the number of fragments right + */ + uint no_fragments; + { +#if MYSQL_VERSION_ID >= 50000 + uint acc_row_size= 25 + /*safety margin*/ 2; +#else + uint acc_row_size= pk_length*4; + /* add acc overhead */ + if (pk_length <= 8) /* main page will set the limit */ + acc_row_size+= 25 + /*safety margin*/ 2; + else /* overflow page will set the limit */ + acc_row_size+= 4 + /*safety margin*/ 4; +#endif + ulonglong acc_fragment_size= 512*1024*1024; + ulonglong max_rows= form->s->max_rows; +#if MYSQL_VERSION_ID >= 50100 + no_fragments= (max_rows*acc_row_size)/acc_fragment_size+1; +#else + no_fragments= ((max_rows*acc_row_size)/acc_fragment_size+1 + +1/*correct rounding*/)/2; +#endif + } + { + uint no_nodes= g_ndb_cluster_connection->no_db_nodes(); + NDBTAB::FragmentType ftype; + if (no_fragments > 2*no_nodes) + { + ftype= NDBTAB::FragAllLarge; + if (no_fragments > 4*no_nodes) + push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Ndb might have problems storing the max amount of rows specified"); + } + else if (no_fragments > no_nodes) + ftype= NDBTAB::FragAllMedium; + else + ftype= NDBTAB::FragAllSmall; + tab.setFragmentType(ftype); + } +} + int ha_ndbcluster::create(const char *name, TABLE *form, HA_CREATE_INFO *info) @@ -3751,7 +3806,9 @@ int ha_ndbcluster::create(const char *name, break; } } - + + ndb_set_fragmentation(tab, form, pk_length); + if ((my_errno= check_ndb_connection())) DBUG_RETURN(my_errno); @@ -4017,7 +4074,6 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): m_force_send(TRUE), m_autoincrement_prefetch(32), m_transaction_on(TRUE), - m_use_local_query_cache(FALSE), m_multi_cursor(NULL) { int i; @@ -4068,6 +4124,7 @@ ha_ndbcluster::~ha_ndbcluster() } + /* Open a table for further use - fetch metadata for this table from NDB @@ -4168,16 +4225,14 @@ void ha_ndbcluster::release_thd_ndb(Thd_ndb* thd_ndb) Ndb* check_ndb_in_thd(THD* thd) { - DBUG_ENTER("check_ndb_in_thd"); - Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; - + Thd_ndb *thd_ndb= (Thd_ndb*)thd->transaction.thd_ndb; if (!thd_ndb) { if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) - DBUG_RETURN(NULL); + return NULL; thd->transaction.thd_ndb= thd_ndb; } - DBUG_RETURN(thd_ndb->ndb); + return thd_ndb->ndb; } @@ -4530,13 +4585,21 @@ bool ndbcluster_init() (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0, (hash_get_key) ndbcluster_get_key,0,0); pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_ndb_util_thread, NULL); + + // Create utility thread + pthread_t tmp; + if (pthread_create(&tmp, &connection_attrib, ndb_util_thread_func, 0)) + { + DBUG_PRINT("error", ("Could not create ndb utility thread")); + goto ndbcluster_init_error; + } + ndbcluster_inited= 1; -#ifdef USE_DISCOVER_ON_STARTUP - if (ndb_discover_tables() != 0) - goto ndbcluster_init_error; -#endif DBUG_RETURN(FALSE); + ndbcluster_init_error: ndbcluster_end(); DBUG_RETURN(TRUE); @@ -4546,12 +4609,19 @@ bool ndbcluster_init() /* End use of the NDB Cluster table handler - free all global variables allocated by - ndcluster_init() + ndbcluster_init() */ bool ndbcluster_end() { DBUG_ENTER("ndbcluster_end"); + + // Kill ndb utility thread + (void) pthread_mutex_lock(&LOCK_ndb_util_thread); + DBUG_PRINT("exit",("killing ndb util thread: %lx", ndb_util_thread)); + (void) pthread_cond_signal(&COND_ndb_util_thread); + (void) pthread_mutex_unlock(&LOCK_ndb_util_thread); + if(g_ndb) delete g_ndb; g_ndb= NULL; @@ -4562,6 +4632,8 @@ bool ndbcluster_end() DBUG_RETURN(0); hash_free(&ndbcluster_open_tables); pthread_mutex_destroy(&ndbcluster_mutex); + pthread_mutex_destroy(&LOCK_ndb_util_thread); + pthread_cond_destroy(&COND_ndb_util_thread); ndbcluster_inited= 0; DBUG_RETURN(0); } @@ -4754,16 +4826,174 @@ const char* ha_ndbcluster::index_type(uint key_number) return "HASH"; } } + uint8 ha_ndbcluster::table_cache_type() { - if (m_use_local_query_cache) - return HA_CACHE_TBL_TRANSACT; - else - return HA_CACHE_TBL_NOCACHE; + DBUG_ENTER("ha_ndbcluster::table_cache_type=HA_CACHE_TBL_ASKTRANSACT"); + DBUG_RETURN(HA_CACHE_TBL_ASKTRANSACT); } + +uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, + Uint64 *commit_count) +{ + DBUG_ENTER("ndb_get_commitcount"); + + if (ndb_cache_check_time > 0) + { + /* Use cached commit_count from share */ + char name[FN_REFLEN]; + NDB_SHARE *share; + (void)strxnmov(name, FN_REFLEN, + "./",dbname,"/",tabname,NullS); + DBUG_PRINT("info", ("name: %s", name)); + pthread_mutex_lock(&ndbcluster_mutex); + if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, + (byte*) name, + strlen(name)))) + { + pthread_mutex_unlock(&ndbcluster_mutex); + DBUG_RETURN(1); + } + *commit_count= share->commit_count; + DBUG_PRINT("info", ("commit_count: %d", *commit_count)); + pthread_mutex_unlock(&ndbcluster_mutex); + DBUG_RETURN(0); + } + + /* Get commit_count from NDB */ + Ndb *ndb; + if (!(ndb= check_ndb_in_thd(thd))) + DBUG_RETURN(1); + ndb->setDatabaseName(dbname); + + struct Ndb_statistics stat; + if (ndb_get_table_statistics(ndb, tabname, &stat)) + DBUG_RETURN(1); + *commit_count= stat.commit_count; + DBUG_RETURN(0); +} + + /* - Handling the shared NDB_SHARE structure that is needed to + Check if a cached query can be used. + This is done by comparing the supplied engine_data to commit_count of + the table. + The commit_count is either retrieved from the share for the table, where + it has been cached by the util thread. If the util thread is not started, + NDB has to be contacetd to retrieve the commit_count, this will introduce + a small delay while waiting for NDB to answer. + + + SYNOPSIS + ndbcluster_cache_retrieval_allowed + thd thread handle + full_name concatenation of database name, + the null character '\0', and the table + name + full_name_len length of the full name, + i.e. len(dbname) + len(tablename) + 1 + + engine_data parameter retrieved when query was first inserted into + the cache. If the value of engine_data is changed, + all queries for this table should be invalidated. + + RETURN VALUE + TRUE Yes, use the query from cache + FALSE No, don't use the cached query, and if engine_data + has changed, all queries for this table should be invalidated + +*/ + +static my_bool +ndbcluster_cache_retrieval_allowed(THD *thd, + char *full_name, uint full_name_len, + ulonglong *engine_data) +{ + DBUG_ENTER("ndbcluster_cache_retrieval_allowed"); + + Uint64 commit_count; + bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + char *dbname= full_name; + char *tabname= dbname+strlen(dbname)+1; + + DBUG_PRINT("enter",("dbname=%s, tabname=%s, autocommit=%d", + dbname, tabname, is_autocommit)); + + if (!is_autocommit) + DBUG_RETURN(FALSE); + + if (ndb_get_commitcount(thd, dbname, tabname, &commit_count)) + { + *engine_data+= 1; /* invalidate */ + DBUG_RETURN(FALSE); + } + DBUG_PRINT("info", ("*engine_data=%llu, commit_count=%llu", + *engine_data, commit_count)); + if (*engine_data != commit_count) + { + *engine_data= commit_count; /* invalidate */ + DBUG_PRINT("exit",("Do not use cache, commit_count has changed")); + DBUG_RETURN(FALSE); + } + + DBUG_PRINT("exit",("OK to use cache, *engine_data=%llu",*engine_data)); + DBUG_RETURN(TRUE); +} + + +/** + Register a table for use in the query cache. Fetch the commit_count + for the table and return it in engine_data, this will later be used + to check if the table has changed, before the cached query is reused. + + SYNOPSIS + ha_ndbcluster::can_query_cache_table + thd thread handle + full_name concatenation of database name, + the null character '\0', and the table + name + full_name_len length of the full name, + i.e. len(dbname) + len(tablename) + 1 + qc_engine_callback function to be called before using cache on this table + engine_data out, commit_count for this table + + RETURN VALUE + TRUE Yes, it's ok to cahce this query + FALSE No, don't cach the query + +*/ + +my_bool +ha_ndbcluster::register_query_cache_table(THD *thd, + char *full_name, uint full_name_len, + qc_engine_callback *engine_callback, + ulonglong *engine_data) +{ + DBUG_ENTER("ha_ndbcluster::register_query_cache_table"); + + bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + DBUG_PRINT("enter",("dbname=%s, tabname=%s, is_autocommit=%d", + m_dbname,m_tabname,is_autocommit)); + if (!is_autocommit) + DBUG_RETURN(FALSE); + + Uint64 commit_count; + if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count)) + { + *engine_data= 0; + DBUG_PRINT("error", ("Could not get commitcount")) + DBUG_RETURN(FALSE); + } + *engine_data= commit_count; + *engine_callback= ndbcluster_cache_retrieval_allowed; + DBUG_PRINT("exit",("*engine_data=%llu", *engine_data)); + DBUG_RETURN(TRUE); +} + + +/* + Handling the shared NDB_SHARE structure that is needed to provide table locking. It's also used for sharing data with other NDB handlers in the same MySQL Server. There is currently not much @@ -4800,8 +5030,14 @@ static NDB_SHARE* get_share(const char *table_name) } thr_lock_init(&share->lock); pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST); + share->commit_count= 0; } } + DBUG_PRINT("share", + ("table_name: %s, length: %d, use_count: %d, commit_count: %d", + share->table_name, share->table_name_length, share->use_count, + share->commit_count)); + share->use_count++; pthread_mutex_unlock(&ndbcluster_mutex); return share; @@ -5374,7 +5610,7 @@ ha_ndbcluster::update_table_comment( const char* comment)/* in: table comment defined by user */ { uint length= strlen(comment); - if(length > 64000 - 3) + if(length > 64000 - 3) { return((char*)comment); /* string too long */ } @@ -5407,4 +5643,125 @@ ha_ndbcluster::update_table_comment( return str; } + +// Utility thread main loop +extern "C" pthread_handler_decl(ndb_util_thread_func, + arg __attribute__((unused))) +{ + THD *thd; /* needs to be first for thread_stack */ + int error= 0; + struct timespec abstime; + + my_thread_init(); + DBUG_ENTER("ndb_util_thread"); + DBUG_PRINT("enter", ("ndb_cache_check_time: %d", ndb_cache_check_time)); + + thd= new THD; /* note that contructor of THD uses DBUG_ */ + THD_CHECK_SENTRY(thd); + + pthread_detach_this_thread(); + ndb_util_thread= pthread_self(); + + thd->thread_stack= (char*)&thd; /* remember where our stack is */ + if (thd->store_globals()) + { + thd->cleanup(); + delete thd; + DBUG_RETURN(NULL); + } + + List<NDB_SHARE> util_open_tables; + set_timespec(abstime, ndb_cache_check_time); + for (;;) + { + + pthread_mutex_lock(&LOCK_ndb_util_thread); + error= pthread_cond_timedwait(&COND_ndb_util_thread, + &LOCK_ndb_util_thread, + &abstime); + pthread_mutex_unlock(&LOCK_ndb_util_thread); + + DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %d", + ndb_cache_check_time)); + + if (abort_loop) + break; /* Shutting down server */ + + if (ndb_cache_check_time == 0) + { + set_timespec(abstime, 10); + continue; + } + + /* Set new time to wake up */ + set_timespec(abstime, ndb_cache_check_time); + + /* Lock mutex and fill list with pointers to all open tables */ + NDB_SHARE *share; + pthread_mutex_lock(&ndbcluster_mutex); + for (uint i= 0; i < ndbcluster_open_tables.records; i++) + { + share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i); + share->use_count++; /* Make sure the table can't be closed */ + + DBUG_PRINT("ndb_util_thread", + ("Found open table[%d]: %s, use_count: %d", + i, share->table_name, share->use_count)); + + /* Store pointer to table */ + util_open_tables.push_back(share); + } + pthread_mutex_unlock(&ndbcluster_mutex); + + /* Iterate through the open files list */ + List_iterator_fast<NDB_SHARE> it(util_open_tables); + while (share= it++) + { + /* Split tab- and dbname */ + char buf[FN_REFLEN]; + char *tabname, *db; + uint length= dirname_length(share->table_name); + tabname= share->table_name+length; + memcpy(buf, share->table_name, length-1); + buf[length-1]= 0; + db= buf+dirname_length(buf); + DBUG_PRINT("ndb_util_thread", + ("Fetching commit count for: %s, db: %s, tab: %s", + share->table_name, db, tabname)); + + /* Contact NDB to get commit count for table */ + g_ndb->setDatabaseName(db); + struct Ndb_statistics stat;; + if(ndb_get_table_statistics(g_ndb, tabname, &stat) == 0) + { + DBUG_PRINT("ndb_util_thread", + ("Table: %s, rows: %llu, commit_count: %llu", + share->table_name, stat.row_count, stat.commit_count)); + share->commit_count= stat.commit_count; + } + else + { + DBUG_PRINT("ndb_util_thread", + ("Error: Could not get commit count for table %s", + share->table_name)); + share->commit_count++; /* Invalidate */ + } + /* Decrease the use count and possibly free share */ + free_share(share); + } + + /* Clear the list of open tables */ + util_open_tables.empty(); + + } + + thd->cleanup(); + delete thd; + DBUG_PRINT("exit", ("ndb_util_thread")); + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(NULL); +} + + #endif /* HAVE_NDBCLUSTER_DB */ diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 942a4988252..b27908c20ea 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -37,6 +37,7 @@ class NdbBlob; // connectstring to cluster if given by mysqld extern const char *ndbcluster_connectstring; +extern ulong ndb_cache_check_time; typedef enum ndb_index_type { UNDEFINED_INDEX = 0, @@ -59,6 +60,7 @@ typedef struct st_ndbcluster_share { pthread_mutex_t mutex; char *table_name; uint table_name_length,use_count; + ulonglong commit_count; } NDB_SHARE; /* @@ -155,8 +157,11 @@ class ha_ndbcluster: public handler static Thd_ndb* seize_thd_ndb(); static void release_thd_ndb(Thd_ndb* thd_ndb); uint8 table_cache_type(); - - private: + my_bool register_query_cache_table(THD *thd, char *table_key, + uint key_length, + qc_engine_callback *engine_callback, + ulonglong *engine_data); +private: int alter_table_name(const char *to); int drop_table(); int create_index(const char *name, KEY *key_info, bool unique); @@ -256,7 +261,6 @@ class ha_ndbcluster: public handler bool m_force_send; ha_rows m_autoincrement_prefetch; bool m_transaction_on; - bool m_use_local_query_cache; bool m_disable_multi_read; byte *m_multi_range_result_ptr; diff --git a/sql/handler.cc b/sql/handler.cc index a9bb19158dd..b4fed363e87 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -234,15 +234,6 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) } } -bool ha_caching_allowed(THD* thd, char* table_key, - uint key_length, uint8 cache_type) -{ -#ifdef HAVE_INNOBASE_DB - if (cache_type == HA_CACHE_TBL_ASKTRANSACT) - return innobase_query_caching_of_table_permitted(thd, table_key, key_length); -#endif - return 1; -} /* diff --git a/sql/handler.h b/sql/handler.h index 2b5c66fd886..8ad49456bf6 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -455,8 +455,7 @@ public: int compare_key(key_range *range); virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } void ft_end() { ft_handler=NULL; } - virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key, - uint keylen) + virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } virtual int ft_read(byte *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(byte *buf)=0; @@ -592,6 +591,16 @@ public: /* Type of table for caching query */ virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } + /* ask handler about permission to cache table when query is to be cached */ + virtual my_bool register_query_cache_table(THD *thd, char *table_key, + uint key_length, + qc_engine_callback + *engine_callback, + ulonglong *engine_data) + { + *engine_callback= 0; + return 1; + } /* RETURN @@ -622,8 +631,6 @@ extern TYPELIB tx_isolation_typelib; T != DB_TYPE_BERKELEY_DB && \ T != DB_TYPE_NDBCLUSTER) -bool ha_caching_allowed(THD* thd, char* table_key, - uint key_length, uint8 cache_type); enum db_type ha_resolve_by_name(const char *name, uint namelen); const char *ha_get_storage_engine(enum db_type db_type); handler *get_new_handler(TABLE *table, enum db_type db_type); diff --git a/sql/item.cc b/sql/item.cc index 763ab84582d..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); @@ -1775,12 +2164,13 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) if (select_ref != not_found_item && !ambiguous_fields) { DBUG_ASSERT(*select_ref); - if (! (*select_ref)->fixed) + if (!select->ref_pointer_array[counter]) { my_error(ER_ILLEGAL_REFERENCE, MYF(0), ref->name, "forward reference in item list"); return NULL; } + DBUG_ASSERT((*select_ref)->fixed); return (select->ref_pointer_array + counter); } if (group_by_ref) @@ -1930,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); @@ -1994,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); @@ -2015,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)); } } } @@ -2088,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; @@ -2262,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; + }; } @@ -2317,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); @@ -2452,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 */ @@ -2491,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(); @@ -2532,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(); } @@ -2543,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; @@ -2565,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) @@ -2575,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) { @@ -2638,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; @@ -2727,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))) @@ -2817,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. @@ -2887,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. */ { @@ -2917,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); @@ -3068,6 +3516,10 @@ void Item_ref::set_properties() maybe_null= (*ref)->maybe_null; decimals= (*ref)->decimals; collation.set((*ref)->collation); + /* + We have to remember if we refer to a sum function, to ensure that + split_sum_func() doesn't try to change the reference. + */ with_sum_func= (*ref)->with_sum_func; if ((*ref)->type() == FIELD_ITEM) alias_name_used= ((Item_ident *) (*ref))->alias_name_used; @@ -3140,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); @@ -3151,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); @@ -3375,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; } @@ -3389,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; @@ -3402,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); @@ -3448,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; @@ -3462,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: @@ -3489,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; } @@ -3499,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); @@ -3520,7 +4240,6 @@ void Item_cache_str::store(Item *item) } } - double Item_cache_str::val_real() { DBUG_ASSERT(fixed == 1); @@ -3544,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) { @@ -3632,7 +4361,53 @@ void Item_cache_row::bring_value() } -Item_type_holder::Item_type_holder(THD *thd, Item *item) +/* + Returns field for temporary table dependind on item type + + SYNOPSIS + get_holder_example_field() + thd - thread handler + item - pointer to item + table - empty table object + + NOTE + It is possible to return field for Item_func + items only if field type of this item is + date or time or datetime type. + also see function field_types_to_be_kept() from + field.cc + + RETURN + # - field + 0 - no field +*/ + +Field *get_holder_example_field(THD *thd, Item *item, TABLE *table) +{ + DBUG_ASSERT(table); + + Item_func *tmp_item= 0; + if (item->type() == Item::FIELD_ITEM) + return (((Item_field*) item)->field); + if (item->type() == Item::FUNC_ITEM) + tmp_item= (Item_func *) item; + else if (item->type() == Item::SUM_FUNC_ITEM) + { + Item_sum *item_sum= (Item_sum *) item; + if (item_sum->keep_field_type()) + { + if (item_sum->args[0]->type() == Item::FIELD_ITEM) + return (((Item_field*) item_sum->args[0])->field); + if (item_sum->args[0]->type() == Item::FUNC_ITEM) + tmp_item= (Item_func *) item_sum->args[0]; + } + } + return (tmp_item && field_types_to_be_kept(tmp_item->field_type()) ? + tmp_item->tmp_table_field(table) : 0); +} + + +Item_type_holder::Item_type_holder(THD *thd, Item *item, TABLE *table) :Item(thd, item), item_type(item->result_type()), orig_type(item_type) { @@ -3642,10 +4417,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) It is safe assign pointer on field, because it will be used just after all JOIN::prepare calls and before any SELECT execution */ - if (item->type() == Item::FIELD_ITEM) - field_example= ((Item_field*) item)->field; - else - field_example= 0; + field_example= get_holder_example_field(thd, item, table); max_length= real_length(item); maybe_null= item->maybe_null; collation.set(item->collation); @@ -3653,18 +4425,18 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) /* - 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. @@ -3682,28 +4454,32 @@ 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 || - my_charset_same(from->collation.collation, - to->collation.collation))); + (from->collation.collation == to->collation.collation))); } -bool Item_type_holder::join_types(THD *thd, Item *item) +bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table) { uint32 new_length= real_length(item); bool use_new_field= 0, use_expression_type= 0; Item_result new_result_type= type_convertor[item_type][item->result_type()]; - bool item_is_a_field= item->type() == Item::FIELD_ITEM; - + Field *field= get_holder_example_field(thd, item, table); + bool item_is_a_field= field; /* Check if both items point to fields: in this case we can adjust column types of result table in the union smartly. */ if (field_example && item_is_a_field) { - Field *field= ((Item_field *)item)->field; /* Can 'field_example' field store data of the column? */ if ((use_new_field= (!field->field_cast_compatible(field_example->field_cast_type()) || @@ -3732,6 +4508,9 @@ bool Item_type_holder::join_types(THD *thd, Item *item) 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)) { @@ -3744,7 +4523,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) It is safe to assign a pointer to field here, because it will be used before any table is closed. */ - field_example= ((Item_field*) item)->field; + field_example= field; } old_cs= collation.collation->name; @@ -3759,8 +4538,20 @@ bool Item_type_holder::join_types(THD *thd, Item *item) 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; } @@ -3777,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; @@ -3802,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 6857b4acf82..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; } @@ -1579,14 +1638,15 @@ protected: Item_result orig_type; Field *field_example; public: - Item_type_holder(THD*, Item*); + Item_type_holder(THD*, Item*, TABLE *); Item_result result_type () const { return item_type; } 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 *); + bool join_types(THD *thd, Item *, TABLE *); Field *example() { return field_example; } static uint32 real_length(Item *item); void cleanup() 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 96689bd0ac2..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); @@ -1530,7 +1781,11 @@ void in_string::set(uint pos,Item *item) String *str=((String*) base)+pos; String *res=item->val_str(str); if (res && res != str) + { + if (res->uses_buffer_owned_by(str)) + res->copy(); *str= *res; + } if (!str->charset()) { CHARSET_INFO *cs; @@ -1608,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) { - switch (item->result_type()) { + 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 (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; @@ -1677,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; @@ -1748,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; @@ -1803,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; @@ -1824,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; } @@ -1999,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 @@ -2025,6 +2345,35 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) } +/* + Move SUM items out from item tree and replace with reference + + SYNOPSIS + split_sum_func() + thd Thread handler + ref_pointer_array Pointer to array of reference fields + fields All fields in select + + NOTES + This function is run on all expression (SELECT list, WHERE, HAVING etc) + that have or refer (HAVING) to a SUM expression. + + The split is done to get an unique item for each SUM function + so that we can easily find and calculate them. + (Calculation done by update_sum_func() and copy_sum_funcs() in + sql_select.cc) + + All found SUM items are added FIRST in the fields list and + we replace the item with a reference. + + We also replace all functions without side effects (like RAND() or UDF's) + that uses columns as arguments. + For functions with side effects, we just remember any fields referred + by the function to ensure that we get a copy of the field value for the + first accepted row. This ensures that we can do things like + SELECT a*SUM(b) FROM t1 WHERE a=1 +*/ + void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields) { @@ -2034,16 +2383,27 @@ void Item_cond::split_sum_func(THD *thd, Item **ref_pointer_array, const_item_cache=1; while ((item=li++)) { - if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) + /* with_sum_func is set for items that contains a SUM expression */ + if (item->type() != SUM_FUNC_ITEM && + (item->with_sum_func || + (item->used_tables() & PSEUDO_TABLE_BITS))) item->split_sum_func(thd, ref_pointer_array, fields); - else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) + else if (item->type() == SUM_FUNC_ITEM || + (item->used_tables() && item->type() != REF_ITEM)) { + /* + Replace item with a reference so that we can easily calculate + it (in case of sum functions) or copy it (in case of fields) + + The test above is to ensure we don't do a reference for things + that are constants or are not yet calculated as in: + SELECT RAND() as r1, SUM(a) as r2 FROM t1 HAVING r1 > 1 AND r2 > 0 + */ Item **ref= li.ref(); uint el= fields.elements; 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(); @@ -2112,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 @@ -2137,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 @@ -2155,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; @@ -2394,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; @@ -2768,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 @@ -2946,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 @@ -2977,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 @@ -3114,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 a156322e13c..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; }; @@ -215,7 +217,7 @@ class Item_bool_rowready_func2 :public Item_bool_func2 public: Item_bool_rowready_func2(Item *a, Item *b) :Item_bool_func2(a, b) { - allowed_arg_cols= a->cols(); + allowed_arg_cols= 0; // Fetch this value from first argument } Item *neg_transformer(THD *thd); virtual Item *negated_item(); @@ -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,37 +423,67 @@ 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) { allowed_arg_cols= a->cols(); } + :Item_int_func(a),row(a),intervals(0) + { + allowed_arg_cols= 0; // Fetch this value from first argument + } longlong val_int(); void fix_length_and_dec(); const char *func_name() const { return "interval"; } }; -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; } }; @@ -465,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) { @@ -488,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"; } @@ -497,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; @@ -526,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) { @@ -543,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; } @@ -583,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); @@ -610,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 */ @@ -624,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) { @@ -645,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): @@ -657,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 */ @@ -711,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; @@ -780,7 +820,7 @@ class Item_func_in :public Item_int_func Item_func_in(List<Item> &list) :Item_int_func(list), array(0), in_item(0), have_null(0) { - allowed_arg_cols= args[0]->cols(); + allowed_arg_cols= 0; // Fetch this value from first argument } longlong val_int(); void fix_length_and_dec(); @@ -993,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: @@ -1023,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: @@ -1041,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 @@ -1172,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 */ @@ -1189,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 434a4741cf3..a4c1110da32 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); } @@ -308,10 +313,23 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) We can't yet set item to *arg as fix_fields may change *arg We shouldn't call fix_fields() twice, so check 'fixed' field first */ - if ((!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg)) || - (*arg)->check_cols(allowed_arg_cols)) + if ((!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg))) return TRUE; /* purecov: inspected */ item= *arg; + + if (allowed_arg_cols) + { + if (item->check_cols(allowed_arg_cols)) + return 1; + } + else + { + /* we have to fetch allowed_arg_cols from first argument */ + DBUG_ASSERT(arg == args); // it is first argument + allowed_arg_cols= item->cols(); + DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more + } + if (item->maybe_null) maybe_null=1; @@ -343,6 +361,7 @@ bool Item_func::walk (Item_processor processor, byte *argument) } + /* Transform an Item_func object with a transformer callback function @@ -389,15 +408,17 @@ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, for (arg= args, arg_end= args+arg_count; arg != arg_end ; arg++) { Item *item=* arg; - if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) + if (item->type() != SUM_FUNC_ITEM && + (item->with_sum_func || + (item->used_tables() & PSEUDO_TABLE_BITS))) item->split_sum_func(thd, ref_pointer_array, fields); - else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) + else if (item->type() == SUM_FUNC_ITEM || + (item->used_tables() && item->type() != REF_ITEM)) { uint el= fields.elements; 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); } } @@ -502,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) { @@ -523,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(decimals,args[i]->decimals); - max_length=float_length(decimals); + 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++) + { + 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; @@ -585,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; +} + + +/* + 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; } -String *Item_num_op::val_str(String *str) + +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); @@ -642,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); } @@ -679,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; +} + + +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::val_real() +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; } -double Item_func_div::val_real() +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::real_op() { DBUG_ASSERT(fixed == 1); double value= args[0]->val_real(); @@ -741,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; } @@ -791,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(); @@ -814,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; +} + - if (args[0]->result_type() == INT_RESULT) +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; +} + + +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 @@ -886,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; } @@ -1122,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; /* @@ -1184,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) { @@ -1274,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()); @@ -1302,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(); @@ -1340,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; } @@ -1393,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); @@ -1516,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(); @@ -1759,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 @@ -1775,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); @@ -1786,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))) || @@ -1886,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)) @@ -1913,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; } @@ -1958,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() { @@ -2004,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() @@ -2364,20 +3086,17 @@ longlong Item_func_release_lock::val_int() longlong Item_func_last_insert_id::val_int() { + THD *thd= current_thd; DBUG_ASSERT(fixed == 1); if (arg_count) { - longlong value=args[0]->val_int(); - current_thd->insert_id(value); - null_value=args[0]->null_value; - return value; - } - else - { - Item *it= get_system_var(current_thd, OPT_SESSION, "last_insert_id", 14, - "last_insert_id()"); - return it->val_int(); + longlong value= args[0]->val_int(); + thd->insert_id(value); + null_value= args[0]->null_value; + return value; // Avoid side effect of insert_id() } + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return thd->insert_id(); } /* This function is just used to test speed of different functions */ @@ -2403,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; } @@ -2569,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); @@ -2584,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; @@ -2594,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: @@ -2616,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; @@ -2644,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 @@ -2654,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. @@ -2690,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; } @@ -2711,7 +3479,7 @@ Item_func_set_user_var::check() the value method used by the user RETURN - 0 Ok + 0 OK 1 EOM Error */ @@ -2748,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; } @@ -2763,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() @@ -2783,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); @@ -2819,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); } @@ -2850,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 */ @@ -2874,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(); @@ -2906,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; @@ -2968,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; } } @@ -3129,9 +3932,7 @@ void Item_func_match::init_search(bool no_order) if (join_key && !no_order) flags|=FT_SORTED; - ft_handler=table->file->ft_init_ext(flags, key, - (byte*) ft_tmp->ptr(), - ft_tmp->length()); + ft_handler=table->file->ft_init_ext(flags, key, ft_tmp); if (join_key) table->file->ft_handler=ft_handler; @@ -3173,12 +3974,12 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) } /* Check that all columns come from the same table. - We've already checked that columns in MATCH are fields so + We've already checked that columns in MATCH are fields so PARAM_TABLE_BIT can only appear from AGAINST argument. */ if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables()) key=NO_SUCH_KEY; - + if (key == NO_SUCH_KEY && !(flags & FT_BOOL)) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); @@ -3527,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 ` @@ -3556,13 +4357,18 @@ Item_func_sp::execute(Item **itp) #endif if (! m_sp) - m_sp= sp_find_function(thd, m_name); + m_sp= sp_find_function(thd, m_name, TRUE); // cache only if (! m_sp) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); DBUG_RETURN(-1); } +#ifndef EMBEDDED_LIBRARY + my_bool nsok= thd->net.no_send_ok; + thd->net.no_send_ok= TRUE; +#endif + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_procedure_access(thd, EXECUTE_ACL, m_sp->m_db.str, m_sp->m_name.str, 0)) @@ -3578,7 +4384,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. */ @@ -3588,6 +4394,9 @@ Item_func_sp::execute(Item **itp) sp_restore_security_context(thd, m_sp, &save_ctx); #endif +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif DBUG_RETURN(res); } @@ -3598,7 +4407,7 @@ Item_func_sp::field_type() const DBUG_ENTER("Item_func_sp::field_type"); if (! m_sp) - m_sp= sp_find_function(current_thd, m_name); + m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if (m_sp) { DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns)); @@ -3616,7 +4425,7 @@ Item_func_sp::result_type() const DBUG_PRINT("info", ("m_sp = %p", m_sp)); if (! m_sp) - m_sp= sp_find_function(current_thd, m_name); + m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if (m_sp) { DBUG_RETURN(m_sp->result()); @@ -3631,7 +4440,7 @@ Item_func_sp::fix_length_and_dec() DBUG_ENTER("Item_func_sp::fix_length_and_dec"); if (! m_sp) - m_sp= sp_find_function(current_thd, m_name); + m_sp= sp_find_function(current_thd, m_name, TRUE); // cache only if (! m_sp) { my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); @@ -3651,9 +4460,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 fb8d77d5b83..20f70fce575 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -32,6 +32,10 @@ class Item_func :public Item_result_field { protected: Item **args, *tmp_arg[2]; + /* + Allowed numbers of columns in result (usually 1, which means scalar value) + 0 means get this number from first argument + */ uint allowed_arg_cols; public: uint arg_count; @@ -129,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)); @@ -144,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, @@ -168,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) {} + + void fix_num_length_and_dec(); + void find_num_type(); +}; -class Item_num_op :public Item_func + +/* 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); @@ -253,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(); }; @@ -278,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(); }; @@ -287,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; } }; @@ -310,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 { @@ -487,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(); }; @@ -550,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); } }; @@ -565,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; } @@ -751,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 @@ -805,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(); } @@ -821,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(); }; @@ -848,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(); }; @@ -872,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: @@ -930,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; @@ -941,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; @@ -957,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(); @@ -982,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); @@ -1091,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 }; @@ -1157,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 62f31186b09..08c682afa85 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -84,21 +84,25 @@ bool Item_row::fix_fields(THD *thd, TABLE_LIST *tabl, Item **ref) return FALSE; } + void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields) { Item **arg, **arg_end; for (arg= items, arg_end= items+arg_count; arg != arg_end ; arg++) { - if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM) - (*arg)->split_sum_func(thd, ref_pointer_array, fields); - else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM) + Item *item= *arg; + if (item->type() != SUM_FUNC_ITEM && + (item->with_sum_func || + (item->used_tables() & PSEUDO_TABLE_BITS))) + item->split_sum_func(thd, ref_pointer_array, fields); + else if (item->type() == SUM_FUNC_ITEM || + (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 f9843692b7b..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; @@ -1776,15 +1778,17 @@ String *Item_func_elt::val_str(String *str) void Item_func_make_set::split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields) { - if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) + if (item->type() != SUM_FUNC_ITEM && + (item->with_sum_func || + (item->used_tables() & PSEUDO_TABLE_BITS))) item->split_sum_func(thd, ref_pointer_array, fields); - else if (item->used_tables() || item->type() == SUM_FUNC_ITEM) + else if (item->type() == SUM_FUNC_ITEM || + (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); @@ -1800,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(); @@ -2189,6 +2193,7 @@ String *Item_func_conv::val_str(String *str) return 0; } null_value=0; + unsigned_flag= !(from_base < 0); if (from_base < 0) dec= my_strntoll(res->charset(),res->ptr(),res->length(),-from_base,&endptr,&err); else @@ -2643,18 +2648,13 @@ String *Item_func_quote::val_str(String *str) for (from= (char*) arg->ptr(), end= from + arg_length; from < end; from++) new_length+= get_esc_bit(escmask, (uchar) *from); - /* - We have to use realloc() instead of alloc() as we want to keep the - old result in arg - */ - if (arg->realloc(new_length)) + if (tmp_value.alloc(new_length)) goto null; /* - As 'arg' and 'str' may be the same string, we must replace characters - from the end to the beginning + We replace characters from the end to the beginning */ - to= (char*) arg->ptr() + new_length - 1; + to= (char*) tmp_value.ptr() + new_length - 1; *to--= '\''; for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--) { @@ -2682,10 +2682,10 @@ String *Item_func_quote::val_str(String *str) } } *to= '\''; - arg->length(new_length); - str->set_charset(collation.collation); + tmp_value.length(new_length); + tmp_value.set_charset(collation.collation); null_value= 0; - return arg; + return &tmp_value; null: null_value= 1; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index e322e5616a1..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)); } @@ -596,6 +595,7 @@ public: class Item_func_quote :public Item_str_func { + String tmp_value; public: Item_func_quote(Item *a) :Item_str_func(a) {} const char *func_name() const { return "quote"; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d6d58adaf7c..8d392232f02 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -53,7 +53,7 @@ void Item_subselect::init(st_select_lex *select_lex, { DBUG_ENTER("Item_subselect::init"); - DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex)); + DBUG_PRINT("enter", ("select_lex: 0x%x", (ulong) select_lex)); unit= select_lex->master_unit(); if (unit->item) @@ -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; @@ -995,10 +1070,14 @@ Item_in_subselect::row_value_transformer(JOIN *join) List_iterator_fast<Item> li(select_lex->item_list); for (uint i= 0; i < n; i++) { + DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); + if (select_lex->ref_pointer_array[i]-> + 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), @@ -1019,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; @@ -1034,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; } @@ -1117,6 +1204,7 @@ void subselect_single_select_engine::cleanup() DBUG_ENTER("subselect_single_select_engine::cleanup"); prepared= optimized= executed= 0; join= 0; + result->cleanup(); DBUG_VOID_RETURN; } @@ -1125,6 +1213,7 @@ void subselect_union_engine::cleanup() { DBUG_ENTER("subselect_union_engine::cleanup"); unit->reinit_exec_mechanism(); + result->cleanup(); DBUG_VOID_RETURN; } @@ -1132,6 +1221,10 @@ void subselect_union_engine::cleanup() void subselect_uniquesubquery_engine::cleanup() { DBUG_ENTER("subselect_uniquesubquery_engine::cleanup"); + /* + subselect_uniquesubquery_engine have not 'result' assigbed, so we do not + cleanup() it + */ DBUG_VOID_RETURN; } @@ -1415,13 +1508,15 @@ int subselect_indexsubquery_engine::exec() uint subselect_single_select_engine::cols() { - return select_lex->item_list.elements; + DBUG_ASSERT(select_lex->join); // should be called after fix_fields() + return select_lex->join->fields_list.elements; } uint subselect_union_engine::cols() { - return unit->first_select()->item_list.elements; + DBUG_ASSERT(unit->is_prepared()); // should be called after fix_fields() + return unit->types.elements; } @@ -1521,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); @@ -1533,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 @@ -1555,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 @@ -1578,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 @@ -1599,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 @@ -1615,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 @@ -1636,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.cc b/sql/log.cc index 6c97581d144..7d6854b6fb4 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1346,7 +1346,7 @@ bool MYSQL_LOG::write(Log_event* event_info) if (thd) { - /* NOTE: CHARSET AND TZ REPL WILL BE REWRITTEN SHORTLY */ +#if MYSQL_VERSION_ID < 50003 /* To make replication of charsets working in 4.1 we are writing values of charset related variables before every statement in the binlog, @@ -1375,9 +1375,15 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u", if (e.write(file)) goto err; } +#endif /* We use the same ONE_SHOT trick for making replication of time zones working in 4.1. Again in 5.0 we have better means for doing this. + + TODO: we should do like we now do with charsets (no more ONE_SHOT; + logging in each event in a compact format). Dmitri says we can do: + if (time_zone_used) write the timezone to binlog (in a format to be + defined). */ if (thd->time_zone_used && thd->variables.time_zone != global_system_variables.time_zone) diff --git a/sql/log_event.cc b/sql/log_event.cc index 5ee034d785e..d2a0e8642f9 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -953,7 +953,13 @@ void Query_log_event::pack_info(Protocol *protocol) bool Query_log_event::write(IO_CACHE* file) { - uchar buf[QUERY_HEADER_LEN+1+4+1+8+1+1+FN_REFLEN+5], *start, *start_of_status; + uchar buf[QUERY_HEADER_LEN+ + 1+4+ // code of flags2 and flags2 + 1+8+ // code of sql_mode and sql_mode + 1+1+FN_REFLEN+ // code of catalog and catalog length and catalog + 1+4+ // code of autoinc and the 2 autoinc variables + 1+6 // code of charset and charset + ], *start, *start_of_status; ulong event_length; if (!query) @@ -1048,9 +1054,15 @@ bool Query_log_event::write(IO_CACHE* file) int2store(start+2, auto_increment_offset); start+= 4; } + if (charset_inited) + { + *(start++)= Q_CHARSET_CODE; + memcpy(start, charset, 6); + start+= 6; + } /* Here there could be code like - if (command-line-option-which-says-"log_this_variable") + if (command-line-option-which-says-"log_this_variable" && inited) { *(start++)= Q_THIS_VARIABLE_CODE; int4store(start, this_variable); @@ -1095,7 +1107,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, thread_id(thd_arg->thread_id), /* save the original thread id; we already know the server id */ slave_proxy_id(thd_arg->variables.pseudo_thread_id), - flags2_inited(1), sql_mode_inited(1), flags2(0), + flags2_inited(1), sql_mode_inited(1), charset_inited(1), sql_mode(thd_arg->variables.sql_mode), auto_increment_increment(thd_arg->variables.auto_increment_increment), auto_increment_offset(thd_arg->variables.auto_increment_offset) @@ -1104,7 +1116,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, time(&end_time); exec_time = (ulong) (end_time - thd->start_time); catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; - status_vars_len= 1+4+1+8+1+1+catalog_len+1; + /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; /* If we don't use flags2 for anything else than options contained in @@ -1114,7 +1126,12 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, we will probably want to reclaim the 29 bits. So we need the &. */ flags2= thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG; - + DBUG_ASSERT(thd->variables.character_set_client->number < 256*256); + DBUG_ASSERT(thd->variables.collation_connection->number < 256*256); + DBUG_ASSERT(thd->variables.collation_server->number < 256*256); + int2store(charset, thd_arg->variables.character_set_client->number); + int2store(charset+2, thd_arg->variables.collation_connection->number); + int2store(charset+4, thd_arg->variables.collation_server->number); DBUG_PRINT("info",("Query_log_event has flags2=%lu sql_mode=%lu",flags2,sql_mode)); } #endif /* MYSQL_CLIENT */ @@ -1129,7 +1146,8 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, const Format_description_log_event *description_event) :Log_event(buf, description_event), data_buf(0), query(NullS), catalog(NullS), db(NullS), catalog_len(0), status_vars_len(0), - flags2_inited(0), sql_mode_inited(0) + flags2_inited(0), sql_mode_inited(0), charset_inited(0), + auto_increment_increment(1), auto_increment_offset(1) { ulong data_len; uint32 tmp; @@ -1156,8 +1174,6 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET); db_len = (uint)buf[Q_DB_LEN_OFFSET]; error_code = uint2korr(buf + Q_ERR_CODE_OFFSET); - /* If auto_increment is not set by query_event, they should not be used */ - auto_increment_increment= auto_increment_offset= 1; /* 5.0 format starts here. @@ -1216,6 +1232,13 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, auto_increment_offset= uint2korr(pos+2); pos+= 4; break; + case Q_CHARSET_CODE: + { + charset_inited= 1; + memcpy(charset, pos, 6); + pos+= 6; + break; + } default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -1348,6 +1371,27 @@ void Query_log_event::print(FILE* file, bool short_form, last_event_info->auto_increment_offset= auto_increment_offset; } + if (likely(charset_inited)) + { + if (unlikely(!last_event_info->charset_inited)) /* first Query event */ + { + last_event_info->charset_inited= 1; + last_event_info->charset[0]= ~charset[0]; // force a difference to force write + } + if (unlikely(bcmp(last_event_info->charset, charset, 6))) + { + fprintf(file,"SET " + "@@session.character_set_client=%d," + "@@session.collation_connection=%d," + "@@session.collation_server=%d" + ";\n", + uint2korr(charset), + uint2korr(charset+2), + uint2korr(charset+4)); + memcpy(last_event_info->charset, charset, 6); + } + } + my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)); fputs(";\n", file); } @@ -1400,34 +1444,64 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) thd->variables.pseudo_thread_id= thread_id; // for temp tables mysql_log.write(thd,COM_QUERY,"%s",thd->query); DBUG_PRINT("query",("%s",thd->query)); - - if (flags2_inited) - /* - all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must - take their value from flags2. - */ - thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG); - /* - else, we are in a 3.23/4.0 binlog; we previously received a - Rotate_log_event which reset thd->options and sql_mode, so nothing to do. - */ - - /* - We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a - slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not - force us to ignore the dir too. Imagine you are a ring of machines, and - one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE - on this machine; you don't want it to propagate elsewhere (you don't want - all slaves to start ignoring the dirs). - */ - if (sql_mode_inited) - thd->variables.sql_mode= - (ulong) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) | - (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE)); if (ignored_error_code((expected_error= error_code)) || !check_expected_error(thd,rli,expected_error)) + { + if (flags2_inited) + /* + all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must + take their value from flags2. + */ + thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG); + /* + else, we are in a 3.23/4.0 binlog; we previously received a + Rotate_log_event which reset thd->options and sql_mode etc, so nothing to do. + */ + /* + We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a + slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not + force us to ignore the dir too. Imagine you are a ring of machines, and + one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE + on this machine; you don't want it to propagate elsewhere (you don't want + all slaves to start ignoring the dirs). + */ + if (sql_mode_inited) + thd->variables.sql_mode= + (ulong) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) | + (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE)); + if (charset_inited) + { + if (rli->cached_charset_compare(charset)) + { + /* Verify that we support the charsets found in the event. */ + if (!(thd->variables.character_set_client= + get_charset(uint2korr(charset), MYF(MY_WME))) || + !(thd->variables.collation_connection= + get_charset(uint2korr(charset+2), MYF(MY_WME))) || + !(thd->variables.collation_server= + get_charset(uint2korr(charset+4), MYF(MY_WME)))) + { + /* + We updated the thd->variables with nonsensical values (0), and the + thread is not guaranteed to terminate now (as it may be configured + to ignore EE_UNKNOWN_CHARSET);if we're going to execute a next + statement we'll have a new charset info with it, so no problem to + have stored 0 in thd->variables. But we invalidate cached + charset to force a check next time (otherwise if next time + charset is unknown again we won't detect it). + */ + rli->cached_charset_invalidate(); + goto compare_errors; + } + thd->update_charset(); // for the charset change to take effect + } + } + + /* Execute the query (note that we bypass dispatch_command()) */ mysql_parse(thd, thd->query, q_len); + + } else { /* @@ -1452,6 +1526,8 @@ START SLAVE; . Query: '%s'", expected_error, thd->query); } goto end; } + +compare_errors: /* If we expected a non-zero error code, and we don't get the same error @@ -1666,12 +1742,7 @@ bool Start_log_event_v3::write(IO_CACHE* file) int Start_log_event_v3::exec_event(struct st_relay_log_info* rli) { DBUG_ENTER("Start_log_event_v3::exec_event"); - /* - If the I/O thread has not started, mi->old_format is BINLOG_FORMAT_CURRENT - (that's what the MASTER_INFO constructor does), so the test below is not - perfect at all. - */ - switch (rli->relay_log.description_event_for_exec->binlog_version) + switch (binlog_version) { case 3: case 4: @@ -2789,14 +2860,24 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) rli->group_master_log_name, (ulong) rli->group_master_log_pos)); /* - Reset thd->options and sql_mode, because this could be the signal of a - master's downgrade from 5.0 to 4.0. + Reset thd->options and sql_mode etc, because this could be the signal of + a master's downgrade from 5.0 to 4.0. However, no need to reset description_event_for_exec: indeed, if the next master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next master is 4.0 then the events are in the slave's format (conversion). */ set_slave_thread_options(thd); thd->variables.sql_mode= global_system_variables.sql_mode; + thd->variables.auto_increment_increment= + thd->variables.auto_increment_offset= 1; + thd->variables.character_set_client= + global_system_variables.character_set_client; + thd->variables.collation_connection= + global_system_variables.collation_connection; + thd->variables.collation_server= + global_system_variables.collation_server; + thd->update_charset(); + rli->cached_charset_invalidate(); } pthread_mutex_unlock(&rli->data_lock); pthread_cond_broadcast(&rli->data_cond); @@ -3018,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)); @@ -3086,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; @@ -3101,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: @@ -3111,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; @@ -3119,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 */ @@ -3166,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: { /* @@ -3242,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; @@ -3252,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/log_event.h b/sql/log_event.h index 64bb9d502e9..7f04582a32d 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -232,6 +232,7 @@ struct sql_ex_info #define Q_SQL_MODE_CODE 1 #define Q_CATALOG_CODE 2 #define Q_AUTO_INCREMENT 3 +#define Q_CHARSET_CODE 4 /* Intvar event post-header */ @@ -401,11 +402,19 @@ typedef struct st_last_event_info bool sql_mode_inited; ulong sql_mode; /* must be same as THD.variables.sql_mode */ ulong auto_increment_increment, auto_increment_offset; + bool charset_inited; + char charset[6]; // 3 variables, each of them storable in 2 bytes st_last_event_info() - :flags2_inited(0), flags2(0), sql_mode_inited(0), sql_mode(0), - auto_increment_increment(1),auto_increment_offset(1) + :flags2_inited(0), sql_mode_inited(0), + auto_increment_increment(1),auto_increment_offset(1), charset_inited(0) { - db[0]= 0; /* initially, the db is unknown */ + /* + Currently we only use static LAST_EVENT_INFO objects, so zeroed at + program's startup, but these explicit bzero() is for the day someone + creates dynamic instances. + */ + bzero(db, sizeof(db)); + bzero(charset, sizeof(charset)); } } LAST_EVENT_INFO; #endif @@ -634,7 +643,7 @@ public: status_vars on disk is a sequence of pairs (code, value) where 'code' means 'sql_mode', 'affected' etc. Sometimes 'value' must be a short string, so its first byte is its length. For now the order of status vars is: - flags2 - sql_mode - catalog. + flags2 - sql_mode - catalog - autoinc - charset We should add the same thing to Load_log_event, but in fact LOAD DATA INFILE is going to be logged with a new type of event (logging of the plain text query), so Load_log_event would be frozen, so no need. The @@ -655,11 +664,13 @@ public: */ bool flags2_inited; bool sql_mode_inited; + bool charset_inited; uint32 flags2; /* In connections sql_mode is 32 bits now but will be 64 bits soon */ ulong sql_mode; ulong auto_increment_increment, auto_increment_offset; + char charset[6]; #ifndef MYSQL_CLIENT diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc new file mode 100644 index 00000000000..027b33f1d1c --- /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-from) != length && !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 e725cd177c3..fdcf061ab7a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -285,6 +285,12 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2) #define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2) #define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2) +/* + Replication uses 8 bytes to store SQL_MODE in the binary log. The day you + use strictly more than 64 bits by adding one more define above, you should + contact the replication team because the replication code should then be + updated (to store more bytes on disk). +*/ #define RAID_BLOCK_SIZE 1024 @@ -337,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, @@ -401,9 +409,13 @@ inline THD *_current_thd(void) } #define current_thd _current_thd() +typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, + uint key_length, + ulonglong *engine_data); #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" @@ -412,6 +424,7 @@ inline THD *_current_thd(void) #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); @@ -715,7 +728,6 @@ bool mysql_do(THD *thd, List<Item> &values); /* sql_analyse.h */ bool append_escaped(String *to_str, String *from_str); -bool append_escaped(String *to_str, char *from, uint from_len); /* sql_show.cc */ bool mysqld_show_open_tables(THD *thd,const char *wild); @@ -1324,7 +1336,7 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr) table->null_row= 0; table->status= STATUS_NO_RECORD; table->keys_in_use_for_query= table->s->keys_in_use; - table->maybe_null= test(table->outer_join= table_list->outer_join); + table->maybe_null= table_list->outer_join; table->tablenr= tablenr; table->map= (table_map) 1 << tablenr; table->force_index= table_list->force_index; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e4df359d35c..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 */ @@ -291,6 +292,7 @@ my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam, opt_ndbcluster; #ifdef HAVE_NDBCLUSTER_DB const char *opt_ndbcluster_connectstring= 0; my_bool opt_ndb_shm, opt_ndb_optimized_node_selection; +ulong opt_ndb_cache_check_time= 0; #endif my_bool opt_readonly, use_temp_pool, relay_log_purge; my_bool opt_sync_bdb_logs, opt_sync_frm; @@ -371,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; @@ -2383,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; @@ -3375,39 +3379,34 @@ int main(int argc, char **argv) return 0; } } - else if (argc >= 4) + else if (argc == 4 || argc == 5) { - const char *defaults_file = "--defaults-file"; - const char *service = "--local-service"; - char extra_opt[FN_REFLEN] = ""; + /* + This may seem strange, because we handle --local-service while + preserving 4.1's behavior of allowing any one other argument that is + passed to the service on startup. (The assumption is that this is + --defaults-file=file, but that was not enforced in 4.1, so we don't + enforce it here.) + */ + char *extra_opt= NULL; char *account_name = NULL; - char *option; int index; for (index = 3; index < argc; index++) { - option= argv[index]; - /* - Install an optional service with optional config file - mysqld --install-manual mysqldopt --defaults-file=c:\miguel\my.ini - */ - if (strncmp(option, defaults_file, strlen(defaults_file)) == 0) + if (strncmp(argv[index], "--local-service", 15) == 0) { - strmov(extra_opt, option); + account_name=(char*)malloc(27); + strmov(account_name, "NT AUTHORITY\\LocalService\0"); } else - /* - Install an optional service as local service - mysqld --install-manual mysqldopt --local-service - */ - if (strncmp(option, service, strlen(service)) == 0) { - account_name=(char*)malloc(27); - strmov(account_name, "NT AUTHORITY\\LocalService\0"); + extra_opt= argv[index]; } } - if (!default_service_handling(argv, argv[2], argv[2], file_path, extra_opt, account_name)) - return 0; + if (argc != 5 || account_name) + if (!default_service_handling(argv, argv[2], argv[2], file_path, extra_opt, account_name)) + return 0; } else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME)) { @@ -4174,7 +4173,7 @@ enum options_mysqld OPT_INNODB, OPT_ISAM, OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, OPT_NDB_USE_EXACT_COUNT, OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ, - OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, + OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME, OPT_SKIP_SAFEMALLOC, OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE, OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, @@ -4251,7 +4250,11 @@ enum options_mysqld OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE, OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE, - OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_BDB_NOSYNC, + OPT_SYNC_FRM, OPT_SYNC_BINLOG, + OPT_SYNC_REPLICATION, + OPT_SYNC_REPLICATION_SLAVE_ID, + OPT_SYNC_REPLICATION_TIMEOUT, + OPT_BDB_NOSYNC, OPT_ENABLE_SHARED_MEMORY, OPT_SHARED_MEMORY_BASE_NAME, OPT_OLD_PASSWORDS, @@ -4702,6 +4705,10 @@ Disable with --skip-ndbcluster (will save memory).", (gptr*) &opt_ndb_optimized_node_selection, (gptr*) &opt_ndb_optimized_node_selection, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0}, + { "ndb_cache_check_time", OPT_NDB_CACHE_CHECK_TIME, + "A dedicated thread is created to update cached commit count value at the given interval.", + (gptr*) &opt_ndb_cache_check_time, (gptr*) &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG, + 0, 0, LONG_TIMEOUT, 0, 1, 0}, #endif {"new", 'n', "Use very new possible 'unsafe' functions.", (gptr*) &global_system_variables.new_mode, @@ -5447,6 +5454,23 @@ The minimum value for this variable is 4096.", (gptr*) &sync_binlog_period, (gptr*) &sync_binlog_period, 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1, 0}, +#ifdef DOES_NOTHING_YET + {"sync-replication", OPT_SYNC_REPLICATION, + "Enable synchronous replication", + (gptr*) &global_system_variables.sync_replication, + (gptr*) &global_system_variables.sync_replication, + 0, GET_ULONG, REQUIRED_ARG, 0, 0, 1, 0, 1, 0}, + {"sync-replication-slave-id", OPT_SYNC_REPLICATION_SLAVE_ID, + "Synchronous replication is wished for this slave", + (gptr*) &global_system_variables.sync_replication_slave_id, + (gptr*) &global_system_variables.sync_replication_slave_id, + 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1, 0}, + {"sync-replication-timeout", OPT_SYNC_REPLICATION_TIMEOUT, + "Synchronous replication timeout", + (gptr*) &global_system_variables.sync_replication_timeout, + (gptr*) &global_system_variables.sync_replication_timeout, + 0, GET_ULONG, REQUIRED_ARG, 10, 0, ~0L, 0, 1, 0}, +#endif {"sync-frm", OPT_SYNC_FRM, "Sync .frm to disk on create. Enabled by default", (gptr*) &opt_sync_frm, (gptr*) &opt_sync_frm, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e5799bfd509..ceb9f97bbbc 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3611,7 +3611,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, if (!value) // IS NULL or IS NOT NULL { - if (field->table->outer_join) // Can't use a key on this + if (field->table->maybe_null) // Can't use a key on this DBUG_RETURN(0); if (!maybe_null) // Not null field DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 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 541bcf0ff7d..6fb44ae3ea3 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -358,6 +358,14 @@ sys_var_thd_storage_engine sys_storage_engine("storage_engine", &SV::table_type); #ifdef HAVE_REPLICATION sys_var_sync_binlog_period sys_sync_binlog_period("sync_binlog", &sync_binlog_period); +sys_var_thd_ulong sys_sync_replication("sync_replication", + &SV::sync_replication); +sys_var_thd_ulong sys_sync_replication_slave_id( + "sync_replication_slave_id", + &SV::sync_replication_slave_id); +sys_var_thd_ulong sys_sync_replication_timeout( + "sync_replication_timeout", + &SV::sync_replication_timeout); #endif sys_var_bool_ptr sys_sync_frm("sync_frm", &opt_sync_frm); sys_var_long_ptr sys_table_cache_size("table_cache", @@ -405,6 +413,7 @@ sys_var_thd_bool sys_ndb_use_exact_count("ndb_use_exact_count", &SV::ndb_use_exact_count); sys_var_thd_bool sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions); +sys_var_long_ptr sys_ndb_cache_check_time("ndb_cache_check_time", &ndb_cache_check_time); #endif /* Time/date/datetime formats */ @@ -647,6 +656,9 @@ sys_var *sys_variables[]= &sys_storage_engine, #ifdef HAVE_REPLICATION &sys_sync_binlog_period, + &sys_sync_replication, + &sys_sync_replication_slave_id, + &sys_sync_replication_timeout, #endif &sys_sync_frm, &sys_table_cache_size, @@ -677,6 +689,7 @@ sys_var *sys_variables[]= &sys_ndb_force_send, &sys_ndb_use_exact_count, &sys_ndb_use_transactions, + &sys_ndb_cache_check_time, #endif &sys_unique_checks, &sys_updatable_views_with_limit, @@ -857,6 +870,7 @@ struct show_var_st init_vars[]= { {sys_ndb_force_send.name, (char*) &sys_ndb_force_send, SHOW_SYS}, {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count, SHOW_SYS}, {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS}, + {sys_ndb_cache_check_time.name,(char*) &sys_ndb_cache_check_time, SHOW_SYS}, #endif {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, {sys_net_read_timeout.name, (char*) &sys_net_read_timeout, SHOW_SYS}, @@ -915,6 +929,9 @@ struct show_var_st init_vars[]= { {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, #ifdef HAVE_REPLICATION {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS}, + {sys_sync_replication.name, (char*) &sys_sync_replication, SHOW_SYS}, + {sys_sync_replication_slave_id.name, (char*) &sys_sync_replication_slave_id,SHOW_SYS}, + {sys_sync_replication_timeout.name, (char*) &sys_sync_replication_timeout,SHOW_SYS}, #endif {sys_sync_frm.name, (char*) &sys_sync_frm, SHOW_SYS}, #ifdef HAVE_TZNAME @@ -1522,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; @@ -1559,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; @@ -1770,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; @@ -1874,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))) { @@ -1908,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))) { @@ -2047,9 +2064,15 @@ void sys_var_character_set_server::set_default(THD *thd, enum_var_type type) } } -#if defined(HAVE_REPLICATION) +#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50003) bool sys_var_character_set_server::check(THD *thd, set_var *var) { + /* + To be perfect we should fail even if we are a 5.0.3 slave, a 4.1 master, + and user wants to change our global character set variables. Because + replicating a 4.1 assumes those are not changed. But that's not easy to + do. + */ if ((var->type == OPT_GLOBAL) && (mysql_bin_log.is_open() || active_mi->slave_running || active_mi->rli.slave_running)) @@ -2154,7 +2177,7 @@ void sys_var_collation_database::set_default(THD *thd, enum_var_type type) } } -#if defined(HAVE_REPLICATION) +#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50003) bool sys_var_collation_server::check(THD *thd, set_var *var) { if ((var->type == OPT_GLOBAL) && @@ -2959,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())) @@ -3098,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/set_var.h b/sql/set_var.h index 8514b518660..3104fd38976 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -564,7 +564,7 @@ class sys_var_character_set_server :public sys_var_character_set public: sys_var_character_set_server(const char *name_arg) : sys_var_character_set(name_arg) {} -#if defined(HAVE_REPLICATION) +#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50003) bool check(THD *thd, set_var *var); #endif void set_default(THD *thd, enum_var_type type); @@ -602,7 +602,7 @@ class sys_var_collation_server :public sys_var_collation { public: sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {} -#if defined(HAVE_REPLICATION) +#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50003) bool check(THD *thd, set_var *var); #endif bool update(THD *thd, set_var *var); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 7826be85679..030949e15d1 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5094,7 +5094,7 @@ ER_SP_BADSELECT 0A000 ER_SP_BADRETURN 42000 eng "RETURN is only allowed in a FUNCTION" ER_SP_BADSTATEMENT 0A000 - eng "Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION" + eng "LOCK and UNLOCK tables are not allowed in stored procedures" ER_UPDATE_LOG_DEPRECATED_IGNORED 42000 eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored" ER_UPDATE_LOG_DEPRECATED_TRANSLATED 42000 diff --git a/sql/slave.cc b/sql/slave.cc index 0c5ebe0744a..f3ab4b21832 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -660,13 +660,14 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, pthread_cond_t* term_cond, volatile uint *slave_running) { + DBUG_ENTER("terminate_slave_thread"); if (term_lock) { pthread_mutex_lock(term_lock); if (!*slave_running) { pthread_mutex_unlock(term_lock); - return ER_SLAVE_NOT_RUNNING; + DBUG_RETURN(ER_SLAVE_NOT_RUNNING); } } DBUG_ASSERT(thd != 0); @@ -678,6 +679,7 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, while (*slave_running) // Should always be true { + DBUG_PRINT("loop", ("killing slave thread")); KICK_SLAVE(thd); /* There is a small chance that slave thread might miss the first @@ -689,7 +691,7 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, } if (term_lock) pthread_mutex_unlock(term_lock); - return 0; + DBUG_RETURN(0); } @@ -1418,13 +1420,20 @@ not always make sense; please check the manual before using it)."; values of these 2 are never used (new connections don't use them). We don't test equality of global collation_database either as it's is going to be deprecated (made read-only) in 4.1 very soon. - We don't do it for <3.23.57 because masters <3.23.50 hang on - SELECT @@unknown_var (BUG#7965 - see changelog of 3.23.50). + The test is only relevant if master < 5.0.3 (we'll test only if it's older + than the 5 branch; < 5.0.3 was alpha...), as >= 5.0.3 master stores + charset info in each binlog event. + We don't do it for 3.23 because masters <3.23.50 hang on + SELECT @@unknown_var (BUG#7965 - see changelog of 3.23.50). So finally we + test only if master is 4.x. */ - if (strncmp(mi->rli.relay_log.description_event_for_queue->server_version, - "3.23.57",7) < 0) + + /* redundant with rest of code but safer against later additions */ + if (*mysql->server_version == '3') goto err; - if (!mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) && + + if ((*mysql->server_version == '4') && + !mysql_real_query(mysql, "SELECT @@GLOBAL.COLLATION_SERVER", 32) && (master_res= mysql_store_result(mysql))) { if ((master_row= mysql_fetch_row(master_res)) && @@ -1447,8 +1456,12 @@ be equal for replication to work"; such check will broke everything for them. (And now everything will work for them because by default both their master and slave will have 'SYSTEM' time zone). + + TODO: when the new replication of timezones is sorted out with Dmitri, + change >= '4' to == '4'. */ - if (!mysql_real_query(mysql, "SELECT @@GLOBAL.TIME_ZONE", 25) && + if ((*mysql->server_version >= '4') && + !mysql_real_query(mysql, "SELECT @@GLOBAL.TIME_ZONE", 25) && (master_res= mysql_store_result(mysql))) { if ((master_row= mysql_fetch_row(master_res)) && @@ -2527,6 +2540,7 @@ st_relay_log_info::st_relay_log_info() bzero((char*) &info_file, sizeof(info_file)); bzero((char*) &cache_buf, sizeof(cache_buf)); + cached_charset_invalidate(); pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); @@ -2755,6 +2769,7 @@ void set_slave_thread_options(THD* thd) { thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | OPTION_AUTO_IS_NULL; + thd->variables.completion_type= 0; } /* @@ -3078,6 +3093,24 @@ bool st_relay_log_info::is_until_satisfied() } +void st_relay_log_info::cached_charset_invalidate() +{ + /* Full of zeroes means uninitialized. */ + bzero(cached_charset, sizeof(cached_charset)); +} + + +bool st_relay_log_info::cached_charset_compare(char *charset) +{ + if (bcmp(cached_charset, charset, sizeof(cached_charset))) + { + memcpy(cached_charset, charset, sizeof(cached_charset)); + return 1; + } + return 0; +} + + static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { /* @@ -3722,6 +3755,8 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions")); pthread_cond_broadcast(&rli->data_cond); rli->ignore_log_space_limit= 0; /* don't need any lock */ + /* we die so won't remember charset - re-update them on next thread start */ + rli->cached_charset_invalidate(); rli->save_temporary_tables = thd->temporary_tables; /* diff --git a/sql/slave.h b/sql/slave.h index e0816fd45a7..598ff0a7845 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -291,6 +291,8 @@ typedef struct st_relay_log_info UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1, UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1 } until_log_names_cmp_result; + + char cached_charset[6]; st_relay_log_info(); ~st_relay_log_info(); @@ -334,6 +336,14 @@ typedef struct st_relay_log_info return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos : group_relay_log_pos); } + /* + Last charset (6 bytes) seen by slave SQL thread is cached here; it helps + the thread save 3 get_charset() per Query_log_event if the charset is not + changing from event to event (common situation). + When the 6 bytes are equal to 0 is used to mean "cache is invalidated". + */ + void cached_charset_invalidate(); + bool cached_charset_compare(char *charset); } RELAY_LOG_INFO; diff --git a/sql/sp.cc b/sql/sp.cc index 84b126e5ecd..46b08c3e847 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -854,13 +854,14 @@ sp_show_status_procedure(THD *thd, const char *wild) ******************************************************************************/ sp_head * -sp_find_function(THD *thd, sp_name *name) +sp_find_function(THD *thd, sp_name *name, bool cache_only) { sp_head *sp; DBUG_ENTER("sp_find_function"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name))) + if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name)) && + !cache_only) { if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) != SP_OK) sp= NULL; @@ -963,7 +964,7 @@ sp_function_exists(THD *thd, sp_name *name) byte * -sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first) +sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first) { LEX_STRING *lsp= (LEX_STRING *)ptr; *plen= lsp->length; @@ -972,37 +973,36 @@ sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first) void -sp_add_fun_to_lex(LEX *lex, sp_name *fun) +sp_add_to_hash(HASH *h, sp_name *fun) { - if (! hash_search(&lex->spfuns, - (byte *)fun->m_qname.str, fun->m_qname.length)) + if (! hash_search(h, (byte *)fun->m_qname.str, fun->m_qname.length)) { LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING)); ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length); ls->length= fun->m_qname.length; - my_hash_insert(&lex->spfuns, (byte *)ls); + my_hash_insert(h, (byte *)ls); } } void -sp_merge_funs(LEX *dst, LEX *src) +sp_merge_hash(HASH *dst, HASH *src) { - for (uint i=0 ; i < src->spfuns.records ; i++) + for (uint i=0 ; i < src->records ; i++) { - LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i); + LEX_STRING *ls= (LEX_STRING *)hash_element(src, i); - if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length)) - my_hash_insert(&dst->spfuns, (byte *)ls); + if (! hash_search(dst, (byte *)ls->str, ls->length)) + my_hash_insert(dst, (byte *)ls); } } int -sp_cache_functions(THD *thd, LEX *lex) +sp_cache_routines(THD *thd, LEX *lex, int type) { - HASH *h= &lex->spfuns; + HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs); int ret= 0; for (uint i=0 ; i < h->records ; i++) @@ -1011,7 +1011,9 @@ sp_cache_functions(THD *thd, LEX *lex) sp_name name(*ls); name.m_qname= *ls; - if (! sp_cache_lookup(&thd->sp_func_cache, &name)) + if (! sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? + &thd->sp_func_cache : &thd->sp_proc_cache), + &name)) { sp_head *sp; LEX *oldlex= thd->lex; @@ -1027,11 +1029,23 @@ sp_cache_functions(THD *thd, LEX *lex) name.m_name.str+= 1; name.m_name.length= name.m_qname.length - name.m_db.length - 1; - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, &name, &sp) - == SP_OK) + if (db_find_routine(thd, type, &name, &sp) == SP_OK) { - sp_cache_insert(&thd->sp_func_cache, sp); - ret= sp_cache_functions(thd, newlex); + if (type == TYPE_ENUM_FUNCTION) + sp_cache_insert(&thd->sp_func_cache, sp); + else + sp_cache_insert(&thd->sp_proc_cache, sp); + ret= sp_cache_routines(thd, newlex, TYPE_ENUM_FUNCTION); + if (!ret) + { + sp_merge_hash(&lex->spfuns, &newlex->spfuns); + ret= sp_cache_routines(thd, newlex, TYPE_ENUM_PROCEDURE); + } + if (!ret) + { + sp_merge_hash(&lex->spprocs, &newlex->spprocs); + sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs); + } delete newlex; thd->lex= oldlex; if (ret) @@ -1041,9 +1055,6 @@ sp_cache_functions(THD *thd, LEX *lex) { delete newlex; thd->lex= oldlex; - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", ls->str); - ret= 1; - break; } } } @@ -56,7 +56,7 @@ int sp_show_status_procedure(THD *thd, const char *wild); sp_head * -sp_find_function(THD *thd, sp_name *name); +sp_find_function(THD *thd, sp_name *name, bool cache_only = 0); int sp_create_function(THD *thd, sp_head *sp); @@ -77,14 +77,15 @@ bool sp_function_exists(THD *thd, sp_name *name); -// This is needed since we have to read the functions before we -// do anything else. +/* + * For precaching of functions and procedures + */ void -sp_add_fun_to_lex(LEX *lex, sp_name *fun); +sp_add_to_hash(HASH *h, sp_name *fun); void -sp_merge_funs(LEX *dst, LEX *src); +sp_merge_hash(HASH *dst, HASH *src); int -sp_cache_functions(THD *thd, LEX *lex); +sp_cache_routines(THD *thd, LEX *lex, int type); // diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3f2969768c5..075aef9d286 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -23,6 +23,7 @@ #include "sp.h" #include "sp_pcontext.h" #include "sp_rcontext.h" +#include "sp_cache.h" Item_result sp_map_result_type(enum enum_field_types type) @@ -36,6 +37,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 +130,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 +150,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 +242,9 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) } break; } + case ROW_RESULT: + default: + DBUG_ASSERT(0); } } @@ -259,11 +332,14 @@ sp_head::sp_head() :Item_arena((bool)FALSE), m_returns_cs(NULL), m_has_return(FALSE), m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE) { + extern byte * + sp_table_key(const byte *ptr, uint *plen, my_bool first); DBUG_ENTER("sp_head::sp_head"); state= INITIALIZED; m_backpatch.empty(); m_lex.empty(); + hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); DBUG_VOID_RETURN; } @@ -424,10 +500,10 @@ sp_head::~sp_head() void sp_head::destroy() { - DBUG_ENTER("sp_head::destroy"); - DBUG_PRINT("info", ("name: %s", m_name.str)); sp_instr *i; LEX *lex; + DBUG_ENTER("sp_head::destroy"); + DBUG_PRINT("info", ("name: %s", m_name.str)); for (uint ip = 0 ; (i = get_instr(ip)) ; ip++) delete i; @@ -439,6 +515,8 @@ sp_head::destroy() if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left delete lex; } + if (m_sptabs.array.buffer) + hash_free(&m_sptabs); DBUG_VOID_RETURN; } @@ -732,6 +810,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(); @@ -799,48 +881,10 @@ sp_head::restore_lex(THD *thd) oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); // Collect some data from the sub statement lex. - sp_merge_funs(oldlex, sublex); -#ifdef NOT_USED_NOW - // QQ We're not using this at the moment. - if (sublex.sql_command == SQLCOM_CALL) - { - // It would be slightly faster to keep the list sorted, but we need - // an "insert before" method to do that. - char *proc= sublex.udf.name.str; - - List_iterator_fast<char *> li(m_calls); - char **it; - - while ((it= li++)) - if (my_strcasecmp(system_charset_info, proc, *it) == 0) - break; - if (! it) - m_calls.push_back(&proc); - - } + sp_merge_hash(&oldlex->spfuns, &sublex->spfuns); + sp_merge_hash(&oldlex->spprocs, &sublex->spprocs); // Merge used tables - // QQ ...or just open tables in thd->open_tables? - // This is not entirerly clear at the moment, but for now, we collect - // tables here. - for (sl= sublex.all_selects_list ; - sl ; - sl= sl->next_select()) - { - for (TABLE_LIST *tables= sl->get_table_list() ; - tables ; - tables= tables->next) - { - List_iterator_fast<char *> li(m_tables); - char **tb; - - while ((tb= li++)) - if (my_strcasecmp(system_charset_info, tables->table_name, *tb) == 0) - break; - if (! tb) - m_tables.push_back(&tables->table_name); - } - } -#endif + sp_merge_table_list(thd, &m_sptabs, sublex->query_tables, sublex); if (! sublex->sp_lex_in_use) delete sublex; thd->lex= oldlex; @@ -1864,3 +1908,242 @@ sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ + +/* + * Table merge hash table + * + */ +typedef struct st_sp_table +{ + LEX_STRING qname; + bool temp; + TABLE_LIST *table; +} SP_TABLE; + +byte * +sp_table_key(const byte *ptr, uint *plen, my_bool first) +{ + SP_TABLE *tab= (SP_TABLE *)ptr; + *plen= tab->qname.length; + return (byte *)tab->qname.str; +} + +/* + * Merge the table list into the hash table. + * If the optional lex is provided, it's used to check and set + * the flag for creation of a temporary table. + */ +bool +sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table, + LEX *lex_for_tmp_check) +{ + for (; table ; table= table->next_global) + if (!table->derived && + (!table->select_lex || + !(table->select_lex->options & OPTION_SCHEMA_TABLE))) + { + char tname[64+1+64+1+64+1]; // db.table.alias\0 + uint tlen, alen; + SP_TABLE *tab; + + tlen= table->db_length; + memcpy(tname, table->db, tlen); + tname[tlen++]= '.'; + memcpy(tname+tlen, table->table_name, table->table_name_length); + tlen+= table->table_name_length; + tname[tlen++]= '.'; + alen= strlen(table->alias); + memcpy(tname+tlen, table->alias, alen); + tlen+= alen; + tname[tlen]= '\0'; + + if ((tab= (SP_TABLE *)hash_search(h, (byte *)tname, tlen))) + { + if (tab->table->lock_type < table->lock_type) + tab->table= table; // Use the table with the highest lock type + } + else + { + if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE)))) + return FALSE; + tab->qname.length= tlen; + tab->qname.str= (char *)thd->strmake(tname, tab->qname.length); + if (!tab->qname.str) + return FALSE; + if (lex_for_tmp_check && + lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE && + lex_for_tmp_check->query_tables == table && + lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE) + tab->temp= TRUE; + tab->table= table; + my_hash_insert(h, (byte *)tab); + } + } + return TRUE; +} + +void +sp_merge_routine_tables(THD *thd, LEX *lex) +{ + uint i; + + for (i= 0 ; i < lex->spfuns.records ; i++) + { + sp_head *sp; + LEX_STRING *ls= (LEX_STRING *)hash_element(&lex->spfuns, i); + sp_name name(*ls); + + name.m_qname= *ls; + if ((sp= sp_cache_lookup(&thd->sp_func_cache, &name))) + sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs); + } + for (i= 0 ; i < lex->spprocs.records ; i++) + { + sp_head *sp; + LEX_STRING *ls= (LEX_STRING *)hash_element(&lex->spprocs, i); + sp_name name(*ls); + + name.m_qname= *ls; + if ((sp= sp_cache_lookup(&thd->sp_proc_cache, &name))) + sp_merge_table_hash(&lex->sptabs, &sp->m_sptabs); + } +} + +void +sp_merge_table_hash(HASH *hdst, HASH *hsrc) +{ + for (uint i=0 ; i < hsrc->records ; i++) + { + SP_TABLE *tabdst; + SP_TABLE *tabsrc= (SP_TABLE *)hash_element(hsrc, i); + + if (! (tabdst= (SP_TABLE *)hash_search(hdst, + tabsrc->qname.str, + tabsrc->qname.length))) + { + my_hash_insert(hdst, (byte *)tabsrc); + } + else + { + if (tabdst->table->lock_type < tabsrc->table->lock_type) + tabdst->table= tabsrc->table; // Use the highest lock type + } + } +} + +TABLE_LIST * +sp_hash_to_table_list(THD *thd, HASH *h) +{ + uint i; + TABLE_LIST *tables= NULL; + DBUG_ENTER("sp_hash_to_table_list"); + + for (i=0 ; i < h->records ; i++) + { + SP_TABLE *stab= (SP_TABLE *)hash_element(h, i); + if (stab->temp) + continue; + TABLE_LIST *table, *otable= stab->table; + + if (! (table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) + return NULL; + table->db= otable->db; + table->db_length= otable->db_length; + table->alias= otable->alias; + table->table_name= otable->table_name; + table->table_name_length= otable->table_name_length; + table->lock_type= otable->lock_type; + table->updating= otable->updating; + table->force_index= otable->force_index; + table->ignore_leaves= otable->ignore_leaves; + table->derived= otable->derived; + table->schema_table= otable->schema_table; + table->select_lex= otable->select_lex; + table->cacheable_table= otable->cacheable_table; + table->use_index= otable->use_index; + table->ignore_index= otable->ignore_index; + table->option= otable->option; + + table->next_global= tables; + tables= table; + } + DBUG_RETURN(tables); +} + +bool +sp_open_and_lock_tables(THD *thd, TABLE_LIST *tables) +{ + DBUG_ENTER("sp_open_and_lock_tables"); + bool ret; + + thd->in_lock_tables= 1; + thd->options|= OPTION_TABLE_LOCK; + if (simple_open_n_lock_tables(thd, tables)) + { + thd->options&= ~(ulong)(OPTION_TABLE_LOCK); + ret= FALSE; + } + else + { +#if 0 + // QQ What about this? +#ifdef HAVE_QUERY_CACHE + if (thd->variables.query_cache_wlock_invalidate) + query_cache.invalidate_locked_for_write(first_table); // QQ first_table? +#endif /* HAVE_QUERY_CACHE */ +#endif + thd->locked_tables= thd->lock; + thd->lock= 0; + ret= TRUE; + } + thd->in_lock_tables= 0; + DBUG_RETURN(ret); +} + +void +sp_unlock_tables(THD *thd) +{ + thd->lock= thd->locked_tables; + thd->locked_tables= 0; + close_thread_tables(thd); // Free tables + if (thd->options & OPTION_TABLE_LOCK) + { +#if 0 + // QQ What about this? + end_active_trans(thd); +#endif + thd->options&= ~(ulong)(OPTION_TABLE_LOCK); + } + if (thd->global_read_lock) + unlock_global_read_lock(thd); +} + +/* + * Simple function for adding an explicetly named (systems) table to + * the global table list, e.g. "mysql", "proc". + * + */ +TABLE_LIST * +sp_add_to_query_tables(THD *thd, LEX *lex, + const char *db, const char *name, + thr_lock_type locktype) +{ + TABLE_LIST *table; + + if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) + { + my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST)); + return NULL; + } + table->db_length= strlen(db); + table->db= thd->strmake(db, table->db_length); + table->table_name_length= strlen(name); + table->table_name= thd->strmake(name, table->table_name_length); + table->alias= thd->strdup(name); + table->lock_type= locktype; + table->select_lex= lex->current_select; // QQ? + table->cacheable_table= 1; + + lex->add_to_query_tables(table); + return table; +} diff --git a/sql/sp_head.h b/sql/sp_head.h index c4d2068661c..5df9c753048 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -92,11 +92,6 @@ public: uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; ulong m_sql_mode; // For SHOW CREATE -#if NOT_USED_NOW - // QQ We're not using this at the moment. - List<char *> m_calls; // Called procedures. - List<char *> m_tables; // Used tables. -#endif LEX_STRING m_qname; // db.name LEX_STRING m_db; LEX_STRING m_name; @@ -108,6 +103,7 @@ public: LEX_STRING m_definer_host; longlong m_created; longlong m_modified; + HASH m_sptabs; /* Merged table lists */ // Pointers set during parsing uchar *m_param_begin, *m_param_end, *m_returns_begin, *m_returns_end, *m_body_begin; @@ -897,4 +893,22 @@ void sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ +bool +sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table, + LEX *lex_for_tmp_check = 0); +void +sp_merge_routine_tables(THD *thd, LEX *lex); +void +sp_merge_table_hash(HASH *hdst, HASH *hsrc); +TABLE_LIST * +sp_hash_to_table_list(THD *thd, HASH *h); +bool +sp_open_and_lock_tables(THD *thd, TABLE_LIST *tables); +void +sp_unlock_tables(THD *thd); +TABLE_LIST * +sp_add_to_query_tables(THD *thd, LEX *lex, + const char *db, const char *name, + thr_lock_type locktype); + #endif /* _SP_HEAD_H_ */ 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 3f97cab1511..c2eea524cac 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -59,8 +59,11 @@ int compare_ulonglong2(void* cmp_arg __attribute__((unused)), return compare_ulonglong(s,t); } -bool append_escaped(String *to_str, String *from_str); -bool append_escaped(String *to_str, char *from, uint from_len); +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, @@ -142,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); } @@ -263,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); @@ -451,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]; @@ -888,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) @@ -922,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) @@ -1023,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; @@ -1087,38 +1252,3 @@ bool append_escaped(String *to_str, String *from_str) } return 0; } - -bool append_escaped(String *to_str, char *from, uint from_len) -{ - char *end, c; - - if (to_str->realloc(to_str->length() + from_len)) - return 1; - - end= from + from_len; - - for (; from < end; from++) - { - c= *from; - switch (c) { - case '\0': - c= '0'; - break; - case '\032': - c= 'Z'; - break; - case '\\': - case '\'': - break; - default: - goto normal_character; - } - if (to_str->append('\\')) - return 1; - - normal_character: - if (to_str->append(c)) - return 1; - } - return 0; -} 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 0b78f9a4dfb..eef86921012 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -496,58 +496,58 @@ void close_temporary(TABLE *table,bool delete_table) void close_temporary_tables(THD *thd) { TABLE *table,*next; - char *query, *end; - uint query_buf_size; - bool found_user_tables = 0; + char *query, *name_in_query, *end; + uint greatest_key_length= 0; if (!thd->temporary_tables) return; + /* + We write a DROP TEMPORARY TABLE for each temp table left, so that our + replication slave can clean them up. Not one multi-table DROP TABLE binlog + event: this would cause problems if slave uses --replicate-*-table. + */ LINT_INIT(end); - query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS + /* We'll re-use always same buffer so make it big enough for longest name */ for (table=thd->temporary_tables ; table ; table=table->next) - /* - We are going to add 4 ` around the db/table names, so 1 does not look - enough; indeed it is enough, because table->key_length is greater (by 8, - because of server_id and thread_id) than db||table. - */ - query_buf_size+= table->s->key_length+1; + greatest_key_length= max(greatest_key_length, table->s->key_length); - if ((query = alloc_root(thd->mem_root, query_buf_size))) + if ((query = alloc_root(thd->mem_root, greatest_key_length+50))) // Better add "if exists", in case a RESET MASTER has been done - end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "); + name_in_query= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `"); for (table=thd->temporary_tables ; table ; table=next) { - if (query) // we might be out of memory, but this is not fatal + /* + In we are OOM for 'query' this is not fatal. We skip temporary tables + not created directly by the user. + */ + if (query && mysql_bin_log.is_open() && (table->s->table_name[0] != '#')) { - // skip temporary tables not created directly by the user - if (table->s->table_name[0] != '#') - found_user_tables = 1; - end = strxmov(end,"`",table->s->db,"`.`", - table->s->table_name,"`,", NullS); + /* + Here we assume table_cache_key always starts + with \0 terminated db name + */ + end = strxmov(name_in_query, table->s->db, "`.`", + table->s->table_name, "`", NullS); + Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE); + /* + Imagine the thread had created a temp table, then was doing a SELECT, and + the SELECT was killed. Then it's not clever to mark the statement above as + "killed", because it's not really a statement updating data, and there + are 99.99% chances it will succeed on slave. And, if thread is + killed now, it's not clever either. + If a real update (one updating a persistent table) was killed on the + master, then this real update will be logged with error_code=killed, + rightfully causing the slave to stop. + */ + qinfo.error_code= 0; + mysql_bin_log.write(&qinfo); } next=table->next; close_temporary(table); } - if (query && found_user_tables && mysql_bin_log.is_open()) - { - /* The -1 is to remove last ',' */ - thd->clear_error(); - Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE); - /* - Imagine the thread had created a temp table, then was doing a SELECT, and - the SELECT was killed. Then it's not clever to mark the statement above as - "killed", because it's not really a statement updating data, and there - are 99.99% chances it will succeed on slave. - If a real update (one updating a persistent table) was killed on the - master, then this real update will be logged with error_code=killed, - rightfully causing the slave to stop. - */ - qinfo.error_code= 0; - mysql_bin_log.write(&qinfo); - } thd->temporary_tables=0; } @@ -853,7 +853,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= share->keys_in_use; table->used_keys= share->keys_for_keyread; @@ -918,10 +918,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (table->s->key_length == key_length && !memcmp(table->s->table_cache_key,key,key_length) && - !my_strcasecmp(system_charset_info, table->alias, alias) && - table->query_id != thd->query_id) + !my_strcasecmp(system_charset_info, table->alias, alias)) { - table->query_id=thd->query_id; + if (table->query_id != thd->query_id) + table->query_id=thd->query_id; DBUG_PRINT("info",("Using locked table")); goto reset; } @@ -1077,7 +1077,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; + table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= table->s->keys_in_use; table->insert_values= 0; @@ -1149,7 +1149,6 @@ bool reopen_table(TABLE *table,bool locked) tmp.tablenr= table->tablenr; tmp.used_fields= table->used_fields; tmp.const_table= table->const_table; - tmp.outer_join= table->outer_join; tmp.null_row= table->null_row; tmp.maybe_null= table->maybe_null; tmp.status= table->status; @@ -2119,13 +2118,31 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list, table_list->alias, name, item_name, (ulong) ref)); if (table_list->field_translation) { - DBUG_ASSERT(ref != 0 && table_list->view != 0); - uint num= table_list->view->select_lex.item_list.elements; + uint num; + if (table_list->schema_table_reformed) + { + num= thd->lex->current_select->item_list.elements; + } + else + { + DBUG_ASSERT(ref != 0 && table_list->view != 0); + num= table_list->view->select_lex.item_list.elements; + } Field_translator *trans= table_list->field_translation; for (uint i= 0; i < num; i ++) { if (!my_strcasecmp(system_charset_info, trans[i].name, name)) { + if (table_list->schema_table_reformed) + { + /* + Translation table items are always Item_fields + and fixed already('mysql_schema_table' function). + So we can return ->field. It is used only for + 'show & where' commands. + */ + DBUG_RETURN(((Item_field*) (trans[i].item))->field); + } #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_grants_view && check_grant_column(thd, &table_list->grant, @@ -2151,6 +2168,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); } @@ -2757,6 +2776,20 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, thd->allow_sum_func= allow_sum_func; thd->where="field list"; + /* + To prevent fail on forward lookup we fill it with zerows, + then if we got pointer on zero after find_item_in_list we will know + that it is forward lookup. + + There is other way to solve problem: fill array with pointers to list, + but it will be slower. + + TODO: remove it when (if) we made one list for allfields and + ref_pointer_array + */ + if (ref_pointer_array) + bzero(ref_pointer_array, sizeof(Item *) * fields.elements); + Item **ref= ref_pointer_array; while ((item= it++)) { @@ -3357,7 +3390,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); @@ -3366,7 +3400,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; } } @@ -3378,8 +3413,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_cache.cc b/sql/sql_cache.cc index d63877dc755..e38e417e6df 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -914,12 +914,12 @@ end: int Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) { + ulonglong engine_data; Query_cache_query *query; Query_cache_block *first_result_block, *result_block; Query_cache_block_table *block_table, *block_table_end; ulong tot_length; Query_cache_query_flags flags; - bool check_tables; DBUG_ENTER("Query_cache::send_result_to_client"); if (query_cache_size == 0 || thd->variables.query_cache_type == 0) @@ -1027,7 +1027,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) goto err_unlock; } - check_tables= query->tables_type() & HA_CACHE_TBL_ASKTRANSACT; // Check access; block_table= query_block->table(0); block_table_end= block_table+query_block->n_tables; @@ -1088,19 +1087,30 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) goto err_unlock; // Parse query } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ - if (check_tables && !ha_caching_allowed(thd, table->db(), - table->key_length(), - table->type())) + engine_data= table->engine_data(); + if (table->callback() && + !(*table->callback())(thd, table->db(), + table->key_length(), + &engine_data)) { DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", table_list.db, table_list.alias)); BLOCK_UNLOCK_RD(query_block); - thd->lex->safe_to_cache_query= 0; // Don't try to cache this + if (engine_data != table->engine_data()) + { + DBUG_PRINT("qcache", + ("Handler require invalidation queries of %s.%s %lld-%lld", + table_list.db, table_list.alias, + engine_data, table->engine_data())); + invalidate_table(table->db(), table->key_length()); + } + else + thd->lex->safe_to_cache_query= 0; // Don't try to cache this goto err_unlock; // Parse query } else - DBUG_PRINT("qcache", ("handler allow caching (%d) %s,%s", - check_tables, table_list.db, table_list.alias)); + DBUG_PRINT("qcache", ("handler allow caching %s,%s", + table_list.db, table_list.alias)); } move_to_query_list_end(query_block); hits++; @@ -2128,7 +2138,9 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, if (!insert_table(tables_used->table->s->key_length, tables_used->table->s->table_cache_key, block_table, tables_used->db_length, - tables_used->table->file->table_cache_type())) + tables_used->table->file->table_cache_type(), + tables_used->callback_func, + tables_used->engine_data)) break; if (tables_used->table->s->db_type == DB_TYPE_MRG_MYISAM) @@ -2144,9 +2156,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, uint key_length= filename_2_table_key(key, table->table->filename, &db_length); (++block_table)->n= ++n; + /* + There are not callback function for for MyISAM, and engine data + */ if (!insert_table(key_length, key, block_table, db_length, - tables_used->table->file->table_cache_type())) + tables_used->table->file->table_cache_type(), + 0, 0)) goto err; } } @@ -2173,7 +2189,9 @@ err: my_bool Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length, uint8 cache_type) + uint32 db_length, uint8 cache_type, + qc_engine_callback callback, + ulonglong engine_data) { DBUG_ENTER("Query_cache::insert_table"); DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", @@ -2183,6 +2201,23 @@ Query_cache::insert_table(uint key_len, char *key, hash_search(&tables, (byte*) key, key_len)); + if (table_block && + table_block->table()->engine_data() != engine_data) + { + DBUG_PRINT("qcache", + ("Handler require invalidation queries of %s.%s %lld-%lld", + table_block->table()->db(), + table_block->table()->table(), + engine_data, + table_block->table()->engine_data())); + /* + as far as we delete all queries with this table, table block will be + deleted, too + */ + invalidate_table(table_block); + table_block= 0; + } + if (table_block == 0) { DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)", @@ -2213,6 +2248,8 @@ Query_cache::insert_table(uint key_len, char *key, header->table(db + db_length + 1); header->key_length(key_len); header->type(cache_type); + header->callback(callback); + header->engine_data(engine_data); } Query_cache_block_table *list_root = table_block->table(0); @@ -2733,9 +2770,11 @@ my_bool Query_cache::ask_handler_allowance(THD *thd, for (; tables_used; tables_used= tables_used->next_global) { TABLE *table= tables_used->table; - if (!ha_caching_allowed(thd, table->s->table_cache_key, - table->s->key_length, - table->file->table_cache_type())) + handler *handler= table->file; + if (!handler->register_query_cache_table(thd, table->s->table_cache_key, + table->s->key_length, + &tables_used->callback_func, + &tables_used->engine_data)) { DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", tables_used->db, tables_used->alias)); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 93d89aeae4f..e7116c7718a 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -146,6 +146,10 @@ struct Query_cache_table char *tbl; uint32 key_len; uint8 table_type; + /* unique for every engine reference */ + qc_engine_callback callback_func; + /* data need by some engines */ + ulonglong engine_data_buff; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } @@ -154,6 +158,10 @@ struct Query_cache_table inline void key_length(uint32 len) { key_len= len; } inline uint8 type() { return table_type; } inline void type(uint8 t) { table_type= t; } + inline qc_engine_callback callback() { return callback_func; } + inline void callback(qc_engine_callback fn){ callback_func= fn; } + inline ulonglong engine_data() { return engine_data_buff; } + inline void engine_data(ulonglong data) { engine_data_buff= data; } inline gptr data() { return (gptr)(((byte*)this)+ @@ -282,7 +290,9 @@ protected: TABLE_COUNTER_TYPE tables); my_bool insert_table(uint key_len, char *key, Query_cache_block_table *node, - uint32 db_length, uint8 cache_type); + uint32 db_length, uint8 cache_type, + qc_engine_callback callback, + ulonglong engine_data); void unlink_table(Query_cache_block_table *node); Query_cache_block *get_free_block (ulong len, my_bool not_less, ulong min); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c9545a0141e..32c9e2a50f7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1303,6 +1303,14 @@ bool select_singlerow_subselect::send_data(List<Item> &items) } +void select_max_min_finder_subselect::cleanup() +{ + DBUG_ENTER("select_max_min_finder_subselect::cleanup"); + cache= 0; + DBUG_VOID_RETURN; +} + + bool select_max_min_finder_subselect::send_data(List<Item> &items) { DBUG_ENTER("select_max_min_finder_subselect::send_data"); @@ -1332,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); @@ -1373,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; @@ -1439,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 561cf099592..b04ad449ba3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -431,6 +431,11 @@ struct system_variables my_bool low_priority_updates; my_bool new_mode; my_bool query_cache_wlock_invalidate; +#ifdef HAVE_REPLICATION + ulong sync_replication; + ulong sync_replication_slave_id; + ulong sync_replication_timeout; +#endif /* HAVE_REPLICATION */ #ifdef HAVE_INNOBASE_DB my_bool innodb_table_locks; #endif /* HAVE_INNOBASE_DB */ @@ -1026,6 +1031,8 @@ public: sp_rcontext *spcont; // SP runtime context sp_cache *sp_proc_cache; sp_cache *sp_func_cache; + bool shortcut_make_view; /* Don't do full mysql_make_view() + during pre-opening of tables. */ /* If we do a purge of binary logs, log index info of the threads @@ -1510,9 +1517,11 @@ public: select_max_min_finder_subselect(Item_subselect *item, bool mx) :select_subselect(item), cache(0), fmax(mx) {} + void cleanup(); bool send_data(List<Item> &items); bool cmp_real(); bool cmp_int(); + bool cmp_decimal(); bool cmp_str(); }; @@ -1586,9 +1595,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; }; @@ -1617,9 +1627,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_derived.cc b/sql/sql_derived.cc index bed65f90c00..c01728e68d5 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -165,16 +165,21 @@ exit: else { if (!thd->fill_derived_tables()) + { delete derived_result; + derived_result= NULL; + } orig_table_list->derived_result= derived_result; orig_table_list->table= table; orig_table_list->table_name= (char*) table->s->table_name; + orig_table_list->table_name_length= strlen((char*)table->s->table_name); table->derived_select_number= first_select->select_number; table->s->tmp_table= TMP_TABLE; #ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.privilege= SELECT_ACL; #endif orig_table_list->db= (char *)""; + orig_table_list->db_length= 0; // Force read of table stats in the optimizer table->file->info(HA_STATUS_VARIABLE); /* Add new temporary table to list of open derived tables */ 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_insert.cc b/sql/sql_insert.cc index 4cb62d5e9d7..fa6f1e05dc6 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1811,13 +1811,13 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) is the same table (Bug #6034). Do the preparation after the select phase in select_insert::prepare2(). */ - if (info.ignore || info.handle_duplicates != DUP_ERROR) - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); table->file->start_bulk_insert((ha_rows) 0); } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; thd->cuted_fields=0; + if (info.ignore || info.handle_duplicates != DUP_ERROR) + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); thd->no_trans_update= 0; thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & @@ -1847,14 +1847,9 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) int select_insert::prepare2(void) { DBUG_ENTER("select_insert::prepare2"); - if (thd->lex->current_select->options & OPTION_BUFFER_RESULT) - { - if (info.ignore || info.handle_duplicates != DUP_ERROR) - table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); table->file->start_bulk_insert((ha_rows) 0); - } - return 0; + DBUG_RETURN(0); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 43e82ff57c9..30c657c3b79 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 */ @@ -115,9 +111,14 @@ void lex_free(void) void lex_start(THD *thd, uchar *buf,uint length) { LEX *lex= thd->lex; + DBUG_ENTER("lex_start"); + + lex->thd= lex->unit.thd= thd; + lex->buf= lex->ptr= buf; + lex->end_of_query= buf+length; + lex->unit.init_query(); lex->unit.init_select(); - lex->thd= lex->unit.thd= thd; lex->select_lex.init_query(); lex->value_list.empty(); lex->update_list.empty(); @@ -150,8 +151,6 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; lex->next_state=MY_LEX_START; - lex->buf= lex->ptr= buf; - lex->end_of_query=buf+length; lex->yylineno = 1; lex->in_comment=0; lex->length=0; @@ -173,11 +172,15 @@ void lex_start(THD *thd, uchar *buf,uint length) if (lex->spfuns.records) my_hash_reset(&lex->spfuns); + if (lex->spprocs.records) + my_hash_reset(&lex->spprocs); + if (lex->sptabs.records) + my_hash_reset(&lex->sptabs); + DBUG_VOID_RETURN; } void lex_end(LEX *lex) { - lex->select_lex.expr_list.delete_elements(); // If error when parsing sql-varargs x_free(lex->yacc_yyss); x_free(lex->yacc_yyvs); } @@ -334,7 +337,8 @@ static char *get_text(LEX *lex) continue; } #endif - if (*str == '\\' && str+1 != end) + if (!(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) && + *str == '\\' && str+1 != end) { switch(*++str) { case 'n': @@ -433,12 +437,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 @@ -454,10 +458,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 { @@ -795,7 +799,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_lex.h b/sql/sql_lex.h index b2c214bf1fa..5d232d60e79 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -416,6 +416,7 @@ public: void print(String *str); ulong init_prepare_fake_select_lex(THD *thd); + inline bool is_prepared() { return prepared; } bool change_result(select_subselect *result, select_subselect *old_result); void set_limit(st_select_lex *values, st_select_lex *sl); @@ -747,6 +748,8 @@ typedef struct st_lex bool all_privileges; sp_pcontext *spcont; HASH spfuns; /* Called functions */ + HASH spprocs; /* Called procedures */ + HASH sptabs; /* Merged table lists */ st_sp_chistics sp_chistics; bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* @@ -767,14 +770,21 @@ typedef struct st_lex st_lex() :result(0), sql_command(SQLCOM_END) { - extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first); - hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_spfuns_key, 0, 0); + extern byte *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first); + extern byte *sp_table_key(const byte *ptr, uint *plen, my_bool first); + hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0); + hash_init(&spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0); + hash_init(&sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); } ~st_lex() { if (spfuns.array.buffer) hash_free(&spfuns); + if (spprocs.array.buffer) + hash_free(&spprocs); + if (sptabs.array.buffer) + hash_free(&sptabs); } inline void uncacheable(uint8 cause) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 05d6a00805c..8462066dcff 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -712,6 +712,8 @@ static int check_connection(THD *thd) DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); + vio_in_addr(net->vio,&thd->remote.sin_addr); + if (!thd->host) // If TCP/IP connection { char ip[30]; @@ -756,7 +758,6 @@ static int check_connection(THD *thd) DBUG_PRINT("info",("Host: %s",thd->host)); thd->host_or_ip= thd->host; thd->ip= 0; - bzero((char*) &thd->remote, sizeof(struct sockaddr)); } vio_keepalive(net->vio, TRUE); ulong pkt_len= 0; @@ -2155,6 +2156,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, } TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; table_list->schema_select_lex= sel; + table_list->schema_table_reformed= 1; DBUG_RETURN(0); } @@ -2229,6 +2231,10 @@ mysql_execute_command(THD *thd) TABLE_LIST *all_tables; /* most outer SELECT_LEX_UNIT of query */ SELECT_LEX_UNIT *unit= &lex->unit; + /* Locked closure of all tables */ + TABLE_LIST *locked_tables= NULL; + /* Saved variable value */ + my_bool old_innodb_table_locks= thd->variables.innodb_table_locks; DBUG_ENTER("mysql_execute_command"); /* @@ -2250,11 +2256,89 @@ mysql_execute_command(THD *thd) /* should be assigned after making first tables same */ all_tables= lex->query_tables; + thd->shortcut_make_view= 0; if (lex->sql_command != SQLCOM_CREATE_PROCEDURE && - lex->sql_command != SQLCOM_CREATE_SPFUNCTION) + lex->sql_command != SQLCOM_CREATE_SPFUNCTION && + lex->sql_command != SQLCOM_LOCK_TABLES && + lex->sql_command != SQLCOM_UNLOCK_TABLES) { - if (sp_cache_functions(thd, lex)) - DBUG_RETURN(-1); + while (1) + { + if (sp_cache_routines(thd, lex, TYPE_ENUM_FUNCTION)) + DBUG_RETURN(-1); + if (sp_cache_routines(thd, lex, TYPE_ENUM_PROCEDURE)) + DBUG_RETURN(-1); + if (!thd->locked_tables && + lex->sql_command != SQLCOM_CREATE_TABLE && + lex->sql_command != SQLCOM_CREATE_VIEW) + { + MEM_ROOT *thdmemroot= NULL; + + sp_merge_routine_tables(thd, lex); + // QQ Preopen tables to find views and triggers. + // This means we open, close and open again, which sucks, but + // right now it's the easiest way to get it to work. A better + // solution will hopefully be found soon... + if (lex->sptabs.records || lex->query_tables) + { + uint procs, funs, tabs; + + if (thd->mem_root != thd->current_arena->mem_root) + { + thdmemroot= thd->mem_root; + thd->mem_root= thd->current_arena->mem_root; + } + if (!sp_merge_table_list(thd, &lex->sptabs, lex->query_tables)) + DBUG_RETURN(-1); + procs= lex->spprocs.records; + funs= lex->spfuns.records; + tabs= lex->sptabs.records; + + if ((locked_tables= sp_hash_to_table_list(thd, &lex->sptabs))) + { + // We don't want these updated now + uint ctmpdtabs= thd->status_var.created_tmp_disk_tables; + uint ctmptabs= thd->status_var.created_tmp_tables; + uint count; + + thd->shortcut_make_view= TRUE; + open_tables(thd, locked_tables, &count); + thd->shortcut_make_view= FALSE; + close_thread_tables(thd); + thd->status_var.created_tmp_disk_tables= ctmpdtabs; + thd->status_var.created_tmp_tables= ctmptabs; + thd->clear_error(); + mysql_reset_errors(thd); + locked_tables= NULL; + } + // A kludge: Decrease all temp. table's query ids to allow a + // second opening. + for (TABLE *table= thd->temporary_tables; table ; table=table->next) + table->query_id-= 1; + if (procs < lex->spprocs.records || + funs < lex->spfuns.records || + tabs < lex->sptabs.records) + { + if (thdmemroot) + thd->mem_root= thdmemroot; + continue; // Found more SPs or tabs, try again + } + } + if (lex->sptabs.records && + (lex->spfuns.records || lex->spprocs.records) && + sp_merge_table_list(thd, &lex->sptabs, lex->query_tables)) + { + if ((locked_tables= sp_hash_to_table_list(thd, &lex->sptabs))) + { + thd->variables.innodb_table_locks= FALSE; + sp_open_and_lock_tables(thd, locked_tables); + } + } + if (thdmemroot) + thd->mem_root= thdmemroot; + } + break; + } // while (1) } /* @@ -2525,7 +2609,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; @@ -3744,7 +3829,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)); @@ -4259,6 +4344,12 @@ cleanup: thd->lock= 0; } + if (locked_tables) + { + thd->variables.innodb_table_locks= old_innodb_table_locks; + if (thd->locked_tables) + sp_unlock_tables(thd); + } DBUG_RETURN(res || thd->net.report_error); } @@ -4718,19 +4809,21 @@ bool mysql_new_select(LEX *lex, bool move_down) { SELECT_LEX *select_lex; + DBUG_ENTER("mysql_new_select"); + if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; + DBUG_RETURN(1); select_lex->select_number= ++lex->thd->select_number; select_lex->init_query(); select_lex->init_select(); select_lex->parent_lex= lex; if (move_down) { + SELECT_LEX_UNIT *unit; lex->subqueries= TRUE; /* first select_lex of subselect or derived table */ - SELECT_LEX_UNIT *unit; if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT())) - return 1; + DBUG_RETURN(1); unit->init_query(); unit->init_select(); @@ -4747,7 +4840,7 @@ mysql_new_select(LEX *lex, bool move_down) if (lex->current_select->order_list.first && !lex->current_select->braces) { my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY"); - return 1; + DBUG_RETURN(1); } select_lex->include_neighbour(lex->current_select); SELECT_LEX_UNIT *unit= select_lex->master_unit(); @@ -4759,7 +4852,7 @@ mysql_new_select(LEX *lex, bool move_down) fake SELECT_LEX for UNION processing */ if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; + DBUG_RETURN(1); fake->include_standalone(unit, (SELECT_LEX_NODE**)&unit->fake_select_lex); fake->select_number= INT_MAX; @@ -4773,7 +4866,7 @@ mysql_new_select(LEX *lex, bool move_down) select_lex->include_global((st_select_lex_node**)&lex->all_selects_list); lex->current_select= select_lex; select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; - return 0; + DBUG_RETURN(0); } /* @@ -5066,21 +5159,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 @@ -5266,6 +5357,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..93197b1a2eb 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; @@ -1541,9 +1554,11 @@ static int check_prepared_statement(Prepared_statement *stmt, if (lex->sql_command != SQLCOM_CREATE_PROCEDURE && lex->sql_command != SQLCOM_CREATE_SPFUNCTION) { - /* the error is print inside */ - if (sp_cache_functions(thd, lex)) - DBUG_RETURN(1); + /* The error is printed inside */ + if (sp_cache_routines(thd, lex, TYPE_ENUM_FUNCTION)) + DBUG_RETURN(-1); + if (sp_cache_routines(thd, lex, TYPE_ENUM_PROCEDURE)) + DBUG_RETURN(-1); } switch (sql_command) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 69b5c667f6b..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(); @@ -1435,7 +1439,7 @@ JOIN::exec() curr_join->select_distinct=0; /* Each row is unique */ curr_join->join_free(0); /* Free quick selects */ - if (select_distinct && ! group_list) + if (curr_join->select_distinct && ! curr_join->group_list) { thd->proc_info="Removing duplicates"; if (curr_join->tmp_having) @@ -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: @@ -9181,7 +9165,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) tab->info="const row not found"; /* Mark for EXPLAIN that the row was not found */ pos->records_read=0.0; - if (!table->outer_join || error > 0) + if (!table->maybe_null || error > 0) DBUG_RETURN(error); } } @@ -9200,7 +9184,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) tab->info="unique row not found"; /* Mark for EXPLAIN that the row was not found */ pos->records_read=0.0; - if (!table->outer_join || error > 0) + if (!table->maybe_null || error > 0) DBUG_RETURN(error); } if (table->key_read) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e9126871045..c50d594a7aa 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -311,7 +311,9 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, if (db && !(col_access & TABLE_ACLS)) { table_list.db= (char*) db; + table_list.db_length= strlen(db); table_list.table_name= file->name; + table_list.table_name_length= strlen(file->name); table_list.grant.privilege=col_access; if (check_grant(thd, TABLE_ACLS, &table_list, 1, UINT_MAX, 1)) continue; @@ -1786,7 +1788,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) enum enum_schema_tables schema_table_idx; thr_lock_type lock_type; List<char> bases; + List_iterator_fast<char> it(bases); COND *partial_cond; + int error= 1; DBUG_ENTER("get_all_tables"); LINT_INIT(end); @@ -1803,13 +1807,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (schema_table->process_table(thd, show_table_list, table, res, show_table_list->db, show_table_list->alias)) - { - DBUG_RETURN(1); - } + goto err; close_thread_tables(thd, 0, 0, old_open_tables); show_table_list->table= 0; - lex->all_selects_list= select_lex; - DBUG_RETURN(0); + error= 0; + goto err; } lex->all_selects_list= &sel; @@ -1822,14 +1824,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) /* information schema name always is first in list */ if (schema_db_add(thd, &bases, idx_field_vals.db_value, &with_i_schema)) - return 1; + goto err; if (mysql_find_files(thd, &bases, NullS, mysql_data_home, idx_field_vals.db_value, 1)) - return 1; + goto err; - List_iterator_fast<char> it(bases); partial_cond= make_cond_for_info_schema(cond, tables); + it.rewind(); /* To get access to new elements in basis list */ while ((base_name= it++) || /* generate error for non existing database. @@ -1851,7 +1853,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (with_i_schema) // information schema table names { if (schema_tables_add(thd, &files, idx_field_vals.table_value)) - DBUG_RETURN(1); + goto err; } else { @@ -1860,7 +1862,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) len= FN_LEN - len; if (mysql_find_files(thd, &files, base_name, path, idx_field_vals.table_value, 0)) - DBUG_RETURN(1); + goto err; } List_iterator_fast<char> it_files(files); @@ -1906,16 +1908,14 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) int res; TABLE *old_open_tables= thd->open_tables; if (make_table_list(thd, &sel, base_name, file_name)) - DBUG_RETURN(1); + goto err; TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first; show_table_list->lock_type= lock_type; res= open_and_lock_tables(thd, show_table_list); if (schema_table->process_table(thd, show_table_list, table, res, base_name, show_table_list->alias)) - { - DBUG_RETURN(1); - } + goto err; close_thread_tables(thd, 0, 0, old_open_tables); } } @@ -1927,8 +1927,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) with_i_schema= 0; } } + + error= 0; +err: lex->all_selects_list= select_lex; - DBUG_RETURN(0); + DBUG_RETURN(error); } @@ -2249,15 +2252,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); @@ -2265,8 +2274,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: @@ -2282,8 +2291,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; } @@ -2518,7 +2527,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) bzero((char*) &proc_tables,sizeof(proc_tables)); proc_tables.db= (char*) "mysql"; + proc_tables.db_length= 5; proc_tables.table_name= proc_tables.alias= (char*) "proc"; + proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; if (!(proc_table= open_ltable(thd, &proc_tables, TL_READ))) { @@ -3196,11 +3207,51 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) table_list->schema_table_name, table_list->alias); table_list->table_name= (char*) table->s->table_name; + table_list->table_name_length= strlen(table->s->table_name); table_list->table= table; table->next= thd->derived_tables; thd->derived_tables= table; table_list->select_lex->options |= OPTION_SCHEMA_TABLE; lex->safe_to_cache_query= 0; + + if (table_list->schema_table_reformed) // show command + { + SELECT_LEX *sel= lex->current_select; + uint i= 0; + Item *item; + Field_translator *transl; + + if (table_list->field_translation) + { + Field_translator *end= table_list->field_translation + + sel->item_list.elements; + for (transl= table_list->field_translation; transl < end; transl++) + { + if (!transl->item->fixed && + transl->item->fix_fields(thd, table_list, &transl->item)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); + } + List_iterator_fast<Item> it(sel->item_list); + if (!(transl= + (Field_translator*)(thd->current_arena-> + alloc(sel->item_list.elements * + sizeof(Field_translator))))) + { + DBUG_RETURN(1); + } + while ((item= it++)) + { + char *name= item->name; + transl[i].item= item; + if (!item->fixed && item->fix_fields(thd, table_list, &transl[i].item)) + DBUG_RETURN(1); + transl[i++].name= name; + } + table_list->field_translation= transl; + } + DBUG_RETURN(0); } @@ -3233,8 +3284,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, information_schema_name.length, 0); make_lex_string(thd, &table, schema_table->table_name, strlen(schema_table->table_name), 0); - if (!sel->item_list.elements && /* Handle old syntax */ - schema_table->old_format(thd, schema_table) || + if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */ !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0), 0, 0, TL_READ, (List<String> *) 0, (List<String> *) 0)) diff --git a/sql/sql_string.h b/sql/sql_string.h index cb9db52c830..a5c7cf77630 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -210,6 +210,11 @@ public: { if (&s != this) { + /* + It is forbidden to do assignments like + some_string = substring_of_that_string + */ + DBUG_ASSERT(!s.uses_buffer_owned_by(this)); free(); Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length; alloced=0; @@ -343,4 +348,9 @@ public: /* Swap two string objects. Efficient way to exchange data without memcpy. */ void swap(String &s); + + inline bool uses_buffer_owned_by(const String *s) const + { + return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length); + } }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 880cce06c27..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) @@ -2394,7 +2402,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, strxmov(src_path, (*tmp_table)->s->path, reg_ext, NullS); else { - fn_format( src_path, src_table, src_db, reg_ext, MYF(MY_UNPACK_FILENAME)); + strxmov(src_path, mysql_data_home, "/", src_db, "/", src_table, + reg_ext, NullS); + /* Resolve symlinks (for windows) */ + fn_format(src_path, src_path, "", "", MYF(MY_UNPACK_FILENAME)); if (access(src_path, F_OK)) { my_error(ER_BAD_TABLE_ERROR, MYF(0), src_table); @@ -2421,7 +2432,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, } else { - fn_format( dst_path, table_name, db, reg_ext, MYF(MY_UNPACK_FILENAME)); + strxmov(dst_path, mysql_data_home, "/", db, "/", table_name, + reg_ext, NullS); + fn_format(dst_path, dst_path, "", "", MYF(MY_UNPACK_FILENAME)); if (!access(dst_path, F_OK)) goto table_exists; } 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_union.cc b/sql/sql_union.cc index 0a91eb4c0e1..29897c70023 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -153,6 +153,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT_LEX *sl, *first_select; select_result *tmp_result; bool is_union; + TABLE *empty_table= 0; DBUG_ENTER("st_select_lex_unit::prepare"); describe= test(additional_options & SELECT_DESCRIBE); @@ -249,13 +250,21 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, goto err; if (sl == first_select) { + /* + We need to create an empty table object. It is used + to create tmp_table fields in Item_type_holder. + The main reason of this is that we can't create + field object without table. + */ + DBUG_ASSERT(!empty_table); + empty_table= (TABLE*) thd->calloc(sizeof(TABLE)); types.empty(); List_iterator_fast<Item> it(sl->item_list); Item *item_tmp; while ((item_tmp= it++)) { /* Error's in 'new' will be detected after loop */ - types.push_back(new Item_type_holder(thd_arg, item_tmp)); + types.push_back(new Item_type_holder(thd_arg, item_tmp, empty_table)); } if (thd_arg->is_fatal_error) @@ -274,7 +283,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, Item *type, *item_tmp; while ((type= tp++, item_tmp= it++)) { - if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp)) + if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp, + empty_table)) DBUG_RETURN(TRUE); } } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f9ad513ea6a..f9df1be2abd 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -120,7 +120,7 @@ int mysql_update(THD *thd, bool used_key_is_modified, transactional_table, log_delayed; int res; int error=0; - uint used_index= MAX_KEY; + uint used_index; #ifndef NO_EMBEDDED_ACCESS_CHECKS uint want_privilege; #endif @@ -264,7 +264,10 @@ int mysql_update(THD *thd, else if ((used_index=table->file->key_used_on_scan) < MAX_KEY) used_key_is_modified=check_if_key_used(table, used_index, fields); else + { used_key_is_modified=0; + used_index= MAX_KEY; + } if (used_key_is_modified || order) { /* @@ -498,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= @@ -1380,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_view.cc b/sql/sql_view.cc index e2c4b1289fd..456f513dab0 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -556,6 +556,7 @@ my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table) { DBUG_ENTER("mysql_make_view"); + DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name)); if (table->view) { @@ -612,7 +613,9 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local; lex_start(thd, (uchar*)table->query.str, table->query.length); view_select= &lex->select_lex; - view_select->select_number= ++thd->select_number; + /* Only if we're not in the pre-open phase */ + if (!thd->shortcut_make_view) + view_select->select_number= ++thd->select_number; old_lex->derived_tables|= DERIVED_VIEW; { ulong options= thd->options; @@ -657,27 +660,29 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) TABLE_LIST *view_tables_tail= 0; TABLE_LIST *tbl; + /* move SP to main LEX */ if (lex->spfuns.records) - { - /* move SP to main LEX */ - sp_merge_funs(old_lex, lex); - /* open mysq.proc for functions which are not in cache */ - if (old_lex->proc_table == 0 && - (old_lex->proc_table= - (TABLE_LIST*)thd->calloc(sizeof(TABLE_LIST))) != 0) - { - TABLE_LIST *table= old_lex->proc_table; - table->db= (char*)"mysql"; - table->db_length= 5; - table->table_name= table->alias= (char*)"proc"; - table->table_name_length= 4; - table->cacheable_table= 1; - old_lex->add_to_query_tables(table); - } - } + sp_merge_hash(&old_lex->spfuns, &lex->spfuns); + /* cleanup LEX */ if (lex->spfuns.array.buffer) hash_free(&lex->spfuns); + if (lex->spprocs.array.buffer) + hash_free(&lex->spprocs); + if (lex->sptabs.array.buffer) + hash_free(&lex->sptabs); + + /* If we're pre-opening tables to find SPs and tables we need + not go any further; doing so will cause an infinite loop. */ + if (thd->shortcut_make_view) + { + extern bool + sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table, + LEX *lex_for_tmp_check = 0); + + sp_merge_table_list(thd, &old_lex->sptabs, view_tables); + goto ok; + } /* check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e1c7c26060a..c4649c57269 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 @@ -1464,6 +1464,7 @@ call: lex->sql_command= SQLCOM_CALL; lex->spname= $2; lex->value_list.empty(); + sp_add_to_hash(&lex->spprocs, $2); } '(' sp_cparam_list ')' {} ; @@ -1869,36 +1870,21 @@ sp_proc_stmt: if (lex->sql_command != SQLCOM_SET_OPTION || ! lex->var_list.is_empty()) { - /* - Currently we can't handle queries inside a FUNCTION or - TRIGGER, because of the way table locking works. This is - unfortunate, and limits the usefulness of functions and - especially triggers a tremendously, but it's nothing we - can do about this at the moment. - */ - if (sp->m_type != TYPE_ENUM_PROCEDURE) - { - my_message(ER_SP_BADSTATEMENT, ER(ER_SP_BADSTATEMENT), MYF(0)); - YYABORT; - } + sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), + lex->spcont); + + /* Extract the query statement from the tokenizer: + The end is either lex->tok_end or tok->ptr. */ + if (lex->ptr - lex->tok_end > 1) + i->m_query.length= lex->ptr - sp->m_tmp_query; else - { - sp_instr_stmt *i=new sp_instr_stmt(sp->instructions(), - lex->spcont); - - /* Extract the query statement from the tokenizer: - The end is either lex->tok_end or tok->ptr. */ - if (lex->ptr - lex->tok_end > 1) - i->m_query.length= lex->ptr - sp->m_tmp_query; - else - i->m_query.length= lex->tok_end - sp->m_tmp_query; - i->m_query.str= strmake_root(YYTHD->mem_root, - (char *)sp->m_tmp_query, - i->m_query.length); - i->set_lex(lex); - sp->add_instr(i); - lex->sp_lex_in_use= TRUE; - } + i->m_query.length= lex->tok_end - sp->m_tmp_query; + i->m_query.str= strmake_root(YYTHD->mem_root, + (char *)sp->m_tmp_query, + i->m_query.length); + i->set_lex(lex); + sp->add_instr(i); + lex->sp_lex_in_use= TRUE; } sp->restore_lex(YYTHD); } @@ -1915,11 +1901,6 @@ sp_proc_stmt: { sp_instr_freturn *i; - if ($2->type() == Item::SUBSELECT_ITEM) - { /* QQ For now, just disallow subselects as values */ - my_message(ER_SP_BADSTATEMENT, ER(ER_SP_BADSTATEMENT), MYF(0)); - YYABORT; - } i= new sp_instr_freturn(lex->sphead->instructions(), lex->spcont, $2, lex->sphead->m_returns); @@ -2658,6 +2639,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 +2820,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 +2886,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: @@ -3895,10 +3877,11 @@ select_into: select_from: FROM join_table_list where_clause group_clause having_clause opt_order_clause opt_limit_clause procedure_clause - | FROM DUAL_SYM /* oracle compatibility: oracle always requires FROM - clause, and DUAL is system table without fields. - Is "SELECT 1 FROM DUAL" any better than - "SELECT 1" ? Hmmm :) */ + | FROM DUAL_SYM opt_limit_clause + /* oracle compatibility: oracle always requires FROM clause, + and DUAL is system table without fields. + Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? + Hmmm :) */ ; select_options: @@ -4032,14 +4015,15 @@ bool_test: bool_pri: bool_pri IS NULL_SYM { $$= new Item_func_isnull($1); } | bool_pri IS not NULL_SYM { $$= new Item_func_isnotnull($1); } - | predicate BETWEEN_SYM bit_expr AND_SYM bool_pri - { $$= new Item_func_between($1,$3,$5); } - | predicate not BETWEEN_SYM bit_expr AND_SYM bool_pri - { $$= negate_expression(YYTHD, new Item_func_between($1,$4,$6)); } + | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); } + | bool_pri comp_op predicate %prec EQ + { $$= (*$2)(0)->create($1,$3); } + | bool_pri comp_op all_or_any in_subselect %prec EQ + { $$= all_any_subquery_creator($1, $2, $3, $4); } | predicate ; predicate: - bit_expr IN_SYM '(' expr_list ')' + bit_expr IN_SYM '(' expr_list ')' { $4->push_front($1); $$= new Item_func_in(*$4); } | bit_expr not IN_SYM '(' expr_list ')' { $5->push_front($1); $$= negate_expression(YYTHD, new Item_func_in(*$5)); } @@ -4047,6 +4031,10 @@ predicate: { $$= new Item_in_subselect($1, $3); } | bit_expr not IN_SYM in_subselect { $$= negate_expression(YYTHD, new Item_in_subselect($1, $4)); } + | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + { $$= new Item_func_between($1,$3,$5); } + | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + { $$= negate_expression(YYTHD, new Item_func_between($1,$4,$6)); } | bit_expr SOUNDS_SYM LIKE bit_expr { $$= new Item_func_eq(new Item_func_soundex($1), new Item_func_soundex($4)); } @@ -4057,11 +4045,6 @@ predicate: | bit_expr REGEXP bit_expr { $$= new Item_func_regex($1,$3); } | bit_expr not REGEXP bit_expr { $$= negate_expression(YYTHD, new Item_func_regex($1,$4)); } - | bit_expr EQUAL_SYM bit_expr { $$= new Item_func_equal($1,$3); } - | bit_expr comp_op bit_expr %prec EQ - { $$= (*$2)(0)->create($1,$3); } - | bit_expr comp_op all_or_any in_subselect %prec EQ - { $$= all_any_subquery_creator($1, $2, $3, $4); } | bit_expr ; bit_expr: @@ -4186,13 +4169,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 ); } @@ -4200,6 +4185,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 ')' @@ -4502,7 +4488,7 @@ simple_expr: sp_name *name= new sp_name($1, $3); name->init_qname(YYTHD); - sp_add_fun_to_lex(Lex, name); + sp_add_to_hash(&Lex->spfuns, name); if ($5) $$= new Item_func_sp(name, *$5); else @@ -4564,6 +4550,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; } @@ -4573,7 +4575,7 @@ simple_expr: { sp_name *name= sp_name_current_db_new(YYTHD, $1); - sp_add_fun_to_lex(Lex, name); + sp_add_to_hash(&Lex->spfuns, name); if ($3) $$= new Item_func_sp(name, *$3); else @@ -4813,16 +4815,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; } - | 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; } + 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; Lex->dec=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: @@ -5325,7 +5328,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); } ; @@ -5333,7 +5336,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); } ; @@ -5879,6 +5882,9 @@ show: SHOW { LEX *lex=Lex; lex->wild=0; + lex->lock_option= TL_READ; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; bzero((char*) &lex->create_info,sizeof(lex->create_info)); } show_param @@ -5886,7 +5892,7 @@ show: SHOW ; show_param: - DATABASES ext_select_item_list wild_and_where + DATABASES wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; @@ -5894,44 +5900,44 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA)) YYABORT; } - | opt_full TABLES ext_select_item_list opt_db wild_and_where + | opt_full TABLES opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $4; + lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES)) YYABORT; } - | TABLE_SYM STATUS_SYM ext_select_item_list opt_db wild_and_where + | TABLE_SYM STATUS_SYM opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $4; + lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES)) YYABORT; } - | OPEN_SYM TABLES ext_select_item_list opt_db wild_and_where + | OPEN_SYM TABLES opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $4; + lex->select_lex.db= $3; if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES)) YYABORT; } | ENGINE_SYM storage_engines { Lex->create_info.db_type= $2; } show_engine_param - | opt_full COLUMNS ext_select_item_list from_or_in table_ident opt_db wild_and_where + | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_FIELDS; - if ($6) - $5->change_db($6); - if (prepare_schema_table(YYTHD, lex, $5, SCH_COLUMNS)) + if ($5) + $4->change_db($5); + if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS)) YYABORT; } | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ @@ -5957,14 +5963,14 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; } opt_limit_clause_init - | keys_or_index ext_select_item_list from_or_in table_ident opt_db where_clause + | keys_or_index from_or_in table_ident opt_db where_clause { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_KEYS; - if ($5) - $4->change_db($5); - if (prepare_schema_table(YYTHD, lex, $4, SCH_STATISTICS)) + if ($4) + $3->change_db($4); + if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS)) YYABORT; } | COLUMN_SYM TYPES_SYM @@ -5996,7 +6002,7 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_WARNS;} | ERRORS opt_limit_clause_init { Lex->sql_command = SQLCOM_SHOW_ERRORS;} - | opt_var_type STATUS_SYM ext_select_item_list wild_and_where + | opt_var_type STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; @@ -6011,7 +6017,7 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_MUTEX_STATUS; } | opt_full PROCESSLIST_SYM { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} - | opt_var_type VARIABLES ext_select_item_list wild_and_where + | opt_var_type VARIABLES wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; @@ -6020,7 +6026,7 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) YYABORT; } - | charset ext_select_item_list wild_and_where + | charset wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; @@ -6028,7 +6034,7 @@ show_param: if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS)) YYABORT; } - | COLLATION_SYM ext_select_item_list wild_and_where + | COLLATION_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; @@ -6114,19 +6120,23 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; lex->spname= $3; } - | PROCEDURE STATUS_SYM ext_select_item_list wild_and_where + | PROCEDURE STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_STATUS_PROC; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) YYABORT; } - | FUNCTION_SYM STATUS_SYM ext_select_item_list wild_and_where + | FUNCTION_SYM STATUS_SYM wild_and_where { LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_STATUS_FUNC; + if (!sp_add_to_query_tables(YYTHD, lex, "mysql", "proc", TL_READ)) + YYABORT; if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES)) YYABORT; }; @@ -6196,20 +6206,6 @@ wild_and_where: } ; -ext_select_item_list: - { - LEX *lex=Lex; - SELECT_LEX *sel= lex->current_select; - lex->lock_option= TL_READ; - mysql_init_select(lex); - lex->current_select->parsing_place= SELECT_LIST; - } - ext_select_item_list2; - -ext_select_item_list2: - /* empty */ {} - | select_item_list {}; - /* A Oracle compatible synonym for show */ describe: @@ -6585,9 +6581,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; @@ -7444,7 +7440,14 @@ set_expr_or_default: lock: LOCK_SYM table_or_tables { - Lex->sql_command=SQLCOM_LOCK_TABLES; + LEX *lex= Lex; + + if (lex->sphead) + { + my_message(ER_SP_BADSTATEMENT, ER(ER_SP_BADSTATEMENT), MYF(0)); + YYABORT; + } + lex->sql_command= SQLCOM_LOCK_TABLES; } table_lock_list {} @@ -7474,7 +7477,19 @@ lock_option: ; unlock: - UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } + UNLOCK_SYM + { + LEX *lex= Lex; + + if (lex->sphead) + { + my_message(ER_SP_BADSTATEMENT, ER(ER_SP_BADSTATEMENT), MYF(0)); + YYABORT; + } + lex->sql_command= SQLCOM_UNLOCK_TABLES; + } + table_or_tables + {} ; diff --git a/sql/table.h b/sql/table.h index 8240a3445ec..5ab1f900195 100644 --- a/sql/table.h +++ b/sql/table.h @@ -217,14 +217,18 @@ struct st_table { uint derived_select_number; int current_lock; /* Type of lock on table */ my_bool copy_blobs; /* copy_blobs when storing */ + + /* + 0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0. + If maybe_null !=0, this table is inner w.r.t. some outer join operation, + and null_row may be true. + */ + uint maybe_null; /* - Used in outer joins: if true, all columns are considered to have NULL - values, including columns declared as "not null". + If true, the current table row is considered to have all columns set to + NULL, including columns declared as "not null" (see maybe_null). */ my_bool null_row; - /* 0 or JOIN_TYPE_{LEFT|RIGHT}, same as TABLE_LIST::outer_join */ - my_bool outer_join; - my_bool maybe_null; /* true if (outer_join != 0) */ my_bool force_index; my_bool distinct,const_table,no_rows; my_bool key_read, no_keyread; @@ -356,6 +360,7 @@ typedef struct st_table_list st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ ST_SCHEMA_TABLE *schema_table; /* Information_schema table */ st_select_lex *schema_select_lex; + bool schema_table_reformed; /* link to select_lex where this table was used */ st_select_lex *select_lex; st_lex *view; /* link on VIEW lex for merging */ @@ -387,6 +392,10 @@ typedef struct st_table_list uint effective_algorithm; /* which algorithm was really used */ uint privilege_backup; /* place for saving privileges */ GRANT_INFO grant; + /* data need by some engines in query cache*/ + ulonglong engine_data; + /* call back function for asking handler about caching in query cache */ + qc_engine_callback callback_func; thr_lock_type lock_type; uint outer_join; /* Which join type */ uint shared; /* Used in multi-upd */ diff --git a/sql/unireg.h b/sql/unireg.h index eca540b61b9..8d88683241b 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -77,6 +77,8 @@ #define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3)) #define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2)) #define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1)) +#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \ + RAND_TABLE_BIT) #define MAX_FIELDS 4096 /* Limit in the .frm file */ #define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD) |