Skip to content

Commit 3e1d8e8

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. Move the functionality gate for FULL JOIN further into the codebase: convert LEX::has_full_outer_join to a counter so we can see how many FULL JOINs remain which makes the gate work correctly after simplify_joins and eliminate_tables are called. Fixes an old bug where, when running the server as a debug build and in debug mode, a null pointer deference in Dep_analysis_context::dbug_print_deps would cause a crash.
1 parent e3f59c1 commit 3e1d8e8

File tree

8 files changed

+93
-28
lines changed

8 files changed

+93
-28
lines changed

mysql-test/main/table_elim.result

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,3 +1112,35 @@ 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+
create table t3 (a int primary key, b int)
1123+
as select a, a as b from t1 where a in (1,3);
1124+
# These will not be eliminated because contains a FULL OUTER JOIN.
1125+
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
1126+
id select_type table type possible_keys key key_len ref rows filtered Extra
1127+
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1128+
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1129+
Warnings:
1130+
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` full join `test`.`t2` on(multiple equal(`test`.`t2`.`a`, `test`.`t1`.`a`))
1131+
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;
1132+
id select_type table type possible_keys key key_len ref rows filtered Extra
1133+
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1134+
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1135+
1 SIMPLE t3 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1136+
Warnings:
1137+
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`))
1138+
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;
1139+
id select_type table type possible_keys key key_len ref rows filtered Extra
1140+
1 SIMPLE t1 UNKNOWN NULL NULL NULL NULL 0 0.00
1141+
1 SIMPLE t2 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1142+
1 SIMPLE t3 UNKNOWN PRIMARY NULL NULL NULL 0 0.00
1143+
Warnings:
1144+
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`))
1145+
drop table t1, t2, t3;
1146+
# End of 12.3 tests

mysql-test/main/table_elim.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,3 +844,24 @@ 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+
create table t3 (a int primary key, b int)
858+
as select a, a as b from t1 where a in (1,3);
859+
860+
--echo # These will not be eliminated because contains a FULL OUTER JOIN.
861+
explain extended select t1.a from t1 full join t2 on t2.a=t1.a;
862+
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;
863+
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;
864+
865+
drop table t1, t2, t3;
866+
867+
--echo # End of 12.3 tests

sql/opt_table_elimination.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,8 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
825825
if (tbl->nested_join)
826826
{
827827
/* This is "... LEFT JOIN (join_nest) ON cond" */
828-
if (eliminate_tables_for_list(join,
828+
if (!(tbl->outer_join & JOIN_TYPE_FULL) &&
829+
eliminate_tables_for_list(join,
829830
&tbl->nested_join->join_list,
830831
tbl->nested_join->used_tables,
831832
tbl->on_expr,
@@ -840,7 +841,8 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
840841
else
841842
{
842843
/* This is "... LEFT JOIN tbl ON cond" */
843-
if (!(tbl->table->map & outside_used_tables) &&
844+
if (!(tbl->outer_join & JOIN_TYPE_FULL) &&
845+
!(tbl->table->map & outside_used_tables) &&
844846
check_func_dependency(join, tbl->table->map, NULL, tbl,
845847
tbl->on_expr))
846848
{
@@ -2105,7 +2107,8 @@ void Dep_analysis_context::dbug_print_deps()
21052107
char buf[128];
21062108
String str(buf, sizeof(buf), &my_charset_bin);
21072109
str.length(0);
2108-
eq_mod->expr->print(&str, QT_ORDINARY);
2110+
if (eq_mod->expr)
2111+
eq_mod->expr->print(&str, QT_ORDINARY);
21092112
if (eq_mod->field)
21102113
{
21112114
fprintf(DBUG_FILE, " equality%ld: %s -> %s.%s\n",

sql/sql_lex.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,7 @@ void LEX::start(THD *thd_arg)
13431343

13441344
memset(&trg_chistics, 0, sizeof(trg_chistics));
13451345
selects_for_hint_resolution.empty();
1346-
has_full_outer_join= false;
1346+
full_join_count= 0;
13471347
DBUG_VOID_RETURN;
13481348
}
13491349

sql/sql_lex.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,8 +3593,8 @@ struct LEX: public Query_tables_list
35933593
vers_select_conds_t vers_conditions;
35943594
vers_select_conds_t period_conditions;
35953595

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

35993599
inline void free_set_stmt_mem_root()
36003600
{

sql/sql_select.cc

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,7 +2773,7 @@ JOIN::optimize_inner()
27732773
mature enough to correctly execute the queries. But for now
27742774
this allows for some EXPLAIN EXTENDED support.
27752775
*/
2776-
else if (!thd->lex->has_full_outer_join)
2776+
else if (!thd->lex->full_join_count)
27772777
{
27782778
if (optimize_stage2())
27792779
DBUG_RETURN(1);
@@ -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 (thd->lex->full_join_count && !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;
@@ -20353,23 +20375,10 @@ static COND *rewrite_full_outer_joins(JOIN *join,
2035320375
see the right table first. If, on this call to rewrite_full_outer_joins,
2035420376
the current table is left member of the JOIN (e.g., left_member FULL JOIN
2035520377
...) 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).
20378+
INNER JOIN, so pass along the unmodified FULL JOIN.
2035820379
*/
2035920380
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-
}
20381+
return conds;
2037320382

2037420383
/*
2037520384
Must always see the right table before the left. Down below, we deal
@@ -20420,7 +20429,7 @@ static COND *rewrite_full_outer_joins(JOIN *join,
2042020429
RIGHT JOINs don't actually exist in MariaDB!
2042120430
*/
2042220431
*table_ptr= li.swap_next();
20423-
join->thd->lex->has_full_outer_join= false;
20432+
--join->thd->lex->full_join_count;
2042420433
}
2042520434
else
2042620435
{
@@ -20457,7 +20466,7 @@ static COND *rewrite_full_outer_joins(JOIN *join,
2045720466
if (peeked_map & not_null_tables)
2045820467
{
2045920468
rewrite_full_to_left(left_table, right_table);
20460-
join->thd->lex->has_full_outer_join= false;
20469+
--join->thd->lex->full_join_count;
2046120470
}
2046220471
// else the FULL JOIN cannot be rewritten, pass it along.
2046320472
}

sql/sql_view.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *view_table_alias,
19091909
DBUG_ASSERT(view_query_lex == thd->lex);
19101910
thd->lex= parent_query_lex; // Needed for prepare_security
19111911
result= !view_table_alias->prelocking_placeholder && view_table_alias->prepare_security(thd);
1912-
parent_query_lex->has_full_outer_join= view_query_lex->has_full_outer_join;
1912+
parent_query_lex->full_join_count= view_query_lex->full_join_count;
19131913

19141914
lex_end(view_query_lex);
19151915
end:

sql/sql_yacc.yy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12392,7 +12392,7 @@ join_table:
1239212392
$5->on_context= $1->on_context;
1239312393
Select->parsing_place= NO_MATTER;
1239412394
$$= $1;
12395-
Lex->has_full_outer_join= true;
12395+
++Lex->full_join_count;
1239612396
}
1239712397
| table_ref FULL opt_outer JOIN_SYM table_factor
1239812398
{
@@ -12408,7 +12408,7 @@ join_table:
1240812408
USING '(' using_list ')'
1240912409
{
1241012410
add_join_natural($1,$5,$9,Select);
12411-
Lex->has_full_outer_join= true;
12411+
++Lex->full_join_count;
1241212412
}
1241312413
| table_ref NATURAL FULL opt_outer JOIN_SYM table_factor
1241412414
{
@@ -12425,7 +12425,7 @@ join_table:
1242512425
JOIN_TYPE_NATURAL);
1242612426

1242712427
add_join_natural($6,$1,NULL,Select);
12428-
Lex->has_full_outer_join= true;
12428+
++Lex->full_join_count;
1242912429
}
1243012430
;
1243112431

0 commit comments

Comments
 (0)