1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-27624 Wrong result for nested left join using not_exists optimization

This bug affected queries with nested left joins having the same last inner
table such that not_exists optimization could be applied to the most inner
outer join when optimizer chose to use join buffers. The bug could lead to
producing wrong a result set.
If the WHERE condition a query contains a conjunctive IS NULL predicate
over a non-nullable column of an inner table of a not nested outer join
then not_exists optimization can be applied to tho the outer join. With
this optimization when looking for matches for a certain record from the
outer table of the join the records of the inner table can be ignored
right after the first match satisfying the ON condition is found.
In the case of nested outer joins having the same last inner table this
optimization still can be applied but only if all ON conditions of the
embedding outer joins are satisfied. Such check was missing in the code
that tried to apply not_exists optimization when join buffers were used
for outer join operations.
This problem has been already fixed in the patch for bug MDEV-7992. Yet
there it was resolved only for the cases when join buffers were not used
for outer joins.

Approved by Oleksandr Byelkin <sanja@mariadb.com>
This commit is contained in:
Igor Babaev
2022-10-26 22:08:32 -07:00
parent af0ff8b455
commit b21832ef15
4 changed files with 171 additions and 6 deletions

View File

@ -2287,11 +2287,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
int error;
enum_nested_loop_state rc= NESTED_LOOP_OK;
join_tab->table->null_row= 0;
bool check_only_first_match=
join_tab->check_only_first_match() &&
(!join_tab->first_inner || // semi-join case
join_tab->first_inner == join_tab->first_unmatched); // outer join case
bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join();
bool check_only_first_match= join_tab->check_only_first_match();
DBUG_ENTER("JOIN_CACHE::join_matching_records");
/* Return at once if there are no records in the join buffer */
@ -2355,7 +2351,34 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
Also those records that must be null complemented are not considered
as candidates for matches.
*/
if ((!check_only_first_match && !outer_join_first_inner) ||
bool not_exists_opt_is_applicable= true;
if (check_only_first_match && join_tab->first_inner)
{
/*
This is the case with not_exists optimization for nested outer join
when join_tab is the last inner table for one or more embedding outer
joins. To safely use 'not_exists' optimization in this case we have
to check that the match flags for all these embedding outer joins are
in the 'on' state.
(See also a similar check in evaluate_join_record() for the case when
join buffer are not used.)
*/
for (JOIN_TAB *tab= join_tab->first_inner;
tab && tab->first_inner && tab->last_inner == join_tab;
tab= tab->first_inner->first_upper)
{
if (get_match_flag_by_pos_from_join_buffer(rec_ptr, tab) !=
MATCH_FOUND)
{
not_exists_opt_is_applicable= false;
break;
}
}
}
if (!check_only_first_match ||
(join_tab->first_inner && !not_exists_opt_is_applicable) ||
!skip_next_candidate_for_match(rec_ptr))
{
read_next_candidate_for_match(rec_ptr);