diff options
Diffstat (limited to 'sql/item_subselect.cc')
-rw-r--r-- | sql/item_subselect.cc | 215 |
1 files changed, 132 insertions, 83 deletions
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 3a1e1918e55..301740c50ef 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -369,25 +369,13 @@ Item_singlerow_subselect::select_transformer(JOIN *join) } substitution= select_lex->item_list.head(); /* - as far as we moved content to upper leven, field which depend of + as far as we moved content to upper level, field which depend of 'upper' select is not really dependent => we remove this dependence */ 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; @@ -542,7 +530,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; @@ -616,8 +604,14 @@ String *Item_exists_subselect::val_str(String *str) return str; } + double Item_in_subselect::val() { + /* + 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()) { @@ -630,6 +624,7 @@ double Item_in_subselect::val() return (double) value; } + longlong Item_in_subselect::val_int() { DBUG_ASSERT(fixed == 1); @@ -644,8 +639,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()) { @@ -669,18 +670,9 @@ Item_subselect::trans_res Item_in_subselect::single_value_transformer(JOIN *join, Comp_creator *func) { - const char *save_where= thd->where; 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 @@ -689,7 +681,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); } /* @@ -709,11 +701,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())) { @@ -748,7 +741,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, reference */ 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); @@ -764,25 +757,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; @@ -791,7 +775,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; @@ -835,7 +819,7 @@ Item_in_subselect::single_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; } @@ -865,7 +849,7 @@ Item_in_subselect::single_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; item= new Item_cond_or(item, @@ -879,7 +863,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, */ select_lex->where= join->conds= and_items(join->conds, item); if (join->conds->fix_fields(thd, join->tables_list, 0)) - goto err; + DBUG_RETURN(RES_ERROR); } else { @@ -901,7 +885,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, 0)) { select_lex->having_fix_field= 0; - goto err; + DBUG_RETURN(RES_ERROR); } select_lex->having_fix_field= 0; } @@ -919,54 +903,34 @@ Item_in_subselect::single_value_transformer(JOIN *join, push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } - if (arena) - thd->restore_backup_item_arena(arena, &backup); DBUG_RETURN(RES_REDUCE); } } } -ok: - if (arena) - thd->restore_backup_item_arena(arena, &backup); - thd->where= save_where; DBUG_RETURN(RES_OK); - -err: - if (arena) - thd->restore_backup_item_arena(arena, &backup); - DBUG_RETURN(RES_ERROR); } Item_subselect::trans_res Item_in_subselect::row_value_transformer(JOIN *join) { - const char *save_where= thd->where; DBUG_ENTER("Item_in_subselect::row_value_transformer"); - if (changed) - { - DBUG_RETURN(RES_OK); - } - Item_arena *arena, backup; Item *item= 0; SELECT_LEX *select_lex= join->select_lex; - thd->where= "row IN/ALL/ANY subquery"; - 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(); @@ -974,7 +938,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 apper level cache array => we have to save it in PS @@ -993,7 +957,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>", @@ -1021,7 +985,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; } @@ -1034,27 +998,112 @@ Item_in_subselect::row_value_transformer(JOIN *join) */ select_lex->where= join->conds= and_items(join->conds, item); if (join->conds->fix_fields(thd, join->tables_list, 0)) - goto err; + DBUG_RETURN(RES_ERROR); } - if (arena) - thd->restore_backup_item_arena(arena, &backup); - thd->where= save_where; DBUG_RETURN(RES_OK); - -err: - if (arena) - thd->restore_backup_item_arena(arena, &backup); - DBUG_RETURN(RES_ERROR); } 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); } @@ -1077,7 +1126,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); } @@ -1191,7 +1240,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() |