diff options
Diffstat (limited to 'sql/item_subselect.cc')
-rw-r--r-- | sql/item_subselect.cc | 225 |
1 files changed, 143 insertions, 82 deletions
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 378144c707c..6f87adcd068 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -379,20 +379,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join) */ substitution->walk(&Item::remove_dependence_processor, (byte *) select_lex->outer_select()); - if (join->conds || join->having) - { - Item *cond; - if (!join->having) - cond= join->conds; - else if (!join->conds) - cond= join->having; - else - if (!(cond= new Item_cond_and(join->conds, join->having))) - goto err; - if (!(substitution= new Item_func_if(cond, substitution, - new Item_null()))) - goto err; - } + /* SELECT without FROM clause can't have WHERE or HAVING clause */ + DBUG_ASSERT(join->conds == 0 && join->having == 0); return RES_REDUCE; } return RES_OK; @@ -579,7 +567,7 @@ bool Item_in_subselect::test_limit(SELECT_LEX_UNIT *unit) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), transformed(0), upper_item(0) + Item_exists_subselect(), optimizer(0), transformed(0), upper_item(0) { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); left_expr= left_exp; @@ -656,6 +644,11 @@ String *Item_exists_subselect::val_str(String *str) my_decimal *Item_exists_subselect::val_decimal(my_decimal *decimal_value) { + /* + As far as Item_in_subselect called only from Item_in_optimizer this + method should not be used + */ + DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); if (exec()) { @@ -681,6 +674,11 @@ bool Item_exists_subselect::val_bool() double Item_in_subselect::val_real() { + /* + As far as Item_in_subselect called only from Item_in_optimizer this + method should not be used + */ + DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); if (exec()) { @@ -693,8 +691,14 @@ double Item_in_subselect::val_real() return (double) value; } + longlong Item_in_subselect::val_int() { + /* + As far as Item_in_subselect called only from Item_in_optimizer this + method should not be used + */ + DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); if (exec()) { @@ -707,8 +711,14 @@ longlong Item_in_subselect::val_int() return value; } + String *Item_in_subselect::val_str(String *str) { + /* + As far as Item_in_subselect called only from Item_in_optimizer this + method should not be used + */ + DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); if (exec()) { @@ -732,19 +742,10 @@ Item_subselect::trans_res Item_in_subselect::single_value_transformer(JOIN *join, Comp_creator *func) { - const char *save_where= thd->where; Item_subselect::trans_res result= RES_ERROR; DBUG_ENTER("Item_in_subselect::single_value_transformer"); - if (changed) - { - DBUG_RETURN(RES_OK); - } - SELECT_LEX *select_lex= join->select_lex; - Item_arena *arena, backup; - arena= thd->change_arena_if_needed(&backup); - thd->where= "scalar IN/ALL/ANY subquery"; /* Check that the right part of the subselect contains no more than one @@ -753,7 +754,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (select_lex->item_list.elements > 1) { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); - goto err; + DBUG_RETURN(RES_ERROR); } /* @@ -773,11 +774,12 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (substitution) { // It is second (third, ...) SELECT of UNION => All is done - goto ok; + DBUG_RETURN(RES_OK); } Item *subs; if (!select_lex->group_list.elements && + !select_lex->having && !select_lex->with_sum_func && !(select_lex->next_select())) { @@ -813,7 +815,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, we do not check item->fixed */ if (item->fix_fields(thd, join->tables_list, 0)) - goto err; + DBUG_RETURN(RES_ERROR); /* we added aggregate function => we have to change statistic */ count_field_types(&join->tmp_table_param, join->all_fields, 0); @@ -829,25 +831,16 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (upper_item) upper_item->set_sub_test(item); } - // left expression belong to outer select - SELECT_LEX *current= thd->lex->current_select, *up; - thd->lex->current_select= up= current->return_after_parsing(); - if (!left_expr->fixed && - left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) - { - thd->lex->current_select= current; - goto err; - } - thd->lex->current_select= current; + /* fix fields is already called for left expression */ substitution= func->create(left_expr, subs); - goto ok; + DBUG_RETURN(RES_OK); } if (!substitution) { //first call for this unit SELECT_LEX_UNIT *unit= select_lex->master_unit(); - substitution= optimizer= new Item_in_optimizer(left_expr, this); + substitution= optimizer; SELECT_LEX *current= thd->lex->current_select, *up; @@ -856,7 +849,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { thd->lex->current_select= current; - goto err; + DBUG_RETURN(RES_ERROR); } thd->lex->current_select= current; @@ -907,7 +900,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) - goto err; + DBUG_RETURN(RES_ERROR); } else { @@ -943,7 +936,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) - goto err; + DBUG_RETURN(RES_ERROR); item= new Item_cond_or(item, new Item_func_isnull(orig_item)); #ifdef CORRECT_BUT_TOO_SLOW_TO_BE_USABLE @@ -963,7 +956,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, after creation */ if (join->conds->fix_fields(thd, join->tables_list, 0)) - goto err; + DBUG_RETURN(RES_ERROR); } else { @@ -992,7 +985,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, tmp= join->having->fix_fields(thd, join->tables_list, 0); select_lex->having_fix_field= 0; if (tmp) - goto err; + DBUG_RETURN(RES_ERROR); } else { @@ -1008,51 +1001,33 @@ Item_in_subselect::single_value_transformer(JOIN *join, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } - result= RES_REDUCE; - goto err; + DBUG_RETURN(RES_REDUCE); } } } -ok: - thd->where= save_where; - result= RES_OK; - -err: - if (arena) - thd->restore_backup_item_arena(arena, &backup); - DBUG_RETURN(result); + DBUG_RETURN(RES_OK); } Item_subselect::trans_res Item_in_subselect::row_value_transformer(JOIN *join) { - const char *save_where= thd->where; Item *item= 0; SELECT_LEX *select_lex= join->select_lex; DBUG_ENTER("Item_in_subselect::row_value_transformer"); - if (changed) - { - DBUG_RETURN(RES_OK); - } - thd->where= "row IN/ALL/ANY subquery"; - - Item_arena *arena, backup; - arena= thd->change_arena_if_needed(&backup); - if (select_lex->item_list.elements != left_expr->cols()) { my_error(ER_OPERAND_COLUMNS, MYF(0), left_expr->cols()); - goto err; + DBUG_RETURN(RES_ERROR); } if (!substitution) { //first call for this unit SELECT_LEX_UNIT *unit= select_lex->master_unit(); - substitution= optimizer= new Item_in_optimizer(left_expr, this); + substitution= optimizer; SELECT_LEX *current= thd->lex->current_select, *up; thd->lex->current_select= up= current->return_after_parsing(); @@ -1060,7 +1035,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0)) { thd->lex->current_select= current; - goto err; + DBUG_RETURN(RES_ERROR); } // we will refer to upper level cache array => we have to save it in PS @@ -1079,7 +1054,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->el(i)->cols())) - goto err; + DBUG_RETURN(RES_ERROR); Item *func= new Item_ref_null_helper(this, select_lex->ref_pointer_array+i, (char *) "<no matter>", @@ -1111,7 +1086,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) if (join->having->fix_fields(thd, join->tables_list, 0)) { select_lex->having_fix_field= 0; - goto err; + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; } @@ -1128,27 +1103,113 @@ Item_in_subselect::row_value_transformer(JOIN *join) join->conds->fixed */ if (join->conds->fix_fields(thd, join->tables_list, 0)) - goto err; + DBUG_RETURN(RES_ERROR); } - thd->where= save_where; - if (arena) - thd->restore_backup_item_arena(arena, &backup); - DBUG_RETURN(RES_OK); -err: - if (arena) - thd->restore_backup_item_arena(arena, &backup); - DBUG_RETURN(RES_ERROR); + DBUG_RETURN(RES_OK); } Item_subselect::trans_res Item_in_subselect::select_transformer(JOIN *join) { + return select_in_like_transformer(join, &eq_creator); +} + + +/* + Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate + transformation function + + SYNOPSIS + Item_in_subselect::select_in_like_transformer() + join JOIN object of transforming subquery + func creator of condition function of subquery + + DESCRIPTION + To decide which transformation procedure (scalar or row) applicable here + we have to call fix_fields() for left expression to be able to call + cols() method on it. Also this method make arena management for + underlying transformation methods. + + RETURN + RES_OK OK + RES_REDUCE OK, and current subquery was reduced during transformation + RES_ERROR Error +*/ + +Item_subselect::trans_res +Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) +{ + Item_arena *arena, backup; + SELECT_LEX *current= thd->lex->current_select, *up; + const char *save_where= thd->where; + Item_subselect::trans_res res= RES_ERROR; + bool result; + + DBUG_ENTER("Item_in_subselect::select_in_like_transformer"); + + if (changed) + { + DBUG_RETURN(RES_OK); + } + + thd->where= "IN/ALL/ANY subquery"; + + /* + In some optimisation cases we will not need this Item_in_optimizer + object, but we can't know it here, but here we need address correct + reference on left expresion. + */ + if (!optimizer) + { + arena= thd->change_arena_if_needed(&backup); + result= (!(optimizer= new Item_in_optimizer(left_expr, this))); + if (arena) + thd->restore_backup_item_arena(arena, &backup); + if (result) + goto err; + } + + thd->lex->current_select= up= current->return_after_parsing(); + result= (!left_expr->fixed && + left_expr->fix_fields(thd, up->get_table_list(), + optimizer->arguments())); + /* fix_fields can change reference to left_expr, we need reassign it */ + left_expr= optimizer->arguments()[0]; + + thd->lex->current_select= current; + if (result) + goto err; + transformed= 1; + arena= thd->change_arena_if_needed(&backup); + /* + Both transformers call fix_fields() only for Items created inside them, + and all that items do not make permanent changes in current item arena + which allow to us call them with changed arena (if we do not know nature + of Item, we have to call fix_fields() for it only with original arena to + avoid memory leack) + */ if (left_expr->cols() == 1) - return single_value_transformer(join, &eq_creator); - return row_value_transformer(join); + res= single_value_transformer(join, func); + else + { + /* we do not support row operation for ALL/ANY/SOME */ + if (func != &eq_creator) + { + if (arena) + thd->restore_backup_item_arena(arena, &backup); + my_error(ER_OPERAND_COLUMNS, MYF(0), 1); + DBUG_RETURN(RES_ERROR); + } + res= row_value_transformer(join); + } + if (arena) + thd->restore_backup_item_arena(arena, &backup); +err: + thd->where= save_where; + DBUG_RETURN(res); } @@ -1171,7 +1232,7 @@ Item_allany_subselect::select_transformer(JOIN *join) transformed= 1; if (upper_item) upper_item->show= 1; - return single_value_transformer(join, func); + return select_in_like_transformer(join, func); } @@ -1279,7 +1340,7 @@ int subselect_single_select_engine::prepare() int subselect_union_engine::prepare() { - return unit->prepare(thd, result, SELECT_NO_UNLOCK); + return unit->prepare(thd, result, SELECT_NO_UNLOCK, ""); } int subselect_uniquesubquery_engine::prepare() |