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
303 changes: 278 additions & 25 deletions mysql-test/main/join.result

Large diffs are not rendered by default.

109 changes: 103 additions & 6 deletions mysql-test/main/join.test
Original file line number Diff line number Diff line change
Expand Up @@ -2073,34 +2073,34 @@ explain extended select * from t1 natural full outer join t2;
select * from t1 natural full join t2;
explain extended select * from t1 natural full join t2;

create view v1 as select * from t1 full join t2 on t1.a = t2.a;
create view v1 as select t1.a as t1a, t2.a as t2a from t1 full join t2 on t1.a = t2.a;
--error ER_NOT_SUPPORTED_YET
select * from v1;
explain extended select * from v1;
drop view v1;

create view v1 as select * from t1 full outer join t2 on t1.a = t2.a;
create view v1 as select t1.a as t1a, t2.a as t2a from t1 full outer join t2 on t1.a = t2.a;
--error ER_NOT_SUPPORTED_YET
select * from v1;
explain extended select * from v1;
drop view v1;

create view v1 as select * from t1 natural full join t2;
create view v1 as select t1.a as t1a, t2.a as t2a from t1 natural full join t2;
--error ER_NOT_SUPPORTED_YET
select * from v1;
explain extended select * from v1;
drop view v1;

create view v1 as select * from t1 natural full outer join t2;
create view v1 as select t1.a as t1a, t2.a as t2a from t1 natural full outer join t2;
--error ER_NOT_SUPPORTED_YET
select * from v1;
explain extended select * from v1;
drop view v1;

--error ER_BAD_FIELD_ERROR
--error ER_NOT_SUPPORTED_YET
select * from (select t1.a from t1 full join t2 on t1.a = t2.a union select * from t1) dt;

--error ER_BAD_FIELD_ERROR
--error ER_NOT_SUPPORTED_YET
select * from (select t1.a from t1 full outer join t2 on t1.a = t2.a union select * from t1) dt;

--error ER_NOT_SUPPORTED_YET
Expand All @@ -2127,7 +2127,104 @@ select * from t1, t2 natural full join t3;
--error ER_NO_SUCH_TABLE
select * from t1, t2 natural full outer join t3;

--error ER_FULL_JOIN_BASE_TABLES_ONLY
select * from (select * from t1) dt natural full join (select * from t2) du;

--error ER_FULL_JOIN_BASE_TABLES_ONLY
select * from (select * from t1) dt natural full join t2;

--error ER_FULL_JOIN_BASE_TABLES_ONLY
select * from t1 natural full join (select * from t2) du;

drop table t1, t2;

--echo # Exercise FULL JOIN rewrites to LEFT, RIGHT, and INNER JOIN.
create table x (pk int auto_increment, x int, y int, primary key (pk));
create table xsq (pk int auto_increment, x int, y int, primary key (pk));
insert into x (x, y) values (-5,-5),(-4,-4),(-3,-3),(-2,-2),(-1,-1),(0,0),(1,1),(2,2),(3,3),(4,4),(5,5);
insert into xsq (x, y) values (-5,25),(-4,16),(-3,9),(-2,4),(-1,1),(0,0),(1,1),(2,4),(3,9),(4,16),(5,25);

--echo # FULL to RIGHT JOIN, these two queries should be equal:
select * from x full join xsq on x.y = xsq.y where xsq.pk is not null;
select * from x right join xsq on x.y = xsq.y;

--echo # FULL to RIGHT JOIN, these two queries should be equal:
select * from x full join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1 where xsq.pk is not null;
select * from x right join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1;

--echo # FULL to INNER JOIN, these two queries should be equal:
select * from x full join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1 where x.pk is not null and xsq.pk is not null;
select * from x inner join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1;

--echo # FULL to LEFT JOIN, these two queries should be equal:
select * from x full join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1 where x.pk is not null;
select * from x left join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1;

--echo # FULL NATURAL to INNER JOIN, these two queries should be equal:
select * from x natural full join xsq where x.pk is not null and xsq.pk is not null;
select * from x inner join xsq on x.x = xsq.x and x.y = xsq.y;

--echo # FULL NATURAL to LEFT JOIN, these two queries should be equal:
select * from x natural full join xsq where x.pk is not null;
select * from x left join xsq on xsq.pk = x.pk and xsq.x = x.x and xsq.y = x.y;

--echo # FULL NATURAL to RIGHT JOIN
select * from x natural full join xsq where xsq.pk is not null;
select * from x right join xsq on x.pk = xsq.pk and x.x = xsq.x and x.y = xsq.y;

--echo # These two will fail because it cannot be rewritten to a LEFT, RIGHT, nor INNER JOIN.
--error ER_NOT_SUPPORTED_YET
select * from x full join xsq on x.x >= 0 and x.x <= 1 and xsq.x >= 0 and xsq.x <= 1;
--error ER_NOT_SUPPORTED_YET
select * from x natural full join xsq;

drop table x, xsq;

--echo # Nested JOINs
create table one (v int);
insert into one (v) values (1);
create table two (v int);
insert into two (v) values (2);
create table three (v int);
insert into three (v) values (3);

--echo # (FULL)FULL to (INNER)INNER JOIN
select * from one full join two on one.v = two.v full join three on two.v = three.v where one.v is not null and two.v is not null and three.v is not null;
select * from one inner join two on one.v = two.v inner join three on two.v = three.v;

