diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 811 |
1 files changed, 728 insertions, 83 deletions
diff --git a/sql/item.cc b/sql/item.cc index 2fb2f5e91e2..310e6994c7d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -28,6 +28,9 @@ 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); + /****************************************************************************/ /* Hybrid_type_traits {_real} */ @@ -374,8 +377,8 @@ int Item::save_str_value_in_field(Field *field, String *result) Item::Item(): - rsize(0), name(0), orig_name(0), name_length(0), fixed(0), - is_autogenerated_name(TRUE), + 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) { marker= 0; @@ -412,6 +415,7 @@ Item::Item(): tables. */ Item::Item(THD *thd, Item *item): + is_expensive_cache(-1), rsize(0), str_value(item->str_value), name(item->name), @@ -543,6 +547,36 @@ Item* Item::transform(Item_transformer transformer, uchar *arg) } +/** + Create and set up an expression cache for this item + + @param thd Thread handle + @param depends_on List of the expression parameters + + @details + The function creates an expression cache for an item and its parameters + specified by the 'depends_on' list. Then the expression cache is placed + into a cache wrapper that is returned as the result of the function. + + @returns + A pointer to created wrapper item if successful, NULL - otherwise +*/ + +Item* Item::set_expr_cache(THD *thd, List<Item *> &depends_on) +{ + 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)) + DBUG_RETURN(NULL); + DBUG_RETURN(wrapper); + } + DBUG_RETURN(NULL); +} + + Item_ident::Item_ident(Name_resolution_context *context_arg, const char *db_name_arg,const char *table_name_arg, const char *field_name_arg) @@ -551,7 +585,7 @@ Item_ident::Item_ident(Name_resolution_context *context_arg, db_name(db_name_arg), table_name(table_name_arg), field_name(field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), - cached_table(0), depended_from(0) + cached_table(0), depended_from(0), can_be_depended(TRUE) { name = (char*) field_name_arg; } @@ -563,7 +597,7 @@ Item_ident::Item_ident(TABLE_LIST *view_arg, const char *field_name_arg) db_name(NullS), table_name(view_arg->alias), field_name(field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), - cached_table(NULL), depended_from(NULL) + cached_table(NULL), depended_from(NULL), can_be_depended(TRUE) { name = (char*) field_name_arg; } @@ -585,7 +619,8 @@ Item_ident::Item_ident(THD *thd, Item_ident *item) alias_name_used(item->alias_name_used), cached_field_index(item->cached_field_index), cached_table(item->cached_table), - depended_from(item->depended_from) + depended_from(item->depended_from), + can_be_depended(item->can_be_depended) {} void Item_ident::cleanup() @@ -603,7 +638,8 @@ void Item_ident::cleanup() db_name= orig_db_name; table_name= orig_table_name; field_name= orig_field_name; - depended_from= 0; + /* Store if this Item was depended */ + can_be_depended= test(depended_from); DBUG_VOID_RETURN; } @@ -652,6 +688,16 @@ bool Item_field::collect_item_field_processor(uchar *arg) } +bool Item_field::add_field_to_set_processor(uchar *arg) +{ + DBUG_ENTER("Item_field::add_field_to_set_processor"); + DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname")); + TABLE *table= (TABLE *) arg; + if (field->table == table) + bitmap_set_bit(&table->tmp_set, field->field_index); + DBUG_RETURN(FALSE); +} + /** Check if an Item_field references some field from a list of fields. @@ -737,7 +783,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. @@ -747,16 +794,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)) @@ -1416,7 +1467,7 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, /* An item of type Item_sum is registered <=> ref_by != 0 */ if (type() == SUM_FUNC_ITEM && skip_registered && ((Item_sum *) this)->ref_by) - return; + return; if ((type() != SUM_FUNC_ITEM && with_sum_func) || (type() == FUNC_ITEM && (((Item_func *) this)->functype() == Item_func::ISNOTNULLTEST_FUNC || @@ -1737,7 +1788,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, subselect transformation does not happen in view_prepare_mode and thus val_...() methods can not be called for const items. */ - bool resolve_const= ((*arg)->type() == Item::SUBSELECT_ITEM && + bool resolve_const= (((*arg)->type() == Item::SUBSELECT_ITEM || + ((*arg)->get_cached_item() && + (*arg)->get_cached_item()->type() == + Item::SUBSELECT_ITEM)) && thd->lex->view_prepare_mode) ? FALSE : TRUE; conv= new Item_func_conv_charset(*arg, coll.collation, resolve_const); } @@ -1963,7 +2017,7 @@ void Item_field::reset_field(Field *f) bool Item_field::enumerate_field_refs_processor(uchar *arg) { Field_enumerator *fe= (Field_enumerator*)arg; - fe->visit_field(field); + fe->visit_field(this); return FALSE; } @@ -2123,6 +2177,12 @@ bool Item_field::get_time(MYSQL_TIME *ltime) return 0; } +void Item_field::save_result(Field *to) +{ + save_field_in_field(result_field, &null_value, to, TRUE); +} + + double Item_field::val_result() { if ((null_value=result_field->is_null())) @@ -2216,6 +2276,24 @@ table_map Item_field::used_tables() const } +void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + if (new_parent == 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 + ctx->table_list= NULL; // We rely on first_name_resolution_table instead + ctx->select_lex= new_parent; + ctx->first_name_resolution_table= context->first_name_resolution_table; + ctx->last_name_resolution_table= context->last_name_resolution_table; + ctx->error_processor= context->error_processor; + ctx->error_processor_data= context->error_processor_data; + ctx->resolve_in_select_list= context->resolve_in_select_list; + ctx->security_ctx= context->security_ctx; + this->context=ctx; +} + + Item *Item_field::get_tmp_table_item(THD *thd) { Item_field *new_item= new Item_field(thd, this); @@ -2495,14 +2573,12 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *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))) { - /* - We can use str_value.ptr() here as Item_string is gurantee to put an - end \0 here. - */ + char buff[80]; + strmake(buff, cptr, min(sizeof(buff)-1, (size_t) (org_end-cptr))); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", - cptr); + buff); } return tmp; } @@ -2511,8 +2587,10 @@ 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(), + (char *) str_value.ptr() + + str_value.length()); } @@ -3570,6 +3648,15 @@ bool Item::fix_fields(THD *thd, Item **ref) return FALSE; } + +void Item_ref_null_helper::save_val(Field *to) +{ + DBUG_ASSERT(fixed == 1); + (*ref)->save_val(to); + owner->was_null|= null_value= (*ref)->null_value; +} + + double Item_ref_null_helper::val_real() { DBUG_ASSERT(fixed == 1); @@ -3633,7 +3720,7 @@ bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, uint fuzzydate) substitution) */ -static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, +static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, Item_ident *resolved_item, Item_ident *mark_item) { @@ -3642,9 +3729,11 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, const char *table_name= (resolved_item->table_name ? resolved_item->table_name : ""); /* store pointer on SELECT_LEX from which item is dependent */ - if (mark_item) + if (mark_item && mark_item->can_be_depended) mark_item->depended_from= last; - current->mark_as_dependent(last, resolved_item); + if (current->mark_as_dependent(thd, last, /** resolved_item psergey-thu + **/mark_item)) + return TRUE; if (thd->lex->describe & DESCRIBE_EXTENDED) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -3654,6 +3743,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, resolved_item->field_name, current->select_number, last->select_number); } + return FALSE; } @@ -4105,6 +4195,9 @@ 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 @@ -4203,8 +4296,12 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) return -1; mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, + context->select_lex, rf, rf); + context->select_lex-> + register_dependency_item(last_checked_context->select_lex, + reference); + return 0; } else @@ -4212,6 +4309,9 @@ 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; @@ -4518,6 +4618,7 @@ void Item_field::cleanup() { DBUG_ENTER("Item_field::cleanup"); Item_ident::cleanup(); + depended_from= NULL; /* Even if this object was created by direct link to field in setup_wild() it will be linked correctly next time by name of field and table alias. @@ -4746,7 +4847,7 @@ Item *Item_field::replace_equal_field(uchar *arg) return this; return const_item; } - Item_field *subst= item_equal->get_first(); + Item_field *subst= item_equal->get_first(this); if (subst && field->table != subst->field->table && !field->eq(subst->field)) return subst; } @@ -5076,47 +5177,69 @@ void Item_field::make_field(Send_field *tmp_field) /** - Set a field's value from a item. -*/ + Save a field value in another field -void Item_field::save_org_in_field(Field *to) -{ - if (field->is_null()) - { - null_value=1; - set_field_to_null_with_conversions(to, 1); - } - else - { - to->set_notnull(); - field_conv(to,field); - null_value=0; - } -} + @param from Field to take the value from + @param [out] null_value Pointer to the null_value flag to set + @param to Field to save the value in + @param no_conversions How to deal with NULL value -int Item_field::save_in_field(Field *to, bool no_conversions) + @details + The function takes the value of the field 'from' and, if this value + is not null, it saves in the field 'to' setting off the flag referenced + by 'null_value'. Otherwise this flag is set on and field 'to' is + also set to null possibly with conversion. + + @note + This function is used by the functions Item_field::save_in_field, + Item_field::save_org_in_field and Item_ref::save_in_field + + @retval FALSE OK + @retval TRUE Error + +*/ + +static int save_field_in_field(Field *from, bool *null_value, + Field *to, bool no_conversions) { int res; - if (result_field->is_null()) + DBUG_ENTER("save_field_in_field"); + if (from->is_null()) { - null_value=1; - return set_field_to_null_with_conversions(to, no_conversions); + (*null_value)= 1; + DBUG_RETURN(set_field_to_null_with_conversions(to, no_conversions)); } to->set_notnull(); /* If we're setting the same field as the one we're reading from there's nothing to do. This can happen in 'SET x = x' type of scenarios. - */ - if (to == result_field) + */ + if (to == from) { - null_value=0; - return 0; + (*null_value)= 0; + DBUG_RETURN(0); } - res= field_conv(to,result_field); - null_value=0; - return res; + res= field_conv(to, from); + (*null_value)= 0; + DBUG_RETURN(res); +} + + +/** + Set a field's value from a item. +*/ + +void Item_field::save_org_in_field(Field *to) +{ + save_field_in_field(field, &null_value, to, TRUE); +} + + +int Item_field::save_in_field(Field *to, bool no_conversions) +{ + return save_field_in_field(result_field, &null_value, to, no_conversions); } @@ -5789,6 +5912,35 @@ Item_ref::Item_ref(Name_resolution_context *context_arg, set_properties(); } +/* + A Field_enumerator-compatible class that invokes mark_as_dependent() for + each field that is a reference to some ancestor of current_select. +*/ +class Dependency_marker: public Field_enumerator +{ +public: + THD *thd; + st_select_lex *current_select; + virtual void visit_field(Item_field *item) + { + // Find which select the field is in. This is achieved by walking up + // the select tree and looking for the table of interest. + st_select_lex *sel; + for (sel= current_select; sel; sel= sel->outer_select()) + { + TABLE_LIST *tbl; + for (tbl= sel->leaf_tables; tbl; tbl= tbl->next_leaf) + { + if (tbl->table == item->field->table) + { + if (sel != current_select) + mark_as_dependent(thd, sel, current_select, item, item); + return; + } + } + } + } +}; Item_ref::Item_ref(TABLE_LIST *view_arg, Item **item, const char *field_name_arg, bool alias_name_used_arg) @@ -5977,6 +6129,9 @@ 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 @@ -6026,7 +6181,10 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) goto error; thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, - thd->lex->current_select, this, fld); + 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 @@ -6050,6 +6208,9 @@ 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 @@ -6062,6 +6223,24 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) last_checked_context->select_lex->nest_level); } } + else + { + 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 + 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); /* @@ -6241,6 +6420,25 @@ bool Item_ref::val_bool_result() } +void Item_ref::save_result(Field *to) +{ + if (result_field) + { + save_field_in_field(result_field, &null_value, to, TRUE); + return; + } + (*ref)->save_result(to); + null_value= (*ref)->null_value; +} + + +void Item_ref::save_val(Field *to) +{ + (*ref)->save_result(to); + null_value= (*ref)->null_value; +} + + double Item_ref::val_real() { DBUG_ASSERT(fixed); @@ -6358,6 +6556,13 @@ void Item_ref_null_helper::print(String *str, enum_query_type query_type) } +void Item_direct_ref::save_val(Field *to) +{ + (*ref)->save_val(to); + null_value=(*ref)->null_value; +} + + double Item_direct_ref::val_real() { double tmp=(*ref)->val_real(); @@ -6410,6 +6615,385 @@ 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 */ +} + + +Item_cache_wrapper::Item_cache_wrapper(Item *item_arg) +:orig_item(item_arg), expr_cache(NULL), expr_value(NULL) +{ + DBUG_ASSERT(orig_item->fixed); + max_length= orig_item->max_length; + maybe_null= orig_item->maybe_null; + decimals= orig_item->decimals; + collation.set(orig_item->collation); + with_sum_func= orig_item->with_sum_func; + unsigned_flag= orig_item->unsigned_flag; + name= item_arg->name; + name_length= item_arg->name_length; + + if ((expr_value= Item_cache::get_cache(orig_item))) + expr_value->setup(orig_item); + + fixed= 1; +} + + +void Item_cache_wrapper::print(String *str, enum_query_type query_type) +{ + str->append(func_name()); + if (expr_cache) + expr_cache->print(str, query_type); + else + str->append(STRING_WITH_LEN("<<DISABLED>>")); + str->append('('); + orig_item->print(str, query_type); + str->append(')'); +} + + +/** + Prepare the expression cache wrapper (do nothing) + + @retval FALSE OK +*/ + +bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)), + Item **it __attribute__((unused))) +{ + DBUG_ASSERT(orig_item->fixed); + DBUG_ASSERT(fixed); + return FALSE; +} + + +/** + Clean the expression cache wrapper up before reusing it. +*/ + +void Item_cache_wrapper::cleanup() +{ + delete expr_cache; + expr_cache= 0; + // expr_value is Item so it will be destroyed from list of Items + expr_value= 0; +} + + +/** + Create an expression cache that uses a temporary table + + @param thd Thread handle + @param depends_on Parameters of the expression to create cache for + + @details + The function takes 'depends_on' as the list of all parameters for + the expression wrapped into this object and creates an expression + cache in a temporary table containing the field for the parameters + and the result of the expression. + + @retval FALSE OK + @retval TRUE Error +*/ + +bool Item_cache_wrapper::set_cache(THD *thd, List<Item*> &depends_on) +{ + DBUG_ENTER("Item_cache_wrapper::set_cache"); + expr_cache= new Expression_cache_tmptable(thd, depends_on, expr_value); + DBUG_RETURN(expr_cache == NULL); +} + + +/** + Check if the current values of the parameters are in the expression cache + + @details + The function checks whether the current set of the parameters of the + referenced item can be found in the expression cache. If so the function + returns the item by which the result of the expression can be easily + extracted from the cache with the corresponding val_* method. + + @retval NULL - parameters are not in the cache + @retval <item*> - item providing the result of the expression found in cache +*/ + +Item *Item_cache_wrapper::check_cache() +{ + DBUG_ENTER("Item_cache_wrapper::check_cache"); + if (expr_cache) + { + Expression_cache_tmptable::result res; + Item *cached_value; + res= expr_cache->check_value(&cached_value); + if (res == Expression_cache_tmptable::HIT) + DBUG_RETURN(cached_value); + } + DBUG_RETURN(NULL); +} + + +/** + Get the value of the cached expression and put it in the cache +*/ + +inline void Item_cache_wrapper::cache() +{ + expr_value->store(orig_item); + expr_value->cache_value(); + expr_cache->put_value(expr_value); // put in expr_cache +} + + +/** + Get the value of the possibly cached item into the field. +*/ + +void Item_cache_wrapper::save_val(Field *to) +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_int"); + if (!expr_cache) + { + orig_item->save_val(to); + null_value= orig_item->null_value; + DBUG_VOID_RETURN; + } + + if ((cached_value= check_cache())) + { + cached_value->save_val(to); + null_value= cached_value->null_value; + DBUG_VOID_RETURN; + } + cache(); + null_value= expr_value->null_value; + expr_value->save_val(to); + DBUG_VOID_RETURN; +} + + +/** + Get the integer value of the possibly cached item. +*/ + +longlong Item_cache_wrapper::val_int() +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_int"); + if (!expr_cache) + { + longlong tmp= orig_item->val_int(); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + longlong tmp= cached_value->val_int(); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + null_value= expr_value->null_value; + DBUG_RETURN(expr_value->val_int()); +} + + +/** + Get the real value of the possibly cached item +*/ + +double Item_cache_wrapper::val_real() +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_real"); + if (!expr_cache) + { + double tmp= orig_item->val_real(); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + double tmp= cached_value->val_real(); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + null_value= expr_value->null_value; + DBUG_RETURN(expr_value->val_real()); +} + + +/** + Get the string value of the possibly cached item +*/ + +String *Item_cache_wrapper::val_str(String* str) +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_str"); + if (!expr_cache) + { + String *tmp= orig_item->val_str(str); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + String *tmp= cached_value->val_str(str); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + if ((null_value= expr_value->null_value)) + DBUG_RETURN(NULL); + DBUG_RETURN(expr_value->val_str(str)); +} + + +/** + Get the decimal value of the possibly cached item +*/ + +my_decimal *Item_cache_wrapper::val_decimal(my_decimal* decimal_value) +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_decimal"); + if (!expr_cache) + { + my_decimal *tmp= orig_item->val_decimal(decimal_value); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + my_decimal *tmp= cached_value->val_decimal(decimal_value); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + if ((null_value= expr_value->null_value)) + DBUG_RETURN(NULL); + DBUG_RETURN(expr_value->val_decimal(decimal_value)); +} + + +/** + Get the boolean value of the possibly cached item +*/ + +bool Item_cache_wrapper::val_bool() +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::val_bool"); + if (!expr_cache) + { + bool tmp= orig_item->val_bool(); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + bool tmp= cached_value->val_bool(); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + null_value= expr_value->null_value; + DBUG_RETURN(expr_value->val_bool()); +} + + +/** + Check for NULL the value of the possibly cached item +*/ + +bool Item_cache_wrapper::is_null() +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::is_null"); + if (!expr_cache) + { + bool tmp= orig_item->is_null(); + null_value= orig_item->null_value; + DBUG_RETURN(tmp); + } + + if ((cached_value= check_cache())) + { + bool tmp= cached_value->is_null(); + null_value= cached_value->null_value; + DBUG_RETURN(tmp); + } + cache(); + DBUG_RETURN((null_value= expr_value->null_value)); +} + + +/** + Get the date value of the possibly cached item +*/ + +bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, uint fuzzydate) +{ + Item *cached_value; + DBUG_ENTER("Item_cache_wrapper::get_date"); + if (!expr_cache) + DBUG_RETURN((null_value= orig_item->get_date(ltime, fuzzydate))); + + if ((cached_value= check_cache())) + DBUG_RETURN((null_value= cached_value->get_date(ltime, fuzzydate))); + + cache(); + DBUG_RETURN((null_value= expr_value->get_date(ltime, 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; + DBUG_ASSERT(!result_field); + res= orig_item->save_in_field(to, no_conversions); + null_value= orig_item->null_value; + return res; +} + + +Item* Item_cache_wrapper::get_tmp_table_item(THD *thd_arg) +{ + if (!orig_item->with_sum_func && !orig_item->const_item()) + return new Item_field(result_field); + return copy_or_same(thd_arg); +} + + /** Prepare referenced field then call usual Item_direct_ref::fix_fields . @@ -6478,6 +7062,23 @@ 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) + { + *ref= outer_ref; + (*ref)->fix_after_pullout(new_parent, 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) + depended_from= NULL; +} + + /** Mark references from inner selects used in group by clause @@ -7156,8 +7757,11 @@ void Item_cache_int::store_longlong(Item *item, longlong val_arg) String *Item_cache_int::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } str->set(value, default_charset()); return str; } @@ -7166,8 +7770,11 @@ String *Item_cache_int::val_str(String *str) my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); return decimal_val; } @@ -7175,16 +7782,22 @@ my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) double Item_cache_int::val_real() { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0.0; + } return (double) value; } longlong Item_cache_int::val_int() { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0; + } return value; } @@ -7202,16 +7815,22 @@ bool Item_cache_real::cache_value() double Item_cache_real::val_real() { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0.0; + } return value; } longlong Item_cache_real::val_int() { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0; + } return (longlong) rint(value); } @@ -7219,8 +7838,11 @@ longlong Item_cache_real::val_int() String* Item_cache_real::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } str->set_real(value, decimals, default_charset()); return str; } @@ -7229,8 +7851,11 @@ String* Item_cache_real::val_str(String *str) my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); return decimal_val; } @@ -7251,8 +7876,11 @@ double Item_cache_decimal::val_real() { DBUG_ASSERT(fixed); double res; - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0.0; + } my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); return res; } @@ -7261,8 +7889,11 @@ longlong Item_cache_decimal::val_int() { DBUG_ASSERT(fixed); longlong res; - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0; + } my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); return res; } @@ -7270,8 +7901,11 @@ longlong Item_cache_decimal::val_int() String* Item_cache_decimal::val_str(String *str) { DBUG_ASSERT(fixed); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, &decimal_value); my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); @@ -7281,8 +7915,11 @@ String* Item_cache_decimal::val_str(String *str) my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) { DBUG_ASSERT(fixed); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; + } return &decimal_value; } @@ -7317,12 +7954,13 @@ double Item_cache_str::val_real() DBUG_ASSERT(fixed == 1); int err_not_used; char *end_not_used; - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; 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 my_strntod(value->charset(), (char*) value->ptr(), + value->length(), &end_not_used, &err_not_used); } @@ -7330,21 +7968,24 @@ longlong Item_cache_str::val_int() { DBUG_ASSERT(fixed == 1); int err; - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0; - if (value) - return my_strntoll(value->charset(), value->ptr(), - value->length(), 10, (char**) 0, &err); - else - return (longlong)0; + } + return my_strntoll(value->charset(), value->ptr(), + value->length(), 10, (char**) 0, &err); } String* Item_cache_str::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return 0; + } return value; } @@ -7352,20 +7993,24 @@ String* Item_cache_str::val_str(String *str) my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) { DBUG_ASSERT(fixed == 1); - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; return NULL; - if (value) - string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); - else - decimal_val= 0; + } + string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); return decimal_val; } int Item_cache_str::save_in_field(Field *field, bool no_conversions) { - if (!value_cached && !cache_value()) + if ((!value_cached && !cache_value()) || null_value) + { + field->set_notnull(); + null_value= TRUE; return 0; + } 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; |