Commit 3d6d85b4 authored by mithun's avatar mithun

Bug #18167356: EXPLAIN W/ EXISTS(SELECT* UNION SELECT*)

               WHERE ONE OF SELECT* IS DISTINCT FAILS.
ISSUE:
------
There are 2 issues related to explain union.
1. If we have subquery with union of selects. And, one of
   the select need temp table to materialize its results
   then it will replace its query structure with a simple
   select from temporary table. Trying to display new
   internal temporary table scan resulted in crash. But to
   display the query plan, we should save the original
   query structure.
2. Multiple execution of prepared explain statement which
   have union of subqueries resulted in crash. If we have
   constant subqueries, fake select used in union operation
   will be evaluated once before using it for explain.
   During first execution we have set fake select options to
   SELECT_DESCRIBE, but did not reset after the explain.
   Hence during next execution of prepared statement during
   first time evaluation of fake select we had our select
   options as SELECT_DESCRIBE this resulted in improperly
   initialized data structures and crash.

SOLUTION:
---------
1. If called by explain now we save the original query
   structure. And this will be used for displaying.
2. Reset the fake select options after it is called for
   explain of union.

sql/sql_select.cc:
  Reset the fake select options after it is called for explain of union
sql/sql_union.cc:
  If called by explain but not from select_describe and we
  need a temp table, then we create a temp join to preserve
  original query structure.
parent 5e881cc4
...@@ -17299,6 +17299,11 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) ...@@ -17299,6 +17299,11 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
unit->fake_select_lex->options|= SELECT_DESCRIBE; unit->fake_select_lex->options|= SELECT_DESCRIBE;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec(); res= unit->exec();
/*
Reset select option. Needed if fake_select_lex is used and not called
from select describe.
*/
unit->fake_select_lex->options&= ~SELECT_DESCRIBE;
res|= unit->cleanup(); res|= unit->cleanup();
} }
else else
......
...@@ -511,6 +511,35 @@ bool st_select_lex_unit::exec() ...@@ -511,6 +511,35 @@ bool st_select_lex_unit::exec()
(select_limit_cnt == HA_POS_ERROR || sl->braces) ? (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union; sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize(); saved_error= sl->join->optimize();
/*
If called by explain statement then we may need to save the original
JOIN LAYOUT so that we can display the plan. Otherwise original plan
will be replaced by a simple scan on temp table if subquery uses temp
table.
We check for following conditions to force join_tmp creation
1. This is an EXPLAIN statement, and
2. JOIN not yet saved in JOIN::optimize(), and
3. Not called directly from select_describe(), and
4. Belongs to a subquery that is const, and
5. Need a temp table.
*/
if (thd->lex->describe && // 1
!sl->uncacheable && // 2
!(sl->join->select_options & SELECT_DESCRIBE) && // 3
item && item->const_item()) // 4
{
/*
Force join->join_tmp creation, because this subquery will be
replaced by a simple select from the materialization temp table
by optimize() called by EXPLAIN and we need to preserve the
initial query structure so we can display it.
*/
sl->uncacheable|= UNCACHEABLE_EXPLAIN;
sl->master_unit()->uncacheable|= UNCACHEABLE_EXPLAIN;
if (sl->join->need_tmp && sl->join->init_save_join_tab()) // 5
DBUG_RETURN(1);
}
} }
if (!saved_error) if (!saved_error)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment