1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-XXXXX: Save and restore the status of tables inside a run

Walking back over the tables a join buffer scan feeds from takes a copy
of each table's status and clears it, then puts the copy back when the
scan closes.  For a table standing for a run of other tables, the tables
in that run were left out of the copy but were still written to when the
copy was put back, so they received a value the copy had never taken.
They now take part in both directions.

The work for one table moved into a function that reaches the tables of
any run it stands for, however deeply those runs nest, which is what the
rest of the join order walking already does.

This is a defect in the handling of a materialized semi join, which is
the only kind of run a released version produces, so it is not specific
to FULL JOIN.  No query is known to reach it today.  The FULL JOIN work
that follows produces runs that nest, which reaches it.
This commit is contained in:
Dave Gosselin
2026-08-17 11:36:55 -04:00
parent eb094f2358
commit 11644fd1d7

View File

@@ -3561,46 +3561,55 @@ int JOIN_TAB_SCAN::next()
}
/*
Take a copy of table->status for one JOIN_TAB, or put the copy back. A
JOIN_TAB that stands for a run of other JOIN_TABs is dealt with together
with every JOIN_TAB of that run, however deeply those runs nest, because
the scan buffers the columns of the tables in the run and not those of
the temporary table the run was materialized into.
A JOIN_TAB keeps the copy in its own status member.
*/
static void save_or_restore_tab_status(JOIN_TAB *tab, bool save)
{
if (tab->has_bush_children())
{
for (JOIN_TAB *child= tab->bush_children.start;
child != tab->bush_children.end;
child++)
save_or_restore_tab_status(child, save);
}
if (save)
tab->table->status= tab->status;
else
{
tab->status= tab->table->status;
tab->table->status= 0;
}
}
/*
Walk back in join order from join_tab until we encounter a join tab with
tab->cache!=NULL, and save/restore tab->table->status along the way.
@param save TRUE save
FALSE restore
Note that the argument reads the opposite way round from the two things
the function does.
@param save FALSE take the copy and clear table->status, which is what
a scan wants as it opens
TRUE put the copy back, which is what it wants as it closes
*/
static void save_or_restore_used_tabs(JOIN_TAB *join_tab, bool save)
{
JOIN_TAB *first= join_tab->bush_root_tab?
join_tab->bush_root_tab->bush_children.start :
join_tab->join->join_tab + join_tab->join->const_tables;
JOIN_TAB *first= bush_start(join_tab, join_tab->join->join_tab +
join_tab->join->const_tables);
for (JOIN_TAB *tab= join_tab-1; tab != first && !tab->cache; tab--)
{
if (tab->has_bush_children())
{
for (JOIN_TAB *child= tab->bush_children.start;
child != tab->bush_children.end;
child++)
{
if (save)
child->table->status= child->status;
else
{
tab->status= tab->table->status;
tab->table->status= 0;
}
}
}
if (save)
tab->table->status= tab->status;
else
{
tab->status= tab->table->status;
tab->table->status= 0;
}
}
save_or_restore_tab_status(tab, save);
}