summaryrefslogtreecommitdiff
path: root/sql/item_subselect.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/item_subselect.cc')
-rw-r--r--sql/item_subselect.cc225
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()