diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index c2adb58677d..0c48592eb9f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1442,6 +1442,12 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) bool Item_in_optimizer::fix_left(THD *thd, Item **ref) { DBUG_ENTER("Item_in_optimizer::fix_left"); + /* + Here we will store pointer on place of main storage of left expression. + For usual IN (ALL/ANY) it is subquery left_expr. + For other cases (MAX/MIN optimization, non-transformed EXISTS (10.0)) + it is args[0]. + */ Item **ref0= args; if (args[1]->type() == Item::SUBSELECT_ITEM && ((Item_subselect *)args[1])->is_in_predicate()) @@ -1455,12 +1461,17 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) next execution we need to copy args[1]->left_expr again. */ ref0= &(((Item_in_subselect *)args[1])->left_expr); + args[0]= ((Item_in_subselect *)args[1])->left_expr; } if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) || (!cache && !(cache= Item_cache::get_cache(*ref0)))) DBUG_RETURN(1); + /* + During fix_field() expression could be substituted. + So we copy changes before use + */ if (args[0] != (*ref0)) - current_thd->change_item_tree(args, (*ref0)); + args[0]= (*ref0); DBUG_PRINT("info", ("actual fix fields")); cache->setup(args[0]); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 930bd665e3a..0ee5f73eb35 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -449,6 +449,11 @@ protected: Item **having_item); public: Item *left_expr; + /* + Important for PS/SP: left_expr_orig is the item that left_expr originally + pointed at. That item is allocated on the statement arena, while + left_expr could later be changed to something on the execution arena. + */ Item *left_expr_orig; /* Priority of this predicate in the convert-to-semi-join-nest process. */ int sj_convert_priority; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 1363be002b9..827290fd15b 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1592,6 +1592,15 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) if (subq_pred->left_expr->cols() == 1) { nested_join->sj_outer_expr_list.push_back(subq_pred->left_expr); + /* + Create Item_func_eq. Note that + 1. this is done on the statement, not execution, arena + 2. if it's a PS then this happens only once - on the first execution. + On following re-executions, the item will be fix_field-ed normally. + 3. Thus it should be created as if it was fix_field'ed, in particular + all pointers to items in the execution arena should be protected + with thd->change_item_tree + */ Item_func_eq *item_eq= new Item_func_eq(subq_pred->left_expr_orig, subq_lex->ref_pointer_array[0]); if (subq_pred->left_expr_orig != subq_pred->left_expr)