1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

Merge 10.6 into 10.7

This commit is contained in:
Marko Mäkelä
2022-06-09 14:11:43 +03:00
126 changed files with 1296 additions and 1145 deletions

View File

@ -392,6 +392,7 @@ POSITION::POSITION()
ref_depend_map= dups_producing_tables= 0;
inner_tables_handled_with_other_sjs= 0;
type= JT_UNKNOWN;
key_dependent= 0;
dups_weedout_picker.set_empty();
firstmatch_picker.set_empty();
loosescan_picker.set_empty();
@ -6290,7 +6291,11 @@ add_key_field(JOIN *join,
Field IN ...
*/
if (field->flags & PART_KEY_FLAG)
stat[0].key_dependent|=used_tables;
{
stat[0].key_dependent|= used_tables;
if (field->key_start.bits_set())
stat[0].key_start_dependent= 1;
}
bool is_const=1;
for (uint i=0; i<num_values; i++)
@ -7241,6 +7246,7 @@ bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
use= save_pos= dynamic_element(keyuse,0,KEYUSE*);
prev= &key_end;
found_eq_constant= 0;
/* Loop over all elements except the last 'key_end' */
for (i=0 ; i < keyuse->elements-1 ; i++,use++)
{
if (!use->is_for_hash_join())
@ -7634,7 +7640,7 @@ double cost_for_index_read(const THD *thd, const TABLE *table, uint key,
Adjust cost from table->quick_costs calculated by
multi_range_read_info_const() to be comparable with cost_for_index_read()
This functions is needed because best_access_patch doesn't add
This functions is needed because best_access_path() doesn't add
TIME_FOR_COMPARE to it's costs until very late.
Preferably we should fix so that all costs are comparably.
(All compared costs should include TIME_FOR_COMPARE for all found
@ -7698,6 +7704,13 @@ best_access_path(JOIN *join,
double best_time= DBL_MAX;
double records= DBL_MAX;
table_map best_ref_depends_map= 0;
/*
key_dependent is 0 if all key parts could be used or if there was an
EQ_REF table found (which uses all key parts). In other words, we cannot
find a better key for the table even if remaining_tables is reduced.
Otherwise it's a bitmap of tables that could improve key usage.
*/
table_map key_dependent= 0;
Range_rowid_filter_cost_info *best_filter= 0;
double tmp;
ha_rows rec;
@ -7749,6 +7762,8 @@ best_access_path(JOIN *join,
key_part_map const_part= 0;
/* The or-null keypart in ref-or-null access: */
key_part_map ref_or_null_part= 0;
key_part_map all_parts= 0;
if (is_hash_join_key_no(key))
{
/*
@ -7780,15 +7795,16 @@ best_access_path(JOIN *join,
do /* For each keypart */
{
uint keypart= keyuse->keypart;
table_map best_part_found_ref= 0;
table_map best_part_found_ref= 0, key_parts_dependent= 0;
double best_prev_record_reads= DBL_MAX;
do /* For each way to access the keypart */
{
/*
if 1. expression doesn't refer to forward tables
2. we won't get two ref-or-null's
*/
all_parts|= keyuse->keypart_map;
if (!(remaining_tables & keyuse->used_tables) &&
(!keyuse->validity_ref || *keyuse->validity_ref) &&
s->access_from_tables_is_allowed(keyuse->used_tables,
@ -7797,6 +7813,7 @@ best_access_path(JOIN *join,
KEY_OPTIMIZE_REF_OR_NULL)))
{
found_part|= keyuse->keypart_map;
key_parts_dependent= 0;
if (!(keyuse->used_tables & ~join->const_table_map))
const_part|= keyuse->keypart_map;
@ -7819,10 +7836,16 @@ best_access_path(JOIN *join,
if (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL)
ref_or_null_part |= keyuse->keypart_map;
}
else if (!(found_part & keyuse->keypart_map))
key_parts_dependent|= keyuse->used_tables;
loose_scan_opt.add_keyuse(remaining_tables, keyuse);
keyuse++;
} while (keyuse->table == table && keyuse->key == key &&
keyuse->keypart == keypart);
/* If we found a usable key, remember the dependent tables */
if (all_parts & 1)
key_dependent|= key_parts_dependent;
found_ref|= best_part_found_ref;
} while (keyuse->table == table && keyuse->key == key);
@ -8209,6 +8232,27 @@ best_access_path(JOIN *join,
} /* for each key */
records= best_records;
}
else
{
/*
No usable keys found. However, there may still be an option to use
"Range checked for each record" when all depending tables has
been read. s->key_dependent tells us which tables these could be and
s->key_start_dependent tells us if a first key part was used.
s->key_dependent may include more tables than could be used,
but this is ok as not having any usable keys is a rare thing and
the performance penalty for extra table bits is that
best_extension_by_limited_search() would not be able to prune tables
earlier.
Example query:
SELECT * FROM t1,t2 where t1.key1=t2.key1 OR t2.key2<1
*/
if (s->key_start_dependent)
key_dependent= s->key_dependent;
}
/* Check that s->key_dependent contains all used_tables found in s->keyuse */
key_dependent&= ~PSEUDO_TABLE_BITS;
DBUG_ASSERT((key_dependent & s->key_dependent) == key_dependent);
/*
If there is no key to access the table, but there is an equi-join
@ -8460,6 +8504,8 @@ best_access_path(JOIN *join,
pos->use_join_buffer= best_uses_jbuf;
pos->spl_plan= spl_plan;
pos->range_rowid_filter_info= best_filter;
pos->key_dependent= (best_type == JT_EQ_REF ? (table_map) 0 :
key_dependent & remaining_tables);
loose_scan_opt.save_to_position(s, loose_scan_pos);
@ -8662,6 +8708,10 @@ choose_plan(JOIN *join, table_map join_tables)
{
choose_initial_table_order(join);
}
/*
Note: constant tables are already in the join prefix. We don't
put them into the cur_sj_inner_tables, though.
*/
join->cur_sj_inner_tables= 0;
if (straight_join)
@ -8941,7 +8991,7 @@ determine_search_depth(JOIN *join)
*/
static void
optimize_straight_join(JOIN *join, table_map join_tables)
optimize_straight_join(JOIN *join, table_map remaining_tables)
{
JOIN_TAB *s;
uint idx= join->const_tables;
@ -8959,30 +9009,32 @@ optimize_straight_join(JOIN *join, table_map join_tables)
Json_writer_object trace_one_table(thd);
if (unlikely(thd->trace_started()))
{
trace_plan_prefix(join, idx, join_tables);
trace_plan_prefix(join, idx, remaining_tables);
trace_one_table.add_table_name(s);
}
/* Find the best access method from 's' to the current partial plan */
best_access_path(join, s, join_tables, join->positions, idx,
best_access_path(join, s, remaining_tables, join->positions, idx,
disable_jbuf, record_count,
position, &loose_scan_pos);
/* compute the cost of the new plan extended with 's' */
/* Compute the cost of the new plan extended with 's' */
record_count= COST_MULT(record_count, position->records_read);
const double filter_cmp_gain= position->range_rowid_filter_info
? position->range_rowid_filter_info->get_cmp_gain(record_count)
: 0;
read_time+= COST_ADD(read_time - filter_cmp_gain,
COST_ADD(position->read_time,
record_count / TIME_FOR_COMPARE));
advance_sj_state(join, join_tables, idx, &record_count, &read_time,
&loose_scan_pos);
read_time= COST_ADD(read_time,
COST_ADD(position->read_time -
filter_cmp_gain,
record_count /
TIME_FOR_COMPARE));
optimize_semi_joins(join, remaining_tables, idx, &record_count, &read_time,
&loose_scan_pos);
join_tables&= ~(s->table->map);
remaining_tables&= ~(s->table->map);
double pushdown_cond_selectivity= 1.0;
if (use_cond_selectivity > 1)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
join_tables);
remaining_tables);
position->cond_selectivity= pushdown_cond_selectivity;
++idx;
}
@ -9156,6 +9208,12 @@ greedy_search(JOIN *join,
/* This has been already checked by best_extension_by_limited_search */
DBUG_ASSERT(!is_interleave_error);
/*
Also, update the semi-join optimization state. Information about the
picked semi-join operation is in best_pos->...picker, but we need to
update the global state in the JOIN object, too.
*/
update_sj_state(join, best_table, idx, remaining_tables);
/* find the position of 'best_table' in 'join->best_ref' */
best_idx= idx;
@ -9163,8 +9221,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);
@ -9383,10 +9446,7 @@ double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
double sel= 1.0;
COND_EQUAL *cond_equal= join->cond_equal;
if (!cond_equal || !cond_equal->current_level.elements)
return sel;
if (!s->keyuse)
if (!cond_equal || !cond_equal->current_level.elements || !s->keyuse)
return sel;
Item_equal *item_equal;
@ -9911,7 +9971,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;
@ -9931,7 +9991,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.
*/
@ -9939,11 +9999,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))
@ -9964,24 +10026,24 @@ best_extension_by_limited_search(JOIN *join,
best_access_path(join, s, remaining_tables, join->positions, idx,
disable_jbuf, record_count, position, &loose_scan_pos);
/* Compute the cost of extending the plan with 's' */
/* Compute the cost of the new plan extended with 's' */
current_record_count= COST_MULT(record_count, position->records_read);
const double filter_cmp_gain= position->range_rowid_filter_info
? position->range_rowid_filter_info->get_cmp_gain(current_record_count)
: 0;
current_read_time=COST_ADD(read_time,
COST_ADD(position->read_time -
filter_cmp_gain,
current_record_count /
TIME_FOR_COMPARE));
current_read_time= COST_ADD(read_time,
COST_ADD(position->read_time -
filter_cmp_gain,
current_record_count /
TIME_FOR_COMPARE));
if (unlikely(thd->trace_started()))
{
trace_one_table.add("rows_for_plan", current_record_count);
trace_one_table.add("cost_for_plan", current_read_time);
}
advance_sj_state(join, remaining_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
optimize_semi_joins(join, remaining_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
/* Expand only partial plans with lower cost than the best QEP so far */
if (current_read_time >= join->best_read)
@ -10008,11 +10070,18 @@ best_extension_by_limited_search(JOIN *join,
(idx == join->const_tables && // 's' is the first table in the QEP
s->table == join->sort_by_table))
{
/*
Store the current record count and cost as the best
possible cost at this level if the following holds:
- It's the lowest record number and cost so far
- There is no remaing table that could improve index usage
or we found an EQ_REF or REF key with less than 2
matching records (good enough).
*/
if (best_record_count >= current_record_count &&
best_read_time >= current_read_time &&
/* TODO: What is the reasoning behind this condition? */
(!(s->key_dependent & allowed_tables & remaining_tables) ||
join->positions[idx].records_read < 2.0))
(!(position->key_dependent & allowed_tables) ||
position->records_read < 2.0))
{
best_record_count= current_record_count;
best_read_time= current_read_time;
@ -10056,7 +10125,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,
@ -10069,8 +10137,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) !=
@ -10115,11 +10182,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);
}
@ -12519,6 +12602,7 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
(uchar *) &first_keyuse))
{
JOIN_TAB *tab;
first_keyuse= save_first_keyuse;
if (table->add_tmp_key(table->s->keys, parts,
get_next_field_for_derived_key,
@ -12526,6 +12610,9 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
FALSE))
return TRUE;
table->reginfo.join_tab->keys.set_bit(table->s->keys);
tab= table->reginfo.join_tab;
for (uint i=0; i < parts; i++)
tab->key_dependent|= save_first_keyuse[i].used_tables;
}
else
{