diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 1168 |
1 files changed, 793 insertions, 375 deletions
diff --git a/sql/item.cc b/sql/item.cc index 48449f9033d..0d4b25f4440 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -47,6 +47,16 @@ 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); + +/** + Compare two Items for List<Item>::add_unique() +*/ + +bool cmp_items(Item *a, Item *b) +{ + return a->eq(b, FALSE); +} + /****************************************************************************/ /* Hybrid_type_traits {_real} */ @@ -213,10 +223,12 @@ bool Item::val_bool() case STRING_RESULT: return val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return 0; // Wrong (but safe) } + return 0; // Wrong (but safe) } @@ -254,7 +266,7 @@ String *Item::val_string_from_real(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set_real(nr,decimals, &my_charset_bin); + str->set_real(nr,decimals, &my_charset_numeric); return str; } @@ -264,7 +276,7 @@ String *Item::val_string_from_int(String *str) longlong nr= val_int(); if (null_value) return 0; - str->set_int(nr, unsigned_flag, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_numeric); return str; } @@ -280,6 +292,21 @@ String *Item::val_string_from_decimal(String *str) } +String *Item::val_string_from_date(String *str) +{ + MYSQL_TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE) || + str->alloc(MAX_DATE_STRING_REP_LENGTH)) + { + null_value= 1; + return (String *) 0; + } + str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); + str->set_charset(&my_charset_numeric); + return str; +} + + my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value) { double nr= val_real(); @@ -377,17 +404,20 @@ int Item::save_time_in_field(Field *field) if (get_time(<ime)) return set_field_to_null_with_conversions(field, 0); field->set_notnull(); - return field->store_time(<ime, MYSQL_TIMESTAMP_TIME); + return field->store_time_dec(<ime, decimals); } int Item::save_date_in_field(Field *field) { MYSQL_TIME ltime; - if (get_date(<ime, TIME_FUZZY_DATE)) + if (get_date(<ime, TIME_FUZZY_DATE | + (current_thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | + MODE_INVALID_DATES)))) return set_field_to_null_with_conversions(field, 0); field->set_notnull(); - return field->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); + return field->store_time_dec(<ime, decimals); } @@ -428,10 +458,13 @@ Item::Item(): collation(&my_charset_bin, DERIVATION_COERCIBLE) { marker= 0; - maybe_null=null_value=with_sum_func=unsigned_flag=0; + maybe_null=null_value=with_sum_func=with_field=unsigned_flag=0; + in_rollup= 0; decimals= 0; max_length= 0; with_subselect= 0; - cmp_context= (Item_result)-1; + cmp_context= IMPOSSIBLE_RESULT; + /* 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; @@ -460,6 +493,7 @@ Item::Item(): tables. */ Item::Item(THD *thd, Item *item): + join_tab_idx(item->join_tab_idx), is_expensive_cache(-1), rsize(0), str_value(item->str_value), @@ -470,9 +504,11 @@ Item::Item(THD *thd, Item *item): marker(item->marker), decimals(item->decimals), 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_field(item->with_field), fixed(item->fixed), is_autogenerated_name(item->is_autogenerated_name), with_subselect(item->with_subselect), @@ -512,11 +548,40 @@ void Item::print_item_w_name(String *str, enum_query_type query_type) } +void Item::print_value(String *str) +{ + char buff[MAX_FIELD_WIDTH]; + String *ptr, tmp(buff,sizeof(buff),str->charset()); + ptr= val_str(&tmp); + if (!ptr) + str->append("NULL"); + else + { + switch (result_type()) { + case STRING_RESULT: + append_unescaped(str, ptr->ptr(), ptr->length()); + break; + case DECIMAL_RESULT: + case REAL_RESULT: + case INT_RESULT: + str->append(*ptr); + break; + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: + DBUG_ASSERT(0); + } + } +} + + void Item::cleanup() { DBUG_ENTER("Item::cleanup"); + DBUG_PRINT("enter", ("this: %p", this)); fixed=0; marker= 0; + join_tab_idx= MAX_TABLES; if (orig_name) name= orig_name; DBUG_VOID_RETURN; @@ -554,6 +619,45 @@ void Item::rename(char *new_name) name= new_name; } +Item_result Item::cmp_type() const +{ + switch (field_type()) { + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DECIMAL_RESULT; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_BIT: + return INT_RESULT; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + return REAL_RESULT; + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_GEOMETRY: + return STRING_RESULT; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_NEWDATE: + return TIME_RESULT; + }; + DBUG_ASSERT(0); + return IMPOSSIBLE_RESULT; +} /** Traverse item tree possibly transforming it (replacing items). @@ -607,14 +711,14 @@ Item* Item::transform(Item_transformer transformer, uchar *arg) A pointer to created wrapper item if successful, NULL - otherwise */ -Item* Item::set_expr_cache(THD *thd, List<Item *> &depends_on) +Item* Item::set_expr_cache(THD *thd) { DBUG_ENTER("Item::set_expr_cache"); Item_cache_wrapper *wrapper; if ((wrapper= new Item_cache_wrapper(this)) && !wrapper->fix_fields(thd, (Item**)&wrapper)) { - if (wrapper->set_cache(thd, depends_on)) + if (wrapper->set_cache(thd)) DBUG_RETURN(NULL); DBUG_RETURN(wrapper); } @@ -691,13 +795,22 @@ void Item_ident::cleanup() bool Item_ident::remove_dependence_processor(uchar * arg) { DBUG_ENTER("Item_ident::remove_dependence_processor"); - if (depended_from == (st_select_lex *) arg) + if (get_depended_from() == (st_select_lex *) arg) depended_from= 0; context= &((st_select_lex *) arg)->context; DBUG_RETURN(0); } +bool Item_ident::collect_outer_ref_processor(uchar *param) +{ + Collect_deps_prm *prm= (Collect_deps_prm *)param; + if (depended_from && depended_from->nest_level < prm->nest_level) + prm->parameters->add_unique(this, &cmp_items); + return FALSE; +} + + /** Store the pointer to this item field into a list if not already there. @@ -806,6 +919,23 @@ bool Item_field::register_field_in_bitmap(uchar *arg) return 0; } + +/* + Mark field in write_map + + NOTES + This is used by UPDATE to register underlying fields of used view fields. +*/ + +bool Item_field::register_field_in_write_map(uchar *arg) +{ + TABLE *table= (TABLE *) arg; + if (field->table == table || !table) + bitmap_set_bit(field->table->write_set, field->field_index); + return 0; +} + + bool Item::check_cols(uint c) { if (c != 1) @@ -828,7 +958,8 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) } if (cs->ctype) { - uint orig_len= length; + const char *str_start= str; + /* This will probably need a better implementation in the future: a function in CHARSET_INFO structure. @@ -838,16 +969,20 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) length--; str++; } - if (orig_len != length && !is_autogenerated_name) + 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), - str + length - orig_len); + buff); else push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), - str + length - orig_len); + buff); } } if (!my_charset_same(cs, system_charset_info)) @@ -1043,12 +1178,52 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const /** Get the value of the function as a MYSQL_TIME structure. - As a extra convenience the time structure is reset on error! + As a extra convenience the time structure is reset on error or NULL values! */ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) { - if (result_type() == STRING_RESULT) + if (field_type() == MYSQL_TYPE_TIME) + fuzzydate|= TIME_TIME_ONLY; + + switch (result_type()) { + case INT_RESULT: + { + longlong value= val_int(); + if (field_type() == MYSQL_TYPE_YEAR) + { + if (max_length == 2) + { + if (value < 70) + value+= 2000; + else if (value <= 1900) + value+= 1900; + } + value*= 10000; /* make it YYYYMMHH */ + } + if (null_value || int_to_datetime_with_warn(value, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case REAL_RESULT: + { + double value= val_real(); + if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case DECIMAL_RESULT: + { + my_decimal value, *res; + if (!(res= val_decimal(&value)) || + decimal_to_datetime_with_warn(res, ltime, fuzzydate, + field_name_or_null())) + goto err; + break; + } + case STRING_RESULT: { char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; @@ -1056,25 +1231,12 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate) str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) goto err; + break; } - else - { - int was_cut; - longlong value= val_int(); - - if (null_value) - goto err; - - if (number_to_datetime(value, ltime, fuzzydate, &was_cut) == LL(-1)) - { - char buff[22], *end; - end= longlong10_to_str(value, buff, -10); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - buff, (int) (end-buff), MYSQL_TIMESTAMP_NONE, - NullS); - goto err; - } + default: + DBUG_ASSERT(0); } + return 0; err: @@ -1082,23 +1244,20 @@ err: return 1; } -/** - Get time of first argument.\ - - As a extra convenience the time structure is reset on error! -*/ - -bool Item::get_time(MYSQL_TIME *ltime) +bool Item::get_seconds(ulonglong *sec, ulong *sec_part) { - char buff[40]; - String tmp(buff,sizeof(buff),&my_charset_bin),*res; - if (!(res=val_str_ascii(&tmp)) || - str_to_time_with_warn(res->charset(), res->ptr(), res->length(), ltime)) - { - bzero((char*) ltime,sizeof(*ltime)); - return 1; + if (result_type() == INT_RESULT) + { // optimize for an important special case + longlong val= val_int(); + bool neg= val < 0 && !unsigned_flag; + *sec= neg ? -val : val; + *sec_part= 0; + return neg; } - return 0; + my_decimal tmp, *dec= val_decimal(&tmp); + if (!dec) + return 0; + return my_decimal2seconds(dec, sec, sec_part); } CHARSET_INFO *Item::default_charset() @@ -1124,6 +1283,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); ulonglong sql_mode= thd->variables.sql_mode; thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); + thd->variables.sql_mode|= MODE_INVALID_DATES; thd->count_cuted_fields= CHECK_FIELD_IGNORE; res= save_in_field(field, no_conversions); @@ -1566,6 +1726,11 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, */ 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; @@ -1995,6 +2160,7 @@ Item_field::Item_field(Field *f) if this item is to be reused */ orig_table_name= orig_field_name= ""; + with_field= 1; } @@ -2043,6 +2209,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, name= (char*) orig_field_name; } set_field(f); + with_field= 1; } @@ -2057,6 +2224,7 @@ Item_field::Item_field(Name_resolution_context *context_arg, collation.set(DERIVATION_IMPLICIT); if (select && select->parsing_place != IN_HAVING) select->select_n_where_fields++; + with_field= 1; } /** @@ -2073,6 +2241,7 @@ Item_field::Item_field(THD *thd, Item_field *item) any_privileges(item->any_privileges) { collation.set(DERIVATION_IMPLICIT); + with_field= 1; } @@ -2310,24 +2479,14 @@ bool Item_field::get_date(MYSQL_TIME *ltime,uint fuzzydate) bool Item_field::get_date_result(MYSQL_TIME *ltime,uint fuzzydate) { - if ((null_value=result_field->is_null()) || - result_field->get_date(ltime,fuzzydate)) + if (result_field->is_null() || result_field->get_date(ltime,fuzzydate)) { bzero((char*) ltime,sizeof(*ltime)); - return 1; + return (null_value= 1); } - return 0; + return (null_value= 0); } -bool Item_field::get_time(MYSQL_TIME *ltime) -{ - if ((null_value=field->is_null()) || field->get_time(ltime)) - { - bzero((char*) ltime,sizeof(*ltime)); - return 1; - } - return 0; -} void Item_field::save_result(Field *to) { @@ -2377,10 +2536,12 @@ bool Item_field::val_bool_result() case STRING_RESULT: return result_field->val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return 0; // Shut up compiler } + return 0; } @@ -2424,13 +2585,17 @@ table_map Item_field::used_tables() const { if (field->table->const_table) return 0; // const item - return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); + return (get_depended_from() ? OUTER_REF_TABLE_BIT : field->table->map); } +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) { - if (new_parent == depended_from) + if (new_parent == get_depended_from()) depended_from= NULL; Name_resolution_context *ctx= new Name_resolution_context(); ctx->outer_context= NULL; // We don't build a complete name resolver @@ -2742,19 +2907,20 @@ void Item_string::print(String *str, enum_query_type query_type) double -double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end) +double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, + const char *end) { int error; - char *org_end; + char *end_of_num= (char*) end; double tmp; - org_end= end; - tmp= my_strntod(cs, (char*) cptr, end - cptr, &end, &error); - if (error || (end != org_end && !check_if_only_end_space(cs, end, org_end))) + 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, cs); + ErrConvString err(cptr, end - cptr, cs); /* - We can use str_value.ptr() here as Item_string is gurantee to put an + 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, @@ -2769,28 +2935,31 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end) double Item_string::val_real() { DBUG_ASSERT(fixed == 1); - return double_from_string_with_check (str_value.charset(), str_value.ptr(), - (char *) str_value.ptr() + str_value.length()); + 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, char *end) +longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, + const char *end) { int err; longlong tmp; - char *org_end= end; + char *end_of_num= (char*) end; - tmp= (*(cs->cset->strtoll10))(cs, cptr, &end, &err); + 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 != org_end && !check_if_only_end_space(cs, end, org_end)))) + (end != end_of_num && !check_if_only_end_space(cs, end_of_num, end)))) { - ErrConvString err(cptr, cs); + 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", @@ -2808,7 +2977,7 @@ longlong Item_string::val_int() { DBUG_ASSERT(fixed == 1); return longlong_from_string_with_check(str_value.charset(), str_value.ptr(), - (char *) str_value.ptr()+ str_value.length()); + str_value.ptr()+ str_value.length()); } @@ -3000,19 +3169,19 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_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.minute > 59 || value.time.second > 59 || + value.time.second_part > TIME_MAX_SECOND_PART) { - char buff[MAX_DATE_STRING_REP_LENGTH]; - uint length= my_TIME_to_str(&value.time, buff); + ErrConvTime str(&value.time); make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - buff, length, time_type, 0); + &str, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } state= TIME_VALUE; maybe_null= 0; max_length= max_length_arg; - decimals= 0; + decimals= tm->second_part > 0 ? TIME_SECOND_PART_DIGITS : 0; DBUG_VOID_RETURN; } @@ -3099,10 +3268,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) case REAL_RESULT: set_double(*(double*)entry->value); item_type= Item::REAL_ITEM; + param_type= MYSQL_TYPE_DOUBLE; break; case INT_RESULT: set_int(*(longlong*)entry->value, MY_INT64_NUM_DECIMAL_DIGITS); item_type= Item::INT_ITEM; + param_type= MYSQL_TYPE_LONGLONG; break; case STRING_RESULT: { @@ -3125,6 +3296,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) charset of connection, so we have to set it later. */ item_type= Item::STRING_ITEM; + param_type= MYSQL_TYPE_VARCHAR; if (set_str((const char *)entry->value, entry->length)) DBUG_RETURN(1); @@ -3140,9 +3312,12 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) my_decimal_precision_to_length_no_truncation(ent_value->precision(), decimals, unsigned_flag); item_type= Item::DECIMAL_ITEM; + param_type= MYSQL_TYPE_NEWDECIMAL; break; } - default: + case ROW_RESULT: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); set_null(); } @@ -3204,7 +3379,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions) case DECIMAL_VALUE: return field->store_decimal(&decimal_value); case TIME_VALUE: - field->store_time(&value.time, value.time.time_type); + field->store_time_dec(&value.time, decimals); return 0; case STRING_VALUE: case LONG_DATA_VALUE: @@ -3220,21 +3395,6 @@ int Item_param::save_in_field(Field *field, bool no_conversions) } -bool Item_param::get_time(MYSQL_TIME *res) -{ - if (state == TIME_VALUE) - { - *res= value.time; - return 0; - } - /* - If parameter value isn't supplied assertion will fire in val_str() - which is called from Item::get_time(). - */ - return Item::get_time(res); -} - - bool Item_param::get_date(MYSQL_TIME *res, uint fuzzydate) { if (state == TIME_VALUE) @@ -3364,7 +3524,8 @@ String *Item_param::val_str(String* str) { if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) break; - str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr())); + str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(), + decimals)); str->set_charset(&my_charset_bin); return str; } @@ -3416,7 +3577,7 @@ const String *Item_param::query_val_str(String* str) const buf= str->c_ptr_quick(); ptr= buf; *ptr++= '\''; - ptr+= (uint) my_TIME_to_str(&value.time, ptr); + ptr+= (uint) my_TIME_to_str(&value.time, ptr, decimals); *ptr++= '\''; str->length((uint32) (ptr - buf)); break; @@ -3759,6 +3920,7 @@ void Item_param::make_field(Send_field *field) /**************************************************************************** Item_copy ****************************************************************************/ + Item_copy *Item_copy::create (Item *item) { switch (item->result_type()) @@ -3772,7 +3934,9 @@ Item_copy *Item_copy::create (Item *item) new Item_copy_uint (item) : new Item_copy_int (item); case DECIMAL_RESULT: return new Item_copy_decimal (item); - default: + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT (0); } /* should not happen */ @@ -3845,8 +4009,7 @@ void Item_copy_int::copy() null_value=item->null_value; } -static int save_int_value_in_field (Field *field, longlong nr, - bool null_value, bool unsigned_flag); +static int save_int_value_in_field (Field *, longlong, bool, bool); int Item_copy_int::save_in_field(Field *field, bool no_conversions) { @@ -4379,6 +4542,34 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) } +/* + @brief + Whether a table belongs to an outer select. + + @param table table to check + @param select current select + + @details + Try to find select the table belongs to by ascending the derived tables chain. +*/ + +static +bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) +{ + DBUG_ASSERT(table->select_lex != select); + TABLE_LIST *tl; + + for (tl= select->master_unit()->derived; + tl && tl->is_merged_derived(); + select= tl->select_lex, tl= select->master_unit()->derived) + { + if (tl->select_lex == table->select_lex) + return FALSE; + } + return TRUE; +} + + /** Resolve the name of an outer select column reference. @@ -4547,9 +4738,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0)); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); /* A reference to a view field had been found and we substituted it instead of this Item (find_field_in_tables @@ -4650,9 +4838,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, rf, rf); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); return 0; } @@ -4661,9 +4846,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, (Item_ident*)*reference); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; @@ -4827,7 +5009,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (!outer_fixed && cached_table && cached_table->select_lex && context->select_lex && - cached_table->select_lex != context->select_lex) + cached_table->select_lex != context->select_lex && + is_outer_table(cached_table, context->select_lex)) { int ret; if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) @@ -5022,13 +5205,14 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) /** - Check whether a field can be substituted by an equal item. + Check whether a field item can be substituted for an equal item - The function checks whether a substitution of the field - occurrence for an equal item is valid. + @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 + @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: @@ -5053,7 +5237,10 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) bool Item_field::subst_argument_checker(uchar **arg) { - return (result_type() != STRING_RESULT) || (*arg); + return *arg && + (*arg == (uchar *) Item::ANY_SUBST || + result_type() != STRING_RESULT || + (field->flags & BINARY_FLAG)); } @@ -5131,12 +5318,7 @@ Item *Item_field::equal_fields_propagator(uchar *arg) item= this; else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type())) { - /* - We don't need to zero-fill timestamp columns here because they will be - first converted to a string (in date/time format) and compared as such if - compared with another string. - */ - if (item && field->type() != FIELD_TYPE_TIMESTAMP && cmp_context != INT_RESULT) + if (item && (cmp_context == STRING_RESULT || cmp_context == IMPOSSIBLE_RESULT)) convert_zerofill_number_to_string(&item, (Field_num *)field); else item= this; @@ -5163,7 +5345,8 @@ bool Item_field::set_no_const_sub(uchar *arg) Replace an Item_field for an equal Item_field that evaluated earlier (if any). - The function returns a pointer to an item that is taken from + If this->item_equal points to some item and coincides with arg then + the function returns a pointer to an item that is taken from the very beginning of the item_equal list which the Item_field object refers to (belongs to) unless item_equal contains a constant item. In this case the function returns this constant item, @@ -5171,12 +5354,12 @@ bool Item_field::set_no_const_sub(uchar *arg) If the Item_field object does not refer any Item_equal object 'this' is returned . - @param arg a dummy parameter, is not used here + @param arg NULL or points to so some item of the Item_equal type @note This function is supposed to be called as a callback parameter in calls - of the thransformer method. + of the transformer method. @return - pointer to a replacement Item_field if there is a better equal item or @@ -5186,7 +5369,7 @@ bool Item_field::set_no_const_sub(uchar *arg) Item *Item_field::replace_equal_field(uchar *arg) { - if (item_equal) + if (item_equal && item_equal == (Item_equal *) arg) { Item *const_item= item_equal->get_const(); if (const_item) @@ -5195,8 +5378,10 @@ Item *Item_field::replace_equal_field(uchar *arg) return this; return const_item; } - Item_field *subst= item_equal->get_first(this); - if (subst && field->table != subst->field->table && !field->eq(subst->field)) + Item_field *subst= (Item_field *)(item_equal->get_first(this)); + if (subst) + subst= (Item_field *) (subst->real_item()); + if (subst && !field->eq(subst->field)) return subst; } return this; @@ -5254,10 +5439,12 @@ enum_field_types Item::field_type() const case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL; case REAL_RESULT: return MYSQL_TYPE_DOUBLE; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VARCHAR; } + return MYSQL_TYPE_VARCHAR; } @@ -5434,16 +5621,19 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) break; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: - field= new Field_newdate(maybe_null, name, &my_charset_bin); + field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin); break; case MYSQL_TYPE_TIME: - field= new Field_time(maybe_null, name, &my_charset_bin); + field= new_Field_time(0, null_ptr, 0, Field::NONE, name, + decimals, &my_charset_bin); break; case MYSQL_TYPE_TIMESTAMP: - field= new Field_timestamp(maybe_null, name, &my_charset_bin); + field= new_Field_timestamp(0, null_ptr, 0, + Field::NONE, name, 0, decimals, &my_charset_bin); break; case MYSQL_TYPE_DATETIME: - field= new Field_datetime(maybe_null, name, &my_charset_bin); + field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, + decimals, &my_charset_bin); break; case MYSQL_TYPE_YEAR: field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, @@ -5615,7 +5805,7 @@ int Item_null::save_safe_in_field(Field *field) Item uses str_value to store something, it should reimplement it's ::save_in_field() as Item_string, for example, does. - Note: all Item_XXX::val_str(str) methods must NOT rely on the fact that + Note: all Item_XXX::val_str(str) methods must NOT assume that str != str_value. For example, see fix for bug #44743. */ @@ -5641,15 +5831,6 @@ int Item::save_in_field(Field *field, bool no_conversions) error=field->store(result->ptr(),result->length(),cs); str_value.set_quick(0, 0, cs); } - else if (result_type() == REAL_RESULT && - field->result_type() == STRING_RESULT) - { - double nr= val_real(); - if (null_value) - return set_field_to_null_with_conversions(field, no_conversions); - field->set_notnull(); - error= field->store(nr); - } else if (result_type() == REAL_RESULT) { double nr= val_real(); @@ -5687,12 +5868,6 @@ int Item_string::save_in_field(Field *field, bool no_conversions) } -int Item_uint::save_in_field(Field *field, bool no_conversions) -{ - /* Item_int::save_in_field handles both signed and unsigned. */ - return Item_int::save_in_field(field, no_conversions); -} - static int save_int_value_in_field (Field *field, longlong nr, bool null_value, bool unsigned_flag) { @@ -5709,6 +5884,22 @@ int Item_int::save_in_field(Field *field, bool no_conversions) } +void Item_datetime::set(longlong packed) +{ + unpack_time(packed, <ime); +} + +int Item_datetime::save_in_field(Field *field, bool no_conversions) +{ + field->set_notnull(); + return field->store_time_dec(<ime, decimals); +} + +longlong Item_datetime::val_int() +{ + return TIME_to_ulonglong(<ime); +} + int Item_decimal::save_in_field(Field *field, bool no_conversions) { field->set_notnull(); @@ -5726,7 +5917,9 @@ bool Item_int::eq(const Item *arg, bool binary_cmp) const a basic constant. */ Item *item= (Item*) arg; - return item->val_int() == value && item->unsigned_flag == unsigned_flag; + return (item->val_int() == value && + ((longlong) value >= 0 || + (item->unsigned_flag == unsigned_flag))); } return FALSE; } @@ -6087,7 +6280,10 @@ bool Item::send(Protocol *protocol, String *buffer) { String *res; if ((res=val_str(buffer))) + { + DBUG_ASSERT(!null_value); result= protocol->store(res->ptr(),res->length(),res->charset()); + } else { DBUG_ASSERT(null_value); @@ -6154,7 +6350,7 @@ bool Item::send(Protocol *protocol, String *buffer) if (f_type == MYSQL_TYPE_DATE) return protocol->store_date(&tm); else - result= protocol->store(&tm); + result= protocol->store(&tm, decimals); } break; } @@ -6163,7 +6359,7 @@ bool Item::send(Protocol *protocol, String *buffer) MYSQL_TIME tm; get_time(&tm); if (!null_value) - result= protocol->store_time(&tm); + result= protocol->store_time(&tm, decimals); break; } } @@ -6306,17 +6502,7 @@ void Item_field::print(String *str, enum_query_type query_type) { if (field && field->table->const_table) { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff),str->charset()); - field->val_str(&tmp); - if (field->is_null()) - str->append("NULL"); - else - { - str->append('\''); - str->append(tmp); - str->append('\''); - } + print_value(str); return; } Item_ident::print(str, query_type); @@ -6328,7 +6514,7 @@ Item_ref::Item_ref(Name_resolution_context *context_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) + result_field(0), ref(item), reference_trough_name(0) { alias_name_used= alias_name_used_arg; /* @@ -6354,8 +6540,9 @@ public: st_select_lex *sel; for (sel= current_select; sel; sel= sel->outer_select()) { + List_iterator<TABLE_LIST> li(sel->leaf_tables); TABLE_LIST *tbl; - for (tbl= sel->leaf_tables; tbl; tbl= tbl->next_leaf) + while ((tbl= li++)) { if (tbl->table == item->field->table) { @@ -6371,7 +6558,7 @@ 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) + result_field(NULL), ref(item), reference_trough_name(0) { alias_name_used= alias_name_used_arg; /* @@ -6454,6 +6641,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) if (!ref || ref == not_found_item) { + DBUG_ASSERT(reference_trough_name != 0); if (!(ref= resolve_ref_in_select_and_group(thd, this, context->select_lex))) goto error; /* Some error occurred (e.g. ambiguous names). */ @@ -6555,9 +6743,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) refer_type == FIELD_ITEM) ? (Item_ident*) (*reference) : 0)); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); /* view reference found, we substituted it instead of this Item, so can quit @@ -6608,9 +6793,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, thd->lex->current_select, fld, fld); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of @@ -6634,9 +6816,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(*ref && (*ref)->fixed); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, this); - context->select_lex-> - register_dependency_item(last_checked_context->select_lex, - reference); /* A reference is resolved to a nest level that's outer or the same as the nest level of the enclosing set function : adjust the value of @@ -6649,13 +6828,8 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) last_checked_context->select_lex->nest_level); } } - else + else if (ref_type() != VIEW_REF) { - if (depended_from && reference) - { - DBUG_ASSERT(context->select_lex != depended_from); - context->select_lex->register_dependency_item(depended_from, reference); - } /* 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 @@ -6712,6 +6886,7 @@ void Item_ref::set_properties() split_sum_func() doesn't try to change the reference. */ with_sum_func= (*ref)->with_sum_func; + with_field= (*ref)->with_field; unsigned_flag= (*ref)->unsigned_flag; fixed= 1; if (alias_name_used) @@ -6728,10 +6903,100 @@ 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 */ + ref= 0; + } DBUG_VOID_RETURN; } +/** + 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 + old item is substituted for a new one. After this the transformer + is applied to the Item_ref object. + + @param transformer the transformer callback function to be applied to + the nodes of the tree of the object + @param argument parameter to be passed to the transformer + + @return Item returned as the result of transformation of the Item_ref object + @retval !NULL The transformation was successful + @retval NULL Out of memory error +*/ + +Item* Item_ref::transform(Item_transformer transformer, uchar *arg) +{ + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); + DBUG_ASSERT((*ref) != NULL); + + /* Transform the object we are referencing. */ + Item *new_item= (*ref)->transform(transformer, arg); + if (!new_item) + return NULL; + + /* + THD::change_item_tree() should be called only if the tree was + really transformed, i.e. when a new item has been created. + Otherwise we'll be allocating a lot of unnecessary memory for + change records at each execution. + */ + if (*ref != new_item) + current_thd->change_item_tree(ref, new_item); + + /* Transform the item ref object. */ + return (this->*transformer)(arg); +} + + +/** + Compile an Item_ref object with a processor and a transformer + callback functions. + + First the function applies the analyzer to the Item_ref object. Then + if the analizer succeeeds we first applies the compile method to the + object the Item_ref object is referencing. 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 itself. + The compile function is not called if the analyzer returns NULL + in the parameter arg_p. + + @param analyzer the analyzer callback function to be applied to the + nodes of the tree of the object + @param[in,out] arg_p parameter to be passed to the processor + @param transformer the transformer callback function to be applied to the + nodes of the tree of the object + @param arg_t parameter to be passed to the transformer + + @return Item returned as the result of transformation of the Item_ref object +*/ + +Item* Item_ref::compile(Item_analyzer analyzer, uchar **arg_p, + Item_transformer transformer, uchar *arg_t) +{ + /* Analyze this Item object. */ + if (!(this->*analyzer)(arg_p)) + return NULL; + + /* Compile the Item we are referencing. */ + DBUG_ASSERT((*ref) != NULL); + if (*arg_p) + { + uchar *arg_v= *arg_p; + Item *new_item= (*ref)->compile(analyzer, &arg_v, transformer, arg_t); + if (new_item && *ref != new_item) + current_thd->change_item_tree(ref, new_item); + } + + /* Transform this Item object. */ + return (this->*transformer)(arg_t); +} + + void Item_ref::print(String *str, enum_query_type query_type) { if (ref) @@ -6838,7 +7103,8 @@ bool Item_ref::val_bool_result() case STRING_RESULT: return result_field->val_real() != 0.0; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } } @@ -6926,7 +7192,19 @@ my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) int Item_ref::save_in_field(Field *to, bool no_conversions) { int res; - DBUG_ASSERT(!result_field); + if (result_field) + { + if (result_field->is_null()) + { + null_value= 1; + res= set_field_to_null_with_conversions(to, no_conversions); + return res; + } + to->set_notnull(); + res= field_conv(to, result_field); + null_value= 0; + return res; + } res= (*ref)->save_in_field(to, no_conversions); null_value= (*ref)->null_value; return res; @@ -7043,8 +7321,7 @@ bool Item_direct_ref::get_date(MYSQL_TIME *ltime,uint fuzzydate) Item_cache_wrapper::~Item_cache_wrapper() { - delete expr_cache; - /* expr_value is Item so it will be destroyed from list of Items */ + DBUG_ASSERT(expr_cache == 0); } Item_cache_wrapper::Item_cache_wrapper(Item *item_arg) @@ -7056,9 +7333,11 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg) decimals= orig_item->decimals; collation.set(orig_item->collation); with_sum_func= orig_item->with_sum_func; + 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); @@ -7067,11 +7346,28 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg) } +/** + Initialize the cache if it is needed +*/ + +void Item_cache_wrapper::init_on_demand() +{ + if (!expr_cache->is_inited()) + { + orig_item->get_cache_parameters(parameters); + expr_cache->init(); + } +} + + void Item_cache_wrapper::print(String *str, enum_query_type query_type) { str->append(func_name()); if (expr_cache) + { + init_on_demand(); expr_cache->print(str, query_type); + } else str->append(STRING_WITH_LEN("<<DISABLED>>")); str->append('('); @@ -7107,10 +7403,14 @@ bool Item_cache_wrapper::send(Protocol *protocol, String *buffer) void Item_cache_wrapper::cleanup() { + DBUG_ENTER("Item_cache_wrapper::cleanup"); + Item_result_field::cleanup(); delete expr_cache; expr_cache= 0; - // expr_value is Item so it will be destroyed from list of Items + /* expr_value is Item so it will be destroyed from list of Items */ expr_value= 0; + parameters.empty(); + DBUG_VOID_RETURN; } @@ -7130,10 +7430,11 @@ void Item_cache_wrapper::cleanup() @retval TRUE Error */ -bool Item_cache_wrapper::set_cache(THD *thd, List<Item*> &depends_on) +bool Item_cache_wrapper::set_cache(THD *thd) { DBUG_ENTER("Item_cache_wrapper::set_cache"); - expr_cache= new Expression_cache_tmptable(thd, depends_on, expr_value); + DBUG_ASSERT(expr_cache == 0); + expr_cache= new Expression_cache_tmptable(thd, parameters, expr_value); DBUG_RETURN(expr_cache == NULL); } @@ -7158,6 +7459,7 @@ Item *Item_cache_wrapper::check_cache() { Expression_cache_tmptable::result res; Item *cached_value; + init_on_demand(); res= expr_cache->check_value(&cached_value); if (res == Expression_cache_tmptable::HIT) DBUG_RETURN(cached_value); @@ -7388,25 +7690,6 @@ bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, uint fuzzydate) } -/** - Get the time value of the possibly cached item -*/ - -bool Item_cache_wrapper::get_time(MYSQL_TIME *ltime) -{ - Item *cached_value; - DBUG_ENTER("Item_cache_wrapper::get_time"); - if (!expr_cache) - DBUG_RETURN((null_value= orig_item->get_time(ltime))); - - if ((cached_value= check_cache())) - DBUG_RETURN((null_value= cached_value->get_time(ltime))); - - cache(); - DBUG_RETURN((null_value= expr_value->get_time(ltime))); -} - - int Item_cache_wrapper::save_in_field(Field *to, bool no_conversions) { int res; @@ -7495,7 +7778,7 @@ bool Item_outer_ref::fix_fields(THD *thd, Item **reference) void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref) { - if (depended_from == new_parent) + if (get_depended_from() == new_parent) { *ref= outer_ref; (*ref)->fix_after_pullout(new_parent, ref); @@ -7505,7 +7788,7 @@ void Item_outer_ref::fix_after_pullout(st_select_lex *new_parent, Item **ref) void Item_ref::fix_after_pullout(st_select_lex *new_parent, Item **refptr) { (*ref)->fix_after_pullout(new_parent, ref); - if (depended_from == new_parent) + if (get_depended_from() == new_parent) depended_from= NULL; } @@ -7577,6 +7860,138 @@ bool Item_direct_view_ref::eq(const Item *item, bool binary_cmp) const return FALSE; } + +Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal) +{ + Item* field_item= real_item(); + if (field_item->type() != FIELD_ITEM) + return NULL; + return ((Item_field *) field_item)->find_item_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). + + @details + The function looks for a multiple equality containing this item of the type + Item_direct_view_ref among those referenced by arg. + In the case such equality exists the function does the following. + If the found multiple equality contains a constant, then the item + is substituted for this constant, otherwise the function sets a pointer + to the multiple equality in the item. + + @param arg reference to list of multiple equalities where + the item (this object) is to be looked for + + @note + This function is supposed to be called as a callback parameter in calls + of the compile method. + + @note + The function calls Item_field::equal_fields_propagator 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. + + @return + - pointer to the replacing constant item, if the field item was substituted + - pointer to the field item, otherwise. +*/ + +Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg) +{ + Item *field_item= real_item(); + if (field_item->type() != FIELD_ITEM) + return this; + Item *item= field_item->equal_fields_propagator(arg); + set_item_equal(field_item->get_item_equal()); + field_item->set_item_equal(NULL); + if (item != field_item) + return item; + return this; +} + + +/** + Replace an Item_direct_view_ref for an equal Item_field evaluated earlier + (if any). + + @details + If this->item_equal points to some item and coincides with arg then + the function returns a pointer to a field item that is referred to by the + first element of the item_equal list which the Item_direct_view_ref + 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 + 'this' is returned . + + @param arg NULL or points to so some item of the Item_equal type + + @note + This function is supposed to be called as a callback parameter in calls + of the transformer method. + + @note + The function calls Item_field::replace_equal_field for the field item + this->real_item() to do the job. + + @return + - pointer to a replacement Item_field if there is a better equal item or + a pointer to a constant equal item; + - this - otherwise. +*/ + +Item *Item_direct_view_ref::replace_equal_field(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); + field_item->set_item_equal(0); + return item != field_item ? item : this; +} + + bool Item_default_value::eq(const Item *item, bool binary_cmp) const { return item->type() == DEFAULT_VALUE_ITEM && @@ -7949,6 +8364,8 @@ Item_result item_cmp_type(Item_result a,Item_result b) return INT_RESULT; else if (a == ROW_RESULT || b == ROW_RESULT) return ROW_RESULT; + else if (a == TIME_RESULT || b == TIME_RESULT) + return TIME_RESULT; if ((a == INT_RESULT || a == DECIMAL_RESULT) && (b == INT_RESULT || b == DECIMAL_RESULT)) return DECIMAL_RESULT; @@ -7962,11 +8379,20 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) Item *new_item= NULL; if (item->basic_const_item()) return; // Can't be better - Item_result res_type=item_cmp_type(comp_item->result_type(), - item->result_type()); + Item_result res_type=item_cmp_type(comp_item->cmp_type(), item->cmp_type()); char *name=item->name; // Alloced by sql_alloc switch (res_type) { + case TIME_RESULT: + { + 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); + if (is_null) + new_item= new Item_null(name); + break; + } case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; @@ -8041,8 +8467,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) (Item*) new Item_decimal(name, result, length, decimals)); break; } - default: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); + break; } if (new_item) thd->change_item_tree(ref, new_item); @@ -8062,6 +8489,9 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) @note We only use this on the range optimizer/partition pruning, because in some cases we can't store the value in the field without some precision/character loss. + + @todo rewrite it to use Arg_comparator (currently it's a simplified and + incomplete version of it) */ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) @@ -8093,9 +8523,7 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) if (field_type == MYSQL_TYPE_DATE) type= MYSQL_TIMESTAMP_DATE; - - if (field_type == MYSQL_TYPE_DATETIME || - field_type == MYSQL_TYPE_TIMESTAMP) + else type= MYSQL_TIMESTAMP_DATETIME; const char *field_name= field->field_name; @@ -8119,6 +8547,25 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) field_val= field->val_decimal(&field_buf); return my_decimal_cmp(item_val, field_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_FUZZY_DATE | TIME_INVALID_DATES); + item->get_date(&item_time, TIME_FUZZY_DATE | TIME_INVALID_DATES); + } + return my_time_compare(&field_time, &item_time); + } double result= item->val_real(); if (item->null_value) return 0; @@ -8155,19 +8602,16 @@ Item_cache* Item_cache::get_cache(const Item *item, const Item_result type) case DECIMAL_RESULT: return new Item_cache_decimal(); case STRING_RESULT: - /* Not all functions that return DATE/TIME are actually DATE/TIME funcs. */ - if ((item->is_datetime() || - item->field_type() == MYSQL_TYPE_TIME) && - (const_cast<Item*>(item))->result_as_longlong()) - return new Item_cache_datetime(item->field_type()); return new Item_cache_str(item); case ROW_RESULT: return new Item_cache_row(); - default: - // should never be in real life + case TIME_RESULT: + return new Item_cache_temporal(item->field_type()); + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); - return 0; + break; } + return 0; // Impossible } void Item_cache::store(Item *item) @@ -8180,6 +8624,11 @@ void Item_cache::store(Item *item) void Item_cache::print(String *str, enum_query_type query_type) { + if (value_cached) + { + print_value(str); + return; + } str->append(STRING_WITH_LEN("<cache>(")); if (example) example->print(str, query_type); @@ -8200,16 +8649,6 @@ bool Item_cache_int::cache_value() } -void Item_cache_int::store_longlong(Item *item, longlong val_arg) -{ - /* An explicit values is given, save it. */ - value_cached= TRUE; - value= val_arg; - null_value= item->null_value; - unsigned_flag= item->unsigned_flag; -} - - String *Item_cache_int::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -8245,171 +8684,104 @@ longlong Item_cache_int::val_int() return value; } -bool Item_cache_datetime::cache_value_int() +int Item_cache_int::save_in_field(Field *field, bool no_conversions) { - if (!example) - return false; - - value_cached= true; - // Mark cached string value obsolete - str_value_cached= false; - - MYSQL_TIME ltime; - const bool eval_error= - (field_type() == MYSQL_TYPE_TIME) ? - example->get_time(<ime) : - example->get_date(<ime, TIME_FUZZY_DATE); - - if (eval_error) - int_value= 0; - else - { - switch(field_type()) - { - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - int_value= TIME_to_ulonglong_datetime(<ime); - break; - case MYSQL_TYPE_TIME: - int_value= TIME_to_ulonglong_time(<ime); - break; - default: - int_value= TIME_to_ulonglong_date(<ime); - break; - } - if (ltime.neg) - int_value= -int_value; - } + int error; + if (!has_value()) + return set_field_to_null_with_conversions(field, no_conversions); - null_value= example->null_value; - unsigned_flag= example->unsigned_flag; + field->set_notnull(); + error= field->store(value, unsigned_flag); - return true; + return error ? error : field->table->in_use->is_error() ? 1 : 0; } -bool Item_cache_datetime::cache_value() +Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg): + Item_cache_int(field_type_arg) { - if (!example) - return FALSE; - - if (cmp_context == INT_RESULT) - return cache_value_int(); - - str_value_cached= TRUE; - // Mark cached int value obsolete - value_cached= FALSE; - /* Assume here that the underlying item will do correct conversion.*/ - String *res= example->str_result(&str_value); - if (res && res != &str_value) - str_value.copy(*res); - null_value= example->null_value; - unsigned_flag= example->unsigned_flag; - return TRUE; + if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_ERROR) + cached_field_type= MYSQL_TYPE_DATETIME; } -void Item_cache_datetime::store(Item *item, longlong val_arg) +String *Item_cache_temporal::val_str(String *str) { - /* An explicit values is given, save it. */ - value_cached= TRUE; - int_value= val_arg; - null_value= item->null_value; - unsigned_flag= item->unsigned_flag; + DBUG_ASSERT(fixed == 1); + if (!has_value()) + { + null_value= true; + return NULL; + } + return val_string_from_date(str); } -void Item_cache_datetime::store(Item *item) +bool Item_cache_temporal::cache_value() { - Item_cache::store(item); - str_value_cached= FALSE; + if (!example) + return false; + + value_cached= true; + + MYSQL_TIME ltime; + if (example->get_date(<ime, TIME_FUZZY_DATE)) + value=0; + else + value= pack_time(<ime); + null_value= example->null_value; + return true; } -String *Item_cache_datetime::val_str(String *str) + +bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, uint fuzzydate) { - DBUG_ASSERT(fixed == 1); + ErrConvInteger str(value); - if ((value_cached || str_value_cached) && null_value) - return NULL; + if (!has_value()) + { + bzero((char*) ltime,sizeof(*ltime)); + return 1; + } - if (!str_value_cached) + unpack_time(value, ltime); + ltime->time_type= mysql_type_to_time_type(field_type()); + if (ltime->time_type == MYSQL_TIMESTAMP_TIME) { - /* - When it's possible the Item_cache_datetime uses INT datetime - representation due to speed reasons. But still, it always has the STRING - result type and thus it can be asked to return a string value. - It is possible that at this time cached item doesn't contain correct - string value, thus we have to convert cached int value to string and - return it. - */ - if (value_cached) - { - MYSQL_TIME ltime; - /* Return NULL in case of OOM/conversion error. */ - null_value= TRUE; - if (str_value.alloc(MAX_DATE_STRING_REP_LENGTH)) - return NULL; - if (cached_field_type == MYSQL_TYPE_TIME) - { - longlong time= int_value; - set_zero_time(<ime, MYSQL_TIMESTAMP_TIME); - if (time < 0) - { - time= -time; - ltime.neg= TRUE; - } - DBUG_ASSERT(time <= TIME_MAX_VALUE); - ltime.second= time % 100; - time/= 100; - ltime.minute= time % 100; - time/= 100; - ltime.hour= time; - } - else - { - int was_cut; - longlong res; - res= number_to_datetime(int_value, <ime, TIME_FUZZY_DATE, &was_cut); - if (res == -1) - return NULL; - } - str_value.length(my_TIME_to_str(<ime, - const_cast<char*>(str_value.ptr()))); - str_value_cached= TRUE; - null_value= FALSE; - } - else if (!cache_value()) - return NULL; + ltime->hour+= (ltime->month*32+ltime->day)*24; + ltime->month= ltime->day= 0; } - return null_value ? NULL : &str_value; + return 0; + } -my_decimal *Item_cache_datetime::val_decimal(my_decimal *decimal_val) +int Item_cache_temporal::save_in_field(Field *field, bool no_conversions) { - DBUG_ASSERT(fixed == 1); + int error; if (!has_value()) - return NULL; - int2my_decimal(E_DEC_FATAL_ERROR, int_value, unsigned_flag, decimal_val); - return decimal_val; -} + return set_field_to_null_with_conversions(field, no_conversions); -double Item_cache_datetime::val_real() -{ - DBUG_ASSERT(fixed == 1); - if ((!value_cached && !cache_value_int()) || null_value) - return 0.0; - return (double) int_value; + field->set_notnull(); + + MYSQL_TIME ltime; + unpack_time(value, <ime); + ltime.time_type= mysql_type_to_time_type(field_type()); + error= field->store_time_dec(<ime, decimals); + + return error ? error : field->table->in_use->is_error() ? 1 : 0; } -longlong Item_cache_datetime::val_int() + +void Item_cache_temporal::store_packed(longlong val_arg) { - DBUG_ASSERT(fixed == 1); - if ((!value_cached && !cache_value_int()) || null_value) - return 0; - return int_value; + /* An explicit values is given, save it. */ + value_cached= true; + value= val_arg; + null_value= false; } + bool Item_cache_real::cache_value() { if (!example) @@ -8587,7 +8959,7 @@ my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) int Item_cache_str::save_in_field(Field *field, bool no_conversions) { if (!has_value()) - return 0; + return set_field_to_null_with_conversions(field, no_conversions); int res= Item_cache::save_in_field(field, no_conversions); return (is_varbinary && field->type() == MYSQL_TYPE_STRING && value->length() < field->field_length) ? 1 : res; @@ -8741,12 +9113,14 @@ Item_result Item_type_holder::result_type() const enum_field_types Item_type_holder::get_real_type(Item *item) { + if (item->type() == REF_ITEM) + item= item->real_item(); switch(item->type()) { case FIELD_ITEM: { /* - Item_fields::field_type ask Field_type() but sometimes field return + Item_field::field_type ask Field_type() but sometimes field return a different type, like for enum/set, so we need to ask real type. */ Field *field= ((Item_field *) item)->field; @@ -8788,7 +9162,8 @@ enum_field_types Item_type_holder::get_real_type(Item *item) case DECIMAL_RESULT: return MYSQL_TYPE_NEWDECIMAL; case ROW_RESULT: - default: + case TIME_RESULT: + case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); return MYSQL_TYPE_VAR_STRING; } @@ -9115,6 +9490,49 @@ void view_error_processor(THD *thd, void *data) ((TABLE_LIST *)data)->hide_view_error(thd); } + +st_select_lex *Item_ident::get_depended_from() const +{ + st_select_lex *dep; + if ((dep= depended_from)) + for ( ; dep->merged_into; dep= dep->merged_into) ; + return dep; +} + + +table_map Item_ref::used_tables() const +{ + return get_depended_from() ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); +} + + +void Item_ref::update_used_tables() +{ + if (!get_depended_from()) + (*ref)->update_used_tables(); +} + + +table_map Item_direct_view_ref::used_tables() const +{ + return get_depended_from() ? + OUTER_REF_TABLE_BIT : + (view->merged ? (*ref)->used_tables() : view->table->map); +} + + +/* + we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE +*/ +table_map Item_ref_null_helper::used_tables() const +{ + return (get_depended_from() ? + OUTER_REF_TABLE_BIT : + (*ref)->used_tables() | RAND_TABLE_BIT); +} + + + /***************************************************************************** ** Instantiate templates *****************************************************************************/ |