Skip to content

Commit e3a8583

Browse files
MDEV-38136: Prevent elimination of tables in a FULL OUTER JOIN
Prevent elimination of tables participating in a FULL OUTER JOIN during eliminate_tables as part of phase one FULL OUTER JOIN development.
1 parent e3f59c1 commit e3a8583

File tree

5 files changed

+87
-16
lines changed

5 files changed

+87
-16
lines changed

mysql-test/main/table_elim.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,3 +1112,19 @@ DROP TABLE t1, t2;
11121112
#
11131113
# End of 10.11 tests
11141114
#
1115+
#
1116+
# MDEV-38136 Prevent elimination of tables in a FULL OUTER JOIN
1117+
#
1118+
create table t1 (a int);
1119+
insert into t1 values (0),(1),(2),(3);
1120+
create table t2 (a int primary key, b int)
1121+
as select a, a as b from t1 where a in (1,2);
1122+
# This will not be eliminated because it's a FULL OUTER JOIN.
1123+
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
1124+
id select_type table type possible_keys key key_len ref rows filtered Extra
1125+
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1126+
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1127+
Warnings:
1128+
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` full join `test`.`t2` on(multiple equal(`test`.`t2`.`a`, `test`.`t1`.`a`))
1129+
drop table t1, t2;
1130+
# End of 12.3 tests

mysql-test/main/table_elim.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,3 +844,19 @@ DROP TABLE t1, t2;
844844
--echo #
845845
--echo # End of 10.11 tests
846846
--echo #
847+
848+
--echo #
849+
--echo # MDEV-38136 Prevent elimination of tables in a FULL OUTER JOIN
850+
--echo #
851+
create table t1 (a int);
852+
insert into t1 values (0),(1),(2),(3);
853+
854+
create table t2 (a int primary key, b int)
855+
as select a, a as b from t1 where a in (1,2);
856+
857+
--echo # This will not be eliminated because it's a FULL OUTER JOIN.
858+
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
859+
860+
drop table t1, t2;
861+
862+
--echo # End of 12.3 tests

sql/opt_table_elimination.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,10 @@ void eliminate_tables(JOIN *join)
732732
for (ORDER *cur_list= all_lists[i]; cur_list; cur_list= cur_list->next)
733733
used_tables |= (*(cur_list->item))->used_tables();
734734
}
735-
735+
736+
/* Add tables that participate in a FULL JOIN */
737+
used_tables|= join->full_outer_tables();
738+
736739
if (join->select_lex == thd->lex->first_select_lex())
737740
{
738741

sql/sql_select.cc

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5627,7 +5627,16 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
56275627
TABLE **table_vector;
56285628
JOIN_TAB *stat,*stat_end,*s,**stat_ref, **stat_vector;
56295629
KEYUSE *keyuse,*start_keyuse;
5630+
5631+
/*
5632+
outer_join here does not have the same meaning as TABLE_LIST::outer_join.
5633+
Here, outer_join is the union of all table numbers representing tables
5634+
that participate in this join. TABLE_LIST::outer_join marks how a
5635+
TABLE_LIST participates in a particular JOIN (as a right table, left table,
5636+
as part of a FULL JOIN, etc).
5637+
*/
56305638
table_map outer_join=0;
5639+
56315640
table_map no_rows_const_tables= 0;
56325641
SARGABLE_PARAM *sargables= 0;
56335642
List_iterator<TABLE_LIST> ti(tables_list);
@@ -5841,6 +5850,19 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
58415850
join->const_table_map= no_rows_const_tables;
58425851
join->const_tables= const_count;
58435852
eliminate_tables(join);
5853+
5854+
/*
5855+
Temporary gate. As the FULL JOIN implementation matures, this keeps moving
5856+
deeper into the server until it's eventually eliminated.
5857+
*/
5858+
if (join->full_outer_tables() && !thd->lex->describe)
5859+
{
5860+
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
5861+
"FULL JOINs that cannot be converted to LEFT, RIGHT, or "
5862+
"INNER JOINs");
5863+
goto error;
5864+
}
5865+
58445866
join->const_table_map &= ~no_rows_const_tables;
58455867
const_count= join->const_tables;
58465868
found_const_table_map= join->const_table_map;
@@ -6502,6 +6524,20 @@ void JOIN::restore_table_dependencies(table_map *orig_dep_array)
65026524
}
65036525

65046526

6527+
table_map JOIN::full_outer_tables(List<TABLE_LIST> *jl) const
6528+
{
6529+
List_iterator<TABLE_LIST> itr(jl ? *jl : *join_list);
6530+
TABLE_LIST* tbl= nullptr;
6531+
table_map fo_tables= 0;
6532+
while ((tbl=itr++))
6533+
fo_tables|=
6534+
tbl->nested_join ? full_outer_tables(&tbl->nested_join->join_list)
6535+
: (tbl->outer_join & JOIN_TYPE_FULL ? tbl->get_map()
6536+
: 0);
6537+
return fo_tables;
6538+
}
6539+
6540+
65056541
/*****************************************************************************
65066542
Check with keys are used and with tables references with tables
65076543
Updates in stat:
@@ -20348,28 +20384,17 @@ static COND *rewrite_full_outer_joins(JOIN *join,
2034820384
if (!(right_table->outer_join & JOIN_TYPE_FULL))
2034920385
return conds;
2035020386

20387+
DBUG_ASSERT(join->full_outer_tables());
20388+
2035120389
/*
2035220390
The join_list enumerates the tables from t_n, ..., t_0 so we always
2035320391
see the right table first. If, on this call to rewrite_full_outer_joins,
2035420392
the current table is left member of the JOIN (e.g., left_member FULL JOIN
2035520393
...) it means we couldn't rewrite the FULL JOIN as a LEFT, RIGHT, or
20356-
INNER JOIN, so emit an error (unless we're in an EXPLAIN EXTENDED, permit
20357-
that).
20394+
INNER JOIN, so pass along the unmodified FULL JOIN.
2035820395
*/
2035920396
if (right_table->outer_join & JOIN_TYPE_LEFT)
20360-
{
20361-
if (join->thd->lex->describe)
20362-
return conds;
20363-
20364-
/*
20365-
We always see the RIGHT table before the LEFT table, so nothing to
20366-
do here for JOIN_TYPE_LEFT.
20367-
*/
20368-
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
20369-
"FULL JOINs that cannot be converted to LEFT, RIGHT, or "
20370-
"INNER JOINs");
20371-
return nullptr;
20372-
}
20397+
return conds;
2037320398

2037420399
/*
2037520400
Must always see the right table before the left. Down below, we deal

sql/sql_select.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,17 @@ class JOIN :public Sql_alloc
19611961
table_map *export_table_dependencies() const;
19621962
void restore_table_dependencies(table_map *orig_dep_array);
19631963

1964+
/**
1965+
Generates a bitmap corresponding to the table numbers for
1966+
all tables participating in a FULL OUTER JOIN in the passed-in
1967+
join_list. If no join_list is passed-in, then this->join_list
1968+
is used instead.
1969+
1970+
@param join_list the list of tables to check, default is this->join_list
1971+
@return table_map of tables that are part of a FULL JOIN.
1972+
*/
1973+
table_map full_outer_tables(List<TABLE_LIST> *join_list=nullptr) const;
1974+
19641975
private:
19651976
/**
19661977
Create a temporary table to be used for processing DISTINCT/ORDER

0 commit comments

Comments
 (0)