mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-22535 TABLE::initialize_quick_structures() takes 0.5% in oltp_read_only
Fixed by: - Make all quick_* variable allocated according to real number keys instead of MAX_KEY - Store all the quick* items in separated allocated structure (OPT_RANGE) - Ensure we don't access any quick* variable without first checking opt_range_keys.is_set(). Thanks to this, we don't need any pre-initialization of quick* variables anymore. Some renames was done to use the new structure: table->quick_keys -> table->opt_range_keys table->quick_rows[X] -> table->opt_range[X].rows table->quick_key_parts[X] -> table->opt_range[X].key_parts table->quick_costs[X] -> table->opt_range[X].cost table->quick_index_only_costs[X] -> table->opt_range[X].index_only_cost table->quick_n_ranges[X] -> table->opt_range[X].ranges table->quick_condition_rows -> table->opt_range_condition_rows This patch should both decrease memory needed for TABLE objects (3528 -> 984 + keyinfo) and increase performance, thanks to less initializations per query, and more localized memory, thanks to the opt_range structure.
This commit is contained in:
@@ -2599,10 +2599,10 @@ static int fill_used_fields_bitmap(PARAM *param)
|
|||||||
In the table struct the following information is updated:
|
In the table struct the following information is updated:
|
||||||
quick_keys - Which keys can be used
|
quick_keys - Which keys can be used
|
||||||
quick_rows - How many rows the key matches
|
quick_rows - How many rows the key matches
|
||||||
quick_condition_rows - E(# rows that will satisfy the table condition)
|
opt_range_condition_rows - E(# rows that will satisfy the table condition)
|
||||||
|
|
||||||
IMPLEMENTATION
|
IMPLEMENTATION
|
||||||
quick_condition_rows value is obtained as follows:
|
opt_range_condition_rows value is obtained as follows:
|
||||||
|
|
||||||
It is a minimum of E(#output rows) for all considered table access
|
It is a minimum of E(#output rows) for all considered table access
|
||||||
methods (range and index_merge accesses over various indexes).
|
methods (range and index_merge accesses over various indexes).
|
||||||
@@ -2626,7 +2626,7 @@ static int fill_used_fields_bitmap(PARAM *param)
|
|||||||
which is currently produced.
|
which is currently produced.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
* Change the value returned in quick_condition_rows from a pessimistic
|
* Change the value returned in opt_range_condition_rows from a pessimistic
|
||||||
estimate to true E(#rows that satisfy table condition).
|
estimate to true E(#rows that satisfy table condition).
|
||||||
(we can re-use some of E(#rows) calcuation code from
|
(we can re-use some of E(#rows) calcuation code from
|
||||||
index_merge/intersection for this)
|
index_merge/intersection for this)
|
||||||
@@ -2957,7 +2957,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
|||||||
{
|
{
|
||||||
best_trp= intersect_trp;
|
best_trp= intersect_trp;
|
||||||
best_read_time= best_trp->read_cost;
|
best_read_time= best_trp->read_cost;
|
||||||
set_if_smaller(param.table->quick_condition_rows,
|
set_if_smaller(param.table->opt_range_condition_rows,
|
||||||
intersect_trp->records);
|
intersect_trp->records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2977,7 +2977,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
|||||||
{
|
{
|
||||||
new_conj_trp= get_best_disjunct_quick(¶m, imerge, best_read_time);
|
new_conj_trp= get_best_disjunct_quick(¶m, imerge, best_read_time);
|
||||||
if (new_conj_trp)
|
if (new_conj_trp)
|
||||||
set_if_smaller(param.table->quick_condition_rows,
|
set_if_smaller(param.table->opt_range_condition_rows,
|
||||||
new_conj_trp->records);
|
new_conj_trp->records);
|
||||||
if (new_conj_trp &&
|
if (new_conj_trp &&
|
||||||
(!best_conj_trp ||
|
(!best_conj_trp ||
|
||||||
@@ -3004,7 +3004,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
|||||||
restore_nonrange_trees(¶m, tree, backup_keys);
|
restore_nonrange_trees(¶m, tree, backup_keys);
|
||||||
if ((group_trp= get_best_group_min_max(¶m, tree, read_time)))
|
if ((group_trp= get_best_group_min_max(¶m, tree, read_time)))
|
||||||
{
|
{
|
||||||
param.table->quick_condition_rows= MY_MIN(group_trp->records,
|
param.table->opt_range_condition_rows= MY_MIN(group_trp->records,
|
||||||
head->stat_records());
|
head->stat_records());
|
||||||
Json_writer_object grp_summary(thd, "best_group_range_summary");
|
Json_writer_object grp_summary(thd, "best_group_range_summary");
|
||||||
|
|
||||||
@@ -3340,8 +3340,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
|
|||||||
|
|
||||||
for (keynr= 0; keynr < table->s->keys; keynr++)
|
for (keynr= 0; keynr < table->s->keys; keynr++)
|
||||||
{
|
{
|
||||||
if (table->quick_keys.is_set(keynr))
|
if (table->opt_range_keys.is_set(keynr))
|
||||||
set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]);
|
set_if_bigger(max_quick_key_parts, table->opt_range[keynr].key_parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3353,13 +3353,13 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
|
|||||||
{
|
{
|
||||||
for (keynr= 0; keynr < table->s->keys; keynr++)
|
for (keynr= 0; keynr < table->s->keys; keynr++)
|
||||||
{
|
{
|
||||||
if (table->quick_keys.is_set(keynr) &&
|
if (table->opt_range_keys.is_set(keynr) &&
|
||||||
table->quick_key_parts[keynr] == quick_key_parts)
|
table->opt_range[keynr].key_parts == quick_key_parts)
|
||||||
{
|
{
|
||||||
uint i;
|
uint i;
|
||||||
uint used_key_parts= table->quick_key_parts[keynr];
|
uint used_key_parts= table->opt_range[keynr].key_parts;
|
||||||
double quick_cond_selectivity= table->quick_rows[keynr] /
|
double quick_cond_selectivity= (table->opt_range[keynr].rows /
|
||||||
table_records;
|
table_records);
|
||||||
KEY *key_info= table->key_info + keynr;
|
KEY *key_info= table->key_info + keynr;
|
||||||
KEY_PART_INFO* key_part= key_info->key_part;
|
KEY_PART_INFO* key_part= key_info->key_part;
|
||||||
/*
|
/*
|
||||||
@@ -5777,7 +5777,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cost= table->quick_index_only_costs[(*index_scan)->keynr];
|
cost= table->opt_range[(*index_scan)->keynr].index_only_cost;
|
||||||
|
|
||||||
idx_scan.add("cost", cost);
|
idx_scan.add("cost", cost);
|
||||||
|
|
||||||
@@ -7188,7 +7188,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
|
|||||||
ha_rows best_rows = double2rows(intersect_best->out_rows);
|
ha_rows best_rows = double2rows(intersect_best->out_rows);
|
||||||
if (!best_rows)
|
if (!best_rows)
|
||||||
best_rows= 1;
|
best_rows= 1;
|
||||||
set_if_smaller(param->table->quick_condition_rows, best_rows);
|
set_if_smaller(param->table->opt_range_condition_rows, best_rows);
|
||||||
trp->records= best_rows;
|
trp->records= best_rows;
|
||||||
trp->index_scan_costs= intersect_best->index_scan_costs;
|
trp->index_scan_costs= intersect_best->index_scan_costs;
|
||||||
trp->cpk_scan= cpk_scan;
|
trp->cpk_scan= cpk_scan;
|
||||||
@@ -7357,7 +7357,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
|
|||||||
trp->read_cost= total_cost;
|
trp->read_cost= total_cost;
|
||||||
trp->records= records;
|
trp->records= records;
|
||||||
trp->cpk_scan= NULL;
|
trp->cpk_scan= NULL;
|
||||||
set_if_smaller(param->table->quick_condition_rows, records);
|
set_if_smaller(param->table->opt_range_condition_rows, records);
|
||||||
|
|
||||||
DBUG_PRINT("info",
|
DBUG_PRINT("info",
|
||||||
("Returning covering ROR-intersect plan: cost %g, records %lu",
|
("Returning covering ROR-intersect plan: cost %g, records %lu",
|
||||||
@@ -11083,11 +11083,11 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
|
|||||||
about range scan we've evaluated.
|
about range scan we've evaluated.
|
||||||
mrr_flags INOUT MRR access flags
|
mrr_flags INOUT MRR access flags
|
||||||
cost OUT Scan cost
|
cost OUT Scan cost
|
||||||
|
is_ror_scan is set to reflect if the key scan is a ROR (see
|
||||||
|
is_key_scan_ror function for more info)
|
||||||
|
|
||||||
NOTES
|
NOTES
|
||||||
param->is_ror_scan is set to reflect if the key scan is a ROR (see
|
param->table->opt_range*, param->range_count (and maybe others) are
|
||||||
is_key_scan_ror function for more info)
|
|
||||||
param->table->quick_*, param->range_count (and maybe others) are
|
|
||||||
updated with data of given key scan, see quick_range_seq_next for details.
|
updated with data of given key scan, see quick_range_seq_next for details.
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
@@ -11157,6 +11157,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
|||||||
if (param->table->pos_in_table_list->is_non_derived())
|
if (param->table->pos_in_table_list->is_non_derived())
|
||||||
rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0,
|
rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0,
|
||||||
bufsize, mrr_flags, cost);
|
bufsize, mrr_flags, cost);
|
||||||
|
param->quick_rows[keynr]= rows;
|
||||||
if (rows != HA_POS_ERROR)
|
if (rows != HA_POS_ERROR)
|
||||||
{
|
{
|
||||||
ha_rows table_records= param->table->stat_records();
|
ha_rows table_records= param->table->stat_records();
|
||||||
@@ -11164,30 +11165,30 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
For any index the total number of records within all ranges
|
For any index the total number of records within all ranges
|
||||||
cannot be be bigger than the number of records in the table
|
cannot be be bigger than the number of records in the table.
|
||||||
|
This check is needed as sometimes that table statistics or range
|
||||||
|
estimates may be slightly out of sync.
|
||||||
*/
|
*/
|
||||||
rows= table_records;
|
rows= table_records;
|
||||||
set_if_bigger(rows, 1);
|
set_if_bigger(rows, 1);
|
||||||
|
param->quick_rows[keynr]= rows;
|
||||||
}
|
}
|
||||||
param->quick_rows[keynr]= rows;
|
|
||||||
param->possible_keys.set_bit(keynr);
|
param->possible_keys.set_bit(keynr);
|
||||||
if (update_tbl_stats)
|
if (update_tbl_stats)
|
||||||
{
|
{
|
||||||
param->table->quick_keys.set_bit(keynr);
|
param->table->opt_range_keys.set_bit(keynr);
|
||||||
param->table->quick_key_parts[keynr]= param->max_key_parts;
|
param->table->opt_range[keynr].key_parts= param->max_key_parts;
|
||||||
param->table->quick_n_ranges[keynr]= param->range_count;
|
param->table->opt_range[keynr].ranges= param->range_count;
|
||||||
param->table->quick_condition_rows=
|
param->table->opt_range_condition_rows=
|
||||||
MY_MIN(param->table->quick_condition_rows, rows);
|
MY_MIN(param->table->opt_range_condition_rows, rows);
|
||||||
param->table->quick_rows[keynr]= rows;
|
param->table->opt_range[keynr].rows= rows;
|
||||||
param->table->quick_costs[keynr]= cost->total_cost();
|
param->table->opt_range[keynr].cost= cost->total_cost();
|
||||||
if (param->table->file->is_clustering_key(keynr))
|
if (param->table->file->is_clustering_key(keynr))
|
||||||
param->table->quick_index_only_costs[keynr]= 0;
|
param->table->opt_range[keynr].index_only_cost= 0;
|
||||||
else
|
else
|
||||||
param->table->quick_index_only_costs[keynr]= cost->index_only_cost();
|
param->table->opt_range[keynr].index_only_cost= cost->index_only_cost();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
param->quick_rows[keynr]= HA_POS_ERROR;
|
|
||||||
|
|
||||||
/* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
|
/* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
|
||||||
enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm;
|
enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm;
|
||||||
|
@@ -2500,7 +2500,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
|
|||||||
double rows= 1.0;
|
double rows= 1.0;
|
||||||
while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END)
|
while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END)
|
||||||
rows= COST_MULT(rows,
|
rows= COST_MULT(rows,
|
||||||
join->map2table[tableno]->table->quick_condition_rows);
|
join->map2table[tableno]->table->opt_range_condition_rows);
|
||||||
sjm->rows= MY_MIN(sjm->rows, rows);
|
sjm->rows= MY_MIN(sjm->rows, rows);
|
||||||
}
|
}
|
||||||
memcpy((uchar*) sjm->positions,
|
memcpy((uchar*) sjm->positions,
|
||||||
|
@@ -110,10 +110,12 @@ Range_rowid_filter_cost_info::set_adjusted_gain_param(double access_cost_factor)
|
|||||||
void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
|
void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
|
||||||
TABLE *tab, uint idx)
|
TABLE *tab, uint idx)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(tab->opt_range_keys.is_set(idx));
|
||||||
|
|
||||||
container_type= cont_type;
|
container_type= cont_type;
|
||||||
table= tab;
|
table= tab;
|
||||||
key_no= idx;
|
key_no= idx;
|
||||||
est_elements= (ulonglong) (table->quick_rows[key_no]);
|
est_elements= (ulonglong) table->opt_range[key_no].rows;
|
||||||
b= build_cost(container_type);
|
b= build_cost(container_type);
|
||||||
selectivity= est_elements/((double) table->stat_records());
|
selectivity= est_elements/((double) table->stat_records());
|
||||||
a= avg_access_and_eval_gain_per_row(container_type);
|
a= avg_access_and_eval_gain_per_row(container_type);
|
||||||
@@ -134,8 +136,9 @@ double
|
|||||||
Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
|
Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
|
||||||
{
|
{
|
||||||
double cost= 0;
|
double cost= 0;
|
||||||
|
DBUG_ASSERT(table->opt_range_keys.is_set(key_no));
|
||||||
|
|
||||||
cost+= table->quick_index_only_costs[key_no];
|
cost+= table->opt_range[key_no].index_only_cost;
|
||||||
|
|
||||||
switch (cont_type) {
|
switch (cont_type) {
|
||||||
|
|
||||||
@@ -345,7 +348,7 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
|
|||||||
uint key_no;
|
uint key_no;
|
||||||
key_map usable_range_filter_keys;
|
key_map usable_range_filter_keys;
|
||||||
usable_range_filter_keys.clear_all();
|
usable_range_filter_keys.clear_all();
|
||||||
key_map::Iterator it(quick_keys);
|
key_map::Iterator it(opt_range_keys);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
From all indexes that can be used for range accesses select only such that
|
From all indexes that can be used for range accesses select only such that
|
||||||
@@ -359,7 +362,7 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
|
|||||||
continue;
|
continue;
|
||||||
if (file->is_clustering_key(key_no)) // !2
|
if (file->is_clustering_key(key_no)) // !2
|
||||||
continue;
|
continue;
|
||||||
if (quick_rows[key_no] >
|
if (opt_range[key_no].rows >
|
||||||
get_max_range_rowid_filter_elems_for_table(thd, this,
|
get_max_range_rowid_filter_elems_for_table(thd, this,
|
||||||
SORTED_ARRAY_CONTAINER)) // !3
|
SORTED_ARRAY_CONTAINER)) // !3
|
||||||
continue;
|
continue;
|
||||||
|
@@ -492,7 +492,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
|||||||
set_statistics_for_table(thd, table);
|
set_statistics_for_table(thd, table);
|
||||||
|
|
||||||
table->covering_keys.clear_all();
|
table->covering_keys.clear_all();
|
||||||
table->quick_keys.clear_all(); // Can't use 'only index'
|
table->opt_range_keys.clear_all();
|
||||||
|
|
||||||
select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
|
select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
|
||||||
if (unlikely(error))
|
if (unlikely(error))
|
||||||
@@ -518,7 +518,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If running in safe sql mode, don't allow updates without keys */
|
/* If running in safe sql mode, don't allow updates without keys */
|
||||||
if (table->quick_keys.is_clear_all())
|
if (table->opt_range_keys.is_clear_all())
|
||||||
{
|
{
|
||||||
thd->set_status_no_index_used();
|
thd->set_status_no_index_used();
|
||||||
if (safe_update && !using_limit)
|
if (safe_update && !using_limit)
|
||||||
|
@@ -4897,7 +4897,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||||||
table->file->print_error(error, MYF(0));
|
table->file->print_error(error, MYF(0));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
table->quick_keys.clear_all();
|
table->opt_range_keys.clear_all();
|
||||||
table->intersect_keys.clear_all();
|
table->intersect_keys.clear_all();
|
||||||
table->reginfo.join_tab=s;
|
table->reginfo.join_tab=s;
|
||||||
table->reginfo.not_exists_optimize=0;
|
table->reginfo.not_exists_optimize=0;
|
||||||
@@ -4909,7 +4909,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||||||
s->dependent= tables->dep_tables;
|
s->dependent= tables->dep_tables;
|
||||||
if (tables->schema_table)
|
if (tables->schema_table)
|
||||||
table->file->stats.records= table->used_stat_records= 2;
|
table->file->stats.records= table->used_stat_records= 2;
|
||||||
table->quick_condition_rows= table->stat_records();
|
table->opt_range_condition_rows= table->stat_records();
|
||||||
|
|
||||||
s->on_expr_ref= &tables->on_expr;
|
s->on_expr_ref= &tables->on_expr;
|
||||||
if (*s->on_expr_ref)
|
if (*s->on_expr_ref)
|
||||||
@@ -5376,7 +5376,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||||||
get_delayed_table_estimates(s->table, &s->records, &s->read_time,
|
get_delayed_table_estimates(s->table, &s->records, &s->read_time,
|
||||||
&s->startup_cost);
|
&s->startup_cost);
|
||||||
s->found_records= s->records;
|
s->found_records= s->records;
|
||||||
table->quick_condition_rows=s->records;
|
table->opt_range_condition_rows=s->records;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
s->scan_time();
|
s->scan_time();
|
||||||
@@ -7217,8 +7217,8 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
|
|||||||
If applicable, get a more accurate estimate. Don't use the two
|
If applicable, get a more accurate estimate. Don't use the two
|
||||||
heuristics at once.
|
heuristics at once.
|
||||||
*/
|
*/
|
||||||
if (s->table->quick_condition_rows != s->found_records)
|
if (s->table->opt_range_condition_rows != s->found_records)
|
||||||
records= s->table->quick_condition_rows;
|
records= s->table->opt_range_condition_rows;
|
||||||
|
|
||||||
dbl_records= (double)records;
|
dbl_records= (double)records;
|
||||||
return dbl_records;
|
return dbl_records;
|
||||||
@@ -7506,8 +7506,8 @@ best_access_path(JOIN *join,
|
|||||||
type= JT_EQ_REF;
|
type= JT_EQ_REF;
|
||||||
trace_access_idx.add("access_type", join_type_str[type])
|
trace_access_idx.add("access_type", join_type_str[type])
|
||||||
.add("index", keyinfo->name);
|
.add("index", keyinfo->name);
|
||||||
if (!found_ref && table->quick_keys.is_set(key))
|
if (!found_ref && table->opt_range_keys.is_set(key))
|
||||||
tmp= adjust_quick_cost(table->quick_costs[key], 1);
|
tmp= adjust_quick_cost(table->opt_range[key].cost, 1);
|
||||||
else
|
else
|
||||||
tmp= table->file->avg_io_cost();
|
tmp= table->file->avg_io_cost();
|
||||||
tmp*= prev_record_reads(join_positions, idx, found_ref);
|
tmp*= prev_record_reads(join_positions, idx, found_ref);
|
||||||
@@ -7537,12 +7537,12 @@ best_access_path(JOIN *join,
|
|||||||
quick_cond is equivalent to ref_const_cond (if it was an
|
quick_cond is equivalent to ref_const_cond (if it was an
|
||||||
empty interval we wouldn't have got here).
|
empty interval we wouldn't have got here).
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(key))
|
if (table->opt_range_keys.is_set(key))
|
||||||
{
|
{
|
||||||
records= (double) table->quick_rows[key];
|
records= (double) table->opt_range[key].rows;
|
||||||
trace_access_idx.add("used_range_estimates", true);
|
trace_access_idx.add("used_range_estimates", true);
|
||||||
tmp= adjust_quick_cost(table->quick_costs[key],
|
tmp= adjust_quick_cost(table->opt_range[key].cost,
|
||||||
table->quick_rows[key]);
|
table->opt_range[key].rows);
|
||||||
goto got_cost;
|
goto got_cost;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -7575,19 +7575,19 @@ best_access_path(JOIN *join,
|
|||||||
can make an adjustment is a special case of the criteria used
|
can make an adjustment is a special case of the criteria used
|
||||||
in ReuseRangeEstimateForRef-3.
|
in ReuseRangeEstimateForRef-3.
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(key) &&
|
if (table->opt_range_keys.is_set(key) &&
|
||||||
(const_part &
|
(const_part &
|
||||||
(((key_part_map)1 << table->quick_key_parts[key])-1)) ==
|
(((key_part_map)1 << table->opt_range[key].key_parts)-1)) ==
|
||||||
(((key_part_map)1 << table->quick_key_parts[key])-1) &&
|
(((key_part_map)1 << table->opt_range[key].key_parts)-1) &&
|
||||||
table->quick_n_ranges[key] == 1 &&
|
table->opt_range[key].ranges == 1 &&
|
||||||
records > (double) table->quick_rows[key])
|
records > (double) table->opt_range[key].rows)
|
||||||
{
|
{
|
||||||
records= (double) table->quick_rows[key];
|
records= (double) table->opt_range[key].rows;
|
||||||
trace_access_idx.add("used_range_estimates", true);
|
trace_access_idx.add("used_range_estimates", true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (table->quick_keys.is_set(key))
|
if (table->opt_range_keys.is_set(key))
|
||||||
{
|
{
|
||||||
trace_access_idx.add("used_range_estimates",false)
|
trace_access_idx.add("used_range_estimates",false)
|
||||||
.add("cause",
|
.add("cause",
|
||||||
@@ -7661,13 +7661,13 @@ best_access_path(JOIN *join,
|
|||||||
|
|
||||||
(C3) "range optimizer used (have ref_or_null?2:1) intervals"
|
(C3) "range optimizer used (have ref_or_null?2:1) intervals"
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(key) && !found_ref && //(C1)
|
if (table->opt_range_keys.is_set(key) && !found_ref && //(C1)
|
||||||
table->quick_key_parts[key] == max_key_part && //(C2)
|
table->opt_range[key].key_parts == max_key_part && //(C2)
|
||||||
table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part)) //(C3)
|
table->opt_range[key].ranges == 1 + MY_TEST(ref_or_null_part)) //(C3)
|
||||||
{
|
{
|
||||||
records= (double) table->quick_rows[key];
|
records= (double) table->opt_range[key].rows;
|
||||||
tmp= adjust_quick_cost(table->quick_costs[key],
|
tmp= adjust_quick_cost(table->opt_range[key].cost,
|
||||||
table->quick_rows[key]);
|
table->opt_range[key].rows);
|
||||||
trace_access_idx.add("used_range_estimates", true);
|
trace_access_idx.add("used_range_estimates", true);
|
||||||
goto got_cost2;
|
goto got_cost2;
|
||||||
}
|
}
|
||||||
@@ -7692,15 +7692,16 @@ best_access_path(JOIN *join,
|
|||||||
cheaper in some cases ?
|
cheaper in some cases ?
|
||||||
TODO: figure this out and adjust the plan choice if needed.
|
TODO: figure this out and adjust the plan choice if needed.
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(key))
|
if (table->opt_range_keys.is_set(key))
|
||||||
{
|
{
|
||||||
if (table->quick_key_parts[key] >= max_key_part) // (2)
|
if (table->opt_range[key].key_parts >= max_key_part) // (2)
|
||||||
{
|
{
|
||||||
|
double rows= (double) table->opt_range[key].rows;
|
||||||
if (!found_ref && // (1)
|
if (!found_ref && // (1)
|
||||||
records < (double) table->quick_rows[key]) // (3)
|
records < rows) // (3)
|
||||||
{
|
{
|
||||||
trace_access_idx.add("used_range_estimates", true);
|
trace_access_idx.add("used_range_estimates", true);
|
||||||
records= (double) table->quick_rows[key];
|
records= rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else /* (table->quick_key_parts[key] < max_key_part) */
|
else /* (table->quick_key_parts[key] < max_key_part) */
|
||||||
@@ -7766,15 +7767,16 @@ best_access_path(JOIN *join,
|
|||||||
optimizer is the same as in ReuseRangeEstimateForRef-3,
|
optimizer is the same as in ReuseRangeEstimateForRef-3,
|
||||||
applied to first table->quick_key_parts[key] key parts.
|
applied to first table->quick_key_parts[key] key parts.
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(key) &&
|
if (table->opt_range_keys.is_set(key) &&
|
||||||
table->quick_key_parts[key] <= max_key_part &&
|
table->opt_range[key].key_parts <= max_key_part &&
|
||||||
const_part &
|
const_part &
|
||||||
((key_part_map)1 << table->quick_key_parts[key]) &&
|
((key_part_map)1 << table->opt_range[key].key_parts) &&
|
||||||
table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part &
|
table->opt_range[key].ranges == (1 +
|
||||||
const_part) &&
|
MY_TEST(ref_or_null_part &
|
||||||
records > (double) table->quick_rows[key])
|
const_part)) &&
|
||||||
|
records > (double) table->opt_range[key].rows)
|
||||||
{
|
{
|
||||||
records= (double) table->quick_rows[key];
|
records= (double) table->opt_range[key].rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7813,7 +7815,7 @@ best_access_path(JOIN *join,
|
|||||||
tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
|
tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
|
||||||
DBUG_ASSERT(tmp >= 0);
|
DBUG_ASSERT(tmp >= 0);
|
||||||
trace_access_idx.add("rowid_filter_key",
|
trace_access_idx.add("rowid_filter_key",
|
||||||
s->table->key_info[filter->key_no].name);
|
table->key_info[filter->key_no].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace_access_idx.add("rows", records).add("cost", tmp);
|
trace_access_idx.add("rows", records).add("cost", tmp);
|
||||||
@@ -7928,7 +7930,7 @@ best_access_path(JOIN *join,
|
|||||||
if ((records >= s->found_records || best > s->read_time) && // (1)
|
if ((records >= s->found_records || best > s->read_time) && // (1)
|
||||||
!(best_key && best_key->key == MAX_KEY) && // (2)
|
!(best_key && best_key->key == MAX_KEY) && // (2)
|
||||||
!(s->quick && best_key && s->quick->index == best_key->key && // (2)
|
!(s->quick && best_key && s->quick->index == best_key->key && // (2)
|
||||||
best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&// (2)
|
best_max_key_part >= s->table->opt_range[best_key->key].key_parts) &&// (2)
|
||||||
!((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
|
!((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
|
||||||
! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
|
! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
|
||||||
!(s->table->force_index && best_key && !s->quick) && // (4)
|
!(s->table->force_index && best_key && !s->quick) && // (4)
|
||||||
@@ -9193,10 +9195,11 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
|
|||||||
/*
|
/*
|
||||||
Check if we have a prefix of key=const that matches a quick select.
|
Check if we have a prefix of key=const that matches a quick select.
|
||||||
*/
|
*/
|
||||||
if (!is_hash_join_key_no(key))
|
if (!is_hash_join_key_no(key) && table->opt_range_keys.is_set(key))
|
||||||
{
|
{
|
||||||
key_part_map quick_key_map= (key_part_map(1) << table->quick_key_parts[key]) - 1;
|
key_part_map quick_key_map= (key_part_map(1) <<
|
||||||
if (table->quick_rows[key] &&
|
table->opt_range[key].key_parts) - 1;
|
||||||
|
if (table->opt_range[key].rows &&
|
||||||
!(quick_key_map & ~table->const_key_parts[key]))
|
!(quick_key_map & ~table->const_key_parts[key]))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -9223,7 +9226,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
|
|||||||
However if sel becomes greater than 2 then with high probability
|
However if sel becomes greater than 2 then with high probability
|
||||||
something went wrong.
|
something went wrong.
|
||||||
*/
|
*/
|
||||||
sel /= (double)table->quick_rows[key] / (double) table->stat_records();
|
sel /= (double)table->opt_range[key].rows / (double) table->stat_records();
|
||||||
set_if_smaller(sel, 1.0);
|
set_if_smaller(sel, 1.0);
|
||||||
used_range_selectivity= true;
|
used_range_selectivity= true;
|
||||||
}
|
}
|
||||||
@@ -11721,7 +11724,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
|||||||
{
|
{
|
||||||
sel->needed_reg=tab->needed_reg;
|
sel->needed_reg=tab->needed_reg;
|
||||||
}
|
}
|
||||||
sel->quick_keys= tab->table->quick_keys;
|
sel->quick_keys= tab->table->opt_range_keys;
|
||||||
if (!sel->quick_keys.is_subset(tab->checked_keys) ||
|
if (!sel->quick_keys.is_subset(tab->checked_keys) ||
|
||||||
!sel->needed_reg.is_subset(tab->checked_keys))
|
!sel->needed_reg.is_subset(tab->checked_keys))
|
||||||
{
|
{
|
||||||
@@ -13483,14 +13486,14 @@ double JOIN_TAB::scan_time()
|
|||||||
get_delayed_table_estimates(table, &records, &read_time,
|
get_delayed_table_estimates(table, &records, &read_time,
|
||||||
&startup_cost);
|
&startup_cost);
|
||||||
found_records= records;
|
found_records= records;
|
||||||
table->quick_condition_rows= records;
|
table->opt_range_condition_rows= records;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
found_records= records= table->stat_records();
|
found_records= records= table->stat_records();
|
||||||
read_time= table->file->scan_time();
|
read_time= table->file->scan_time();
|
||||||
/*
|
/*
|
||||||
table->quick_condition_rows has already been set to
|
table->opt_range_condition_rows has already been set to
|
||||||
table->file->stats.records
|
table->file->stats.records
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
@@ -18262,6 +18265,7 @@ TABLE *Create_tmp_table::start(THD *thd,
|
|||||||
char *tmpname,path[FN_REFLEN];
|
char *tmpname,path[FN_REFLEN];
|
||||||
Field **reg_field;
|
Field **reg_field;
|
||||||
uint *blob_field;
|
uint *blob_field;
|
||||||
|
key_part_map *const_key_parts;
|
||||||
/* Treat sum functions as normal ones when loose index scan is used. */
|
/* Treat sum functions as normal ones when loose index scan is used. */
|
||||||
m_save_sum_fields|= param->precomputed_group_by;
|
m_save_sum_fields|= param->precomputed_group_by;
|
||||||
DBUG_ENTER("Create_tmp_table::start");
|
DBUG_ENTER("Create_tmp_table::start");
|
||||||
@@ -18358,6 +18362,7 @@ TABLE *Create_tmp_table::start(THD *thd,
|
|||||||
&m_group_buff, (m_group && ! m_using_unique_constraint ?
|
&m_group_buff, (m_group && ! m_using_unique_constraint ?
|
||||||
param->group_length : 0),
|
param->group_length : 0),
|
||||||
&m_bitmaps, bitmap_buffer_size(field_count)*6,
|
&m_bitmaps, bitmap_buffer_size(field_count)*6,
|
||||||
|
&const_key_parts, sizeof(*const_key_parts),
|
||||||
NullS))
|
NullS))
|
||||||
{
|
{
|
||||||
DBUG_RETURN(NULL); /* purecov: inspected */
|
DBUG_RETURN(NULL); /* purecov: inspected */
|
||||||
@@ -18375,12 +18380,15 @@ TABLE *Create_tmp_table::start(THD *thd,
|
|||||||
bzero((char*) reg_field, sizeof(Field*) * (field_count+1));
|
bzero((char*) reg_field, sizeof(Field*) * (field_count+1));
|
||||||
bzero((char*) m_default_field, sizeof(Field*) * (field_count));
|
bzero((char*) m_default_field, sizeof(Field*) * (field_count));
|
||||||
bzero((char*) m_from_field, sizeof(Field*) * field_count);
|
bzero((char*) m_from_field, sizeof(Field*) * field_count);
|
||||||
|
/* const_key_parts is used in sort_and_filter_keyuse */
|
||||||
|
bzero((char*) const_key_parts, sizeof(*const_key_parts));
|
||||||
|
|
||||||
table->mem_root= own_root;
|
table->mem_root= own_root;
|
||||||
mem_root_save= thd->mem_root;
|
mem_root_save= thd->mem_root;
|
||||||
thd->mem_root= &table->mem_root;
|
thd->mem_root= &table->mem_root;
|
||||||
|
|
||||||
table->field=reg_field;
|
table->field=reg_field;
|
||||||
|
table->const_key_parts= const_key_parts;
|
||||||
table->alias.set(table_alias->str, table_alias->length, table_alias_charset);
|
table->alias.set(table_alias->str, table_alias->length, table_alias_charset);
|
||||||
|
|
||||||
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
|
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
|
||||||
@@ -23403,9 +23411,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
|||||||
Otherwise, construct a ref access (todo: it's not clear what is the
|
Otherwise, construct a ref access (todo: it's not clear what is the
|
||||||
win in using ref access when we could use quick select also?)
|
win in using ref access when we could use quick select also?)
|
||||||
*/
|
*/
|
||||||
if ((table->quick_keys.is_set(new_ref_key) &&
|
if ((table->opt_range_keys.is_set(new_ref_key) &&
|
||||||
table->quick_key_parts[new_ref_key] > ref_key_parts) ||
|
table->opt_range[new_ref_key].key_parts > ref_key_parts) ||
|
||||||
!(tab->ref.key >= 0))
|
!(tab->ref.key >= 0))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
The range optimizer constructed QUICK_RANGE for ref_key, and
|
The range optimizer constructed QUICK_RANGE for ref_key, and
|
||||||
@@ -23508,7 +23516,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
|||||||
goto use_filesort;
|
goto use_filesort;
|
||||||
|
|
||||||
if (select && // psergey: why doesn't this use a quick?
|
if (select && // psergey: why doesn't this use a quick?
|
||||||
table->quick_keys.is_set(best_key) && best_key != ref_key)
|
table->opt_range_keys.is_set(best_key) && best_key != ref_key)
|
||||||
{
|
{
|
||||||
key_map tmp_map;
|
key_map tmp_map;
|
||||||
tmp_map.clear_all(); // Force the creation of quick select
|
tmp_map.clear_all(); // Force the creation of quick select
|
||||||
@@ -28064,14 +28072,14 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
|
|||||||
We need to adjust the estimates if we had a quick select (or ref(const)) on
|
We need to adjust the estimates if we had a quick select (or ref(const)) on
|
||||||
index keynr.
|
index keynr.
|
||||||
*/
|
*/
|
||||||
if (table->quick_keys.is_set(keynr))
|
if (table->opt_range_keys.is_set(keynr))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Start from quick select's rows and cost. These are always cheaper than
|
Start from quick select's rows and cost. These are always cheaper than
|
||||||
full index scan/cost.
|
full index scan/cost.
|
||||||
*/
|
*/
|
||||||
double best_rows= (double)table->quick_rows[keynr];
|
double best_rows= (double) table->opt_range[keynr].rows;
|
||||||
double best_cost= (double)table->quick_costs[keynr];
|
double best_cost= (double) table->opt_range[keynr].cost;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if ref(const) access was possible on this index.
|
Check if ref(const) access was possible on this index.
|
||||||
@@ -28098,8 +28106,8 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
|
|||||||
2. ref(const) uses fewer key parts, becasue there is a
|
2. ref(const) uses fewer key parts, becasue there is a
|
||||||
range_cond(key_part+1).
|
range_cond(key_part+1).
|
||||||
*/
|
*/
|
||||||
if (kp == table->quick_key_parts[keynr])
|
if (kp == table->opt_range[keynr].key_parts)
|
||||||
ref_rows= table->quick_rows[keynr];
|
ref_rows= table->opt_range[keynr].rows;
|
||||||
else
|
else
|
||||||
ref_rows= (ha_rows) table->key_info[keynr].actual_rec_per_key(kp-1);
|
ref_rows= (ha_rows) table->key_info[keynr].actual_rec_per_key(kp-1);
|
||||||
|
|
||||||
@@ -28185,7 +28193,7 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
|
|||||||
value for further use in QUICK_SELECT_DESC
|
value for further use in QUICK_SELECT_DESC
|
||||||
|
|
||||||
@note
|
@note
|
||||||
This function takes into account table->quick_condition_rows statistic
|
This function takes into account table->opt_range_condition_rows statistic
|
||||||
(that is calculated by the make_join_statistics function).
|
(that is calculated by the make_join_statistics function).
|
||||||
However, single table procedures such as mysql_update() and mysql_delete()
|
However, single table procedures such as mysql_update() and mysql_delete()
|
||||||
never call make_join_statistics, so they have to update it manually
|
never call make_join_statistics, so they have to update it manually
|
||||||
@@ -28220,7 +28228,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
|||||||
double fanout= 1;
|
double fanout= 1;
|
||||||
ha_rows table_records= table->stat_records();
|
ha_rows table_records= table->stat_records();
|
||||||
bool group= join && join->group && order == join->group_list;
|
bool group= join && join->group && order == join->group_list;
|
||||||
ha_rows refkey_rows_estimate= table->quick_condition_rows;
|
ha_rows refkey_rows_estimate= table->opt_range_condition_rows;
|
||||||
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
|
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
|
||||||
THD* thd= join ? join->thd : table->in_use;
|
THD* thd= join ? join->thd : table->in_use;
|
||||||
|
|
||||||
@@ -28274,7 +28282,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
|||||||
trace_cheaper_ordering.add("read_time", read_time);
|
trace_cheaper_ordering.add("read_time", read_time);
|
||||||
/*
|
/*
|
||||||
Calculate the selectivity of the ref_key for REF_ACCESS. For
|
Calculate the selectivity of the ref_key for REF_ACCESS. For
|
||||||
RANGE_ACCESS we use table->quick_condition_rows.
|
RANGE_ACCESS we use table->opt_range_condition_rows.
|
||||||
*/
|
*/
|
||||||
if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
|
if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
|
||||||
{
|
{
|
||||||
@@ -28285,9 +28293,9 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
|||||||
*/
|
*/
|
||||||
if (tab->ref.const_ref_part_map ==
|
if (tab->ref.const_ref_part_map ==
|
||||||
make_prev_keypart_map(tab->ref.key_parts) &&
|
make_prev_keypart_map(tab->ref.key_parts) &&
|
||||||
table->quick_keys.is_set(ref_key) &&
|
table->opt_range_keys.is_set(ref_key) &&
|
||||||
table->quick_key_parts[ref_key] == tab->ref.key_parts)
|
table->opt_range[ref_key].key_parts == tab->ref.key_parts)
|
||||||
refkey_rows_estimate= table->quick_rows[ref_key];
|
refkey_rows_estimate= table->opt_range[ref_key].rows;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const KEY *ref_keyinfo= table->key_info + ref_key;
|
const KEY *ref_keyinfo= table->key_info + ref_key;
|
||||||
@@ -28501,8 +28509,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
|||||||
possible_key.add("cause", "ref estimates better");
|
possible_key.add("cause", "ref estimates better");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (table->quick_keys.is_set(nr))
|
if (table->opt_range_keys.is_set(nr))
|
||||||
quick_records= table->quick_rows[nr];
|
quick_records= table->opt_range[nr].rows;
|
||||||
possible_key.add("records", quick_records);
|
possible_key.add("records", quick_records);
|
||||||
if (best_key < 0 ||
|
if (best_key < 0 ||
|
||||||
(select_limit <= MY_MIN(quick_records,best_records) ?
|
(select_limit <= MY_MIN(quick_records,best_records) ?
|
||||||
@@ -28597,7 +28605,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
|||||||
@note
|
@note
|
||||||
Side effects:
|
Side effects:
|
||||||
- may deallocate or deallocate and replace select->quick;
|
- may deallocate or deallocate and replace select->quick;
|
||||||
- may set table->quick_condition_rows and table->quick_rows[...]
|
- may set table->opt_range_condition_rows and table->quick_rows[...]
|
||||||
to table->file->stats.records.
|
to table->file->stats.records.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -28662,10 +28670,10 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
|
|||||||
{ // check if some index scan & LIMIT is more efficient than filesort
|
{ // check if some index scan & LIMIT is more efficient than filesort
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Update quick_condition_rows since single table UPDATE/DELETE procedures
|
Update opt_range_condition_rows since single table UPDATE/DELETE procedures
|
||||||
don't call make_join_statistics() and leave this variable uninitialized.
|
don't call make_join_statistics() and leave this variable uninitialized.
|
||||||
*/
|
*/
|
||||||
table->quick_condition_rows= table->stat_records();
|
table->opt_range_condition_rows= table->stat_records();
|
||||||
|
|
||||||
int key, direction;
|
int key, direction;
|
||||||
if (test_if_cheaper_ordering(NULL, order, table,
|
if (test_if_cheaper_ordering(NULL, order, table,
|
||||||
|
@@ -443,7 +443,7 @@ int mysql_update(THD *thd,
|
|||||||
|
|
||||||
/* Calculate "table->covering_keys" based on the WHERE */
|
/* Calculate "table->covering_keys" based on the WHERE */
|
||||||
table->covering_keys= table->s->keys_in_use;
|
table->covering_keys= table->s->keys_in_use;
|
||||||
table->quick_keys.clear_all();
|
table->opt_range_keys.clear_all();
|
||||||
|
|
||||||
query_plan.select_lex= thd->lex->first_select_lex();
|
query_plan.select_lex= thd->lex->first_select_lex();
|
||||||
query_plan.table= table;
|
query_plan.table= table;
|
||||||
@@ -577,7 +577,7 @@ int mysql_update(THD *thd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If running in safe sql mode, don't allow updates without keys */
|
/* If running in safe sql mode, don't allow updates without keys */
|
||||||
if (table->quick_keys.is_clear_all())
|
if (table->opt_range_keys.is_clear_all())
|
||||||
{
|
{
|
||||||
thd->set_status_no_index_used();
|
thd->set_status_no_index_used();
|
||||||
if (safe_update && !using_limit)
|
if (safe_update && !using_limit)
|
||||||
|
55
sql/table.cc
55
sql/table.cc
@@ -3976,6 +3976,15 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
|
|||||||
sizeof(Field*)))))
|
sizeof(Field*)))))
|
||||||
goto err; /* purecov: inspected */
|
goto err; /* purecov: inspected */
|
||||||
|
|
||||||
|
/* Allocate storage for range optimizer */
|
||||||
|
if (!multi_alloc_root(&outparam->mem_root,
|
||||||
|
&outparam->opt_range,
|
||||||
|
share->keys * sizeof(TABLE::OPT_RANGE),
|
||||||
|
&outparam->const_key_parts,
|
||||||
|
share->keys * sizeof(key_part_map),
|
||||||
|
NullS))
|
||||||
|
goto err;
|
||||||
|
|
||||||
outparam->field= field_ptr;
|
outparam->field= field_ptr;
|
||||||
|
|
||||||
record= (uchar*) outparam->record[0]-1; /* Fieldstart = 1 */
|
record= (uchar*) outparam->record[0]-1; /* Fieldstart = 1 */
|
||||||
@@ -5383,9 +5392,9 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
|
|||||||
range_rowid_filter_cost_info_ptr= NULL;
|
range_rowid_filter_cost_info_ptr= NULL;
|
||||||
range_rowid_filter_cost_info= NULL;
|
range_rowid_filter_cost_info= NULL;
|
||||||
vers_write= s->versioned;
|
vers_write= s->versioned;
|
||||||
quick_condition_rows=0;
|
opt_range_condition_rows=0;
|
||||||
no_cache= false;
|
no_cache= false;
|
||||||
initialize_quick_structures();
|
initialize_opt_range_structures();
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
/* used in RBR Triggers */
|
/* used in RBR Triggers */
|
||||||
master_had_triggers= 0;
|
master_had_triggers= 0;
|
||||||
@@ -7773,12 +7782,28 @@ void TABLE::restore_blob_values(String *blob_storage)
|
|||||||
|
|
||||||
bool TABLE::alloc_keys(uint key_count)
|
bool TABLE::alloc_keys(uint key_count)
|
||||||
{
|
{
|
||||||
key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count));
|
KEY *new_key_info;
|
||||||
|
key_part_map *new_const_key_parts;
|
||||||
|
DBUG_ASSERT(s->tmp_table == INTERNAL_TMP_TABLE);
|
||||||
|
|
||||||
|
if (!multi_alloc_root(&mem_root,
|
||||||
|
&new_key_info, sizeof(*key_info)*(s->keys+key_count),
|
||||||
|
&new_const_key_parts,
|
||||||
|
sizeof(*new_const_key_parts)*(s->keys+key_count),
|
||||||
|
NullS))
|
||||||
|
return TRUE;
|
||||||
if (s->keys)
|
if (s->keys)
|
||||||
memmove(key_info, s->key_info, sizeof(KEY)*s->keys);
|
{
|
||||||
s->key_info= key_info;
|
memmove(new_key_info, s->key_info, sizeof(*key_info) * s->keys);
|
||||||
|
memmove(new_const_key_parts, const_key_parts,
|
||||||
|
s->keys * sizeof(const_key_parts));
|
||||||
|
}
|
||||||
|
s->key_info= key_info= new_key_info;
|
||||||
|
const_key_parts= new_const_key_parts;
|
||||||
|
bzero((char*) (const_key_parts + s->keys),
|
||||||
|
sizeof(*const_key_parts) * key_count);
|
||||||
max_keys= s->keys+key_count;
|
max_keys= s->keys+key_count;
|
||||||
return !(key_info);
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -9898,20 +9923,18 @@ bool TABLE::export_structure(THD *thd, Row_definition_list *defs)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
@brief
|
@brief
|
||||||
Initialize all the quick structures that are used to stored the
|
Initialize all the opt_range structures that are used to stored the
|
||||||
estimates when the range optimizer is run.
|
estimates when the range optimizer is run.
|
||||||
@details
|
As these are initialized by the range optimizer for all index
|
||||||
This is specifically needed when we read the TABLE structure from the
|
marked in opt_range_keys, we only mark the memory as undefined
|
||||||
table cache. There can be some garbage data from previous queries
|
to be able to find wrong usage of data with valgrind or MSAN.
|
||||||
that need to be reset here.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void TABLE::initialize_quick_structures()
|
void TABLE::initialize_opt_range_structures()
|
||||||
{
|
{
|
||||||
bzero(quick_rows, sizeof(quick_rows));
|
TRASH_ALLOC(&opt_range_keys, sizeof(opt_range_keys));
|
||||||
bzero(quick_key_parts, sizeof(quick_key_parts));
|
TRASH_ALLOC(opt_range, s->keys * sizeof(*opt_range));
|
||||||
bzero(quick_costs, sizeof(quick_costs));
|
TRASH_ALLOC(const_key_parts, s->keys * sizeof(*const_key_parts));
|
||||||
bzero(quick_n_ranges, sizeof(quick_n_ranges));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
44
sql/table.h
44
sql/table.h
@@ -1257,8 +1257,7 @@ public:
|
|||||||
Map of keys that can be used to retrieve all data from this table
|
Map of keys that can be used to retrieve all data from this table
|
||||||
needed by the query without reading the row.
|
needed by the query without reading the row.
|
||||||
*/
|
*/
|
||||||
key_map covering_keys;
|
key_map covering_keys, intersect_keys;
|
||||||
key_map quick_keys, intersect_keys;
|
|
||||||
/*
|
/*
|
||||||
A set of keys that can be used in the query that references this
|
A set of keys that can be used in the query that references this
|
||||||
table.
|
table.
|
||||||
@@ -1340,28 +1339,29 @@ public:
|
|||||||
/* The estimate of the number of records in the table used by optimizer */
|
/* The estimate of the number of records in the table used by optimizer */
|
||||||
ha_rows used_stat_records;
|
ha_rows used_stat_records;
|
||||||
|
|
||||||
|
key_map opt_range_keys;
|
||||||
/*
|
/*
|
||||||
For each key that has quick_keys.is_set(key) == TRUE: estimate of #records
|
The following structure is filled for each key that has
|
||||||
and max #key parts that range access would use.
|
opt_range_keys.is_set(key) == TRUE
|
||||||
*/
|
*/
|
||||||
ha_rows quick_rows[MAX_KEY];
|
struct OPT_RANGE
|
||||||
uint quick_key_parts[MAX_KEY];
|
{
|
||||||
|
uint key_parts;
|
||||||
double quick_costs[MAX_KEY];
|
uint ranges;
|
||||||
|
ha_rows rows;
|
||||||
|
double cost;
|
||||||
|
/*
|
||||||
|
If there is a range access by i-th index then the cost of
|
||||||
|
index only access for it is stored in index_only_costs[i]
|
||||||
|
*/
|
||||||
|
double index_only_cost;
|
||||||
|
} *opt_range;
|
||||||
/*
|
/*
|
||||||
If there is a range access by i-th index then the cost of
|
Bitmaps of key parts that =const for the duration of join execution. If
|
||||||
index only access for it is stored in quick_index_only_costs[i]
|
we're in a subquery, then the constant may be different across subquery
|
||||||
|
re-executions.
|
||||||
*/
|
*/
|
||||||
double quick_index_only_costs[MAX_KEY];
|
key_part_map *const_key_parts;
|
||||||
|
|
||||||
/*
|
|
||||||
Bitmaps of key parts that =const for the duration of join execution. If
|
|
||||||
we're in a subquery, then the constant may be different across subquery
|
|
||||||
re-executions.
|
|
||||||
*/
|
|
||||||
key_part_map const_key_parts[MAX_KEY];
|
|
||||||
|
|
||||||
uint quick_n_ranges[MAX_KEY];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Estimate of number of records that satisfy SARGable part of the table
|
Estimate of number of records that satisfy SARGable part of the table
|
||||||
@@ -1371,7 +1371,7 @@ public:
|
|||||||
that will pass the table condition (condition that depends on fields of
|
that will pass the table condition (condition that depends on fields of
|
||||||
this table and constants)
|
this table and constants)
|
||||||
*/
|
*/
|
||||||
ha_rows quick_condition_rows;
|
ha_rows opt_range_condition_rows;
|
||||||
|
|
||||||
double cond_selectivity;
|
double cond_selectivity;
|
||||||
List<st_cond_statistic> *cond_selectivity_sampling_explain;
|
List<st_cond_statistic> *cond_selectivity_sampling_explain;
|
||||||
@@ -1637,7 +1637,7 @@ public:
|
|||||||
bool is_filled_at_execution();
|
bool is_filled_at_execution();
|
||||||
|
|
||||||
bool update_const_key_parts(COND *conds);
|
bool update_const_key_parts(COND *conds);
|
||||||
void initialize_quick_structures();
|
void initialize_opt_range_structures();
|
||||||
|
|
||||||
my_ptrdiff_t default_values_offset() const
|
my_ptrdiff_t default_values_offset() const
|
||||||
{ return (my_ptrdiff_t) (s->default_values - record[0]); }
|
{ return (my_ptrdiff_t) (s->default_values - record[0]); }
|
||||||
|
Reference in New Issue
Block a user