MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution of PS with LEFT JOIN and MERGE view or SELECT SQ

1. Transformation of row IN subquery made the same as single value.
2. replace_where_subcondition() made working on several layers of OR/AND because it called on expression before fix_fields().
This commit is contained in:
unknown 2013-11-11 17:28:14 +02:00
parent c85db2c494
commit c98a054fde
5 changed files with 99 additions and 28 deletions

View File

@ -4709,6 +4709,37 @@ q 1 q
q 1 q q 1 q
drop view v1; drop view v1;
drop table t1,t2; drop table t1,t2;
#
# MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution
# of PS with LEFT JOIN and MERGE view or SELECT SQ
#
CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('foobar'),('qux');
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ;
PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)';
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
drop view v1;
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ;
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
EXECUTE stmt;
i1 c1 i1 c1
1 foo NULL NULL
2 bar NULL NULL
deallocate prepare stmt;
drop view v1;
drop table t1,t2;
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# -- End of 5.3 tests. # -- End of 5.3 tests.
# ----------------------------------------------------------------- # -----------------------------------------------------------------

View File

@ -4647,6 +4647,34 @@ SELECT * FROM t2 LEFT JOIN v1 ON ( c=b AND a IN ( 1,6 ) );
drop view v1; drop view v1;
drop table t1,t2; drop table t1,t2;
--echo #
--echo # MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution
--echo # of PS with LEFT JOIN and MERGE view or SELECT SQ
--echo #
CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM;
INSERT INTO t2 VALUES ('foobar'),('qux');
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ;
PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)';
EXECUTE stmt;
EXECUTE stmt;
drop view v1;
CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ;
EXECUTE stmt;
EXECUTE stmt;
deallocate prepare stmt;
drop view v1;
drop table t1,t2;
--echo # ----------------------------------------------------------------- --echo # -----------------------------------------------------------------
--echo # -- End of 5.3 tests. --echo # -- End of 5.3 tests.
--echo # ----------------------------------------------------------------- --echo # -----------------------------------------------------------------

View File

@ -1436,9 +1436,11 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg)
bool Item_in_optimizer::fix_left(THD *thd, Item **ref) bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
{ {
DBUG_ENTER("Item_in_optimizer::fix_left");
if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) ||
(!cache && !(cache= Item_cache::get_cache(args[0])))) (!cache && !(cache= Item_cache::get_cache(args[0]))))
return 1; DBUG_RETURN(1);
DBUG_PRINT("info", ("actual fix fields"));
cache->setup(args[0]); cache->setup(args[0]);
if (cache->cols() == 1) if (cache->cols() == 1)
@ -1460,10 +1462,13 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
{ {
my_error(ER_NOT_SUPPORTED_YET, MYF(0), my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"SUBQUERY in ROW in left expression of IN/ALL/ANY"); "SUBQUERY in ROW in left expression of IN/ALL/ANY");
return 1; DBUG_RETURN(1);
} }
if (args[0]->element_index(i)->used_tables()) if (args[0]->element_index(i)->used_tables())
{
((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT); ((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT);
cache->set_used_tables(OUTER_REF_TABLE_BIT);
}
else else
((Item_cache *)cache->element_index(i))->set_used_tables(0); ((Item_cache *)cache->element_index(i))->set_used_tables(0);
} }
@ -1477,7 +1482,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
cache->store(args[0]); cache->store(args[0]);
cache->cache_value(); cache->cache_value();
} }
return 0; DBUG_RETURN(0);
} }

View File

