diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 729 |
1 files changed, 351 insertions, 378 deletions
diff --git a/sql/item.cc b/sql/item.cc index 3d3f472afc5..bed8824f68f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -19,9 +19,8 @@ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include <mysql.h> #include <m_ctype.h> #include "my_dir.h" @@ -41,7 +40,6 @@ // REPORT_EXCEPT_NOT_FOUND, // find_item_in_list, // RESOLVED_AGAINST_ALIAS, ... -#include "log_event.h" // append_query_string #include "sql_expression_cache.h" const String my_null_string("NULL", 4, default_charset_info); @@ -105,7 +103,7 @@ void Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const { item->decimals= arg->decimals; - item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS, + item->max_length= MY_MIN(arg->max_length + DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_STR_LENGTH); } @@ -198,7 +196,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const void item_init(void) { - item_user_lock_init(); + item_func_sleep_init(); uuid_short_init(); } @@ -234,6 +232,36 @@ bool Item::val_bool() } +/** + Get date/time/datetime. + Optionally extend TIME result to DATETIME. +*/ +bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + /* + Some TIME type items return error when trying to do get_date() + without TIME_TIME_ONLY set (e.g. Item_field for Field_time). + In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY. + In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY + and leave it to get_date() to check date. + */ + ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME && + !(current_thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ? + TIME_TIME_ONLY : 0; + if (get_date(ltime, fuzzydate | time_flag)) + return true; + if (ltime->time_type == MYSQL_TIMESTAMP_TIME && + !(fuzzydate & TIME_TIME_ONLY)) + { + MYSQL_TIME tmp; + if (time_to_datetime_with_warn(current_thd, ltime, &tmp, fuzzydate)) + return null_value= true; + *ltime= tmp; + } + return false; +} + + /* For the items which don't have its own fast val_str_ascii() implementation we provide a generic slower version, @@ -312,11 +340,29 @@ String *Item::val_string_from_decimal(String *str) } +/* + All val_xxx_from_date() must call this method, to expose consistent behaviour + regarding SQL_MODE when converting DATE/DATETIME to other data types. +*/ +bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime) +{ + return get_date(ltime, field_type() == MYSQL_TYPE_TIME + ? TIME_TIME_ONLY + : sql_mode_for_dates(current_thd)); +} + + +bool Item::is_null_from_temporal() +{ + MYSQL_TIME ltime; + return get_temporal_with_sql_mode(<ime); +} + + String *Item::val_string_from_date(String *str) { MYSQL_TIME ltime; - if (get_date(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) || + if (get_temporal_with_sql_mode(<ime) || str->alloc(MAX_DATE_STRING_REP_LENGTH)) { null_value= 1; @@ -360,7 +406,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) decimal_value) & E_DEC_BAD_NUM) { ErrConvString err(res); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", err.ptr()); @@ -373,7 +419,7 @@ my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_temporal_with_sql_mode(<ime)) { my_decimal_set_zero(decimal_value); null_value= 1; // set NULL, stop processing @@ -400,7 +446,7 @@ longlong Item::val_int_from_date() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_temporal_with_sql_mode(<ime)) return 0; longlong v= TIME_to_ulonglong(<ime); return ltime.neg ? -v : v; @@ -411,7 +457,7 @@ double Item::val_real_from_date() { DBUG_ASSERT(fixed == 1); MYSQL_TIME ltime; - if (get_date(<ime, 0)) + if (get_temporal_with_sql_mode(<ime)) return 0; return TIME_to_double(<ime); } @@ -453,9 +499,7 @@ int Item::save_time_in_field(Field *field) int Item::save_date_in_field(Field *field) { MYSQL_TIME ltime; - if (get_date(<ime, (current_thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | - MODE_INVALID_DATES)))) + if (get_date(<ime, sql_mode_for_dates(current_thd))) return set_field_to_null_with_conversions(field, 0); field->set_notnull(); return field->store_time_dec(<ime, decimals); @@ -570,26 +614,10 @@ uint Item::decimal_precision() const uint prec= my_decimal_length_to_precision(max_char_length(), decimals, unsigned_flag); - return min(prec, DECIMAL_MAX_PRECISION); - } - return min(max_char_length(), DECIMAL_MAX_PRECISION); -} - - -#if MARIADB_VERSION_ID < 1000000 -static uint ms_to_precision(uint ms) -{ - uint cut, precision; - for (cut= 10, precision= 6 ; precision > 0 ; cut*= 10, precision--) - { - if (ms % cut) - return precision; + return MY_MIN(prec, DECIMAL_MAX_PRECISION); } - return 0; + return MY_MIN(max_char_length(), DECIMAL_MAX_PRECISION); } -#else -#error Change the code to use MYSQL_TIME_STATUS::precision instead. -#endif uint Item::temporal_precision(enum_field_types type) @@ -599,18 +627,17 @@ uint Item::temporal_precision(enum_field_types type) { MYSQL_TIME ltime; String buf, *tmp; - int was_cut; + MYSQL_TIME_STATUS status; DBUG_ASSERT(fixed); if ((tmp= val_str(&buf)) && - (type == MYSQL_TYPE_TIME ? + !(type == MYSQL_TYPE_TIME ? str_to_time(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_TIME_ONLY, &was_cut) : + <ime, TIME_TIME_ONLY, &status) : str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_FUZZY_DATES, &was_cut)) > - MYSQL_TIMESTAMP_ERROR) - return min(ms_to_precision(ltime.second_part), TIME_SECOND_PART_DIGITS); + <ime, TIME_FUZZY_DATES, &status))) + return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); } - return min(decimals, TIME_SECOND_PART_DIGITS); + return MY_MIN(decimals, TIME_SECOND_PART_DIGITS); } @@ -658,7 +685,7 @@ void Item::cleanup() { DBUG_ENTER("Item::cleanup"); DBUG_PRINT("enter", ("this: %p", this)); - fixed=0; + fixed= 0; marker= 0; join_tab_idx= MAX_TABLES; if (orig_name) @@ -728,9 +755,12 @@ Item_result Item::cmp_type() const case MYSQL_TYPE_GEOMETRY: return STRING_RESULT; case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_NEWDATE: return TIME_RESULT; }; @@ -866,7 +896,7 @@ void Item_ident::cleanup() We can trust that depended_from set correctly only if this item was fixed */ - can_be_depended= test(depended_from); + can_be_depended= MY_TEST(depended_from); } DBUG_VOID_RETURN; } @@ -884,10 +914,15 @@ bool Item_ident::remove_dependence_processor(uchar * arg) bool Item_ident::collect_outer_ref_processor(uchar *param) { Collect_deps_prm *prm= (Collect_deps_prm *)param; - if (depended_from && + if (depended_from && depended_from->nest_level_base == prm->nest_level_base && depended_from->nest_level < prm->nest_level) - prm->parameters->add_unique(this, &cmp_items); + { + if (prm->collect) + prm->parameters->add_unique(this, &cmp_items); + else + prm->count++; + } return FALSE; } @@ -1037,10 +1072,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) name_length= 0; return; } - if (cs->ctype) - { - const char *str_start= str; + const char *str_start= str; + if (!cs->ctype || cs->mbminlen > 1) + { + str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES); + length-= str - str_start; + } + else + { /* This will probably need a better implementation in the future: a function in CHARSET_INFO structure. @@ -1050,21 +1090,21 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) length--; str++; } - if (str != str_start && !is_autogenerated_name) - { - char buff[SAFE_NAME_LEN]; - strmake(buff, str_start, - min(sizeof(buff)-1, length + (int) (str-str_start))); - - if (length == 0) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), - buff); - else - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), - buff); - } + } + if (str != str_start && !is_autogenerated_name) + { + char buff[SAFE_NAME_LEN]; + strmake(buff, str_start, + MY_MIN(sizeof(buff)-1, length + (int) (str-str_start))); + + if (length == 0) + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), + buff); + else + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), + buff); } if (!my_charset_same(cs, system_charset_info)) { @@ -1075,7 +1115,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) name_length= res_length; } else - name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME))); + name= sql_strmake(str, (name_length= MY_MIN(length,MAX_ALIAS_NAME))); } @@ -1130,6 +1170,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const Item *Item::safe_charset_converter(CHARSET_INFO *tocs) { + if (!needs_charset_converter(tocs)) + return this; Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1); return conv->safe ? conv : NULL; } @@ -1192,62 +1234,55 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs) if (!(tocs->state & MY_CS_NONASCII)) return this; - Item_string *conv; - uint conv_errors; - char buf[64], buf2[64]; - String tmp(buf, sizeof(buf), &my_charset_bin); - String cstr(buf2, sizeof(buf2), &my_charset_bin); - String *ostr= val_str(&tmp); - char *ptr; - cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), - cstr.charset(), - collation.derivation))) - { - /* - Safe conversion is not possible (or EOM). - We could not convert a string into the requested character set - without data loss. The target charset does not cover all the - characters from the string. Operation cannot be done correctly. - */ - return NULL; - } - if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length()))) - return NULL; - conv->str_value.set(ptr, cstr.length(), cstr.charset()); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); - conv->fix_char_length(max_char_length()); + Item *conv; + if ((conv= const_charset_converter(tocs, true))) + conv->fix_char_length(max_char_length()); return conv; } -Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs) +/** + Create character set converter for constant items + using Item_null, Item_string or Item_static_string_func. + + @param tocs Character set to to convert the string to. + @param lossless Whether data loss is acceptable. + @param func_name Function name, or NULL. + + @return this, if conversion is not needed, + NULL, if safe conversion is not possible, or + a new item representing the converted constant. +*/ +Item *Item::const_charset_converter(CHARSET_INFO *tocs, + bool lossless, + const char *func_name) { - Item_string *conv; - char buf[64]; - String *s, tmp(buf, sizeof(buf), &my_charset_bin); - s= val_str(&tmp); - if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(), - s->charset()))) + DBUG_ASSERT(const_item()); + DBUG_ASSERT(fixed); + StringBuffer<64>tmp; + String *s= val_str(&tmp); + if (!s) + return new Item_null((char *) func_name, tocs); + + if (!needs_charset_converter(s->length(), tocs)) { - conv->str_value.copy(); - conv->str_value.mark_as_const(); + if (collation.collation == &my_charset_bin && tocs != &my_charset_bin && + !this->check_well_formed_result(s, true)) + return NULL; + return this; } - return conv; -} - -Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) -{ - Item_string *conv; uint conv_errors; - char *ptr; - String tmp, cstr, *ostr= val_str(&tmp); - cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), - cstr.charset(), - collation.derivation))) + Item_string *conv= func_name ? + new Item_static_string_func(func_name, + s, tocs, &conv_errors, + collation.derivation, + collation.repertoire) : + new Item_string(s, tocs, &conv_errors, + collation.derivation, + collation.repertoire); + + if (!conv || (conv_errors && lossless)) { /* Safe conversion is not possible (or EOM). @@ -1257,70 +1292,28 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) */ return NULL; } - if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length()))) + if (s->charset() == &my_charset_bin && tocs != &my_charset_bin && + !conv->check_well_formed_result(true)) return NULL; - conv->str_value.set(ptr, cstr.length(), cstr.charset()); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); return conv; } Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) { - if (const_item()) - { - uint cnv_errors; - String *ostr= val_str(&cnvstr); - cnvitem->str_value.copy(ostr->ptr(), ostr->length(), - ostr->charset(), tocs, &cnv_errors); - if (cnv_errors) - return NULL; - cnvitem->str_value.mark_as_const(); - cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen; - return cnvitem; - } - return Item::safe_charset_converter(tocs); -} - - -Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) -{ - Item_string *conv; - uint conv_errors; - String tmp, cstr, *ostr= val_str(&tmp); - cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - if (conv_errors || - !(conv= new Item_static_string_func(func_name, - cstr.ptr(), cstr.length(), - cstr.charset(), - collation.derivation))) - { - /* - Safe conversion is not possible (or EOM). - We could not convert a string into the requested character set - without data loss. The target charset does not cover all the - characters from the string. Operation cannot be done correctly. - */ - return NULL; - } - conv->str_value.copy(); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); - return conv; -} + /* + Return "this" if in prepare. result_type may change at execition time, + to it's possible that the converter will not be needed at all: + PREPARE stmt FROM 'SELECT * FROM t1 WHERE field = ?'; + SET @@arg= 1; + EXECUTE stms USING @arg; -bool Item_string::eq(const Item *item, bool binary_cmp) const -{ - if (type() == item->type() && item->basic_const_item()) - { - if (binary_cmp) - return !stringcmp(&str_value, &item->str_value); - return (collation.collation == item->collation.collation && - !sortcmp(&str_value, &item->str_value, collation.collation)); - } - return 0; + In the above example result_type is STRING_RESULT at prepare time, + and INT_RESULT at execution time. + */ + return !const_item() || state == NULL_VALUE ? + this : const_charset_converter(tocs, true); } @@ -1379,7 +1372,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) + ltime, fuzzydate)) goto err; break; } @@ -2191,33 +2184,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) { - Item* conv; - uint32 dummy_offset; - if (!String::needs_conversion(1, (*arg)->collation.collation, - coll.collation, - &dummy_offset)) - continue; - - /* - No needs to add converter if an "arg" is NUMERIC or DATETIME - value (which is pure ASCII) and at the same time target DTCollation - is ASCII-compatible. For example, no needs to rewrite: - SELECT * FROM t1 WHERE datetime_field = '2010-01-01'; - to - SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01'; - - TODO: avoid conversion of any values with - repertoire ASCII and 7bit-ASCII-compatible, - not only numeric/datetime origin. - */ - if ((*arg)->collation.derivation == DERIVATION_NUMERIC && - (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII && - !((*arg)->collation.collation->state & MY_CS_NONASCII) && - !(coll.collation->state & MY_CS_NONASCII)) + Item* conv= (*arg)->safe_charset_converter(coll.collation); + if (conv == *arg) continue; - - if (!(conv= (*arg)->safe_charset_converter(coll.collation)) && - ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) + if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) conv= new Item_func_conv_charset(*arg, coll.collation, 1); if (!conv) @@ -2475,7 +2445,7 @@ void Item_field::set_field(Field *field_par) field_name= field_par->field_name; db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; - unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); + unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG); collation.set(field_par->charset(), field_par->derivation(), field_par->repertoire()); fix_char_length(field_par->char_length()); @@ -3003,7 +2973,7 @@ String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); - str->set_real(value,decimals,&my_charset_bin); + str->set_real(value, decimals, &my_charset_numeric); return str; } @@ -3098,7 +3068,7 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, We can use err.ptr() here as ErrConvString is guranteed to put an end \0 here. */ - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", err.ptr()); @@ -3135,7 +3105,7 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, (end != end_of_num && !check_if_only_end_space(cs, end_of_num, end)))) { ErrConvString err(cptr, end - cptr, cs); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); @@ -3162,10 +3132,6 @@ my_decimal *Item_string::val_decimal(my_decimal *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 @@ -3235,8 +3201,6 @@ Item_param::Item_param(uint pos_in_query_arg) : value is set. */ maybe_null= 1; - cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE); - cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin); } @@ -3341,14 +3305,10 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, value.time= *tm; value.time.time_type= time_type; - if (value.time.year > 9999 || value.time.month > 12 || - value.time.day > 31 || - (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) || - value.time.minute > 59 || value.time.second > 59 || - value.time.second_part > TIME_MAX_SECOND_PART) + if (check_datetime_range(&value.time)) { ErrConvTime str(&value.time); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } @@ -3607,7 +3567,7 @@ double Item_param::val_real() This works for example when user says SELECT ?+0.0 and supplies time value for the placeholder. */ - return ulonglong2double(TIME_to_ulonglong(&value.time)); + return TIME_to_double(&value.time); case NULL_VALUE: return 0.0; default: @@ -3665,9 +3625,7 @@ my_decimal *Item_param::val_decimal(my_decimal *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; + return TIME_to_my_decimal(&value.time, dec); } case NULL_VALUE: return 0; @@ -3761,8 +3719,9 @@ const String *Item_param::query_val_str(THD *thd, String* str) const case LONG_DATA_VALUE: { str->length(0); - append_query_string(thd, value.cs_info.character_set_client, &str_value, - str); + append_query_string(value.cs_info.character_set_client, str, + str_value.ptr(), str_value.length(), + thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); break; } case NULL_VALUE: @@ -3801,18 +3760,14 @@ bool Item_param::convert_str_value(THD *thd) str_value.set_charset(value.cs_info.final_character_set_of_str_value); /* Here str_value is guaranteed to be in final_character_set_of_str_value */ - max_length= str_value.numchars() * str_value.charset()->mbmaxlen; - - /* For the strings converted to numeric form within some functions */ - decimals= NOT_FIXED_DEC; /* str_value_ptr is returned from val_str(). It must be not alloced to prevent it's modification by val_str() invoker. */ str_value_ptr.set(str_value.ptr(), str_value.length(), str_value.charset()); - /* Synchronize item charset with value charset */ - collation.set(str_value.charset(), DERIVATION_COERCIBLE); + /* Synchronize item charset and length with value charset */ + fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE); } return rc; } @@ -3842,7 +3797,8 @@ Item_param::clone_item() case STRING_VALUE: case LONG_DATA_VALUE: return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), - str_value.charset()); + str_value.charset(), + collation.derivation, collation.repertoire); case TIME_VALUE: break; case NO_VALUE: @@ -3854,30 +3810,21 @@ Item_param::clone_item() bool -Item_param::eq(const Item *arg, bool binary_cmp) const +Item_param::eq(const Item *item, bool binary_cmp) const { - Item *item; - if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type()) + if (!basic_const_item()) return FALSE; - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - item= (Item*) arg; switch (state) { case NULL_VALUE: - return TRUE; + return null_eq(item); case INT_VALUE: - return value.integer == item->val_int() && - unsigned_flag == item->unsigned_flag; + return int_eq(value.integer, item); case REAL_VALUE: - return value.real == item->val_real(); + return real_eq(value.real, item); case STRING_VALUE: case LONG_DATA_VALUE: - if (binary_cmp) - return !stringcmp(&str_value, &item->str_value); - return !sortcmp(&str_value, &item->str_value, collation.collation); + return str_eq(&str_value, item, binary_cmp); default: break; } @@ -4137,8 +4084,8 @@ double Item_copy_string::val_real() longlong Item_copy_string::val_int() { int err; - return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(), - str_value.length(),10, (char**) 0, + return null_value ? 0 : my_strntoll(str_value.charset(),str_value.ptr(), + str_value.length(), 10, (char**) 0, &err); } @@ -4308,7 +4255,7 @@ double Item_copy_decimal::val_real() longlong Item_copy_decimal::val_int() { if (null_value) - return LL(0); + return 0; else { longlong result; @@ -4434,7 +4381,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, resolved_item->db_name : ""); const char *table_name= (resolved_item->table_name ? resolved_item->table_name : ""); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED), db_name, (db_name[0] ? "." : ""), table_name, (table_name [0] ? "." : ""), @@ -4682,7 +4629,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) !((*group_by_ref)->eq(*select_ref, 0))) { ambiguous_fields= TRUE; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), ref->full_name(), current_thd->where); @@ -5390,13 +5337,6 @@ bool Item_field::vcol_in_partition_func_processor(uchar *int_arg) } -Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) -{ - no_const_subst= 1; - return Item::safe_charset_converter(tocs); -} - - void Item_field::cleanup() { DBUG_ENTER("Item_field::cleanup"); @@ -5714,10 +5654,7 @@ String *Item::check_well_formed_result(String *str, bool send_error) { /* Check whether we got a well-formed string */ CHARSET_INFO *cs= str->charset(); - int well_formed_error; - uint wlen= cs->cset->well_formed_len(cs, - str->ptr(), str->ptr() + str->length(), - str->length(), &well_formed_error); + uint wlen= str->well_formed_length(); if (wlen < str->length()) { THD *thd= current_thd; @@ -5731,8 +5668,7 @@ String *Item::check_well_formed_result(String *str, bool send_error) cs->csname, hexbuf); return 0; } - if ((thd->variables.sql_mode & - (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))) + if (thd->is_strict_mode()) { null_value= 1; str= 0; @@ -5741,7 +5677,7 @@ String *Item::check_well_formed_result(String *str, bool send_error) { str->length(wlen); } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING, ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf); } return str; @@ -5877,29 +5813,23 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) field= new Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name, decimals, 0, unsigned_flag); break; - case MYSQL_TYPE_NULL: - field= new Field_null((uchar*) 0, max_length, Field::NONE, - name, &my_charset_bin); - break; case MYSQL_TYPE_INT24: field= new Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name, 0, unsigned_flag); break; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: - field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin); + field= new Field_newdate(0, null_ptr, 0, Field::NONE, name); break; case MYSQL_TYPE_TIME: - field= new_Field_time(0, null_ptr, 0, Field::NONE, name, - decimals, &my_charset_bin); + field= new_Field_time(0, null_ptr, 0, Field::NONE, name, decimals); break; case MYSQL_TYPE_TIMESTAMP: field= new_Field_timestamp(0, null_ptr, 0, - Field::NONE, name, 0, decimals, &my_charset_bin); + Field::NONE, name, 0, decimals); break; case MYSQL_TYPE_DATETIME: - field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, - decimals, &my_charset_bin); + field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, decimals); break; case MYSQL_TYPE_YEAR: field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, @@ -5913,6 +5843,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) /* This case should never be chosen */ DBUG_ASSERT(0); /* If something goes awfully wrong, it's better to get a string than die */ + case MYSQL_TYPE_NULL: case MYSQL_TYPE_STRING: if (fixed_length && !too_big_for_varchar()) { @@ -6013,13 +5944,51 @@ static int save_field_in_field(Field *from, bool *null_value, } +static int memcpy_field_value(Field *to, Field *from) +{ + if (to->ptr != from->ptr) + memcpy(to->ptr,from->ptr, to->pack_length()); + return 0; +} + +fast_field_copier Item_field::setup_fast_field_copier(Field *to) +{ + DBUG_ENTER("Item_field::setup_fast_field_copier"); + DBUG_RETURN(memcpy_field_possible(to, field) ? + &memcpy_field_value : + &field_conv_incompatible); +} + + /** Set a field's value from a item. */ -void Item_field::save_org_in_field(Field *to) +void Item_field::save_org_in_field(Field *to, + fast_field_copier fast_field_copier_func) { - save_field_in_field(field, &null_value, to, TRUE); + DBUG_ENTER("Item_field::save_org_in_field"); + DBUG_PRINT("enter", ("setup: 0x%lx data: 0x%lx", + (ulong) to, (ulong) fast_field_copier_func)); + if (fast_field_copier_func) + { + if (field->is_null()) + { + null_value= TRUE; + set_field_to_null_with_conversions(to, TRUE); + DBUG_VOID_RETURN; + } + to->set_notnull(); + if (to == field) + { + null_value= 0; + DBUG_VOID_RETURN; + } + (*fast_field_copier_func)(to, field); + } + else + save_field_in_field(field, &null_value, to, TRUE); + DBUG_VOID_RETURN; } @@ -6173,24 +6142,6 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions) } -bool Item_int::eq(const Item *arg, bool binary_cmp) const -{ - /* No need to check for null value as basic constant can't be NULL */ - if (arg->basic_const_item() && arg->type() == type()) - { - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - Item *item= (Item*) arg; - return (item->val_int() == value && - ((longlong) value >= 0 || - (item->unsigned_flag == unsigned_flag))); - } - return FALSE; -} - - Item *Item_int_with_ref::clone_item() { DBUG_ASSERT(ref->const_item()); @@ -6308,27 +6259,6 @@ void Item_float::print(String *str, enum_query_type query_type) } -/* - hex item - In string context this is a binary string. - In number context this is a longlong value. -*/ - -bool Item_float::eq(const Item *arg, bool binary_cmp) const -{ - if (arg->basic_const_item() && arg->type() == type()) - { - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - Item *item= (Item*) arg; - return item->val_real() == value; - } - return FALSE; -} - - inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : @@ -6366,7 +6296,7 @@ longlong Item_hex_hybrid::val_int() // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); char *end=(char*) str_value.ptr()+str_value.length(), - *ptr=end-min(str_value.length(),sizeof(longlong)); + *ptr=end-MY_MIN(str_value.length(),sizeof(longlong)); ulonglong value=0; for (; ptr != end ; ptr++) @@ -6384,8 +6314,6 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions) ulonglong nr; uint32 length= str_value.length(); - if (!length) - return 1; if (length > 8) { @@ -6402,7 +6330,7 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions) warn: if (!field->store((longlong) nr, TRUE)) - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, + field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } @@ -6410,7 +6338,7 @@ warn: void Item_hex_hybrid::print(String *str, enum_query_type query_type) { - uint32 len= min(str_value.length(), sizeof(longlong)); + uint32 len= MY_MIN(str_value.length(), sizeof(longlong)); const char *ptr= str_value.ptr() + str_value.length() - len; str->append("0x"); str->append_hex(ptr, len); @@ -6425,32 +6353,6 @@ void Item_hex_string::print(String *str, enum_query_type query_type) } -bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const -{ - if (arg->basic_const_item() && arg->type() == type() && - arg->cast_to_int_type() == cast_to_int_type()) - { - if (binary_cmp) - return !stringcmp(&str_value, &arg->str_value); - return !sortcmp(&str_value, &arg->str_value, collation.collation); - } - return FALSE; -} - - -Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs) -{ - Item_string *conv; - String tmp, *str= val_str(&tmp); - - if (!(conv= new Item_string(str->ptr(), str->length(), tocs))) - return NULL; - conv->str_value.copy(); - conv->str_value.mark_as_const(); - return conv; -} - - /* bin item. In string context this is a binary string. @@ -6495,6 +6397,78 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length) } +bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const +{ + return + item->basic_const_item() && type() == item->type() && + field_type() == ((Item_temporal_literal *) item)->field_type() && + !my_time_compare(&cached_time, + &((Item_temporal_literal *) item)->cached_time); +} + + +void Item_date_literal::print(String *str, enum_query_type query_type) +{ + str->append("DATE'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_date_to_str(&cached_time, buf); + str->append(buf); + str->append('\''); +} + + +bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + fuzzy_date |= sql_mode_for_dates(current_thd); + *ltime= cached_time; + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + +void Item_datetime_literal::print(String *str, enum_query_type query_type) +{ + str->append("TIMESTAMP'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_datetime_to_str(&cached_time, buf, decimals); + str->append(buf); + str->append('\''); +} + + +bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + fuzzy_date |= sql_mode_for_dates(current_thd); + *ltime= cached_time; + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + +void Item_time_literal::print(String *str, enum_query_type query_type) +{ + str->append("TIME'"); + char buf[MAX_DATE_STRING_REP_LENGTH]; + my_time_to_str(&cached_time, buf, decimals); + str->append(buf); + str->append('\''); +} + + +bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +{ + DBUG_ASSERT(fixed); + *ltime= cached_time; + if (fuzzy_date & TIME_TIME_ONLY) + return (null_value= false); + return (null_value= check_date_with_warn(ltime, fuzzy_date, + MYSQL_TIMESTAMP_ERROR)); +} + + + /** Pack data in buffer for sending. */ @@ -6596,7 +6570,7 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME tm; - get_date(&tm, sql_mode_for_dates()); + get_date(&tm, sql_mode_for_dates(current_thd)); if (!null_value) { if (f_type == MYSQL_TYPE_DATE) @@ -6682,6 +6656,13 @@ Item* Item::cache_const_expr_transformer(uchar *arg) return this; } +/** + Find Item by reference in the expression +*/ +bool Item::find_item_processor(uchar *arg) +{ + return (this == ((Item *) arg)); +} bool Item_field::send(Protocol *protocol, String *buffer) { @@ -7468,9 +7449,9 @@ int Item_ref::save_in_field(Field *to, bool no_conversions) } -void Item_ref::save_org_in_field(Field *field) +void Item_ref::save_org_in_field(Field *field, fast_field_copier optimizer_data) { - (*ref)->save_org_in_field(field); + (*ref)->save_org_in_field(field, optimizer_data); } @@ -8344,7 +8325,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) { TABLE_LIST *view= field_arg->table->pos_in_table_list->top_table(); push_warning_printf(field_arg->table->in_use, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), view->view_db.str, @@ -8353,7 +8334,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) else { push_warning_printf(field_arg->table->in_use, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER(ER_NO_DEFAULT_FOR_FIELD), field_arg->field_name); @@ -8766,6 +8747,28 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) { Item_result res_type=item_cmp_type(field->result_type(), item->result_type()); + /* + We have to check field->cmp_type() instead of res_type, + as result_type() - and thus res_type - can never be TIME_RESULT (yet). + */ + if (field->cmp_type() == TIME_RESULT) + { + MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time; + if (field->type() == MYSQL_TYPE_TIME) + { + field->get_time(&field_time); + item->get_time(&item_time); + } + else + { + field->get_date(&field_time, TIME_INVALID_DATES); + item->get_date(&item_time, TIME_INVALID_DATES); + if (item_time.time_type == MYSQL_TIMESTAMP_TIME) + if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2)) + return 1; + } + return my_time_compare(&field_time, item_time_cmp); + } if (res_type == STRING_RESULT) { char item_buff[MAX_FIELD_WIDTH]; @@ -8816,25 +8819,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) return my_decimal_cmp(field_val, item_val); } /* - We have to check field->cmp_type() instead of res_type, - as result_type() - and thus res_type - can never be TIME_RESULT (yet). - */ - if (field->cmp_type() == TIME_RESULT) - { - MYSQL_TIME field_time, item_time; - if (field->type() == MYSQL_TYPE_TIME) - { - field->get_time(&field_time); - item->get_time(&item_time); - } - else - { - field->get_date(&field_time, TIME_INVALID_DATES); - item->get_date(&item_time, TIME_INVALID_DATES); - } - return my_time_compare(&field_time, &item_time); - } - /* The patch for Bug#13463415 started using this function for comparing BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode. Prefixing the auto variables with volatile fixes the problem.... @@ -9549,7 +9533,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) /* fix variable decimals which always is NOT_FIXED_DEC */ if (Field::result_merge_type(fld_type) == INT_RESULT) item_decimals= 0; - decimals= max(decimals, item_decimals); + decimals= MY_MAX(decimals, item_decimals); } if (fld_type == FIELD_TYPE_GEOMETRY) @@ -9558,10 +9542,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item) if (Field::result_merge_type(fld_type) == DECIMAL_RESULT) { - decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE); + decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE); int item_int_part= item->decimal_int_part(); - int item_prec = max(prev_decimal_int_part, item_int_part) + decimals; - int precision= min(item_prec, DECIMAL_MAX_PRECISION); + int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals; + int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION); unsigned_flag&= item->unsigned_flag; max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, @@ -9592,7 +9576,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) */ if (collation.collation != &my_charset_bin) { - max_length= max(old_max_chars * collation.collation->mbmaxlen, + max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen, display_length(item) / item->collation.collation->mbmaxlen * collation.collation->mbmaxlen); @@ -9614,7 +9598,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) { int delta1= max_length_orig - decimals_orig; int delta2= item->max_length - item->decimals; - max_length= max(delta1, delta2) + decimals; + max_length= MY_MAX(delta1, delta2) + decimals; if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2) { max_length= MAX_FLOAT_STR_LENGTH; @@ -9632,7 +9616,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) break; } default: - max_length= max(max_length, display_length(item)); + max_length= MY_MAX(max_length, display_length(item)); }; maybe_null|= item->maybe_null; get_full_info(item); @@ -9928,14 +9912,3 @@ const char *dbug_print_item(Item *item) #endif /*DBUG_OFF*/ -/***************************************************************************** -** Instantiate templates -*****************************************************************************/ - -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<Item>; -template class List_iterator<Item>; -template class List_iterator_fast<Item>; -template class List_iterator_fast<Item_field>; -template class List<List_item>; -#endif |