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:
@ -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, ¤t_record_count,
|
||||
¤t_read_time, &loose_scan_pos);
|
||||
optimize_semi_joins(join, remaining_tables, idx, ¤t_record_count,
|
||||
¤t_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
|
||||
{
|
||||
|
Reference in New Issue
Block a user