Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions mysql-test/main/table_elim.result
Original file line number Diff line number Diff line change
Expand Up @@ -1112,3 +1112,35 @@ DROP TABLE t1, t2;
#
# End of 10.11 tests
#
#
# MDEV-38136 Prevent elimination of tables in a FULL OUTER JOIN
#
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t2 (a int primary key, b int)
as select a, a as b from t1 where a in (1,2);
create table t3 (a int primary key, b int)
as select a, a as b from t1 where a in (1,3);
# These will not be eliminated because contains a FULL OUTER JOIN.
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` full join `test`.`t2` on(multiple equal(`test`.`t2`.`a`, `test`.`t1`.`a`))
explain extended select t1.a from t1 full join (t2 full join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1 SIMPLE t3 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` full join (`test`.`t3` join `test`.`t2`) on(multiple equal(`test`.`t2`.`a`, `test`.`t1`.`a`, `test`.`t3`.`a`) and multiple equal(`test`.`t2`.`b`, `test`.`t3`.`b`))
explain extended select t1.a from t1 full join (t2 left join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1 SIMPLE t3 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` full join (`test`.`t2` join `test`.`t3`) on(multiple equal(`test`.`t2`.`a`, `test`.`t1`.`a`, `test`.`t3`.`a`) and multiple equal(`test`.`t2`.`b`, `test`.`t3`.`b`))
drop table t1, t2, t3;
# End of 12.3 tests
21 changes: 21 additions & 0 deletions mysql-test/main/table_elim.test
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,24 @@ DROP TABLE t1, t2;
--echo #
--echo # End of 10.11 tests
--echo #

--echo #
--echo # MDEV-38136 Prevent elimination of tables in a FULL OUTER JOIN
--echo #
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);

create table t2 (a int primary key, b int)
as select a, a as b from t1 where a in (1,2);

create table t3 (a int primary key, b int)
as select a, a as b from t1 where a in (1,3);

--echo # These will not be eliminated because contains a FULL OUTER JOIN.
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
explain extended select t1.a from t1 full join (t2 full join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;
explain extended select t1.a from t1 full join (t2 left join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;

drop table t1, t2, t3;

--echo # End of 12.3 tests
9 changes: 6 additions & 3 deletions sql/opt_table_elimination.cc
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,8 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
if (tbl->nested_join)
{
/* This is "... LEFT JOIN (join_nest) ON cond" */
if (eliminate_tables_for_list(join,
if (!(tbl->outer_join & JOIN_TYPE_FULL) &&
eliminate_tables_for_list(join,
&tbl->nested_join->join_list,
tbl->nested_join->used_tables,
tbl->on_expr,
Expand All @@ -840,7 +841,8 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
else
{
/* This is "... LEFT JOIN tbl ON cond" */
if (!(tbl->table->map & outside_used_tables) &&
if (!(tbl->outer_join & JOIN_TYPE_FULL) &&
!(tbl->table->map & outside_used_tables) &&
check_func_dependency(join, tbl->table->map, NULL, tbl,
tbl->on_expr))
{
Expand Down Expand Up @@ -2105,7 +2107,8 @@ void Dep_analysis_context::dbug_print_deps()
char buf[128];
String str(buf, sizeof(buf), &my_charset_bin);
str.length(0);
eq_mod->expr->print(&str, QT_ORDINARY);
if (eq_mod->expr)
eq_mod->expr->print(&str, QT_ORDINARY);
if (eq_mod->field)
{
fprintf(DBUG_FILE, " equality%ld: %s -> %s.%s\n",
Expand Down
2 changes: 1 addition & 1 deletion sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1343,7 +1343,7 @@ void LEX::start(THD *thd_arg)

memset(&trg_chistics, 0, sizeof(trg_chistics));
selects_for_hint_resolution.empty();
has_full_outer_join= false;
full_join_count= 0;
DBUG_VOID_RETURN;
}

Expand Down
4 changes: 2 additions & 2 deletions sql/sql_lex.h
Original file line number Diff line number Diff line change
Expand Up @@ -3593,8 +3593,8 @@ struct LEX: public Query_tables_list
vers_select_conds_t vers_conditions;
vers_select_conds_t period_conditions;

/* False by default, this will be true if the query has a full join. */
bool has_full_outer_join;
/* Zero by default, this counts the number of full joins in the query. */
uint full_join_count;

inline void free_set_stmt_mem_root()
{
Expand Down
45 changes: 27 additions & 18 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2773,7 +2773,7 @@ JOIN::optimize_inner()
mature enough to correctly execute the queries. But for now
this allows for some EXPLAIN EXTENDED support.
*/
else if (!thd->lex->has_full_outer_join)
else if (!thd->lex->full_join_count)
{
if (optimize_stage2())
DBUG_RETURN(1);
Expand Down Expand Up @@ -5627,7 +5627,16 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
TABLE **table_vector;
JOIN_TAB *stat,*stat_end,*s,**stat_ref, **stat_vector;
KEYUSE *keyuse,*start_keyuse;

/*
outer_join here does not have the same meaning as TABLE_LIST::outer_join.
Here, outer_join is the union of all table numbers representing tables
that participate in this join. TABLE_LIST::outer_join marks how a
TABLE_LIST participates in a particular JOIN (as a right table, left table,
as part of a FULL JOIN, etc).
*/
table_map outer_join=0;

table_map no_rows_const_tables= 0;
SARGABLE_PARAM *sargables= 0;
List_iterator<TABLE_LIST> ti(tables_list);
Expand Down Expand Up @@ -5841,6 +5850,19 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
join->const_table_map= no_rows_const_tables;
join->const_tables= const_count;
eliminate_tables(join);

/*
Temporary gate. As the FULL JOIN implementation matures, this keeps moving
deeper into the server until it's eventually eliminated.
*/
if (thd->lex->full_join_count && !thd->lex->describe)
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"FULL JOINs that cannot be converted to LEFT, RIGHT, or "
"INNER JOINs");
goto error;
}

join->const_table_map &= ~no_rows_const_tables;
const_count= join->const_tables;
found_const_table_map= join->const_table_map;
Expand Down Expand Up @@ -20353,23 +20375,10 @@ static COND *rewrite_full_outer_joins(JOIN *join,
see the right table first. If, on this call to rewrite_full_outer_joins,
the current table is left member of the JOIN (e.g., left_member FULL JOIN
...) it means we couldn't rewrite the FULL JOIN as a LEFT, RIGHT, or
INNER JOIN, so emit an error (unless we're in an EXPLAIN EXTENDED, permit
that).
INNER JOIN, so pass along the unmodified FULL JOIN.
*/
if (right_table->outer_join & JOIN_TYPE_LEFT)
{
if (join->thd->lex->describe)
return conds;

/*
We always see the RIGHT table before the LEFT table, so nothing to
do here for JOIN_TYPE_LEFT.
*/
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"FULL JOINs that cannot be converted to LEFT, RIGHT, or "
"INNER JOINs");
return nullptr;
}
return conds;

/*
Must always see the right table before the left. Down below, we deal
Expand Down Expand Up @@ -20420,7 +20429,7 @@ static COND *rewrite_full_outer_joins(JOIN *join,
RIGHT JOINs don't actually exist in MariaDB!
*/
*table_ptr= li.swap_next();
join->thd->lex->has_full_outer_join= false;
--join->thd->lex->full_join_count;
}
else
{
Expand Down Expand Up @@ -20457,7 +20466,7 @@ static COND *rewrite_full_outer_joins(JOIN *join,
if (peeked_map & not_null_tables)
{
rewrite_full_to_left(left_table, right_table);
join->thd->lex->has_full_outer_join= false;
--join->thd->lex->full_join_count;
}
// else the FULL JOIN cannot be rewritten, pass it along.
}
Expand Down
2 changes: 1 addition & 1 deletion sql/sql_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1909,7 +1909,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *view_table_alias,
DBUG_ASSERT(view_query_lex == thd->lex);
thd->lex= parent_query_lex; // Needed for prepare_security
result= !view_table_alias->prelocking_placeholder && view_table_alias->prepare_security(thd);
parent_query_lex->has_full_outer_join= view_query_lex->has_full_outer_join;
parent_query_lex->full_join_count= view_query_lex->full_join_count;

lex_end(view_query_lex);
end:
Expand Down
6 changes: 3 additions & 3 deletions sql/sql_yacc.yy
Original file line number Diff line number Diff line change
Expand Up @@ -12392,7 +12392,7 @@ join_table:
$5->on_context= $1->on_context;
Select->parsing_place= NO_MATTER;
$$= $1;
Lex->has_full_outer_join= true;
++Lex->full_join_count;
}
| table_ref FULL opt_outer JOIN_SYM table_factor
{
Expand All @@ -12408,7 +12408,7 @@ join_table:
USING '(' using_list ')'
{
add_join_natural($1,$5,$9,Select);
Lex->has_full_outer_join= true;
++Lex->full_join_count;
}
| table_ref NATURAL FULL opt_outer JOIN_SYM table_factor
{
Expand All @@ -12425,7 +12425,7 @@ join_table:
JOIN_TYPE_NATURAL);

add_join_natural($6,$1,NULL,Select);
Lex->has_full_outer_join= true;
++Lex->full_join_count;
}
;

Expand Down