@ -2177,11 +2177,11 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
DBUG_RETURN(true); DBUG_RETURN(true);
Item *item_eq= Item *item_eq=
new Item_func_eq(new new Item_func_eq(new
Item_ref(&select_lex->context, Item_direct_ref(&select_lex->context,
(*optimizer->get_cache())-> (*optimizer->get_cache())->
addr(i), addr(i),
(char *)"<no matter>", (char *)"<no matter>",
(char *)in_left_expr_name), (char *)in_left_expr_name),
new new
Item_ref(&select_lex->context, Item_ref(&select_lex->context,
select_lex->ref_pointer_array + i, select_lex->ref_pointer_array + i,
@ -2462,7 +2462,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
bool bool
Item_in_subselect::select_in_like_transformer(JOIN *join) Item_in_subselect::select_in_like_transformer(JOIN *join)
{ {
Query_arena *arena, backup; Query_arena *arena= 0, backup;
SELECT_LEX *current= thd->lex->current_select; SELECT_LEX *current= thd->lex->current_select;
const char *save_where= thd->where; const char *save_where= thd->where;
bool trans_res= true; bool trans_res= true;
@ -2484,9 +2484,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
} }
} }
if (changed)
DBUG_RETURN(false);
thd->where= "IN/ALL/ANY subquery"; thd->where= "IN/ALL/ANY subquery";
/* /*
@ -2497,25 +2494,29 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
note: we won't need Item_in_optimizer when handling degenerate cases note: we won't need Item_in_optimizer when handling degenerate cases
like "... IN (SELECT 1)" like "... IN (SELECT 1)"
*/ */
arena= thd->activate_stmt_arena_if_needed(&backup);
if (!optimizer) if (!optimizer)
{ {
arena= thd->activate_stmt_arena_if_needed(&backup);
result= (!(optimizer= new Item_in_optimizer(left_expr, this))); result= (!(optimizer= new Item_in_optimizer(left_expr, this)));
if (arena)
thd->restore_active_arena(arena, &backup);
if (result) if (result)
goto err; goto out;
} }
thd->lex->current_select= current->return_after_parsing(); thd->lex->current_select= current->return_after_parsing();
result= (!left_expr->fixed && result= optimizer->fix_left(thd, optimizer->arguments());
left_expr->fix_fields(thd, optimizer->arguments()));
/* fix_fields can change reference to left_expr, we need reassign it */ /* fix_fields can change reference to left_expr, we need reassign it */
left_expr= optimizer->arguments()[0]; left_expr= optimizer->arguments()[0];
thd->lex->current_select= current; thd->lex->current_select= current;
if (changed)
{
trans_res= false;
goto out;
}
if (result) if (result)
goto err; goto out;
/* /*
Both transformers call fix_fields() only for Items created inside them, Both transformers call fix_fields() only for Items created inside them,
@ -2524,7 +2525,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
of Item, we have to call fix_fields() for it only with original arena to of Item, we have to call fix_fields() for it only with original arena to
avoid memory leack) avoid memory leack)
*/ */
arena= thd->activate_stmt_arena_if_needed(&backup);
if (left_expr->cols() == 1) if (left_expr->cols() == 1)
trans_res= single_value_transformer(join); trans_res= single_value_transformer(join);
else else
@ -2539,9 +2539,9 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
} }
trans_res= row_value_transformer(join); trans_res= row_value_transformer(join);
} }
out:
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
err:
thd->where= save_where; thd->where= save_where;
DBUG_RETURN(trans_res); DBUG_RETURN(trans_res);
} }

View File

@ -1252,11 +1252,11 @@ void get_delayed_table_estimates(TABLE *table,
@brief Replaces an expression destructively inside the expression tree of @brief Replaces an expression destructively inside the expression tree of
the WHERE clase. the WHERE clase.
@note Because of current requirements for semijoin flattening, we do not @note We substitute AND/OR structure because it was copied by
need to recurse here, hence this function will only examine the top-level copy_andor_structure and some changes could be done in the copy but
AND conditions. (see JOIN::prepare, comment starting with "Check if the should be left permanent, also there could be several layers of AND over
subquery predicate can be executed via materialization". AND and OR over OR because ::fix_field() possibly is not called.
@param join The top-level query. @param join The top-level query.
@param old_cond The expression to be replaced. @param old_cond The expression to be replaced.
@param new_cond The expression to be substituted. @param new_cond The expression to be substituted.
@ -1284,13 +1284,20 @@ static bool replace_where_subcondition(JOIN *join, Item **expr,
Item *item; Item *item;
while ((item= li++)) while ((item= li++))
{ {
if (item == old_cond) if (item == old_cond)
{ {
li.replace(new_cond); li.replace(new_cond);
if (do_fix_fields) if (do_fix_fields)
new_cond->fix_fields(join->thd, li.ref()); new_cond->fix_fields(join->thd, li.ref());
return FALSE; return FALSE;
} }
else if (item->type() == Item::COND_ITEM)
{
DBUG_ASSERT(!(*expr)->fixed);
replace_where_subcondition(join, li.ref(),
old_cond, new_cond,
do_fix_fields);
}
} }
} }
/* /*