diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 2502 |
1 files changed, 1210 insertions, 1292 deletions
diff --git a/sql/item.cc b/sql/item.cc index 719bdcfaf89..46e6e2cc838 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,13 +40,11 @@ // 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); -static int save_field_in_field(Field *from, bool *null_value, - Field *to, bool no_conversions); +static int save_field_in_field(Field *, bool *, Field *, bool); /** @@ -59,133 +56,16 @@ bool cmp_items(Item *a, Item *b) return a->eq(b, FALSE); } -/****************************************************************************/ - -/* Hybrid_type_traits {_real} */ - -void Hybrid_type_traits::fix_length_and_dec(Item *item, Item *arg) const -{ - item->decimals= NOT_FIXED_DEC; - item->max_length= item->float_length(arg->decimals); -} - -static const Hybrid_type_traits real_traits_instance; - -const Hybrid_type_traits *Hybrid_type_traits::instance() -{ - return &real_traits_instance; -} - - -my_decimal * -Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const -{ - double2my_decimal(E_DEC_FATAL_ERROR, val->real, val->dec_buf); - return val->dec_buf; -} - - -String * -Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const -{ - to->set_real(val->real, decimals, &my_charset_bin); - return to; -} - -/* Hybrid_type_traits_decimal */ -static const Hybrid_type_traits_decimal decimal_traits_instance; - -const Hybrid_type_traits_decimal *Hybrid_type_traits_decimal::instance() -{ - return &decimal_traits_instance; -} - - -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, - DECIMAL_MAX_STR_LENGTH); -} - - -void Hybrid_type_traits_decimal::set_zero(Hybrid_type *val) const -{ - my_decimal_set_zero(&val->dec_buf[0]); - val->used_dec_buf_no= 0; -} - - -void Hybrid_type_traits_decimal::add(Hybrid_type *val, Field *f) const -{ - my_decimal_add(E_DEC_FATAL_ERROR, - &val->dec_buf[val->used_dec_buf_no ^ 1], - &val->dec_buf[val->used_dec_buf_no], - f->val_decimal(&val->dec_buf[2])); - val->used_dec_buf_no^= 1; -} - /** - @todo - what is '4' for scale? + Set max_sum_func_level if it is needed */ -void Hybrid_type_traits_decimal::div(Hybrid_type *val, ulonglong u) const -{ - int2my_decimal(E_DEC_FATAL_ERROR, u, TRUE, &val->dec_buf[2]); - /* XXX: what is '4' for scale? */ - my_decimal_div(E_DEC_FATAL_ERROR, - &val->dec_buf[val->used_dec_buf_no ^ 1], - &val->dec_buf[val->used_dec_buf_no], - &val->dec_buf[2], 4); - val->used_dec_buf_no^= 1; -} - - -longlong -Hybrid_type_traits_decimal::val_int(Hybrid_type *val, bool unsigned_flag) const -{ - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], - unsigned_flag, &result); - return result; -} - - -double -Hybrid_type_traits_decimal::val_real(Hybrid_type *val) const -{ - my_decimal2double(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], - &val->real); - return val->real; -} - - -String * -Hybrid_type_traits_decimal::val_str(Hybrid_type *val, String *to, - uint8 decimals) const -{ - my_decimal_round(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], - decimals, FALSE, &val->dec_buf[2]); - my_decimal2string(E_DEC_FATAL_ERROR, &val->dec_buf[2], 0, 0, 0, to); - return to; -} - -/* Hybrid_type_traits_integer */ -static const Hybrid_type_traits_integer integer_traits_instance; - -const Hybrid_type_traits_integer *Hybrid_type_traits_integer::instance() +inline void set_max_sum_func_level(THD *thd, SELECT_LEX *select) { - return &integer_traits_instance; -} - -void -Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const -{ - item->decimals= 0; - item->max_length= MY_INT64_NUM_DECIMAL_DIGITS; - item->unsigned_flag= 0; + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level >= select->nest_level) + set_if_bigger(thd->lex->in_sum_func->max_sum_func_level, + select->nest_level - 1); } /***************************************************************************** @@ -198,7 +78,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(); } @@ -226,7 +106,6 @@ bool Item::val_bool() return val_real() != 0.0; case ROW_RESULT: case TIME_RESULT: - case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return 0; // Wrong (but safe) } @@ -234,6 +113,82 @@ 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) +{ + THD *thd= current_thd; + + /* + 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 && + !(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(thd, ltime, &tmp, fuzzydate)) + return null_value= true; + *ltime= tmp; + } + return false; +} + + +/** + Get date/time/datetime. + If DATETIME or DATE result is returned, it's converted to TIME. +*/ +bool Item::get_time_with_conversion(THD *thd, MYSQL_TIME *ltime, + ulonglong fuzzydate) +{ + if (get_date(ltime, fuzzydate)) + return true; + if (ltime->time_type != MYSQL_TIMESTAMP_TIME) + { + MYSQL_TIME ltime2; + if ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && + (ltime->year || ltime->day || ltime->month)) + { + /* + Old mode conversion from DATETIME with non-zero YYYYMMDD part + to TIME works very inconsistently. Possible variants: + - truncate the YYYYMMDD part + - add (MM*33+DD)*24 to hours + - add (MM*31+DD)*24 to hours + Let's return TRUE here, to disallow equal field propagation. + Note, If we start to use this method in more pieces of the code other + than equal field propagation, we should probably return + TRUE only if some flag in fuzzydate is set. + */ + return true; + } + if (datetime_to_time_with_warn(thd, ltime, <ime2, TIME_SECOND_PART_DIGITS)) + { + /* + If the time difference between CURRENT_DATE and ltime + did not fit into the supported TIME range, then we set the + difference to the maximum possible value in the supported TIME range + */ + DBUG_ASSERT(0); + return (null_value= true); + } + *ltime= ltime2; + } + return false; +} + + /* For the items which don't have its own fast val_str_ascii() implementation we provide a generic slower version, @@ -313,11 +268,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; @@ -356,17 +329,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) if (!(res= val_str(&str_value))) return 0; - if (str2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, - res->ptr(), res->length(), res->charset(), - decimal_value) & E_DEC_BAD_NUM) - { - ErrConvString err(res); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", - err.ptr()); - } - return decimal_value; + return decimal_from_string_with_check(decimal_value, res); } @@ -374,7 +337,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 @@ -401,18 +364,26 @@ 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; } +longlong Item::val_int_from_real() +{ + DBUG_ASSERT(fixed == 1); + bool error; + return double_to_longlong(val_real(), false /*unsigned_flag*/, &error); +} + + 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); } @@ -454,9 +425,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); @@ -494,23 +463,21 @@ int Item::save_str_value_in_field(Field *field, String *result) } -Item::Item(): +Item::Item(THD *thd): is_expensive_cache(-1), rsize(0), name(0), orig_name(0), name_length(0), - fixed(0), is_autogenerated_name(TRUE), - collation(&my_charset_bin, DERIVATION_COERCIBLE) + fixed(0), is_autogenerated_name(TRUE) { + DBUG_ASSERT(thd); marker= 0; - maybe_null=null_value=with_sum_func=with_field=unsigned_flag=0; + maybe_null=null_value=with_sum_func=with_field=0; in_rollup= 0; - decimals= 0; max_length= 0; with_subselect= 0; with_param= 0; - cmp_context= IMPOSSIBLE_RESULT; - /* Initially this item is not attached to any JOIN_TAB. */ + + /* Initially this item is not attached to any JOIN_TAB. */ join_tab_idx= MAX_TABLES; /* Put item in free list so that we can free all items at end */ - THD *thd= current_thd; next= thd->free_list; thd->free_list= this; /* @@ -536,28 +503,24 @@ Item::Item(): tables. */ Item::Item(THD *thd, Item *item): + Type_std_attributes(item), join_tab_idx(item->join_tab_idx), is_expensive_cache(-1), rsize(0), str_value(item->str_value), name(item->name), orig_name(item->orig_name), - max_length(item->max_length), name_length(item->name_length), - decimals(item->decimals), marker(item->marker), maybe_null(item->maybe_null), in_rollup(item->in_rollup), null_value(item->null_value), - unsigned_flag(item->unsigned_flag), with_sum_func(item->with_sum_func), with_param(item->with_param), with_field(item->with_field), fixed(item->fixed), is_autogenerated_name(item->is_autogenerated_name), - with_subselect(item->has_subquery()), - collation(item->collation), - cmp_context(item->cmp_context) + with_subselect(item->has_subquery()) { next= thd->free_list; // Put in free list thd->free_list= this; @@ -573,47 +536,37 @@ 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 MY_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 0; + uint res= max_char_length(); + /* + Return at least one decimal digit, even if Item::max_char_length() + returned 0. This is important to avoid attempts to create fields of types + INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL: + CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a; + */ + return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; } -#else -#error Change the code to use MYSQL_TIME_STATUS::precision instead. -#endif -uint Item::temporal_precision(enum_field_types type) +uint Item::temporal_precision(enum_field_types type_arg) { if (const_item() && result_type() == STRING_RESULT && !is_temporal_type(field_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_arg == 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); } @@ -650,7 +603,6 @@ void Item::print_value(String *str) break; case ROW_RESULT: case TIME_RESULT: - case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } } @@ -661,7 +613,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) @@ -731,14 +683,17 @@ 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; }; DBUG_ASSERT(0); - return IMPOSSIBLE_RESULT; + return STRING_RESULT; } /** @@ -770,11 +725,11 @@ Item_result Item::cmp_type() const pointer to newly allocated item is returned. */ -Item* Item::transform(Item_transformer transformer, uchar *arg) +Item* Item::transform(THD *thd, Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); + DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); - return (this->*transformer)(arg); + return (this->*transformer)(thd, arg); } @@ -797,7 +752,7 @@ Item* Item::set_expr_cache(THD *thd) { DBUG_ENTER("Item::set_expr_cache"); Item_cache_wrapper *wrapper; - if ((wrapper= new Item_cache_wrapper(this)) && + if ((wrapper= new (thd->mem_root) Item_cache_wrapper(thd, this)) && !wrapper->fix_fields(thd, (Item**)&wrapper)) { if (wrapper->set_cache(thd)) @@ -808,10 +763,11 @@ Item* Item::set_expr_cache(THD *thd) } -Item_ident::Item_ident(Name_resolution_context *context_arg, +Item_ident::Item_ident(THD *thd, Name_resolution_context *context_arg, const char *db_name_arg,const char *table_name_arg, const char *field_name_arg) - :orig_db_name(db_name_arg), orig_table_name(table_name_arg), + :Item_result_field(thd), orig_db_name(db_name_arg), + orig_table_name(table_name_arg), orig_field_name(field_name_arg), context(context_arg), db_name(db_name_arg), table_name(table_name_arg), field_name(field_name_arg), @@ -823,8 +779,9 @@ Item_ident::Item_ident(Name_resolution_context *context_arg, } -Item_ident::Item_ident(TABLE_LIST *view_arg, const char *field_name_arg) - :orig_db_name(NullS), orig_table_name(view_arg->table_name), +Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, const char *field_name_arg) + :Item_result_field(thd), orig_db_name(NullS), + orig_table_name(view_arg->table_name), orig_field_name(field_name_arg), context(&view_arg->view->select_lex.context), db_name(NullS), table_name(view_arg->alias), field_name(field_name_arg), @@ -841,7 +798,7 @@ Item_ident::Item_ident(TABLE_LIST *view_arg, const char *field_name_arg) */ Item_ident::Item_ident(THD *thd, Item_ident *item) - :Item(thd, item), + :Item_result_field(thd, item), orig_db_name(item->orig_db_name), orig_table_name(item->orig_table_name), orig_field_name(item->orig_field_name), @@ -860,7 +817,7 @@ void Item_ident::cleanup() { DBUG_ENTER("Item_ident::cleanup"); bool was_fixed= fixed; - Item::cleanup(); + Item_result_field::cleanup(); db_name= orig_db_name; table_name= orig_table_name; field_name= orig_field_name; @@ -871,7 +828,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; } @@ -889,10 +846,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; } @@ -1042,10 +1004,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. @@ -1055,21 +1022,24 @@ 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]; + THD *thd= current_thd; + + strmake(buff, str_start, + MY_MIN(sizeof(buff)-1, length + (int) (str-str_start))); + + if (length == 0) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NAME_BECOMES_EMPTY, + ER_THD(thd, ER_NAME_BECOMES_EMPTY), + buff); + else + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_REMOVED_SPACES, ER_THD(thd, ER_REMOVED_SPACES), + buff); } if (!my_charset_same(cs, system_charset_info)) { @@ -1080,7 +1050,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))); } @@ -1133,9 +1103,11 @@ bool Item::eq(const Item *item, bool binary_cmp) const } -Item *Item::safe_charset_converter(CHARSET_INFO *tocs) +Item *Item::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { - Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1); + if (!needs_charset_converter(tocs)) + return this; + Item_func_conv_charset *conv= new (thd->mem_root) Item_func_conv_charset(thd, this, tocs, 1); return conv->safe ? conv : NULL; } @@ -1147,7 +1119,7 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs) Example: Item_singlerow_subselect has "Item_cache **row". Creating of Item_func_conv_charset followed by THD::change_item_tree() - should not change row[i] from Item_cache directly to Item_func_conv_charset, because Item_singlerow_subselect + should not change row[i] from Item_cache directly to Item_func_conv_charset, because Item_singlerow_subselect later calls Item_cache-specific methods, e.g. row[i]->store() and row[i]->cache_value(). @@ -1160,18 +1132,18 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs) TODO: we should eventually check all other use cases of change_item_tree(). Perhaps some more potentially dangerous substitution examples exist. */ -Item *Item_cache::safe_charset_converter(CHARSET_INFO *tocs) +Item *Item_cache::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { if (!example) - return Item::safe_charset_converter(tocs); - Item *conv= example->safe_charset_converter(tocs); + return Item::safe_charset_converter(thd, tocs); + Item *conv= example->safe_charset_converter(thd, tocs); if (conv == example) return this; Item_cache *cache; - if (!conv || conv->fix_fields(current_thd, (Item **) NULL) || - !(cache= new Item_cache_str(conv))) + if (!conv || conv->fix_fields(thd, (Item **) NULL) || + !(cache= new (thd->mem_root) Item_cache_str(thd, conv))) return NULL; // Safe conversion is not possible, or OEM - cache->setup(conv); + cache->setup(thd, conv); cache->fixed= false; // Make Item::fix_fields() happy return cache; } @@ -1188,7 +1160,7 @@ Item *Item_cache::safe_charset_converter(CHARSET_INFO *tocs) the latter returns a non-fixed Item, so val_str() crashes afterwards. Override Item_num method, to return a fixed item. */ -Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs) +Item *Item_num::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { /* Item_num returns pure ASCII result, @@ -1198,114 +1170,59 @@ 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(thd, tocs, true))) + conv->fix_char_length(max_char_length()); return conv; } -Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs) -{ - 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()))) - { - conv->str_value.copy(); - conv->str_value.mark_as_const(); - } - return conv; -} +/** + 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. -Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) + @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(THD *thd, CHARSET_INFO *tocs, + bool lossless, + const char *func_name) { - 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))) - { - /* - 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(); - return conv; -} + DBUG_ASSERT(const_item()); + DBUG_ASSERT(fixed); + StringBuffer<64>tmp; + String *s= val_str(&tmp); + MEM_ROOT *mem_root= thd->mem_root; + if (!s) + return new (mem_root) Item_null(thd, (char *) func_name, tocs); -Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) -{ - if (const_item()) + if (!needs_charset_converter(s->length(), tocs)) { - uint cnv_errors; - String *ostr= val_str(&cnvstr); - if (null_value) - { - Item_null *n= new Item_null(); - return n ? n->safe_charset_converter(tocs) : NULL; - } - 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; + if (collation.collation == &my_charset_bin && tocs != &my_charset_bin && + !this->check_well_formed_result(s, true)) + return NULL; + return this; } - 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))) + Item_string *conv= (func_name ? + new (mem_root) + Item_static_string_func(thd, func_name, + s, tocs, &conv_errors, + collation.derivation, + collation.repertoire) : + new (mem_root) + Item_string(thd, s, tocs, &conv_errors, + collation.derivation, + collation.repertoire)); + + if (!conv || (conv_errors && lossless)) { /* Safe conversion is not possible (or EOM). @@ -1315,23 +1232,28 @@ Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) */ return NULL; } - conv->str_value.copy(); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); + if (s->charset() == &my_charset_bin && tocs != &my_charset_bin && + !conv->check_well_formed_result(true)) + return NULL; return conv; } -bool Item_string::eq(const Item *item, bool binary_cmp) const +Item *Item_param::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { - 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; + /* + 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 stmt USING @arg; + + 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(thd, tocs, true); } @@ -1365,7 +1287,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) ltime, fuzzydate, field_name_or_null())) goto err; - break; + return null_value= false; } case REAL_RESULT: { @@ -1373,7 +1295,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, field_name_or_null())) goto err; - break; + return null_value= false; } case DECIMAL_RESULT: { @@ -1382,7 +1304,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) decimal_to_datetime_with_warn(res, ltime, fuzzydate, field_name_or_null())) goto err; - break; + return null_value= false; } case STRING_RESULT: { @@ -1390,23 +1312,44 @@ 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; + return null_value= false; } default: + null_value= true; DBUG_ASSERT(0); } - return null_value= 0; - err: + return null_value|= make_zero_date(ltime, fuzzydate); +} + + +bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ /* if the item was not null and convertion failed, we return a zero date if allowed, otherwise - null. */ bzero((char*) ltime,sizeof(*ltime)); - return null_value|= !(fuzzydate & TIME_FUZZY_DATES); + if (fuzzydate & TIME_TIME_ONLY) + { + /* + In the following scenario: + - The caller expected to get a TIME value + - Item returned a not NULL string or numeric value + - But then conversion from string or number to TIME failed + we need to change the default time_type from MYSQL_TIMESTAMP_DATE + (which was set in bzero) to MYSQL_TIMESTAMP_TIME and therefore + return TIME'00:00:00' rather than DATE'0000-00-00'. + If we don't do this, methods like Item::get_time_with_conversion() + will erroneously subtract CURRENT_DATE from '0000-00-00 00:00:00' + and return TIME'-838:59:59' instead of TIME'00:00:00' as a result. + */ + ltime->time_type= MYSQL_TIMESTAMP_TIME; + } + return !(fuzzydate & TIME_FUZZY_DATES); } bool Item::get_seconds(ulonglong *sec, ulong *sec_part) @@ -1464,9 +1407,9 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) Item_sp_variable methods *****************************************************************************/ -Item_sp_variable::Item_sp_variable(char *sp_var_name_str, - uint sp_var_name_length) - :m_thd(0) +Item_sp_variable::Item_sp_variable(THD *thd, char *sp_var_name_str, + uint sp_var_name_length): + Item(thd), m_thd(0) #ifndef DBUG_OFF , m_sp(0) #endif @@ -1574,17 +1517,17 @@ bool Item_sp_variable::is_null() Item_splocal methods *****************************************************************************/ -Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, +Item_splocal::Item_splocal(THD *thd, const LEX_STRING &sp_var_name, uint sp_var_idx, enum_field_types sp_var_type, - uint pos_in_q, uint len_in_q) - :Item_sp_variable(sp_var_name.str, sp_var_name.length), - m_var_idx(sp_var_idx), - limit_clause_param(FALSE), - pos_in_query(pos_in_q), len_in_query(len_in_q) + uint pos_in_q, uint len_in_q): + Item_sp_variable(thd, sp_var_name.str, sp_var_name.length), + Rewritable_query_parameter(pos_in_q, len_in_q), + m_var_idx(sp_var_idx) { maybe_null= TRUE; + sp_var_type= real_type_to_type(sp_var_type); m_type= sp_map_item_type(sp_var_type); m_field_type= sp_var_type; m_result_type= sp_map_result_type(sp_var_type); @@ -1636,9 +1579,9 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) Item_case_expr methods *****************************************************************************/ -Item_case_expr::Item_case_expr(uint case_expr_id) - :Item_sp_variable( C_STRING_WITH_LEN("case_expr")), - m_case_expr_id(case_expr_id) +Item_case_expr::Item_case_expr(THD *thd, uint case_expr_id): + Item_sp_variable(thd, C_STRING_WITH_LEN("case_expr")), + m_case_expr_id(case_expr_id) { } @@ -1726,13 +1669,17 @@ bool Item_name_const::is_null() } -Item_name_const::Item_name_const(Item *name_arg, Item *val): - value_item(val), name_item(name_arg) +Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val): + Item(thd), value_item(val), name_item(name_arg) { + StringBuffer<128> name_buffer; + String *name_str; Item::maybe_null= TRUE; valid_args= true; - if (!name_item->basic_const_item()) + if (!name_item->basic_const_item() || + !(name_str= name_item->val_str(&name_buffer))) // Can't have a NULL name goto err; + set_name(name_str->ptr(), name_str->length(), name_str->charset()); if (value_item->basic_const_item()) return; // ok @@ -1794,25 +1741,18 @@ Item::Type Item_name_const::type() const bool Item_name_const::fix_fields(THD *thd, Item **ref) { - char buf[128]; - String *item_name; - String s(buf, sizeof(buf), &my_charset_bin); - s.length(0); - if (value_item->fix_fields(thd, &value_item) || name_item->fix_fields(thd, &name_item) || !value_item->const_item() || - !name_item->const_item() || - !(item_name= name_item->val_str(&s))) // Can't have a NULL name + !name_item->const_item()) { my_error(ER_RESERVED_SYNTAX, MYF(0), "NAME_CONST"); return TRUE; } - if (is_autogenerated_name) - { - set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info); - } - collation.set(value_item->collation.collation, DERIVATION_IMPLICIT); + if (value_item->collation.derivation == DERIVATION_NUMERIC) + collation.set_numeric(); + else + collation.set(value_item->collation.collation, DERIVATION_IMPLICIT); max_length= value_item->max_length; decimals= value_item->decimals; fixed= 1; @@ -1838,9 +1778,10 @@ void Item_name_const::print(String *str, enum_query_type query_type) class Item_aggregate_ref : public Item_ref { public: - Item_aggregate_ref(Name_resolution_context *context_arg, Item **item, - const char *table_name_arg, const char *field_name_arg) - :Item_ref(context_arg, item, table_name_arg, field_name_arg) {} + Item_aggregate_ref(THD *thd, Name_resolution_context *context_arg, + Item **item, const char *table_name_arg, + const char *field_name_arg): + Item_ref(thd, context_arg, item, table_name_arg, field_name_arg) {} virtual inline void print (String *str, enum_query_type query_type) { @@ -1858,76 +1799,105 @@ public: @param thd Thread handler @param ref_pointer_array Pointer to array of reference fields - @param fields All fields in select + @param fields All fields in select @param ref Pointer to item - @param skip_registered <=> function be must skipped for registered - SUM items + @param split_flags Zero or more of the following flags + SPLIT_FUNC_SKIP_REGISTERED: + Function be must skipped for registered SUM + SUM items + SPLIT_SUM_SELECT + We are called on the select level and have to + register items operated on sum function @note - This is from split_sum_func2() for items that should be split - All found SUM items are added FIRST in the fields list and we replace the item with a reference. + If this is an item in the SELECT list then we also have to split out + all arguments to functions used together with the sum function. + For example in case of SELECT A*sum(B) we have to split out both + A and sum(B). + This is not needed for ORDER BY, GROUP BY or HAVING as all references + to items in the select list are already of type REF + thd->fatal_error() may be called if we are out of memory */ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, List<Item> &fields, Item **ref, - bool skip_registered) + uint split_flags) { - /* An item of type Item_sum is registered <=> ref_by != 0 */ - if (type() == SUM_FUNC_ITEM && skip_registered && - ((Item_sum *) this)->ref_by) - return; - if ((type() != SUM_FUNC_ITEM && with_sum_func) || - (type() == FUNC_ITEM && - (((Item_func *) this)->functype() == Item_func::ISNOTNULLTEST_FUNC || - ((Item_func *) this)->functype() == Item_func::TRIG_COND_FUNC))) + if (unlikely(type() == SUM_FUNC_ITEM)) { - /* Will split complicated items and ignore simple ones */ - split_sum_func(thd, ref_pointer_array, fields); + /* An item of type Item_sum is registered if ref_by != 0 */ + if ((split_flags & SPLIT_SUM_SKIP_REGISTERED) && + ((Item_sum *) this)->ref_by) + return; } - else if ((type() == SUM_FUNC_ITEM || (used_tables() & ~PARAM_TABLE_BIT)) && - type() != SUBSELECT_ITEM && - (type() != REF_ITEM || - ((Item_ref*)this)->ref_type() == Item_ref::VIEW_REF)) + else { - /* - 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 (PARAM_TABLE_BIT is in effect a constant) - or already referenced (for example an item in HAVING) - Exception is Item_direct_view_ref which we need to convert to - Item_ref to allow fields from view being stored in tmp table. - */ - Item_aggregate_ref *item_ref; - uint el= fields.elements; - DBUG_ASSERT(fields.elements <= - thd->lex->current_select->ref_pointer_array_size); - /* - If this is an item_ref, get the original item - This is a safety measure if this is called for things that is - already a reference. - */ - Item *real_itm= real_item(); + /* Not a SUM() function */ + if (unlikely((!with_sum_func && !(split_flags & SPLIT_SUM_SELECT)))) + { + /* + This is not a SUM function and there are no SUM functions inside. + Nothing more to do. + */ + return; + } + if (likely(with_sum_func || + (type() == FUNC_ITEM && + (((Item_func *) this)->functype() == + Item_func::ISNOTNULLTEST_FUNC || + ((Item_func *) this)->functype() == + Item_func::TRIG_COND_FUNC)))) + { + /* Will call split_sum_func2() for all items */ + split_sum_func(thd, ref_pointer_array, fields, split_flags); + return; + } - ref_pointer_array[el]= real_itm; - if (!(item_ref= new Item_aggregate_ref(&thd->lex->current_select->context, - ref_pointer_array + el, 0, name))) - return; // fatal_error is set - if (type() == SUM_FUNC_ITEM) - item_ref->depended_from= ((Item_sum *) this)->depended_from(); - fields.push_front(real_itm); - thd->change_item_tree(ref, item_ref); + if (unlikely((!(used_tables() & ~PARAM_TABLE_BIT) || + type() == SUBSELECT_ITEM || + (type() == REF_ITEM && + ((Item_ref*)this)->ref_type() != Item_ref::VIEW_REF)))) + return; } + + /* + 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 (PARAM_TABLE_BIT is in effect a constant) + or already referenced (for example an item in HAVING) + Exception is Item_direct_view_ref which we need to convert to + Item_ref to allow fields from view being stored in tmp table. + */ + Item_aggregate_ref *item_ref; + uint el= fields.elements; + /* + If this is an item_ref, get the original item + This is a safety measure if this is called for things that is + already a reference. + */ + Item *real_itm= real_item(); + + ref_pointer_array[el]= real_itm; + if (!(item_ref= (new (thd->mem_root) + Item_aggregate_ref(thd, + &thd->lex->current_select->context, + ref_pointer_array + el, 0, name)))) + return; // fatal_error is set + if (type() == SUM_FUNC_ITEM) + item_ref->depended_from= ((Item_sum *) this)->depended_from(); + fields.push_front(real_itm); + thd->change_item_tree(ref, item_ref); } static bool -left_is_superset(DTCollation *left, DTCollation *right) +left_is_superset(const DTCollation *left, const DTCollation *right) { /* Allow convert to Unicode */ if (left->collation->state & MY_CS_UNICODE && @@ -1986,7 +1956,7 @@ left_is_superset(DTCollation *left, DTCollation *right) @endcode */ -bool DTCollation::aggregate(DTCollation &dt, uint flags) +bool DTCollation::aggregate(const DTCollation &dt, uint flags) { if (!my_charset_same(collation, dt.collation)) { @@ -2118,8 +2088,9 @@ void my_coll_agg_error(Item** args, uint count, const char *fname, } -bool agg_item_collations(DTCollation &c, const char *fname, - Item **av, uint count, uint flags, int item_sep) +bool Item_func_or_sum::agg_item_collations(DTCollation &c, const char *fname, + Item **av, uint count, + uint flags, int item_sep) { uint i; Item **arg; @@ -2164,17 +2135,14 @@ bool agg_item_collations(DTCollation &c, const char *fname, } -bool agg_item_collations_for_comparison(DTCollation &c, const char *fname, - Item **av, uint count, uint flags) -{ - return (agg_item_collations(c, fname, av, count, - flags | MY_COLL_DISALLOW_NONE, 1)); -} - - -bool agg_item_set_converter(DTCollation &coll, const char *fname, - Item **args, uint nargs, uint flags, int item_sep) +bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll, + const char *fname, + Item **args, uint nargs, + uint flags, int item_sep) { + THD *thd= current_thd; + if (thd->lex->is_ps_or_view_context_analysis()) + return false; Item **arg, *safe_args[2]= {NULL, NULL}; /* @@ -2190,7 +2158,6 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, safe_args[1]= args[item_sep]; } - THD *thd= current_thd; bool res= FALSE; uint i; @@ -2205,34 +2172,11 @@ 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)) + Item* conv= (*arg)->safe_charset_converter(thd, coll.collation); + if (conv == *arg) 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)) - continue; - - if (!(conv= (*arg)->safe_charset_converter(coll.collation)) && - ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) - conv= new Item_func_conv_charset(*arg, coll.collation, 1); + if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) + conv= new (thd->mem_root) Item_func_conv_charset(thd, *arg, coll.collation, 1); if (!conv) { @@ -2246,8 +2190,6 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, res= TRUE; break; // we cannot return here, we need to restore "arena". } - if ((*arg)->type() == Item::FIELD_ITEM) - ((Item_field *)(*arg))->no_const_subst= 1; /* If in statement prepare, then we create a converter for two constant items, do it once and then reuse it. @@ -2275,46 +2217,6 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, } -/* - Collect arguments' character sets together. - We allow to apply automatic character set conversion in some cases. - The conditions when conversion is possible are: - - arguments A and B have different charsets - - A wins according to coercibility rules - (i.e. a column is stronger than a string constant, - an explicit COLLATE clause is stronger than a column) - - character set of A is either superset for character set of B, - or B is a string constant which can be converted into the - character set of A without data loss. - - If all of the above is true, then it's possible to convert - B into the character set of A, and then compare according - to the collation of A. - - For functions with more than two arguments: - - collect(A,B,C) ::= collect(collect(A,B),C) - - Since this function calls THD::change_item_tree() on the passed Item ** - pointers, it is necessary to pass the original Item **'s, not copies. - Otherwise their values will not be properly restored (see BUG#20769). - If the items are not consecutive (eg. args[2] and args[5]), use the - item_sep argument, ie. - - agg_item_charsets(coll, fname, &args[2], 2, flags, 3) - -*/ - -bool agg_item_charsets(DTCollation &coll, const char *fname, - Item **args, uint nargs, uint flags, int item_sep) -{ - if (agg_item_collations(coll, fname, args, nargs, flags, item_sep)) - return TRUE; - - return agg_item_set_converter(coll, fname, args, nargs, flags, item_sep); -} - - void Item_ident_for_show::make_field(Send_field *tmp_field) { tmp_field->table_name= tmp_field->org_table_name= table_name; @@ -2330,9 +2232,9 @@ void Item_ident_for_show::make_field(Send_field *tmp_field) /**********************************************/ -Item_field::Item_field(Field *f) - :Item_ident(0, NullS, *f->table_name, f->field_name), - item_equal(0), no_const_subst(0), +Item_field::Item_field(THD *thd, Field *f) + :Item_ident(thd, 0, NullS, *f->table_name, f->field_name), + item_equal(0), have_privileges(0), any_privileges(0) { set_field(f); @@ -2354,8 +2256,8 @@ Item_field::Item_field(Field *f) Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, Field *f) - :Item_ident(context_arg, f->table->s->db.str, *f->table_name, f->field_name), - item_equal(0), no_const_subst(0), + :Item_ident(thd, context_arg, f->table->s->db.str, *f->table_name, f->field_name), + item_equal(0), have_privileges(0), any_privileges(0) { /* @@ -2394,14 +2296,14 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, } -Item_field::Item_field(Name_resolution_context *context_arg, +Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, const char *db_arg,const char *table_name_arg, const char *field_name_arg) - :Item_ident(context_arg, db_arg,table_name_arg,field_name_arg), - field(0), result_field(0), item_equal(0), no_const_subst(0), + :Item_ident(thd, context_arg, db_arg, table_name_arg, field_name_arg), + field(0), item_equal(0), have_privileges(0), any_privileges(0) { - SELECT_LEX *select= current_thd->lex->current_select; + SELECT_LEX *select= thd->lex->current_select; collation.set(DERIVATION_IMPLICIT); if (select && select->parsing_place != IN_HAVING) select->select_n_where_fields++; @@ -2415,9 +2317,7 @@ Item_field::Item_field(Name_resolution_context *context_arg, Item_field::Item_field(THD *thd, Item_field *item) :Item_ident(thd, item), field(item->field), - result_field(item->result_field), item_equal(item->item_equal), - no_const_subst(item->no_const_subst), have_privileges(item->have_privileges), any_privileges(item->any_privileges) { @@ -2489,7 +2389,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()); @@ -2529,23 +2429,45 @@ bool Item_field::update_table_bitmaps_processor(uchar *arg) return FALSE; } +static inline void set_field_to_new_field(Field **field, Field **new_field) +{ + if (*field && (*field)->table == new_field[0]->table) + { + Field *newf= new_field[(*field)->field_index]; + if ((*field)->ptr == newf->ptr) + *field= newf; + } +} + +bool Item_field::switch_to_nullable_fields_processor(uchar *arg) +{ + Field **new_fields= (Field **)arg; + set_field_to_new_field(&field, new_fields); + set_field_to_new_field(&result_field, new_fields); + maybe_null= field && field->maybe_null(); + return 0; +} + const char *Item_ident::full_name() const { char *tmp; if (!table_name || !field_name) return field_name ? field_name : name ? name : "tmp_field"; + if (db_name && db_name[0]) { - tmp=(char*) sql_alloc((uint) strlen(db_name)+(uint) strlen(table_name)+ - (uint) strlen(field_name)+3); + THD *thd= current_thd; + tmp=(char*) thd->alloc((uint) strlen(db_name)+(uint) strlen(table_name)+ + (uint) strlen(field_name)+3); strxmov(tmp,db_name,".",table_name,".",field_name,NullS); } else { if (table_name[0]) { - tmp= (char*) sql_alloc((uint) strlen(table_name) + - (uint) strlen(field_name) + 2); + THD *thd= current_thd; + tmp= (char*) thd->alloc((uint) strlen(table_name) + + (uint) strlen(field_name) + 2); strxmov(tmp, table_name, ".", field_name, NullS); } else @@ -2585,7 +2507,14 @@ void Item_ident::print(String *str, enum_query_type query_type) } if (db_name && db_name[0] && !alias_name_used) { - if (!(cached_table && cached_table->belong_to_view && + /* + When printing EXPLAIN, don't print database name when it's the same as + current database. + */ + bool skip_db= (query_type & QT_ITEM_IDENT_SKIP_CURRENT_DATABASE) && + thd->db && !strcmp(thd->db, db_name); + if (!skip_db && + !(cached_table && cached_table->belong_to_view && cached_table->belong_to_view->compact_view_format)) { append_identifier(thd, str, d_name, (uint)strlen(d_name)); @@ -2706,28 +2635,8 @@ my_decimal *Item_field::val_decimal_result(my_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() != 0; - 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: - case TIME_RESULT: - case IMPOSSIBLE_RESULT: - DBUG_ASSERT(0); - return 0; // Shut up compiler - } - return 0; + return false; + return result_field->val_bool(); } @@ -2739,11 +2648,11 @@ bool Item_field::is_null_result() bool Item_field::eq(const Item *item, bool binary_cmp) const { - Item *real_item= ((Item *) item)->real_item(); - if (real_item->type() != FIELD_ITEM) + Item *real_item2= ((Item *) item)->real_item(); + if (real_item2->type() != FIELD_ITEM) return 0; - Item_field *item_field= (Item_field*) real_item; + Item_field *item_field= (Item_field*) real_item2; if (item_field->field && field) return item_field->field == field; /* @@ -2779,7 +2688,8 @@ table_map Item_field::all_used_tables() const return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } -void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) +void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref, + bool merge) { if (new_parent == get_depended_from()) depended_from= NULL; @@ -2823,6 +2733,19 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) if (!need_change) return; + if (!merge) + { + /* + It is transformation without merge. + This field was "outer" for the inner SELECT where it was taken and + moved up. + "Outer" fields uses normal SELECT_LEX context of upper SELECTs for + name resolution, so we can switch everything to it safely. + */ + this->context= &new_parent->context; + return; + } + Name_resolution_context *ctx= new Name_resolution_context(); if (context->select_lex == new_parent) { @@ -2859,7 +2782,7 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) Item *Item_field::get_tmp_table_item(THD *thd) { - Item_field *new_item= new Item_field(thd, this); + Item_field *new_item= new (thd->mem_root) Item_temptable_field(thd, this); if (new_item) new_item->field= new_item->result_field; return new_item; @@ -2877,7 +2800,8 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp) This is always 'signed'. Unsigned values are created with Item_uint() */ -Item_int::Item_int(const char *str_arg, uint length) +Item_int::Item_int(THD *thd, const char *str_arg, uint length): + Item_num(thd) { char *end_ptr= (char*) str_arg + length; int error; @@ -2910,15 +2834,15 @@ void Item_int::print(String *str, enum_query_type query_type) } -Item_uint::Item_uint(const char *str_arg, uint length): - Item_int(str_arg, length) +Item_uint::Item_uint(THD *thd, const char *str_arg, uint length): + Item_int(thd, str_arg, length) { unsigned_flag= 1; } -Item_uint::Item_uint(const char *str_arg, longlong i, uint length): - Item_int(str_arg, i, length) +Item_uint::Item_uint(THD *thd, const char *str_arg, longlong i, uint length): + Item_int(thd, str_arg, i, length) { unsigned_flag= 1; } @@ -2941,8 +2865,9 @@ void Item_uint::print(String *str, enum_query_type query_type) } -Item_decimal::Item_decimal(const char *str_arg, uint length, - CHARSET_INFO *charset) +Item_decimal::Item_decimal(THD *thd, const char *str_arg, uint length, + CHARSET_INFO *charset): + Item_num(thd) { str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value); name= (char*) str_arg; @@ -2954,7 +2879,8 @@ Item_decimal::Item_decimal(const char *str_arg, uint length, unsigned_flag); } -Item_decimal::Item_decimal(longlong val, bool unsig) +Item_decimal::Item_decimal(THD *thd, longlong val, bool unsig): + Item_num(thd) { int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); decimals= (uint8) decimal_value.frac; @@ -2966,7 +2892,8 @@ Item_decimal::Item_decimal(longlong val, bool unsig) } -Item_decimal::Item_decimal(double val, int precision, int scale) +Item_decimal::Item_decimal(THD *thd, double val, int precision, int scale): + Item_num(thd) { double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); decimals= (uint8) decimal_value.frac; @@ -2978,8 +2905,9 @@ Item_decimal::Item_decimal(double val, int precision, int scale) } -Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, - uint decimal_par, uint length) +Item_decimal::Item_decimal(THD *thd, const char *str, const my_decimal *val_arg, + uint decimal_par, uint length): + Item_num(thd) { my_decimal2decimal(val_arg, &decimal_value); name= (char*) str; @@ -2989,7 +2917,8 @@ Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, } -Item_decimal::Item_decimal(my_decimal *value_par) +Item_decimal::Item_decimal(THD *thd, my_decimal *value_par): + Item_num(thd) { my_decimal2decimal(value_par, &decimal_value); decimals= (uint8) decimal_value.frac; @@ -3001,7 +2930,8 @@ Item_decimal::Item_decimal(my_decimal *value_par) } -Item_decimal::Item_decimal(const uchar *bin, int precision, int scale) +Item_decimal::Item_decimal(THD *thd, const uchar *bin, int precision, int scale): + Item_num(thd) { binary2my_decimal(E_DEC_FATAL_ERROR, bin, &decimal_value, precision, scale); @@ -3070,11 +3000,18 @@ void Item_decimal::set_decimal_value(my_decimal *value_par) } +Item *Item_decimal::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_decimal(thd, name, &decimal_value, decimals, + max_length); +} + + 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; } @@ -3088,6 +3025,13 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value) } +Item *Item_float::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_float(thd, name, value, decimals, + max_length); +} + + void Item_string::print(String *str, enum_query_type query_type) { const bool print_introducer= @@ -3121,25 +3065,7 @@ void Item_string::print(String *str, enum_query_type query_type) } else { - if (my_charset_same(str_value.charset(), system_charset_info)) - str_value.print(str); // already in system_charset_info - else // need to convert - { - THD *thd= current_thd; - LEX_STRING utf8_lex_str; - - thd->convert_string(&utf8_lex_str, - system_charset_info, - str_value.c_ptr_safe(), - str_value.length(), - str_value.charset()); - - String utf8_str(utf8_lex_str.str, - utf8_lex_str.length, - system_charset_info); - - utf8_str.print(str); - } + str_value.print(str, system_charset_info); } } else @@ -3152,66 +3078,10 @@ void Item_string::print(String *str, enum_query_type query_type) } -double -double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, - const char *end) -{ - int error; - char *end_of_num= (char*) end; - double tmp; - - tmp= my_strntod(cs, (char*) cptr, end - cptr, &end_of_num, &error); - if (error || (end != end_of_num && - !check_if_only_end_space(cs, end_of_num, end))) - { - ErrConvString err(cptr, end - cptr, cs); - /* - 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, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", - err.ptr()); - } - return tmp; -} - - double Item_string::val_real() { DBUG_ASSERT(fixed == 1); - return double_from_string_with_check(str_value.charset(), - str_value.ptr(), - str_value.ptr() + - str_value.length()); -} - - -longlong -longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, - const char *end) -{ - int err; - longlong tmp; - char *end_of_num= (char*) end; - - tmp= (*(cs->cset->strtoll10))(cs, cptr, &end_of_num, &err); - /* - TODO: Give error if we wanted a signed integer and we got an unsigned - one - */ - if (!current_thd->no_errors && - (err > 0 || - (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, - ER_TRUNCATED_WRONG_VALUE, - ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", - err.ptr()); - } - return tmp; + return double_from_string_with_check(&str_value); } @@ -3222,8 +3092,7 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, longlong Item_string::val_int() { DBUG_ASSERT(fixed == 1); - return longlong_from_string_with_check(str_value.charset(), str_value.ptr(), - str_value.ptr()+ str_value.length()); + return longlong_from_string_with_check(&str_value); } @@ -3233,10 +3102,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 @@ -3266,12 +3131,25 @@ my_decimal *Item_null::val_decimal(my_decimal *decimal_value) } -Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs) +bool Item_null::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) +{ + // following assert is redundant, because fixed=1 assigned in constructor + DBUG_ASSERT(fixed == 1); + make_zero_date(ltime, fuzzydate); + return (null_value= true); +} + + +Item *Item_null::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { - collation.set(tocs); return this; } +Item *Item_null::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_null(thd, name); +} + /*********************** Item_param related ******************************/ /** @@ -3288,15 +3166,15 @@ default_set_param_func(Item_param *param, } -Item_param::Item_param(uint pos_in_query_arg) : +Item_param::Item_param(THD *thd, uint pos_in_query_arg): + Item_basic_value(thd), + Rewritable_query_parameter(pos_in_query_arg, 1), state(NO_VALUE), item_result_type(STRING_RESULT), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), param_type(MYSQL_TYPE_VARCHAR), - pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func), - limit_clause_param(FALSE), m_out_param_info(NULL) { name= (char*) "?"; @@ -3306,8 +3184,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); } @@ -3412,14 +3288,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); } @@ -3523,7 +3395,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) break; case STRING_RESULT: { - CHARSET_INFO *fromcs= entry->collation.collation; + CHARSET_INFO *fromcs= entry->charset(); CHARSET_INFO *tocs= thd->variables.collation_connection; uint32 dummy_offset; @@ -3563,7 +3435,6 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) } case ROW_RESULT: case TIME_RESULT: - case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); set_null(); } @@ -3668,17 +3539,14 @@ double Item_param::val_real() case STRING_VALUE: case LONG_DATA_VALUE: { - int dummy_err; - char *end_not_used; - return my_strntod(str_value.charset(), (char*) str_value.ptr(), - str_value.length(), &end_not_used, &dummy_err); + return double_from_string_with_check(&str_value); } case TIME_VALUE: /* 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: @@ -3707,9 +3575,7 @@ longlong Item_param::val_int() case STRING_VALUE: case LONG_DATA_VALUE: { - int dummy_err; - return my_strntoll(str_value.charset(), str_value.ptr(), - str_value.length(), 10, (char**) 0, &dummy_err); + return longlong_from_string_with_check(&str_value); } case TIME_VALUE: return (longlong) TIME_to_ulonglong(&value.time); @@ -3735,13 +3601,10 @@ my_decimal *Item_param::val_decimal(my_decimal *dec) return dec; case STRING_VALUE: case LONG_DATA_VALUE: - string2my_decimal(E_DEC_FATAL_ERROR, &str_value, dec); - return dec; + return decimal_from_string_with_check(dec, &str_value); 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; @@ -3835,8 +3698,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: @@ -3875,18 +3739,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; } @@ -3900,23 +3760,28 @@ bool Item_param::basic_const_item() const } +/* see comments in the header file */ + Item * -Item_param::clone_item() +Item_param::clone_item(THD *thd) { - /* see comments in the header file */ + MEM_ROOT *mem_root= thd->mem_root; switch (state) { case NULL_VALUE: - return new Item_null(name); + return new (mem_root) Item_null(thd, name); case INT_VALUE: return (unsigned_flag ? - new Item_uint(name, value.integer, max_length) : - new Item_int(name, value.integer, max_length)); + new (mem_root) Item_uint(thd, name, value.integer, max_length) : + new (mem_root) Item_int(thd, name, value.integer, max_length)); case REAL_VALUE: - return new Item_float(name, value.real, decimals, max_length); + return new (mem_root) Item_float(thd, name, value.real, decimals, + max_length); case STRING_VALUE: case LONG_DATA_VALUE: - return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), - str_value.charset()); + return new (mem_root) Item_string(thd, name, str_value.c_ptr_quick(), + str_value.length(), str_value.charset(), + collation.derivation, + collation.repertoire); case TIME_VALUE: break; case NO_VALUE: @@ -3928,30 +3793,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; } @@ -4001,17 +3857,14 @@ void Item_param::print(String *str, enum_query_type query_type) void Item_param::set_param_type_and_swap_value(Item_param *src) { - unsigned_flag= src->unsigned_flag; + Type_std_attributes::set(src); param_type= src->param_type; set_param_func= src->set_param_func; item_type= src->item_type; item_result_type= src->item_result_type; - collation.set(src->collation); maybe_null= src->maybe_null; null_value= src->null_value; - max_length= src->max_length; - decimals= src->decimals; state= src->state; value= src->value; @@ -4169,6 +4022,13 @@ void Item_param::make_field(Send_field *field) field->type= m_out_param_info->type; } +bool Item_param::append_for_log(THD *thd, String *str) +{ + StringBuffer<STRING_BUFFER_USUAL_SIZE> buf; + const String *val= query_val_str(thd, &buf); + return str->append(*val); +} + /**************************************************************************** Item_copy_string @@ -4186,8 +4046,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); } @@ -4333,12 +4193,13 @@ 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, - ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED), - db_name, (db_name[0] ? "." : ""), - table_name, (table_name [0] ? "." : ""), - resolved_item->field_name, - current->select_number, last->select_number); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_FIELD_RESOLVED, + ER_THD(thd,ER_WARN_FIELD_RESOLVED), + db_name, (db_name[0] ? "." : ""), + table_name, (table_name [0] ? "." : ""), + resolved_item->field_name, + current->select_number, last->select_number); } DBUG_RETURN(FALSE); } @@ -4595,9 +4456,10 @@ 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, - ER(ER_NON_UNIQ_ERROR), ref->full_name(), - current_thd->where); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NON_UNIQ_ERROR, + ER_THD(thd,ER_NON_UNIQ_ERROR), ref->full_name(), + thd->where); } } @@ -4719,7 +4581,7 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) @retval 0 column fully fixed and fix_fields() should return FALSE @retval - -1 error occured + -1 error occurred */ int @@ -4806,7 +4668,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (select->join) { marker= select->cur_pos_in_select_list; - select->join->non_agg_fields.push_back(this); + select->join->non_agg_fields.push_back(this, thd->mem_root); } else { @@ -4842,10 +4704,10 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) fix_inner_refs() function. */ ; - if (!(rf= new Item_outer_ref(context, this))) + if (!(rf= new (thd->mem_root) Item_outer_ref(thd, context, this))) return -1; thd->change_item_tree(reference, rf); - select->inner_refs_list.push_back(rf); + select->inner_refs_list.push_back(rf, thd->mem_root); rf->in_sum_func= thd->lex->in_sum_func; } /* @@ -4872,10 +4734,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) else { Item::Type ref_type= (*reference)->type(); - prev_subselect_item->used_tables_cache|= - (*reference)->used_tables(); - prev_subselect_item->const_item_cache&= - (*reference)->const_item(); + prev_subselect_item->used_tables_and_const_cache_join(*reference); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? @@ -4907,8 +4766,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (ref != not_found_item) { DBUG_ASSERT(*ref && (*ref)->fixed); - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); + prev_subselect_item->used_tables_and_const_cache_join(*ref); break; } } @@ -4959,20 +4817,24 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) save= *ref; *ref= NULL; // Don't call set_properties() rf= (place == IN_HAVING ? - new Item_ref(context, ref, (char*) table_name, - (char*) field_name, alias_name_used) : + new (thd->mem_root) + Item_ref(thd, context, ref, (char*) table_name, + (char*) field_name, alias_name_used) : (!select->group_list.elements ? - new Item_direct_ref(context, ref, (char*) table_name, - (char*) field_name, alias_name_used) : - new Item_outer_ref(context, ref, (char*) table_name, - (char*) field_name, alias_name_used))); + new (thd->mem_root) + Item_direct_ref(thd, context, ref, (char*) table_name, + (char*) field_name, alias_name_used) : + new (thd->mem_root) + Item_outer_ref(thd, context, ref, (char*) table_name, + (char*) field_name, alias_name_used))); *ref= save; if (!rf) return -1; if (place != IN_HAVING && select->group_list.elements) { - outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf); + outer_context->select_lex->inner_refs_list.push_back((Item_outer_ref*)rf, + thd->mem_root); ((Item_outer_ref*)rf)->in_sum_func= thd->lex->in_sum_func; } thd->change_item_tree(reference, rf); @@ -4984,6 +4846,11 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (rf->fix_fields(thd, reference) || rf->check_cols(1)) return -1; + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, rf, rf); @@ -4992,13 +4859,18 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) } else { + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, select); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, (Item_ident*)*reference); if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; - rf= new Item_ref(context, (*from_field)->table->s->db.str, + rf= new (thd->mem_root) Item_ref(thd, context, (*from_field)->table->s->db.str, (*from_field)->table->alias.c_ptr(), (char*) field_name); if (!rf) @@ -5094,7 +4966,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) { uint counter; enum_resolution_type resolution; - Item** res= find_item_in_list(this, thd->lex->current_select->item_list, + Item** res= find_item_in_list(this, + thd->lex->current_select->item_list, &counter, REPORT_EXCEPT_NOT_FOUND, &resolution); if (!res) @@ -5118,10 +4991,15 @@ bool Item_field::fix_fields(THD *thd, Item **reference) { /* The column to which we link isn't valid. */ my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name, - current_thd->where); + thd->where); return(1); } + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, thd->lex->current_select); set_field(new_field); return 0; } @@ -5132,11 +5010,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Item_ref to point to the Item in the select list and replace the Item_field created by the parser with the new Item_ref. */ - Item_ref *rf= new Item_ref(context, db_name,table_name,field_name); + Item_ref *rf= new (thd->mem_root) + Item_ref(thd, context, db_name, table_name, + field_name); if (!rf) return 1; - bool ret= rf->fix_fields(thd, (Item **) &rf) || rf->check_cols(1); - if (ret) + bool err= rf->fix_fields(thd, (Item **) &rf) || rf->check_cols(1); + if (err) return TRUE; SELECT_LEX *select= thd->lex->current_select; @@ -5144,6 +5024,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) select->parsing_place == IN_GROUP_BY && alias_name_used ? *rf->ref : rf); + /* + We can not "move" aggregate function in the place where + its arguments are not defined. + */ + set_max_sum_func_level(thd, thd->lex->current_select); return FALSE; } } @@ -5245,7 +5130,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) thd->lex->current_select->cur_pos_in_select_list != UNDEF_POS && thd->lex->current_select->join) { - thd->lex->current_select->join->non_agg_fields.push_back(this); + thd->lex->current_select->join->non_agg_fields.push_back(this, thd->mem_root); marker= thd->lex->current_select->cur_pos_in_select_list; } mark_non_agg_field: @@ -5281,7 +5166,7 @@ mark_non_agg_field: else { if (outer_fixed) - thd->lex->in_sum_func->outer_fields.push_back(this); + thd->lex->in_sum_func->outer_fields.push_back(this, thd->mem_root); else if (thd->lex->in_sum_func->nest_level != thd->lex->current_select->nest_level) select_lex->set_non_agg_field_used(true); @@ -5310,13 +5195,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"); @@ -5327,7 +5205,7 @@ void Item_field::cleanup() it will be linked correctly next time by name of field and table alias. I.e. we can drop 'field'. */ - field= result_field= 0; + field= 0; item_equal= NULL; null_value= FALSE; DBUG_VOID_RETURN; @@ -5373,75 +5251,6 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) /** - Check whether a field item can be substituted for an equal item - - @details - The function checks whether a substitution of a field item for - an equal item is valid. - - @param arg *arg != NULL <-> the field is in the context - where substitution for an equal item is valid - - @note - The following statement is not always true: - @n - x=y => F(x)=F(x/y). - @n - This means substitution of an item for an equal item not always - yields an equavalent condition. Here's an example: - @code - 'a'='a ' - (LENGTH('a')=1) != (LENGTH('a ')=2) - @endcode - Such a substitution is surely valid if either the substituted - field is not of a STRING type or if it is an argument of - a comparison predicate. - - @retval - TRUE substitution is valid - @retval - FALSE otherwise -*/ - -bool Item_field::subst_argument_checker(uchar **arg) -{ - return *arg && - (*arg == (uchar *) Item::ANY_SUBST || - result_type() != STRING_RESULT || - (field->flags & BINARY_FLAG)); -} - - -/** - Convert a numeric value to a zero-filled string - - @param[in,out] item the item to operate on - @param field The field that this value is equated to - - This function converts a numeric value to a string. In this conversion - the zero-fill flag of the field is taken into account. - This is required so the resulting string value can be used instead of - the field reference when propagating equalities. -*/ - -static void convert_zerofill_number_to_string(Item **item, Field_num *field) -{ - char buff[MAX_FIELD_WIDTH],*pos; - String tmp(buff,sizeof(buff), field->charset()), *res; - - res= (*item)->val_str(&tmp); - if ((*item)->is_null()) - *item= new Item_null(); - else - { - field->prepend_zeros(res); - pos= (char *) sql_strmake (res->ptr(), res->length()); - *item= new Item_string(pos, res->length(), field->charset()); - } -} - - -/** Set a pointer to the multiple equality the field reference belongs to (if any). @@ -5465,47 +5274,44 @@ static void convert_zerofill_number_to_string(Item **item, Field_num *field) - pointer to the field item, otherwise. */ -Item *Item_field::equal_fields_propagator(uchar *arg) +Item *Item_field::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *arg) { - if (no_const_subst) + if (!(item_equal= find_item_equal(arg))) return this; - item_equal= find_item_equal((COND_EQUAL *) arg); - Item *item= 0; - if (item_equal) - item= item_equal->get_const(); - /* - Disable const propagation for items used in different comparison contexts. - This must be done because, for example, Item_hex_string->val_int() is not - the same as (Item_hex_string->val_str() in BINARY column)->val_int(). - We cannot simply disable the replacement in a particular context ( - e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since - Items don't know the context they are in and there are functions like - IF (<hex_string>, 'yes', 'no'). - */ - if (!item || !has_compatible_context(item)) - item= this; - else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type())) + if (!field->can_be_substituted_to_equal_item(ctx, item_equal)) { - if (item && (cmp_context == STRING_RESULT || cmp_context == IMPOSSIBLE_RESULT)) - convert_zerofill_number_to_string(&item, (Field_num *)field); - else - item= this; + item_equal= NULL; + return this; } - return item; -} - - -/** - Mark the item to not be part of substitution if it's not a binary item. - - See comments in Arg_comparator::set_compare_func() for details. -*/ + Item *item= item_equal->get_const(); + if (!item) + { + /* + The found Item_equal is Okey, but it does not have a constant + item yet. Keep this->item_equal point to the found Item_equal. + */ + return this; + } + if (!(item= field->get_equal_const_item(thd, ctx, item))) + { + /* + Could not do safe conversion from the original constant item + to a field-compatible constant item. + For example, we tried to optimize: + WHERE date_column=' garbage ' AND LENGTH(date_column)=8; + to + WHERE date_column=' garbage ' AND LENGTH(DATE'XXXX-YY-ZZ'); + but failed to create a valid DATE literal from the given string literal. -bool Item_field::set_no_const_sub(uchar *arg) -{ - if (field->charset() != &my_charset_bin) - no_const_subst=1; - return FALSE; + Do not do constant propagation in such cases and unlink + "this" from the found Item_equal (as this equality not usefull). + */ + item_equal= NULL; + return this; + } + return item; } @@ -5535,17 +5341,28 @@ bool Item_field::set_no_const_sub(uchar *arg) - this - otherwise. */ -Item *Item_field::replace_equal_field(uchar *arg) +Item *Item_field::replace_equal_field(THD *thd, uchar *arg) { REPLACE_EQUAL_FIELD_ARG* param= (REPLACE_EQUAL_FIELD_ARG*)arg; if (item_equal && item_equal == param->item_equal) { - Item *const_item= item_equal->get_const(); - if (const_item) + Item *const_item2= item_equal->get_const(); + if (const_item2) { - if (!has_compatible_context(const_item)) - return this; - return const_item; + /* + Currently we don't allow to create Item_equal with compare_type() + different from its Item_field's cmp_type(). + Field_xxx::test_if_equality_guarantees_uniqueness() prevent this. + Also, Item_field::propagate_equal_fields() does not allow to assign + this->item_equal to any instances of Item_equal if "this" is used + in a non-native comparison context, or with an incompatible collation. + So the fact that we have (item_equal != NULL) means that the currently + processed function (the owner of "this") uses the field in its native + comparison context, and it's safe to replace it to the constant from + item_equal. + */ + DBUG_ASSERT(cmp_type() == item_equal->compare_type()); + return const_item2; } Item_field *subst= (Item_field *)(item_equal->get_first(param->context_tab, this)); @@ -5584,17 +5401,6 @@ void Item::make_field(Send_field *tmp_field) } -enum_field_types Item::string_field_type() const -{ - enum_field_types f_type= MYSQL_TYPE_VAR_STRING; - if (max_length >= 16777216) - f_type= MYSQL_TYPE_LONG_BLOB; - else if (max_length >= 65536) - f_type= MYSQL_TYPE_MEDIUM_BLOB; - return f_type; -} - - void Item_empty_string::make_field(Send_field *tmp_field) { init_make_field(tmp_field, string_field_type()); @@ -5610,7 +5416,6 @@ enum_field_types Item::field_type() const case REAL_RESULT: return MYSQL_TYPE_DOUBLE; case ROW_RESULT: case TIME_RESULT: - case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VARCHAR; } @@ -5634,10 +5439,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(); null_value= false; if (wlen < str->length()) { @@ -5652,8 +5454,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; @@ -5662,12 +5463,53 @@ 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, - ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_CHARACTER_STRING, + ER_THD(thd, ER_INVALID_CHARACTER_STRING), cs->csname, + hexbuf); } return str; } + +/** + Copy a string with optional character set conversion. +*/ +bool +String_copier_for_item::copy_with_warn(CHARSET_INFO *dstcs, String *dst, + CHARSET_INFO *srccs, const char *src, + uint32 src_length, uint32 nchars) +{ + if ((dst->copy(dstcs, srccs, src, src_length, nchars, this))) + return true; // EOM + if (const char *pos= well_formed_error_pos()) + { + ErrConvString err(pos, src_length - (pos - src), &my_charset_bin); + push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN, + ER_INVALID_CHARACTER_STRING, + ER_THD(m_thd, ER_INVALID_CHARACTER_STRING), + srccs == &my_charset_bin ? + dstcs->csname : srccs->csname, + err.ptr()); + return false; + } + if (const char *pos= cannot_convert_error_pos()) + { + char buf[16]; + int mblen= srccs->cset->charlen(srccs, (const uchar *) pos, + (const uchar *) src + src_length); + DBUG_ASSERT(mblen > 0 && mblen * 2 + 1 <= (int) sizeof(buf)); + octet2hex(buf, pos, mblen); + push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN, + ER_CANNOT_CONVERT_CHARACTER, + ER_THD(m_thd, ER_CANNOT_CONVERT_CHARACTER), + srccs->csname, buf, dstcs->csname); + return false; + } + return false; +} + + /* Compare two items using a given collation @@ -5725,22 +5567,26 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs) Field *Item::make_string_field(TABLE *table) { Field *field; + MEM_ROOT *mem_root= table->in_use->mem_root; + DBUG_ASSERT(collation.collation); /* Note: the following check is repeated in subquery_types_allow_materialization(): */ if (too_big_for_varchar()) - field= new Field_blob(max_length, maybe_null, name, - collation.collation, TRUE); + field= new (mem_root) + Field_blob(max_length, maybe_null, name, + collation.collation, TRUE); /* Item_type_holder holds the exact type, do not change it */ else if (max_length > 0 && (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING)) - field= new Field_varstring(max_length, maybe_null, name, table->s, - collation.collation); + field= new (mem_root) + Field_varstring(max_length, maybe_null, name, table->s, + collation.collation); else - field= new Field_string(max_length, maybe_null, name, - collation.collation); + field= new (mem_root) + Field_string(max_length, maybe_null, name, collation.collation); if (field) field->init(table); return field; @@ -5759,83 +5605,94 @@ Field *Item::make_string_field(TABLE *table) \# Created field */ -Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) +Field *Item::tmp_table_field_from_field_type(TABLE *table, + bool fixed_length, + bool set_blob_packlength) { /* The field functions defines a field to be not null if null_ptr is not 0 */ uchar *null_ptr= maybe_null ? (uchar*) "" : 0; Field *field; + MEM_ROOT *mem_root= table->in_use->mem_root; switch (field_type()) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - field= Field_new_decimal::create_from_item(this); + field= Field_new_decimal::create_from_item(mem_root, this); break; case MYSQL_TYPE_TINY: - field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + field= new (mem_root) + Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); break; case MYSQL_TYPE_SHORT: - field= new Field_short((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + field= new (mem_root) + Field_short((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); break; case MYSQL_TYPE_LONG: - field= new Field_long((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + field= new (mem_root) + Field_long((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); break; #ifdef HAVE_LONG_LONG case MYSQL_TYPE_LONGLONG: - field= new Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + field= new (mem_root) + Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, 0, unsigned_flag); break; #endif case MYSQL_TYPE_FLOAT: - field= new Field_float((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, decimals, 0, unsigned_flag); + field= new (mem_root) + Field_float((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, decimals, 0, unsigned_flag); break; case MYSQL_TYPE_DOUBLE: - field= new Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, decimals, 0, unsigned_flag); + field= new (mem_root) + Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE, + name, decimals, 0, unsigned_flag); break; case MYSQL_TYPE_INT24: - field= new Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); + field= new (mem_root) + 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 (mem_root) + 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(mem_root, 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= new_Field_timestamp(mem_root, 0, null_ptr, 0, + 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(mem_root, 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, - name); + field= new (mem_root) + Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name); break; case MYSQL_TYPE_BIT: - field= new Field_bit_as_char(NULL, max_length, null_ptr, 0, - Field::NONE, name); + field= new (mem_root) + Field_bit_as_char(NULL, max_length, null_ptr, 0, Field::NONE, name); break; default: /* 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_STRING: case MYSQL_TYPE_NULL: + case MYSQL_TYPE_STRING: if (fixed_length && !too_big_for_varchar()) { - field= new Field_string(max_length, maybe_null, name, - collation.collation); + field= new (mem_root) + Field_string(max_length, maybe_null, name, collation.collation); break; } /* fall through */ @@ -5848,16 +5705,14 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: - if (this->type() == Item::TYPE_HOLDER) - field= new Field_blob(max_length, maybe_null, name, collation.collation, - 1); - else - field= new Field_blob(max_length, maybe_null, name, collation.collation); + field= new (mem_root) + Field_blob(max_length, maybe_null, name, + collation.collation, set_blob_packlength); break; // Blob handled outside of case #ifdef HAVE_SPATIAL case MYSQL_TYPE_GEOMETRY: - field= new Field_geom(max_length, maybe_null, - name, table->s, get_geometry_type()); + field= new (mem_root) + Field_geom(max_length, maybe_null, name, table->s, get_geometry_type()); #endif /* HAVE_SPATIAL */ } if (field) @@ -5931,13 +5786,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; } @@ -6052,6 +5945,14 @@ int Item_string::save_in_field(Field *field, bool no_conversions) } +Item *Item_string::clone_item(THD *thd) +{ + return new (thd->mem_root) + Item_string(thd, name, str_value.ptr(), + str_value.length(), collation.collation); +} + + static int save_int_value_in_field (Field *field, longlong nr, bool null_value, bool unsigned_flag) { @@ -6068,6 +5969,12 @@ int Item_int::save_in_field(Field *field, bool no_conversions) } +Item *Item_int::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_int(thd, name, value, max_length); +} + + void Item_datetime::set(longlong packed) { unpack_time(packed, <ime); @@ -6091,25 +5998,7 @@ 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() +Item *Item_int_with_ref::clone_item(THD *thd) { DBUG_ASSERT(ref->const_item()); /* @@ -6117,18 +6006,25 @@ Item *Item_int_with_ref::clone_item() parameter markers. */ return (ref->unsigned_flag ? - new Item_uint(ref->name, ref->val_int(), ref->max_length) : - new Item_int(ref->name, ref->val_int(), ref->max_length)); + new (thd->mem_root) + Item_uint(thd, ref->name, ref->val_int(), ref->max_length) : + new (thd->mem_root) + Item_int(thd, ref->name, ref->val_int(), ref->max_length)); } -Item_num *Item_uint::neg() +Item_num *Item_uint::neg(THD *thd) { - Item_decimal *item= new Item_decimal(value, 1); - return item->neg(); + Item_decimal *item= new (thd->mem_root) Item_decimal(thd, value, 1); + return item->neg(thd); } +Item *Item_uint::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_uint(thd, name, value, max_length); +} + static uint nr_of_decimals(const char *str, const char *end) { const char *decimal_point; @@ -6183,7 +6079,8 @@ static uint nr_of_decimals(const char *str, const char *end) Item->name should be fixed to use LEX_STRING eventually. */ -Item_float::Item_float(const char *str_arg, uint length) +Item_float::Item_float(THD *thd, const char *str_arg, uint length): + Item_num(thd) { int error; char *end_not_used; @@ -6226,27 +6123,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' : @@ -6255,10 +6131,11 @@ inline uint char_val(char X) } -void Item_hex_constant::hex_string_init(const char *str, uint str_length) +void Item_hex_constant::hex_string_init(THD *thd, const char *str, + uint str_length) { max_length=(str_length+1)/2; - char *ptr=(char*) sql_alloc(max_length+1); + char *ptr=(char*) thd->alloc(max_length+1); if (!ptr) { str_value.set("", 0, &my_charset_bin); @@ -6284,7 +6161,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++) @@ -6302,8 +6179,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) { @@ -6320,7 +6195,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; } @@ -6328,7 +6203,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); @@ -6343,47 +6218,22 @@ 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. In number context this is a longlong value. */ -Item_bin_string::Item_bin_string(const char *str, uint str_length) +Item_bin_string::Item_bin_string(THD *thd, const char *str, uint str_length): + Item_hex_hybrid(thd) { const char *end= str + str_length - 1; + char *ptr; uchar bits= 0; uint power= 1; max_length= (str_length + 7) >> 3; - char *ptr= (char*) sql_alloc(max_length + 1); - if (!ptr) + if (!(ptr= (char*) thd->alloc(max_length + 1))) return; str_value.set(ptr, max_length, &my_charset_bin); @@ -6413,6 +6263,96 @@ 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('\''); +} + + +Item *Item_date_literal::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_date_literal(thd, &cached_time); +} + + +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('\''); +} + + +Item *Item_datetime_literal::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_datetime_literal(thd, &cached_time, decimals); +} + + +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('\''); +} + + +Item *Item_time_literal::clone_item(THD *thd) +{ + return new (thd->mem_root) Item_time_literal(thd, &cached_time, decimals); +} + + +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. */ @@ -6514,7 +6454,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) @@ -6563,6 +6503,7 @@ bool Item::cache_const_expr_analyzer(uchar **arg) item->type() == Item::NULL_ITEM || /* Item_name_const hack */ item->type() == Item::FIELD_ITEM || item->type() == SUBSELECT_ITEM || + item->type() == CACHE_ITEM || /* Do not cache GET_USER_VAR() function as its const_item() may return TRUE for the current thread but it still may change @@ -6586,21 +6527,28 @@ bool Item::cache_const_expr_analyzer(uchar **arg) @return this otherwise. */ -Item* Item::cache_const_expr_transformer(uchar *arg) +Item* Item::cache_const_expr_transformer(THD *thd, uchar *arg) { if (*(bool*)arg) { *((bool*)arg)= FALSE; - Item_cache *cache= Item_cache::get_cache(this); + Item_cache *cache= Item_cache::get_cache(thd, this); if (!cache) return NULL; - cache->setup(this); + cache->setup(thd, this); cache->store(this); return cache; } 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) { @@ -6608,6 +6556,18 @@ bool Item_field::send(Protocol *protocol, String *buffer) } +Item* Item::propagate_equal_fields_and_change_item_tree(THD *thd, + const Context &ctx, + COND_EQUAL *cond, + Item **place) +{ + Item *item= propagate_equal_fields(thd, ctx, cond); + if (item && item != this) + thd->change_item_tree(place, item); + return item; +} + + void Item_field::update_null_value() { /* @@ -6641,12 +6601,12 @@ void Item_field::update_null_value() UPDATE statement. RETURN - 0 if error occured + 0 if error occurred ref if all conditions are met this field otherwise */ -Item *Item_field::update_value_transformer(uchar *select_arg) +Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg) { SELECT_LEX *select= (SELECT_LEX*)select_arg; DBUG_ASSERT(fixed); @@ -6661,9 +6621,10 @@ Item *Item_field::update_value_transformer(uchar *select_arg) Item_ref *ref; ref_pointer_array[el]= (Item*)this; - all_fields->push_front((Item*)this); - ref= new Item_ref(&select->context, ref_pointer_array + el, - table_name, field_name); + all_fields->push_front((Item*)this, thd->mem_root); + ref= new (thd->mem_root) + Item_ref(thd, &select->context, ref_pointer_array + el, + table_name, field_name); return ref; } return this; @@ -6682,12 +6643,22 @@ void Item_field::print(String *str, enum_query_type query_type) } -Item_ref::Item_ref(Name_resolution_context *context_arg, +void Item_temptable_field::print(String *str, enum_query_type query_type) +{ + /* + Item_ident doesn't have references to the underlying Field/TABLE objects, + so it's ok to use the following: + */ + Item_ident::print(str, query_type); +} + + +Item_ref::Item_ref(THD *thd, Name_resolution_context *context_arg, Item **item, const char *table_name_arg, const char *field_name_arg, - bool alias_name_used_arg) - :Item_ident(context_arg, NullS, table_name_arg, field_name_arg), - result_field(0), ref(item), reference_trough_name(0) + bool alias_name_used_arg): + Item_ident(thd, context_arg, NullS, table_name_arg, field_name_arg), + ref(item), reference_trough_name(0) { alias_name_used= alias_name_used_arg; /* @@ -6732,10 +6703,10 @@ public: } }; -Item_ref::Item_ref(TABLE_LIST *view_arg, Item **item, - const char *field_name_arg, bool alias_name_used_arg) - :Item_ident(view_arg, field_name_arg), - result_field(NULL), ref(item), reference_trough_name(0) +Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item, + const char *field_name_arg, bool alias_name_used_arg): + Item_ident(thd, view_arg, field_name_arg), + ref(item), reference_trough_name(0) { alias_name_used= alias_name_used_arg; /* @@ -6868,8 +6839,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) if (ref != not_found_item) { DBUG_ASSERT(*ref && (*ref)->fixed); - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); + prev_subselect_item->used_tables_and_const_cache_join(*ref); break; } /* @@ -6913,10 +6883,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) if (from_field == view_ref_found) { Item::Type refer_type= (*reference)->type(); - prev_subselect_item->used_tables_cache|= - (*reference)->used_tables(); - prev_subselect_item->const_item_cache&= - (*reference)->const_item(); + prev_subselect_item->used_tables_and_const_cache_join(*reference); DBUG_ASSERT((*reference)->type() == REF_ITEM); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, @@ -6969,7 +6936,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) if (from_field != not_found_field) { Item_field* fld; - if (!(fld= new Item_field(from_field))) + if (!(fld= new (thd->mem_root) Item_field(thd, from_field))) goto error; thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, @@ -7009,19 +6976,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) last_checked_context->select_lex->nest_level); } } - else if (ref_type() != VIEW_REF) - { - /* - It could be that we're referring to something that's in ancestor selects. - We must make an appropriate mark_as_dependent() call for each such - outside reference. - */ - Dependency_marker dep_marker; - dep_marker.current_select= current_sel; - dep_marker.thd= thd; - (*ref)->walk(&Item::enumerate_field_refs_processor, FALSE, - (uchar*)&dep_marker); - } DBUG_ASSERT(*ref); /* @@ -7058,10 +7012,8 @@ error: void Item_ref::set_properties() { - max_length= (*ref)->max_length; + Type_std_attributes::set(*ref); 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. @@ -7069,7 +7021,6 @@ void Item_ref::set_properties() with_sum_func= (*ref)->with_sum_func; with_param= (*ref)->with_param; with_field= (*ref)->with_field; - unsigned_flag= (*ref)->unsigned_flag; fixed= 1; if (alias_name_used) return; @@ -7084,7 +7035,6 @@ void Item_ref::cleanup() { DBUG_ENTER("Item_ref::cleanup"); Item_ident::cleanup(); - result_field= 0; if (reference_trough_name) { /* We have to reset the reference as it may been freed */ @@ -7098,7 +7048,7 @@ void Item_ref::cleanup() Transform an Item_ref object with a transformer callback function. The function first applies the transform method to the item - referenced by this Item_reg object. If this returns a new item the + referenced by this Item_ref object. If this returns a new item the old item is substituted for a new one. After this the transformer is applied to the Item_ref object. @@ -7111,13 +7061,13 @@ void Item_ref::cleanup() @retval NULL Out of memory error */ -Item* Item_ref::transform(Item_transformer transformer, uchar *arg) +Item* Item_ref::transform(THD *thd, Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); + DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); DBUG_ASSERT((*ref) != NULL); /* Transform the object we are referencing. */ - Item *new_item= (*ref)->transform(transformer, arg); + Item *new_item= (*ref)->transform(thd, transformer, arg); if (!new_item) return NULL; @@ -7128,10 +7078,10 @@ Item* Item_ref::transform(Item_transformer transformer, uchar *arg) change records at each execution. */ if (*ref != new_item) - current_thd->change_item_tree(ref, new_item); + thd->change_item_tree(ref, new_item); /* Transform the item ref object. */ - return (this->*transformer)(arg); + return (this->*transformer)(thd, arg); } @@ -7157,7 +7107,7 @@ Item* Item_ref::transform(Item_transformer transformer, uchar *arg) @return Item returned as the result of transformation of the Item_ref object */ -Item* Item_ref::compile(Item_analyzer analyzer, uchar **arg_p, +Item* Item_ref::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t) { /* Analyze this Item object. */ @@ -7169,13 +7119,13 @@ Item* Item_ref::compile(Item_analyzer analyzer, uchar **arg_p, if (*arg_p) { uchar *arg_v= *arg_p; - Item *new_item= (*ref)->compile(analyzer, &arg_v, transformer, arg_t); + Item *new_item= (*ref)->compile(thd, analyzer, &arg_v, transformer, arg_t); if (new_item && *ref != new_item) - current_thd->change_item_tree(ref, new_item); + thd->change_item_tree(ref, new_item); } /* Transform this Item object. */ - return (this->*transformer)(arg_t); + return (this->*transformer)(thd, arg_t); } @@ -7269,26 +7219,8 @@ 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() != 0; - 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: - case TIME_RESULT: - case IMPOSSIBLE_RESULT: - DBUG_ASSERT(0); - } + return false; + return result_field->val_bool(); } return val_bool(); } @@ -7393,9 +7325,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); } @@ -7421,7 +7353,7 @@ Item *Item_ref::get_tmp_table_item(THD *thd) if (!result_field) return (*ref)->get_tmp_table_item(thd); - Item_field *item= new Item_field(result_field); + Item_field *item= new (thd->mem_root) Item_field(thd, result_field); if (item) { item->table_name= table_name; @@ -7506,24 +7438,21 @@ Item_cache_wrapper::~Item_cache_wrapper() DBUG_ASSERT(expr_cache == 0); } -Item_cache_wrapper::Item_cache_wrapper(Item *item_arg) -:orig_item(item_arg), expr_cache(NULL), expr_value(NULL) +Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg): + Item_result_field(thd), orig_item(item_arg), expr_cache(NULL), expr_value(NULL) { DBUG_ASSERT(orig_item->fixed); - max_length= orig_item->max_length; + Type_std_attributes::set(orig_item); maybe_null= orig_item->maybe_null; - decimals= orig_item->decimals; - collation.set(orig_item->collation); with_sum_func= orig_item->with_sum_func; with_param= orig_item->with_param; with_field= orig_item->with_field; - unsigned_flag= orig_item->unsigned_flag; name= item_arg->name; name_length= item_arg->name_length; with_subselect= orig_item->with_subselect; - if ((expr_value= Item_cache::get_cache(orig_item))) - expr_value->setup(orig_item); + if ((expr_value= Item_cache::get_cache(thd, orig_item))) + expr_value->setup(thd, orig_item); fixed= 1; } @@ -7545,7 +7474,14 @@ void Item_cache_wrapper::init_on_demand() void Item_cache_wrapper::print(String *str, enum_query_type query_type) { - str->append(func_name()); + if (query_type & QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS) + { + /* Don't print the cache in EXPLAIN EXTENDED */ + orig_item->print(str, query_type); + return; + } + + str->append("<expr_cache>"); if (expr_cache) { init_on_demand(); @@ -7621,6 +7557,19 @@ bool Item_cache_wrapper::set_cache(THD *thd) DBUG_RETURN(expr_cache == NULL); } +Expression_cache_tracker* Item_cache_wrapper::init_tracker(MEM_ROOT *mem_root) +{ + if (expr_cache) + { + Expression_cache_tracker* tracker= + new(mem_root) Expression_cache_tracker(expr_cache); + if (tracker) + ((Expression_cache_tmptable *)expr_cache)->set_tracker(tracker); + return tracker; + } + return NULL; +} + /** Check if the current values of the parameters are in the expression cache @@ -7883,11 +7832,11 @@ int Item_cache_wrapper::save_in_field(Field *to, bool no_conversions) } -Item* Item_cache_wrapper::get_tmp_table_item(THD *thd_arg) +Item* Item_cache_wrapper::get_tmp_table_item(THD *thd) { if (!orig_item->with_sum_func && !orig_item->const_item()) - return new Item_field(result_field); - return copy_or_same(thd_arg); + return new (thd->mem_root) Item_temptable_field(thd, result_field); + return copy_or_same(thd); } @@ -7971,18 +7920,20 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference) } -void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref) +void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, + Item **ref_arg, bool merge) { if (get_depended_from() == new_parent) { - *ref= outer_ref; - (*ref)->fix_after_pullout(new_parent, ref); + *ref_arg= outer_ref; + (*ref_arg)->fix_after_pullout(new_parent, ref_arg, merge); } } -void Item_ref::fix_after_pullout(st_select_lex *new_parent, Item **refptr) +void Item_ref::fix_after_pullout(st_select_lex *new_parent, Item **refptr, + bool merge) { - (*ref)->fix_after_pullout(new_parent, ref); + (*ref)->fix_after_pullout(new_parent, ref, merge); if (get_depended_from() == new_parent) depended_from= NULL; } @@ -8010,12 +7961,12 @@ bool Item_outer_ref::check_inner_refs_processor(uchar *arg) { List_iterator_fast<Item_outer_ref> *it= ((List_iterator_fast<Item_outer_ref> *) arg); - Item_outer_ref *ref; - while ((ref= (*it)++)) + Item_outer_ref *tmp_ref; + while ((tmp_ref= (*it)++)) { - if (ref == this) + if (tmp_ref == this) { - ref->found_in_group_by= 1; + tmp_ref->found_in_group_by= 1; break; } } @@ -8066,43 +8017,6 @@ Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal) /** - Check whether a reference to field item can be substituted for an equal item - - @details - The function checks whether a substitution of a reference to field item for - an equal item is valid. - - @param arg *arg != NULL <-> the reference is in the context - where substitution for an equal item is valid - - @note - See also the note for Item_field::subst_argument_checker - - @retval - TRUE substitution is valid - @retval - FALSE otherwise -*/ -bool Item_direct_view_ref::subst_argument_checker(uchar **arg) -{ - bool res= FALSE; - if (*arg) - { - Item *item= real_item(); - if (item->type() == FIELD_ITEM && - (*arg == (uchar *) Item::ANY_SUBST || - result_type() != STRING_RESULT || - (((Item_field *) item)->field->flags & BINARY_FLAG))) - res= TRUE; - } - /* Block any substitution into the wrapped object */ - if (*arg) - *arg= NULL; - return res; -} - - -/** Set a pointer to the multiple equality the view field reference belongs to (if any). @@ -8122,7 +8036,7 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg) of the compile method. @note - The function calls Item_field::equal_fields_propagator for the field item + The function calls Item_field::propagate_equal_fields() for the field item this->real_item() to do the job. Then it takes the pointer to equal_item from this field item and assigns it to this->item_equal. @@ -8131,12 +8045,14 @@ bool Item_direct_view_ref::subst_argument_checker(uchar **arg) - pointer to the field item, otherwise. */ -Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg) +Item *Item_direct_view_ref::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *cond) { Item *field_item= real_item(); if (field_item->type() != FIELD_ITEM) return this; - Item *item= field_item->equal_fields_propagator(arg); + Item *item= field_item->propagate_equal_fields(thd, ctx, cond); set_item_equal(field_item->get_item_equal()); field_item->set_item_equal(NULL); if (item != field_item) @@ -8156,7 +8072,7 @@ Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg) object belongs to unless item_equal contains a constant item. In this case the function returns this constant item (if the substitution does not require conversion). - If the Item_direct_view_item object does not refer any Item_equal object + If the Item_direct_view_ref object does not refer any Item_equal object 'this' is returned . @param arg NULL or points to so some item of the Item_equal type @@ -8175,13 +8091,13 @@ Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg) - this - otherwise. */ -Item *Item_direct_view_ref::replace_equal_field(uchar *arg) +Item *Item_direct_view_ref::replace_equal_field(THD *thd, uchar *arg) { Item *field_item= real_item(); if (field_item->type() != FIELD_ITEM) return this; field_item->set_item_equal(item_equal); - Item *item= field_item->replace_equal_field(arg); + Item *item= field_item->replace_equal_field(thd, arg); field_item->set_item_equal(0); return item != field_item ? item : this; } @@ -8223,9 +8139,10 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name); goto error; } - if (!(def_field= (Field*) sql_alloc(field_arg->field->size_of()))) + if (!(def_field= (Field*) thd->alloc(field_arg->field->size_of()))) goto error; - memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); + memcpy((void *)def_field, (void *)field_arg->field, + field_arg->field->size_of()); def_field->move_field_offset((my_ptrdiff_t) (def_field->table->s->default_values - def_field->table->record[0])); @@ -8255,38 +8172,42 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) { if (!arg) { + TABLE *table= field_arg->table; + THD *thd= table->in_use; + if (field_arg->flags & NO_DEFAULT_VALUE_FLAG && field_arg->real_type() != MYSQL_TYPE_ENUM) { if (field_arg->reset()) { my_message(ER_CANT_CREATE_GEOMETRY_OBJECT, - ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); + ER_THD(thd, ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0)); return -1; } if (context->error_processor == &view_error_processor) { - 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, + TABLE_LIST *view= table->pos_in_table_list->top_table(); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, - ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), + ER_THD(thd, ER_NO_DEFAULT_FOR_VIEW_FIELD), view->view_db.str, view->view_name.str); } else { - push_warning_printf(field_arg->table->in_use, - MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, - ER(ER_NO_DEFAULT_FOR_FIELD), + ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), field_arg->field_name); } return 1; } field_arg->set_default(); - return 0; + return + !field_arg->is_null() && + field_arg->validate_value_in_record_with_warn(thd, table->record[0]) && + thd->is_error() ? -1 : 0; } return Item_field::save_in_field(field_arg, no_conversions); } @@ -8297,9 +8218,10 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) same time it can replace some nodes in the tree. */ -Item *Item_default_value::transform(Item_transformer transformer, uchar *args) +Item *Item_default_value::transform(THD *thd, Item_transformer transformer, + uchar *args) { - DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); + DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare()); /* If the value of arg is NULL, then this object represents a constant, @@ -8308,7 +8230,7 @@ Item *Item_default_value::transform(Item_transformer transformer, uchar *args) if (!arg) return 0; - Item *new_item= arg->transform(transformer, args); + Item *new_item= arg->transform(thd, transformer, args); if (!new_item) return 0; @@ -8319,8 +8241,8 @@ Item *Item_default_value::transform(Item_transformer transformer, uchar *args) change records at each execution. */ if (arg != new_item) - current_thd->change_item_tree(&arg, new_item); - return (this->*transformer)(args); + thd->change_item_tree(&arg, new_item); + return (this->*transformer)(thd, args); } @@ -8358,10 +8280,11 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) if (field_arg->field->table->insert_values) { - Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); + Field *def_field= (Field*) thd->alloc(field_arg->field->size_of()); if (!def_field) return TRUE; - memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of()); + memcpy((void *)def_field, (void *)field_arg->field, + field_arg->field->size_of()); def_field->move_field_offset((my_ptrdiff_t) (def_field->table->insert_values - def_field->table->record[0])); @@ -8477,6 +8400,7 @@ bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) int err_code= item->save_in_field(field, 0); field->table->copy_blobs= copy_blobs_saved; + field->set_explicit_default(item); return err_code < 0; } @@ -8563,11 +8487,13 @@ Item_result item_cmp_type(Item_result a,Item_result b) void resolve_const_item(THD *thd, Item **ref, Item *comp_item) { Item *item= *ref; - Item *new_item= NULL; if (item->basic_const_item()) return; // Can't be better - Item_result res_type=item_cmp_type(comp_item->cmp_type(), item->cmp_type()); + + Item *new_item= NULL; + Item_result res_type= item_cmp_type(comp_item, item); char *name=item->name; // Alloced by sql_alloc + MEM_ROOT *mem_root= thd->mem_root; switch (res_type) { case TIME_RESULT: @@ -8575,9 +8501,10 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) bool is_null; Item **ref_copy= ref; /* the following call creates a constant and puts it in new_item */ - get_datetime_value(thd, &ref_copy, &new_item, comp_item, &is_null); + enum_field_types type= item->field_type_for_temporal_comparison(comp_item); + get_datetime_value(thd, &ref_copy, &new_item, type, &is_null); if (is_null) - new_item= new Item_null(name); + new_item= new (mem_root) Item_null(thd, name); break; } case STRING_RESULT: @@ -8586,12 +8513,12 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) String tmp(buff,sizeof(buff),&my_charset_bin),*result; result=item->val_str(&tmp); if (item->null_value) - new_item= new Item_null(name); + new_item= new (mem_root) Item_null(thd, name); else { uint length= result->length(); char *tmp_str= sql_strmake(result->ptr(), length); - new_item= new Item_string(name, tmp_str, length, result->charset()); + new_item= new (mem_root) Item_string(thd, name, tmp_str, length, result->charset()); } break; } @@ -8600,15 +8527,15 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) 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)); + new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) : + (Item*) new (mem_root) Item_int(thd, name, result, length)); break; } case ROW_RESULT: if (item->type() == Item::ROW_ITEM && comp_item->type() == Item::ROW_ITEM) { /* - Substitute constants only in Item_rows. Don't affect other Items + Substitute constants only in Item_row's. Don't affect other Items with ROW_RESULT (eg Item_singlerow_subselect). For such Items more optimal is to detect if it is constant and replace @@ -8620,7 +8547,7 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) uint col; new_item= 0; /* - If item and comp_item are both Item_rows and have same number of cols + If item and comp_item are both Item_row's and have same number of cols then process items in Item_row one by one. We can't ignore NULL values here as this item may be used with <=>, in which case NULL's are significant. @@ -8639,8 +8566,8 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) 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_float(name, result, decimals, length)); + new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) : (Item*) + new (mem_root) Item_float(thd, name, result, decimals, length)); break; } case DECIMAL_RESULT: @@ -8650,13 +8577,10 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) 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)); + (Item*) new (mem_root) Item_null(thd, name) : + (Item*) new (mem_root) Item_decimal(thd, name, result, length, decimals)); break; } - case IMPOSSIBLE_RESULT: - DBUG_ASSERT(0); - break; } if (new_item) thd->change_item_tree(ref, new_item); @@ -8689,6 +8613,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]; @@ -8704,26 +8650,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) if (item->null_value) return 0; String *field_result= field->val_str(&field_tmp); - - enum_field_types field_type= field->type(); - - if (field_type == MYSQL_TYPE_DATE || field_type == MYSQL_TYPE_DATETIME || - field_type == MYSQL_TYPE_TIMESTAMP) - { - enum_mysql_timestamp_type type= MYSQL_TIMESTAMP_ERROR; - - if (field_type == MYSQL_TYPE_DATE) - type= MYSQL_TIMESTAMP_DATE; - else - type= MYSQL_TIMESTAMP_DATETIME; - - const char *field_name= field->field_name; - MYSQL_TIME field_time, item_time; - get_mysql_time_from_str(thd, field_result, type, field_name, &field_time); - get_mysql_time_from_str(thd, item_result, type, field_name, &item_time); - - return my_time_compare(&field_time, &item_time); - } return sortcmp(field_result, item_result, field->charset()); } if (res_type == INT_RESULT) @@ -8739,25 +8665,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.... @@ -8773,9 +8680,9 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) return 0; } -Item_cache* Item_cache::get_cache(const Item *item) +Item_cache* Item_cache::get_cache(THD *thd, const Item *item) { - return get_cache(item, item->cmp_type()); + return get_cache(thd, item, item->cmp_type()); } @@ -8788,24 +8695,23 @@ Item_cache* Item_cache::get_cache(const Item *item) @return cache item */ -Item_cache* Item_cache::get_cache(const Item *item, const Item_result type) +Item_cache* Item_cache::get_cache(THD *thd, const Item *item, + const Item_result type) { + MEM_ROOT *mem_root= thd->mem_root; switch (type) { case INT_RESULT: - return new Item_cache_int(item->field_type()); + return new (mem_root) Item_cache_int(thd, item->field_type()); case REAL_RESULT: - return new Item_cache_real(); + return new (mem_root) Item_cache_real(thd); case DECIMAL_RESULT: - return new Item_cache_decimal(); + return new (mem_root) Item_cache_decimal(thd); case STRING_RESULT: - return new Item_cache_str(item); + return new (mem_root) Item_cache_str(thd, item); case ROW_RESULT: - return new Item_cache_row(); + return new (mem_root) Item_cache_row(thd); case TIME_RESULT: - return new Item_cache_temporal(item->field_type()); - case IMPOSSIBLE_RESULT: - DBUG_ASSERT(0); - break; + return new (mem_root) Item_cache_temporal(thd, item->field_type()); } return 0; // Impossible } @@ -8907,17 +8813,34 @@ int Item_cache_int::save_in_field(Field *field, bool no_conversions) } -Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg): - Item_cache_int(field_type_arg) +Item_cache_temporal::Item_cache_temporal(THD *thd, + enum_field_types field_type_arg): + Item_cache_int(thd, field_type_arg) { if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_ERROR) cached_field_type= MYSQL_TYPE_DATETIME; } -longlong Item_cache_temporal::val_temporal_packed() +longlong Item_cache_temporal::val_datetime_packed() +{ + DBUG_ASSERT(fixed == 1); + if (Item_cache_temporal::field_type() == MYSQL_TYPE_TIME) + return Item::val_datetime_packed(); // TIME-to-DATETIME conversion needed + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; + return 0; + } + return value; +} + + +longlong Item_cache_temporal::val_time_packed() { DBUG_ASSERT(fixed == 1); + if (Item_cache_temporal::field_type() != MYSQL_TYPE_TIME) + return Item::val_time_packed(); // DATETIME-to-TIME conversion needed if ((!value_cached && !cache_value()) || null_value) { null_value= TRUE; @@ -9025,16 +8948,25 @@ int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) } -void Item_cache_temporal::store_packed(longlong val_arg, Item *example) +void Item_cache_temporal::store_packed(longlong val_arg, Item *example_arg) { /* An explicit values is given, save it. */ - store(example); + store(example_arg); value_cached= true; value= val_arg; null_value= false; } +Item *Item_cache_temporal::clone_item(THD *thd) +{ + Item_cache_temporal *item= new (thd->mem_root) + Item_cache_temporal(thd, cached_field_type); + item->store_packed(value, example); + return item; +} + + bool Item_cache_real::cache_value() { if (!example) @@ -9163,28 +9095,18 @@ bool Item_cache_str::cache_value() double Item_cache_str::val_real() { DBUG_ASSERT(fixed == 1); - int err_not_used; - char *end_not_used; if (!has_value()) return 0.0; - if (value) - return my_strntod(value->charset(), (char*) value->ptr(), - value->length(), &end_not_used, &err_not_used); - return (double) 0; + return value ? double_from_string_with_check(value) : 0.0; } longlong Item_cache_str::val_int() { DBUG_ASSERT(fixed == 1); - int err; if (!has_value()) return 0; - if (value) - return my_strntoll(value->charset(), value->ptr(), - value->length(), 10, (char**) 0, &err); - else - return (longlong)0; + return value ? longlong_from_string_with_check(value) : 0; } @@ -9202,11 +9124,7 @@ my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) DBUG_ASSERT(fixed == 1); if (!has_value()) return NULL; - if (value) - string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); - else - decimal_val= 0; - return decimal_val; + return value ? decimal_from_string_with_check(decimal_val, value) : 0; } @@ -9220,27 +9138,26 @@ int Item_cache_str::save_in_field(Field *field, bool no_conversions) } -bool Item_cache_row::allocate(uint num) +bool Item_cache_row::allocate(THD *thd, uint num) { item_count= num; - THD *thd= current_thd; return (!(values= (Item_cache **) thd->calloc(sizeof(Item_cache *)*item_count))); } -bool Item_cache_row::setup(Item * item) +bool Item_cache_row::setup(THD *thd, Item *item) { example= item; - if (!values && allocate(item->cols())) + if (!values && allocate(thd, item->cols())) return 1; for (uint i= 0; i < item_count; i++) { Item *el= item->element_index(i); Item_cache *tmp; - if (!(tmp= values[i]= Item_cache::get_cache(el))) + if (!(tmp= values[i]= Item_cache::get_cache(thd, el))) return 1; - tmp->setup(el); + tmp->setup(thd, el); } return 0; } @@ -9342,7 +9259,10 @@ void Item_cache_row::set_null() Item_type_holder::Item_type_holder(THD *thd, Item *item) - :Item(thd, item), enum_set_typelib(0), fld_type(get_real_type(item)) + :Item(thd, item), + enum_set_typelib(0), + fld_type(get_real_type(item)), + geometry_type(Field::GEOM_GEOMETRY) { DBUG_ASSERT(item->fixed); maybe_null= item->maybe_null; @@ -9431,7 +9351,6 @@ enum_field_types Item_type_holder::get_real_type(Item *item) return MYSQL_TYPE_NEWDECIMAL; case ROW_RESULT: case TIME_RESULT: - case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VAR_STRING; } @@ -9473,7 +9392,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) @@ -9483,10 +9402,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item) if (Field::result_merge_type(fld_type) == DECIMAL_RESULT) { collation.set_numeric(); - 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, @@ -9517,7 +9436,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); @@ -9539,7 +9458,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; @@ -9557,7 +9476,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item) break; } default: - max_length= max(max_length, display_length(item)); + if (fld_type == MYSQL_TYPE_YEAR) + max_length= MY_MAX(max_length, item->max_length); + else + max_length= MY_MAX(max_length, display_length(item)); }; maybe_null|= item->maybe_null; get_full_info(item); @@ -9670,7 +9592,7 @@ Field *Item_type_holder::make_field_by_type(TABLE *table) default: break; } - return tmp_table_field_from_field_type(table, 0); + return tmp_table_field_from_field_type(table, false, true); } @@ -9844,7 +9766,15 @@ const char *dbug_print_item(Item *item) str.length(0); if (!item) return "(Item*)NULL"; - item->print(&str ,QT_ORDINARY); + + THD *thd= current_thd; + ulonglong save_option_bits= thd->variables.option_bits; + thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE; + + item->print(&str, QT_EXPLAIN); + + thd->variables.option_bits= save_option_bits; + if (str.c_ptr_safe() == buf) return buf; else @@ -9852,15 +9782,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 |