|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* Copyright (C) 2009 MySQL AB
|
|
|
|
Copyright (c) 2019, MariaDB Corporation. |
|
|
|
Copyright (c) 2019, 2020, MariaDB Corporation. |
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
|
@ -2231,27 +2231,13 @@ static int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *table_share) |
|
|
|
|
|
|
|
DBUG_ENTER("alloc_statistics_for_table_share"); |
|
|
|
|
|
|
|
DEBUG_SYNC(thd, "statistics_mem_alloc_start1"); |
|
|
|
DEBUG_SYNC(thd, "statistics_mem_alloc_start2"); |
|
|
|
|
|
|
|
mysql_mutex_lock(&table_share->LOCK_share); |
|
|
|
|
|
|
|
if (stats_cb->stats_can_be_read) |
|
|
|
{ |
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
DBUG_RETURN(0); |
|
|
|
} |
|
|
|
|
|
|
|
Table_statistics *table_stats= stats_cb->table_stats; |
|
|
|
if (!table_stats) |
|
|
|
{ |
|
|
|
table_stats= (Table_statistics *) alloc_root(&stats_cb->mem_root, |
|
|
|
sizeof(Table_statistics)); |
|
|
|
if (!table_stats) |
|
|
|
{ |
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
DBUG_RETURN(1); |
|
|
|
} |
|
|
|
memset(table_stats, 0, sizeof(Table_statistics)); |
|
|
|
stats_cb->table_stats= table_stats; |
|
|
|
} |
|
|
|
@ -2317,88 +2303,10 @@ static int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *table_share) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (column_stats && index_stats && idx_avg_frequency) |
|
|
|
stats_cb->stats_can_be_read= TRUE; |
|
|
|
|
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
|
|
|
|
DBUG_RETURN(0); |
|
|
|
DBUG_RETURN(column_stats && index_stats && idx_avg_frequency ? 0 : 1); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief |
|
|
|
Allocate memory for the histogram used by a table share |
|
|
|
|
|
|
|
@param |
|
|
|
thd Thread handler |
|
|
|
@param |
|
|
|
table_share Table share for which the memory for histogram data is allocated |
|
|
|
@param |
|
|
|
is_safe TRUE <-> at any time only one thread can perform the function |
|
|
|
|
|
|
|
@note |
|
|
|
The function allocates the memory for the histogram built for a table in the |
|
|
|
table's share memory with the intention to read the data there from the |
|
|
|
system persistent statistical table mysql.column_stats, |
|
|
|
The memory is allocated in the table_share's mem_root. |
|
|
|
If the parameter is_safe is TRUE then it is guaranteed that at any given time |
|
|
|
only one thread is executed the code of the function. |
|
|
|
|
|
|
|
@retval |
|
|
|
0 If the memory for all statistical data has been successfully allocated |
|
|
|
@retval |
|
|
|
1 Otherwise |
|
|
|
|
|
|
|
@note |
|
|
|
Currently the function always is called with the parameter is_safe set |
|
|
|
to FALSE. |
|
|
|
*/ |
|
|
|
|
|
|
|
static |
|
|
|
int alloc_histograms_for_table_share(THD* thd, TABLE_SHARE *table_share, |
|
|
|
bool is_safe) |
|
|
|
{ |
|
|
|
TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb; |
|
|
|
|
|
|
|
DBUG_ENTER("alloc_histograms_for_table_share"); |
|
|
|
|
|
|
|
if (!is_safe) |
|
|
|
mysql_mutex_lock(&table_share->LOCK_share); |
|
|
|
|
|
|
|
if (stats_cb->histograms_can_be_read) |
|
|
|
{ |
|
|
|
if (!is_safe) |
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
DBUG_RETURN(0); |
|
|
|
} |
|
|
|
|
|
|
|
Table_statistics *table_stats= stats_cb->table_stats; |
|
|
|
ulong total_hist_size= table_stats->total_hist_size; |
|
|
|
|
|
|
|
if (total_hist_size && !table_stats->histograms) |
|
|
|
{ |
|
|
|
uchar *histograms= (uchar *) alloc_root(&stats_cb->mem_root, |
|
|
|
total_hist_size); |
|
|
|
if (!histograms) |
|
|
|
{ |
|
|
|
if (!is_safe) |
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
DBUG_RETURN(1); |
|
|
|
} |
|
|
|
memset(histograms, 0, total_hist_size); |
|
|
|
table_stats->histograms= histograms; |
|
|
|
stats_cb->histograms_can_be_read= TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
if (!is_safe) |
|
|
|
mysql_mutex_unlock(&table_share->LOCK_share); |
|
|
|
|
|
|
|
DBUG_RETURN(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
@brief |
|
|
|
Initialize the aggregation fields to collect statistics on a column |
|
|
|
@ -2991,15 +2899,26 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) |
|
|
|
Field **field_ptr; |
|
|
|
KEY *key_info, *key_info_end; |
|
|
|
TABLE_SHARE *table_share= table->s; |
|
|
|
Table_statistics *read_stats= table_share->stats_cb.table_stats; |
|
|
|
enum_check_fields old_check_level= thd->count_cuted_fields; |
|
|
|
|
|
|
|
DBUG_ENTER("read_statistics_for_table"); |
|
|
|
DEBUG_SYNC(thd, "statistics_mem_alloc_start1"); |
|
|
|
DEBUG_SYNC(thd, "statistics_mem_alloc_start2"); |
|
|
|
|
|
|
|
if (!table_share->stats_cb.start_stats_load()) |
|
|
|
DBUG_RETURN(table_share->stats_cb.stats_are_ready() ? 0 : 1); |
|
|
|
|
|
|
|
if (alloc_statistics_for_table_share(thd, table_share)) |
|
|
|
{ |
|
|
|
table_share->stats_cb.abort_stats_load(); |
|
|
|
DBUG_RETURN(1); |
|
|
|
} |
|
|
|
|
|
|
|
/* Don't write warnings for internal field conversions */ |
|
|
|
thd->count_cuted_fields= CHECK_FIELD_IGNORE; |
|
|
|
|
|
|
|
/* Read statistics from the statistical table table_stats */ |
|
|
|
Table_statistics *read_stats= table_share->stats_cb.table_stats; |
|
|
|
stat_table= stat_tables[TABLE_STAT].table; |
|
|
|
Table_stat table_stat(stat_table, table); |
|
|
|
table_stat.set_key_fields(); |
|
|
|
@ -3016,7 +2935,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) |
|
|
|
column_stat.get_stat_values(); |
|
|
|
total_hist_size+= table_field->read_stats->histogram.get_size(); |
|
|
|
} |
|
|
|
read_stats->total_hist_size= total_hist_size; |
|
|
|
table_share->stats_cb.total_hist_size= total_hist_size; |
|
|
|
|
|
|
|
/* Read statistics from the statistical table index_stats */ |
|
|
|
stat_table= stat_tables[INDEX_STAT].table; |
|
|
|
@ -3078,9 +2997,8 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
table->stats_is_read= TRUE; |
|
|
|
thd->count_cuted_fields= old_check_level; |
|
|
|
|
|
|
|
table_share->stats_cb.end_stats_load(); |
|
|
|
DBUG_RETURN(0); |
|
|
|
} |
|
|
|
|
|
|
|
@ -3118,71 +3036,6 @@ void delete_stat_values_for_table_share(TABLE_SHARE *table_share) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief |
|
|
|
Check whether any statistics is to be read for tables from a table list |
|
|
|
|
|
|
|
@param |
|
|
|
thd The thread handle |
|
|
|
@param |
|
|
|
tables The tables list for whose tables the check is to be done |
|
|
|
|
|
|
|
@details |
|
|
|
The function checks whether for any of the tables opened and locked for |
|
|
|
a statement statistics from statistical tables is needed to be read. |
|
|
|
|
|
|
|
@retval |
|
|
|
TRUE statistics for any of the tables is needed to be read |
|
|
|
@retval |
|
|
|
FALSE Otherwise |
|
|
|
*/ |
|
|
|
|
|
|
|
static |
|
|
|
bool statistics_for_tables_is_needed(THD *thd, TABLE_LIST *tables) |
|
|
|
{ |
|
|
|
if (!tables) |
|
|
|
return FALSE; |
|
|
|
|
|
|
|
/*
|
|
|
|
Do not read statistics for any query that explicity involves |
|
|
|
statistical tables, failure to to do so we may end up |
|
|
|
in a deadlock. |
|
|
|
*/ |
|
|
|
|
|
|
|
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global) |
|
|
|
{ |
|
|
|
if (!tl->is_view_or_derived() && !is_temporary_table(tl) && tl->table) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= tl->table->s; |
|
|
|
if (table_share && |
|
|
|
table_share->table_category != TABLE_CATEGORY_USER |
|
|
|
&& is_stat_table(&tl->db, &tl->alias)) |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global) |
|
|
|
{ |
|
|
|
if (!tl->is_view_or_derived() && !is_temporary_table(tl) && tl->table) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= tl->table->s; |
|
|
|
if (table_share && |
|
|
|
table_share->stats_cb.stats_can_be_read && |
|
|
|
(!table_share->stats_cb.stats_is_read || |
|
|
|
(!table_share->stats_cb.histograms_are_read && |
|
|
|
thd->variables.optimizer_use_condition_selectivity > 3))) |
|
|
|
return TRUE; |
|
|
|
if (table_share->stats_cb.stats_is_read) |
|
|
|
tl->table->stats_is_read= TRUE; |
|
|
|
if (table_share->stats_cb.histograms_are_read) |
|
|
|
tl->table->histograms_are_read= TRUE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief |
|
|
|
Read histogram for a table from the persistent statistical tables |
|
|
|
@ -3218,26 +3071,25 @@ bool statistics_for_tables_is_needed(THD *thd, TABLE_LIST *tables) |
|
|
|
static |
|
|
|
int read_histograms_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= table->s; |
|
|
|
|
|
|
|
TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb; |
|
|
|
DBUG_ENTER("read_histograms_for_table"); |
|
|
|
|
|
|
|
if (!table_share->stats_cb.histograms_can_be_read) |
|
|
|
if (stats_cb->start_histograms_load()) |
|
|
|
{ |
|
|
|
(void) alloc_histograms_for_table_share(thd, table_share, FALSE); |
|
|
|
} |
|
|
|
if (table_share->stats_cb.histograms_can_be_read && |
|
|
|
!table_share->stats_cb.histograms_are_read) |
|
|
|
{ |
|
|
|
Field **field_ptr; |
|
|
|
uchar *histogram= table_share->stats_cb.table_stats->histograms; |
|
|
|
TABLE *stat_table= stat_tables[COLUMN_STAT].table; |
|
|
|
Column_stat column_stat(stat_table, table); |
|
|
|
for (field_ptr= table_share->field; *field_ptr; field_ptr++) |
|
|
|
uchar *histogram= (uchar *) alloc_root(&stats_cb->mem_root, |
|
|
|
stats_cb->total_hist_size); |
|
|
|
if (!histogram) |
|
|
|
{ |
|
|
|
stats_cb->abort_histograms_load(); |
|
|
|
DBUG_RETURN(1); |
|
|
|
} |
|
|
|
memset(histogram, 0, stats_cb->total_hist_size); |
|
|
|
|
|
|
|
Column_stat column_stat(stat_tables[COLUMN_STAT].table, table); |
|
|
|
for (Field **field_ptr= table->s->field; *field_ptr; field_ptr++) |
|
|
|
{ |
|
|
|
Field *table_field= *field_ptr; |
|
|
|
uint hist_size= table_field->read_stats->histogram.get_size(); |
|
|
|
if (hist_size) |
|
|
|
if (uint hist_size= table_field->read_stats->histogram.get_size()) |
|
|
|
{ |
|
|
|
column_stat.set_key_fields(table_field); |
|
|
|
table_field->read_stats->histogram.set_values(histogram); |
|
|
|
@ -3245,8 +3097,9 @@ int read_histograms_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) |
|
|
|
histogram+= hist_size; |
|
|
|
} |
|
|
|
} |
|
|
|
stats_cb->end_histograms_load(); |
|
|
|
} |
|
|
|
|
|
|
|
table->histograms_are_read= true; |
|
|
|
DBUG_RETURN(0); |
|
|
|
} |
|
|
|
|
|
|
|
@ -3294,6 +3147,23 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void dump_stats_from_share_to_table(TABLE *table) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= table->s; |
|
|
|
KEY *key_info= table_share->key_info; |
|
|
|
KEY *key_info_end= key_info + table_share->keys; |
|
|
|
KEY *table_key_info= table->key_info; |
|
|
|
for ( ; key_info < key_info_end; key_info++, table_key_info++) |
|
|
|
table_key_info->read_stats= key_info->read_stats; |
|
|
|
|
|
|
|
Field **field_ptr= table_share->field; |
|
|
|
Field **table_field_ptr= table->field; |
|
|
|
for ( ; *field_ptr; field_ptr++, table_field_ptr++) |
|
|
|
(*table_field_ptr)->read_stats= (*field_ptr)->read_stats; |
|
|
|
table->stats_is_read= true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int read_statistics_for_tables(THD *thd, TABLE_LIST *tables) |
|
|
|
{ |
|
|
|
TABLE_LIST stat_tables[STATISTICS_TABLES]; |
|
|
|
@ -3303,38 +3173,42 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables) |
|
|
|
if (thd->bootstrap || thd->variables.use_stat_tables == NEVER) |
|
|
|
DBUG_RETURN(0); |
|
|
|
|
|
|
|
bool found_stat_table= false; |
|
|
|
bool statistics_for_tables_is_needed= false; |
|
|
|
|
|
|
|
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global) |
|
|
|
{ |
|
|
|
if (tl->table) |
|
|
|
TABLE_SHARE *table_share; |
|
|
|
if (!tl->is_view_or_derived() && tl->table && (table_share= tl->table->s) && |
|
|
|
table_share->tmp_table == NO_TMP_TABLE) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= tl->table->s; |
|
|
|
if (table_share && table_share->table_category == TABLE_CATEGORY_USER && |
|
|
|
table_share->tmp_table == NO_TMP_TABLE) |
|
|
|
if (table_share->table_category == TABLE_CATEGORY_USER) |
|
|
|
{ |
|
|
|
if (table_share->stats_cb.stats_can_be_read || |
|
|
|
!alloc_statistics_for_table_share(thd, table_share)) |
|
|
|
if (table_share->stats_cb.stats_are_ready()) |
|
|
|
{ |
|
|
|
if (table_share->stats_cb.stats_can_be_read) |
|
|
|
{ |
|
|
|
KEY *key_info= table_share->key_info; |
|
|
|
KEY *key_info_end= key_info + table_share->keys; |
|
|
|
KEY *table_key_info= tl->table->key_info; |
|
|
|
for ( ; key_info < key_info_end; key_info++, table_key_info++) |
|
|
|
table_key_info->read_stats= key_info->read_stats; |
|
|
|
Field **field_ptr= table_share->field; |
|
|
|
Field **table_field_ptr= tl->table->field; |
|
|
|
for ( ; *field_ptr; field_ptr++, table_field_ptr++) |
|
|
|
(*table_field_ptr)->read_stats= (*field_ptr)->read_stats; |
|
|
|
tl->table->stats_is_read= table_share->stats_cb.stats_is_read; |
|
|
|
} |
|
|
|
if (!tl->table->stats_is_read) |
|
|
|
dump_stats_from_share_to_table(tl->table); |
|
|
|
tl->table->histograms_are_read= |
|
|
|
table_share->stats_cb.histograms_are_ready(); |
|
|
|
if (table_share->stats_cb.histograms_are_ready() || |
|
|
|
thd->variables.optimizer_use_condition_selectivity <= 3) |
|
|
|
continue; |
|
|
|
} |
|
|
|
statistics_for_tables_is_needed= true; |
|
|
|
} |
|
|
|
else if (is_stat_table(&tl->db, &tl->alias)) |
|
|
|
found_stat_table= true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_SYNC(thd, "statistics_read_start"); |
|
|
|
|
|
|
|
if (!statistics_for_tables_is_needed(thd, tables)) |
|
|
|
/*
|
|
|
|
Do not read statistics for any query that explicity involves |
|
|
|
statistical tables, failure to to do so we may end up |
|
|
|
in a deadlock. |
|
|
|
*/ |
|
|
|
if (found_stat_table || !statistics_for_tables_is_needed) |
|
|
|
DBUG_RETURN(0); |
|
|
|
|
|
|
|
start_new_trans new_trans(thd); |
|
|
|
@ -3344,32 +3218,22 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables) |
|
|
|
|
|
|
|
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global) |
|
|
|
{ |
|
|
|
if (!tl->is_view_or_derived() && !is_temporary_table(tl) && tl->table) |
|
|
|
{ |
|
|
|
TABLE_SHARE *table_share= tl->table->s; |
|
|
|
if (table_share && !(table_share->table_category == TABLE_CATEGORY_USER)) |
|
|
|
continue; |
|
|
|
|
|
|
|
if (table_share && |
|
|
|
table_share->stats_cb.stats_can_be_read && |
|
|
|
!table_share->stats_cb.stats_is_read) |
|
|
|
TABLE_SHARE *table_share; |
|
|
|
if (!tl->is_view_or_derived() && tl->table && (table_share= tl->table->s) && |
|
|
|
table_share->tmp_table == NO_TMP_TABLE && |
|
|
|
table_share->table_category == TABLE_CATEGORY_USER) |
|
|
|
{ |
|
|
|
if (!tl->table->stats_is_read) |
|
|
|
{ |
|
|
|
(void) read_statistics_for_table(thd, tl->table, stat_tables); |
|
|
|
table_share->stats_cb.stats_is_read= TRUE; |
|
|
|
if (!read_statistics_for_table(thd, tl->table, stat_tables)) |
|
|
|
dump_stats_from_share_to_table(tl->table); |
|
|
|
else |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (table_share->stats_cb.stats_is_read) |
|
|
|
tl->table->stats_is_read= TRUE; |
|
|
|
if (thd->variables.optimizer_use_condition_selectivity > 3 && |
|
|
|
table_share && table_share->stats_cb.stats_can_be_read && |
|
|
|
!table_share->stats_cb.histograms_are_read) |
|
|
|
{ |
|
|
|
if (thd->variables.optimizer_use_condition_selectivity > 3) |
|
|
|
(void) read_histograms_for_table(thd, tl->table, stat_tables); |
|
|
|
table_share->stats_cb.histograms_are_read= TRUE; |
|
|
|
} |
|
|
|
if (table_share->stats_cb.histograms_are_read) |
|
|
|
tl->table->histograms_are_read= TRUE; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
thd->commit_whole_transaction_and_close_tables(); |
|
|
|
new_trans.restore_old_transaction(); |
|
|
|
|