--echo # (FULL)FULL to (RIGHT)LEFT JOIN
select * from one full join two on one.v = two.v full join three on one.v = three.v where two.v is not null;
select * from one right join two on one.v = two.v left join three on one.v = three.v;

--echo # (FULL)FULL to (LEFT)LEFT JOIN
select * from one full join two on one.v = two.v full join three on one.v = three.v where one.v is not null;
select * from one left join two on two.v = one.v left join three on three.v = one.v;

--echo # (FULL)LEFT to (LEFT)LEFT JOIN
select * from one full join two on one.v = two.v left join three on two.v = three.v where one.v is not null;
select * from one left join two on one.v = two.v left join three on two.v = three.v;

--echo # (FULL)LEFT to (RIGHT)LEFT JOIN
select * from one full join two on one.v = two.v left join three on two.v = three.v where two.v is not null;
select * from one right join two on one.v = two.v left join three on three.v = two.v;

--echo # (LEFT)FULL to (LEFT)RIGHT JOIN
select * from one left join two on one.v = two.v full join three on two.v = three.v where three.v is not null;
select * from one left join two on one.v = two.v right join three on two.v = three.v;

--echo # (LEFT)FULL to (LEFT)LEFT JOIN
insert into one (v) values (2),(3);
insert into two (v) values (1);
truncate three;
insert into three (v) values (1);
select * from one;
select * from two;
select * from three;
select * from one left join two on one.v = two.v full join three on two.v = three.v where three.v = 1;
select * from three left join one on one.v = 1 left join two on two.v = 1;

drop table one, two, three;

# TODO fix PS protocol before end of FULL OUTER JOIN development
--enable_ps_protocol
--echo # End of 12.3 tests
2 changes: 2 additions & 0 deletions sql/share/errmsg-utf8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12362,3 +12362,5 @@ ER_WARN_CONFLICTING_COMPOUND_INDEX_HINT_FOR_KEY
eng "Hint %s is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key)"
ER_WARN_NO_IMPLICIT_QB_NAMES_IN_VIEW
eng "Implicit query block names are ignored for hints specified within VIEWs"
ER_FULL_JOIN_BASE_TABLES_ONLY
eng "FULL JOIN supported for base tables only, %s is not a base table"
22 changes: 20 additions & 2 deletions sql/sql_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7998,6 +7998,23 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref,
}


/*
If the tables involved participate in a FULL OUTER JOIN then don't
update their name resolution contexts at this point, or it will
break simplify_joins.
*/
static bool is_full_outer_join(Name_resolution_context *context,
TABLE_LIST *right_neighbor)
{
const uint ctx_outer_join= context->first_name_resolution_table->outer_join;
const uint rtn_outer_join=
right_neighbor->first_leaf_for_name_resolution()->outer_join;

return (ctx_outer_join & JOIN_TYPE_FULL) ||
(rtn_outer_join & JOIN_TYPE_FULL);
}


/*
Compute and store the row types of the top-most NATURAL/USING joins
in a FROM clause.
Expand Down Expand Up @@ -8085,8 +8102,9 @@ static bool setup_natural_join_row_types(THD *thd,
FROM clause.
*/
DBUG_ASSERT(right_neighbor);
context->first_name_resolution_table=
right_neighbor->first_leaf_for_name_resolution();
if (!is_full_outer_join(context, right_neighbor))
context->first_name_resolution_table=
right_neighbor->first_leaf_for_name_resolution();
/*
This is only to ensure that first_name_resolution_table doesn't
change on re-execution
Expand Down
28 changes: 28 additions & 0 deletions sql/sql_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,34 @@ template <class T> class List_iterator :public base_list_iterator
inline void remove() { base_list_iterator::remove(); }
inline void after(T *a) { base_list_iterator::after(a); }
inline T** ref(void) { return (T**) base_list_iterator::ref(); }

/*
Swap the current element with the next one in the list.
If this iterator points to no element or to the last element, then this
method does nothing and returns nullptr.
If this iter points to B in the following list
A, B, C, D, ...
then after this method returns, the list will be
A, C, B, D, ...
and this method returns C. This iter will point to the same location
in the list after this method returns as it did before, but the element at
that location will be C instead of B.
Other iterators pointing to the same list remain valid and will see the
updated list order.
*/
T* swap_next()
{
if (!ref() || !*ref() || !peek())
return nullptr;
T* cur= *ref();
List_iterator<T> itr= *this;
T* nxt= itr++;
replace(nxt);
return itr.replace(cur);
}
};


Expand Down
8 changes: 3 additions & 5 deletions sql/sql_parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8672,12 +8672,10 @@ bool st_select_lex::add_cross_joined_table(TABLE_LIST *left_op,

TABLE_LIST *st_select_lex::convert_right_join()
{
TABLE_LIST *tab2= join_list->pop();
TABLE_LIST *tab1= join_list->pop();
DBUG_ENTER("convert_right_join");

join_list->push_front(tab2, parent_lex->thd->mem_root);
join_list->push_front(tab1, parent_lex->thd->mem_root);
List_iterator<TABLE_LIST> li(*join_list);
li++; // points iterator at first element and returns it
TABLE_LIST* tab1= li.swap_next();
tab1->outer_join|= JOIN_TYPE_RIGHT;

DBUG_RETURN(tab1);
Expand Down
Loading