diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2020-06-02 10:25:11 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2020-06-02 10:25:11 +0300 |
commit | 8300f639a14afeb379c224e10dab9d99d31cecbd (patch) | |
tree | c67c0a5afc6062b99ec21194a99b81d3633f8c38 /sql | |
parent | 804761a844906c7f7bd11139a366a5dfc1d8bcd6 (diff) | |
parent | 50641db2d11ad8a2228f7938d851e52decb71a9b (diff) | |
download | mariadb-git-8300f639a14afeb379c224e10dab9d99d31cecbd.tar.gz |
Merge 10.2 into 10.3
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_xmlfunc.cc | 3 | ||||
-rw-r--r-- | sql/set_var.cc | 16 | ||||
-rw-r--r-- | sql/set_var.h | 3 | ||||
-rw-r--r-- | sql/sql_acl.cc | 170 | ||||
-rw-r--r-- | sql/sql_acl.h | 5 | ||||
-rw-r--r-- | sql/sql_statistics.cc | 306 | ||||
-rw-r--r-- | sql/sql_statistics.h | 1 | ||||
-rw-r--r-- | sql/table.cc | 4 | ||||
-rw-r--r-- | sql/table.h | 91 |
9 files changed, 292 insertions, 307 deletions
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 6017a0fa8af..c413969e3df 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -815,7 +815,6 @@ String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset) String *Item_nodeset_func_predicate::val_nodeset(String *str) { Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0]; - Item_func *comp_func= (Item_func*)args[1]; uint pos= 0, size; prepare(str); size= (uint)(fltend - fltbeg); @@ -825,7 +824,7 @@ String *Item_nodeset_func_predicate::val_nodeset(String *str) ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num, flt->pos, size); - if (comp_func->val_int()) + if (args[1]->val_int()) ((XPathFilter*)str)->append_element(flt->num, pos++); } return str; diff --git a/sql/set_var.cc b/sql/set_var.cc index 3711ec64aeb..eb2b9234db3 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -973,8 +973,17 @@ int set_var_default_role::check(THD *thd) { #ifndef NO_EMBEDDED_ACCESS_CHECKS real_user= get_current_user(thd, user); - int status= acl_check_set_default_role(thd, real_user->host.str, real_user->user.str); - return status; + real_role= role.str; + if (role.str == current_role.str) + { + if (!thd->security_ctx->priv_role[0]) + real_role= "NONE"; + else + real_role= thd->security_ctx->priv_role; + } + + return acl_check_set_default_role(thd, real_user->host.str, + real_user->user.str, real_role); #else return 0; #endif @@ -985,7 +994,8 @@ int set_var_default_role::update(THD *thd) #ifndef NO_EMBEDDED_ACCESS_CHECKS Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer; thd->m_reprepare_observer= 0; - int res= acl_set_default_role(thd, real_user->host.str, real_user->user.str, role.str); + int res= acl_set_default_role(thd, real_user->host.str, real_user->user.str, + real_role); thd->m_reprepare_observer= save_reprepare_observer; return res; #else diff --git a/sql/set_var.h b/sql/set_var.h index 7530d3eaff5..12e025e4696 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1,7 +1,7 @@ #ifndef SET_VAR_INCLUDED #define SET_VAR_INCLUDED /* Copyright (c) 2002, 2013, Oracle and/or its affiliates. - Copyright (c) 2009, 2014, SkySQL Ab. + Copyright (c) 2009, 2020, MariaDB 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 @@ -352,6 +352,7 @@ class set_var_default_role: public set_var_base { LEX_USER *user, *real_user; LEX_CSTRING role; + const char *real_role; public: set_var_default_role(LEX_USER *user_arg, LEX_CSTRING role_arg) : user(user_arg), role(role_arg) {} diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index e29fd0345da..58711d0a9d0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2018, Oracle and/or its affiliates. - Copyright (c) 2009, 2018, MariaDB + Copyright (c) 2009, 2020, MariaDB 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 @@ -84,7 +84,6 @@ LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") }; LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") }; LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") }; - #ifndef NO_EMBEDDED_ACCESS_CHECKS static plugin_ref old_password_plugin; #endif @@ -2520,8 +2519,43 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host, DBUG_RETURN(res); } -static int check_user_can_set_role(const char *user, const char *host, - const char *ip, const char *rolename, ulonglong *access) +static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data) +{ + LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data); + if (rolename->length == grantee->user.length && + !strcmp(rolename->str, grantee->user.str)) + return -1; // End search, we've found our role. + + /* Keep looking, we haven't found our role yet. */ + return 0; +} + +/* + unlike find_user_exact and find_user_wild, + this function finds anonymous users too, it's when a + user is not empty, but priv_user (acl_user->user) is empty. +*/ +static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip) +{ + ACL_USER *result= NULL; + mysql_mutex_assert_owner(&acl_cache->lock); + for (uint i=0; i < acl_users.elements; i++) + { + ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); + if ((!acl_user_tmp->user.str || + !strcmp(user, acl_user_tmp->user.str)) && + compare_hostname(&acl_user_tmp->host, host, ip)) + { + result= acl_user_tmp; + break; + } + } + return result; +} + +static int check_user_can_set_role(THD *thd, const char *user, const char *host, + const char *ip, const char *rolename, + ulonglong *access) { ACL_ROLE *role; ACL_USER_BASE *acl_user_base; @@ -2538,10 +2572,7 @@ static int check_user_can_set_role(const char *user, const char *host, /* get the current user */ acl_user= find_user_wild(host, user, ip); if (acl_user == NULL) - { - my_error(ER_INVALID_CURRENT_USER, MYF(0)); - result= -1; - } + result= ER_INVALID_CURRENT_USER; else if (access) *access= acl_user->access; @@ -2551,9 +2582,9 @@ static int check_user_can_set_role(const char *user, const char *host, role= find_acl_role(rolename); /* According to SQL standard, the same error message must be presented */ - if (role == NULL) { - my_error(ER_INVALID_ROLE, MYF(0), rolename); - result= -1; + if (role == NULL) + { + result= ER_INVALID_ROLE; goto end; } @@ -2574,7 +2605,6 @@ static int check_user_can_set_role(const char *user, const char *host, /* According to SQL standard, the same error message must be presented */ if (!is_granted) { - my_error(ER_INVALID_ROLE, MYF(0), rolename); result= 1; goto end; } @@ -2583,17 +2613,66 @@ static int check_user_can_set_role(const char *user, const char *host, { *access = acl_user->access | role->access; } + end: mysql_mutex_unlock(&acl_cache->lock); - return result; + /* We present different error messages depending if the user has sufficient + privileges to know if the INVALID_ROLE exists. */ + switch (result) + { + case ER_INVALID_CURRENT_USER: + my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); + break; + case ER_INVALID_ROLE: + /* Role doesn't exist at all */ + my_error(ER_INVALID_ROLE, MYF(0), rolename); + break; + case 1: + StringBuffer<1024> c_usr; + LEX_CSTRING role_lex; + /* First, check if current user can see mysql database. */ + bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1); + + role_lex.str= rolename; + role_lex.length= strlen(rolename); + mysql_mutex_lock(&acl_cache->lock); + ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host, + thd->security_ctx->priv_user, + thd->security_ctx->ip); + + /* If the current user does not have select priv to mysql database, + see if the current user can discover the role if it was granted to him. + */ + if (cur_user && (read_access || + traverse_role_graph_down(cur_user, &role_lex, + check_role_is_granted_callback, + NULL) == -1)) + { + /* Role is not granted but current user can see the role */ + c_usr.append(user, strlen(user)); + c_usr.append('@'); + c_usr.append(host, strlen(host)); + my_printf_error(ER_INVALID_ROLE, "User %`s has not been granted role %`s", + MYF(0), c_usr.c_ptr(), rolename); + } + else + { + /* Role is not granted and current user cannot see the role */ + my_error(ER_INVALID_ROLE, MYF(0), rolename); + } + mysql_mutex_unlock(&acl_cache->lock); + break; + } + + return result; } + int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access) { - /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */ - return check_user_can_set_role(thd->security_ctx->priv_user, - thd->security_ctx->host, thd->security_ctx->ip, rolename, access); + return check_user_can_set_role(thd, thd->security_ctx->priv_user, + thd->security_ctx->host, thd->security_ctx->ip, rolename, access); } @@ -3374,9 +3453,12 @@ WSREP_ERROR_LABEL: DBUG_RETURN(result); } -int acl_check_set_default_role(THD *thd, const char *host, const char *user) +int acl_check_set_default_role(THD *thd, const char *host, const char *user, + const char *role) { - return check_alter_user(thd, host, user); + DBUG_ENTER("acl_check_set_default_role"); + DBUG_RETURN(check_alter_user(thd, host, user) || + check_user_can_set_role(thd, user, host, NULL, role, NULL)); } int acl_set_default_role(THD *thd, const char *host, const char *user, @@ -3397,16 +3479,6 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'", safe_str(user), safe_str(host), safe_str(rolename))); - if (rolename == current_role.str) { - if (!thd->security_ctx->priv_role[0]) - rolename= "NONE"; - else - rolename= thd->security_ctx->priv_role; - } - - if (check_user_can_set_role(user, host, host, rolename, NULL)) - DBUG_RETURN(result); - if (!strcasecmp(rolename, "NONE")) clear_role= TRUE; @@ -3571,31 +3643,6 @@ bool is_acl_user(const char *host, const char *user) return res; } - -/* - unlike find_user_exact and find_user_wild, - this function finds anonymous users too, it's when a - user is not empty, but priv_user (acl_user->user) is empty. -*/ -static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip) -{ - ACL_USER *result= NULL; - mysql_mutex_assert_owner(&acl_cache->lock); - for (uint i=0; i < acl_users.elements; i++) - { - ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); - if ((!acl_user_tmp->user.str || - !strcmp(user, acl_user_tmp->user.str)) && - compare_hostname(&acl_user_tmp->host, host, ip)) - { - result= acl_user_tmp; - break; - } - } - return result; -} - - /* Find first entry that matches the specified user@host pair */ @@ -3900,7 +3947,7 @@ static bool test_if_create_new_users(THD *thd) if (!(db_access & INSERT_ACL)) { if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) - create_new_users=0; + create_new_users=0; } } return create_new_users; @@ -9225,17 +9272,6 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) mysql_mutex_unlock(&acl_cache->lock); } -static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data) -{ - LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data); - if (rolename->length == grantee->user.length && - !strcmp(rolename->str, grantee->user.str)) - return -1; // End search, we've found our role. - - /* Keep looking, we haven't found our role yet. */ - return 0; -} - /* Modify a privilege table. @@ -11125,7 +11161,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, Security context in THD contains two pairs of (user,host): 1. (user,host) pair referring to inbound connection. 2. (priv_user,priv_host) pair obtained from mysql.user table after doing - authnetication of incoming connection. + authentication of incoming connection. Privileges should be checked wrt (priv_user, priv_host) tuple, because (user,host) pair obtained from inbound connection may have different values than what is actually stored in mysql.user table and while granting @@ -11544,7 +11580,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) ulong j,test_access= want_access & ~GRANT_ACL; for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1) { - if (test_access & j) + if (test_access & j) { if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0, command_array[priv_id], diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 7989367ec44..dc8a085c96c 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -2,7 +2,7 @@ #define SQL_ACL_INCLUDED /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. - Copyright (c) 2017, MariaDB Corporation. + Copyright (c) 2017, 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 @@ -417,7 +417,8 @@ bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user, bool with_grant); int acl_setrole(THD *thd, const char *rolename, ulonglong access); int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access); -int acl_check_set_default_role(THD *thd, const char *host, const char *user); +int acl_check_set_default_role(THD *thd, const char *host, const char *user, + const char *role); int acl_set_default_role(THD *thd, const char *host, const char *user, const char *rolename); diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 83013b8b1e7..b65f460c896 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -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 @@ -2214,27 +2214,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; } @@ -2300,89 +2286,11 @@ 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 @@ -2923,15 +2831,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(); @@ -2948,7 +2867,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; @@ -3010,9 +2929,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); } @@ -3052,71 +2970,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 @param @@ -3150,26 +3003,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); @@ -3177,8 +3029,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); } @@ -3226,6 +3079,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]; @@ -3236,38 +3106,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); if (open_stat_tables(thd, stat_tables, &open_tables_backup, FALSE)) @@ -3275,32 +3149,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; } - } + } close_system_tables(thd, &open_tables_backup); diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index a69dd340dd7..c6b4fed30fa 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -268,7 +268,6 @@ public: Column_statistics *column_stats; /* Array of statistical data for columns */ Index_statistics *index_stats; /* Array of statistical data for indexes */ ulong *idx_avg_frequency; /* Array of records per key for index prefixes */ - ulong total_hist_size; /* Total size of all histograms */ uchar *histograms; /* Sequence of histograms */ }; diff --git a/sql/table.cc b/sql/table.cc index 7d09985094c..1877c3b7c76 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -443,10 +443,6 @@ void TABLE_SHARE::destroy() delete_stat_values_for_table_share(this); delete sequence; free_root(&stats_cb.mem_root, MYF(0)); - stats_cb.stats_can_be_read= FALSE; - stats_cb.stats_is_read= FALSE; - stats_cb.histograms_can_be_read= FALSE; - stats_cb.histograms_are_read= FALSE; /* The mutexes are initialized only for shares that are part of the TDC */ if (tmp_table == NO_TMP_TABLE) diff --git a/sql/table.h b/sql/table.h index 940f135bcf1..2bad6cbf32f 100644 --- a/sql/table.h +++ b/sql/table.h @@ -581,15 +581,94 @@ enum open_frm_error { from persistent statistical tables */ -struct TABLE_STATISTICS_CB +class TABLE_STATISTICS_CB { + class Statistics_state + { + enum state_codes + { + EMPTY, /** data is not loaded */ + LOADING, /** data is being loaded in some connection */ + READY /** data is loaded and available for use */ + }; + int32 state; + + public: + /** No state copy */ + Statistics_state &operator=(const Statistics_state &) { return *this; } + + /** Checks if data loading have been completed */ + bool is_ready() const + { + return my_atomic_load32_explicit(const_cast<int32*>(&state), + MY_MEMORY_ORDER_ACQUIRE) == READY; + } + + /** + Sets mutual exclusion for data loading + + If stats are in LOADING state, waits until state change. + + @return + @retval true atomic EMPTY -> LOADING transfer completed, ok to load + @retval false stats are in READY state, no need to load + */ + bool start_load() + { + for (;;) + { + int32 expected= EMPTY; + if (my_atomic_cas32_weak_explicit(&state, &expected, LOADING, + MY_MEMORY_ORDER_RELAXED, + MY_MEMORY_ORDER_RELAXED)) + return true; + if (expected == READY) + return false; + (void) LF_BACKOFF(); + } + } + + /** Marks data available for subsequent use */ + void end_load() + { + DBUG_ASSERT(my_atomic_load32_explicit(&state, MY_MEMORY_ORDER_RELAXED) == + LOADING); + my_atomic_store32_explicit(&state, READY, MY_MEMORY_ORDER_RELEASE); + } + + /** Restores empty state on error (e.g. OOM) */ + void abort_load() + { + DBUG_ASSERT(my_atomic_load32_explicit(&state, MY_MEMORY_ORDER_RELAXED) == + LOADING); + my_atomic_store32_explicit(&state, EMPTY, MY_MEMORY_ORDER_RELAXED); + } + }; + + class Statistics_state stats_state; + class Statistics_state hist_state; + +public: MEM_ROOT mem_root; /* MEM_ROOT to allocate statistical data for the table */ Table_statistics *table_stats; /* Structure to access the statistical data */ - bool stats_can_be_read; /* Memory for statistical data is allocated */ - bool stats_is_read; /* Statistical data for table has been read - from statistical tables */ - bool histograms_can_be_read; - bool histograms_are_read; + ulong total_hist_size; /* Total size of all histograms */ + + bool histograms_are_ready() const + { + return !total_hist_size || hist_state.is_ready(); + } + + bool start_histograms_load() + { + return total_hist_size && hist_state.start_load(); + } + + void end_histograms_load() { hist_state.end_load(); } + void abort_histograms_load() { hist_state.abort_load(); } + bool stats_are_ready() const { return stats_state.is_ready(); } + bool start_stats_load() { return stats_state.start_load(); } + void end_stats_load() { stats_state.end_load(); } + void abort_stats_load() { stats_state.abort_load(); } }; /** |