mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
greedy_search() and best_extension_by_limited_search() scrambled table order
best_extension_by_limited_search() assumes that tables should be sorted according to size to be able to quickly disregard bad plans. However the current usage of swap_variables() will change the table order to a not sorted one for the next recursive call. This breaks the assumtion and causes performance issues when using many tables (we have to examine many more plans). This patch fixes this by ensuring that the original table order is kept for the not yet used tables when best_extension_by_limited_search() is called. This was done by always calling swap_variables() for each table and restoring the original table order at exit. Some test changed: - In a majority of the test the change was that two "identical tables" where swapped and the optimzer is now using the first/smaller table - In few test the table order was changed. The new plan looks identical or slighly better than the original.
This commit is contained in:
committed by
Sergei Petrunia
parent
f0ea7f7f33
commit
64f24b776d
@ -9176,8 +9176,13 @@ greedy_search(JOIN *join,
|
||||
while (pos && best_table != pos)
|
||||
pos= join->best_ref[++best_idx];
|
||||
DBUG_ASSERT((pos != NULL)); // should always find 'best_table'
|
||||
/* move 'best_table' at the first free position in the array of joins */
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], join->best_ref[best_idx]);
|
||||
/*
|
||||
move 'best_table' at the first free position in the array of joins,
|
||||
keeping the sorted table order intact
|
||||
*/
|
||||
memmove(join->best_ref + idx + 1, join->best_ref + idx,
|
||||
sizeof(JOIN_TAB*) * (best_idx - idx));
|
||||
join->best_ref[idx]= best_table;
|
||||
|
||||
/* compute the cost of the new plan extended with 'best_table' */
|
||||
record_count= COST_MULT(record_count, join->positions[idx].records_read);
|
||||
@ -9924,7 +9929,7 @@ best_extension_by_limited_search(JOIN *join,
|
||||
'join' is a partial plan with lower cost than the best plan so far,
|
||||
so continue expanding it further with the tables in 'remaining_tables'.
|
||||
*/
|
||||
JOIN_TAB *s;
|
||||
JOIN_TAB *s, **pos;
|
||||
double best_record_count= DBL_MAX;
|
||||
double best_read_time= DBL_MAX;
|
||||
bool disable_jbuf= join->thd->variables.join_cache_level == 0;
|
||||
@ -9944,7 +9949,7 @@ best_extension_by_limited_search(JOIN *join,
|
||||
DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time,
|
||||
"part_plan"););
|
||||
|
||||
/*
|
||||
/*
|
||||
If we are searching for the execution plan of a materialized semi-join nest
|
||||
then allowed_tables contains bits only for the tables from this nest.
|
||||
*/
|
||||
@ -9952,11 +9957,13 @@ best_extension_by_limited_search(JOIN *join,
|
||||
if (join->emb_sjm_nest)
|
||||
allowed_tables= join->emb_sjm_nest->sj_inner_tables & ~join->const_table_map;
|
||||
|
||||
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
|
||||
for (pos= join->best_ref + idx ; (s= *pos) ; pos++)
|
||||
{
|
||||
table_map real_table_bit= s->table->map;
|
||||
DBUG_ASSERT(remaining_tables & real_table_bit);
|
||||
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
|
||||
|
||||
if ((allowed_tables & real_table_bit) &&
|
||||
!(remaining_tables & s->dependent) &&
|
||||
!check_interleaving_with_nj(s))
|
||||
@ -10069,7 +10076,6 @@ best_extension_by_limited_search(JOIN *join,
|
||||
allowed_tables)
|
||||
{
|
||||
/* Recursively expand the current partial plan */
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
|
||||
Json_writer_array trace_rest(thd, "rest_of_plan");
|
||||
best_res=
|
||||
best_extension_by_limited_search(join,
|
||||
@ -10082,8 +10088,7 @@ best_extension_by_limited_search(JOIN *join,
|
||||
prune_level,
|
||||
use_cond_selectivity);
|
||||
if ((int) best_res < (int) SEARCH_OK)
|
||||
DBUG_RETURN(best_res); // Abort
|
||||
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
|
||||
goto end; // Return best_res
|
||||
if (best_res == SEARCH_FOUND_EDGE &&
|
||||
check_if_edge_table(join->positions+ idx,
|
||||
pushdown_cond_selectivity) !=
|
||||
@ -10128,11 +10133,27 @@ best_extension_by_limited_search(JOIN *join,
|
||||
if (best_res == SEARCH_FOUND_EDGE)
|
||||
{
|
||||
trace_one_table.add("pruned_by_hanging_leaf", true);
|
||||
DBUG_RETURN(best_res);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(SEARCH_OK);
|
||||
best_res= SEARCH_OK;
|
||||
|
||||
end:
|
||||
/* Restore original table order */
|
||||
if (!*pos)
|
||||
pos--; // Revert last pos++ in for loop
|
||||
if (pos != join->best_ref + idx)
|
||||
{
|
||||
JOIN_TAB *tmp= join->best_ref[idx];
|
||||
uint elements= (uint) (pos - (join->best_ref + idx));
|
||||
|
||||
memmove((void*) (join->best_ref + idx),
|
||||
(void*) (join->best_ref + idx + 1),
|
||||
elements * sizeof(JOIN_TAB*));
|
||||
*pos= tmp;
|
||||
}
|
||||
DBUG_RETURN(best_res);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user