diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 554 |
1 files changed, 518 insertions, 36 deletions
diff --git a/sql/item.cc b/sql/item.cc index dd303c93844..274a467f4e3 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, my_bool *null_value, + Field *to, bool no_conversions); + /****************************************************************************/ /* Hybrid_type_traits {_real} */ @@ -541,6 +544,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(this); + DBUG_RETURN(wrapper); + } + DBUG_RETURN(this); +} + + Item_ident::Item_ident(Name_resolution_context *context_arg, const char *db_name_arg,const char *table_name_arg, const char *field_name_arg) @@ -549,7 +582,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; } @@ -561,7 +594,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; } @@ -583,7 +616,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() @@ -601,7 +635,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; } @@ -1424,7 +1459,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 || @@ -1745,7 +1780,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); } @@ -2131,6 +2169,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())) @@ -3596,6 +3640,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); @@ -3668,7 +3721,7 @@ static bool 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; if (current->mark_as_dependent(thd, last, /** resolved_item psergey-thu **/mark_item)) @@ -4134,7 +4187,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 @@ -4236,6 +4291,10 @@ 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; } else @@ -4243,6 +4302,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; @@ -4549,6 +4611,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. @@ -5107,47 +5170,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, my_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); } @@ -6037,6 +6122,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 @@ -6087,6 +6175,9 @@ 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 @@ -6110,6 +6201,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 @@ -6124,7 +6218,11 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) } 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 @@ -6315,6 +6413,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); @@ -6432,6 +6549,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(); @@ -6484,6 +6608,364 @@ 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; +} + + +/** + 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; +} + + /** Prepare referenced field then call usual Item_direct_ref::fix_fields . |