summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc44
-rw-r--r--sql/item_jsonfunc.cc1
-rw-r--r--sql/log.h14
-rw-r--r--sql/log_event_server.cc37
-rw-r--r--sql/rpl_mi.cc8
-rw-r--r--sql/rpl_mi.h5
-rw-r--r--sql/sql_acl.cc423
-rw-r--r--sql/sql_handler.cc3
-rw-r--r--sql/sql_repl.cc10
-rw-r--r--sql/sql_select.cc23
-rw-r--r--sql/sql_show.cc7
-rw-r--r--sql/sql_table.cc15
-rw-r--r--sql/table.cc15
13 files changed, 368 insertions, 237 deletions
diff --git a/sql/item.cc b/sql/item.cc
index d696d532d80..2f9f6a25a7e 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3812,8 +3812,48 @@ void Item_string::print(String *str, enum_query_type query_type)
}
else
{
- // Caller wants a result in the charset of str_value.
- str_value.print(str);
+ /*
+ We're restoring a parse-able statement from an Item tree.
+ Make sure to revert character set conversions that previously
+ happened in the parser when Item_string was created.
+ */
+ if (print_introducer)
+ {
+ /*
+ Print the string as is, without conversion:
+ Strings with introducers are not converted in the parser.
+ */
+ str_value.print(str);
+ }
+ else
+ {
+ /*
+ Print the string with conversion.
+ Strings without introducers are converted in the parser,
+ from character_set_client to character_set_connection.
+
+ When restoring a CREATE VIEW statement,
+ - str_value.charsets() contains parse time character_set_connection
+ - str->charset() contains parse time character_set_client
+ So we convert the string back from parse-time character_set_connection
+ to parse time character_set_client.
+
+ In some cases, e.g. SHOW PROCEDURE CODE, it's also possible
+ that str->charset() is "utf8mb3" instead of parse time
+ character_set_client. In these cases we convert
+ here from the parse-time character_set_connection to utf8mb3.
+
+ QQ: perhaps the code behind SHOW PROCEDURE CODE should
+ also request the result in the parse-time character_set_client
+ (like the code restoring CREATE VIEW statements does),
+ rather than in utf8mb3:
+ - utf8mb3 does not work well with non-BMP characters (e.g. emoji).
+ - Simply changing utf8mb3 to utf8mb4 will not fully help:
+ some character sets have unassigned characters,
+ they get lost during during cs->utf8mb4->cs round trip.
+ */
+ str_value.print_with_conversion(str, str->charset());
+ }
}
str->append('\'');
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index cf00da79de9..ddf5fc32ea4 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -3569,6 +3569,7 @@ const char *Item_func_json_format::func_name() const
bool Item_func_json_format::fix_length_and_dec()
{
decimals= 0;
+ collation.set(args[0]->collation);
max_length= args[0]->max_length;
maybe_null= 1;
return FALSE;
diff --git a/sql/log.h b/sql/log.h
index e7df904d49e..900e7ef49b9 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -945,6 +945,20 @@ public:
void unlock_binlog_end_pos() { mysql_mutex_unlock(&LOCK_binlog_end_pos); }
mysql_mutex_t* get_binlog_end_pos_lock() { return &LOCK_binlog_end_pos; }
+ /*
+ Ensures the log's state is either LOG_OPEN or LOG_CLOSED. If something
+ failed along the desired path and left the log in invalid state, i.e.
+ LOG_TO_BE_OPENED, forces the state to be LOG_CLOSED.
+ */
+ void try_fix_log_state()
+ {
+ mysql_mutex_lock(get_log_lock());
+ /* Only change the log state if it is LOG_TO_BE_OPENED */
+ if (log_state == LOG_TO_BE_OPENED)
+ log_state= LOG_CLOSED;
+ mysql_mutex_unlock(get_log_lock());
+ }
+
int wait_for_update_binlog_end_pos(THD* thd, struct timespec * timeout);
/*
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 790d63502de..79f197d4ec3 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -5573,20 +5573,14 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
}
}
-#ifdef HAVE_QUERY_CACHE
-#ifdef WITH_WSREP
+#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
/*
Moved invalidation right before the call to rows_event_stmt_cleanup(),
to avoid query cache being polluted with stale entries,
*/
- if (! (WSREP(thd) && wsrep_thd_is_applying(thd)))
- {
-#endif /* WITH_WSREP */
- query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
-#endif
+ if (WSREP(thd) && wsrep_thd_is_applying(thd))
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
}
table= m_table= rgi->m_table_map.get_table(m_table_id);
@@ -5787,19 +5781,20 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
restore_empty_query_table_list(thd->lex);
#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
- if (WSREP(thd) && wsrep_thd_is_applying(thd))
- {
- query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
- }
+ if (WSREP(thd) && wsrep_thd_is_applying(thd))
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
- if (unlikely(get_flags(STMT_END_F) &&
- (error= rows_event_stmt_cleanup(rgi, thd))))
- slave_rows_error_report(ERROR_LEVEL,
- thd->is_error() ? 0 : error,
- rgi, thd, table,
- get_type_str(),
- RPL_LOG_NAME, log_pos);
+ if (get_flags(STMT_END_F))
+ {
+ if (unlikely((error= rows_event_stmt_cleanup(rgi, thd))))
+ slave_rows_error_report(ERROR_LEVEL, thd->is_error() ? 0 : error,
+ rgi, thd, table, get_type_str(),
+ RPL_LOG_NAME, log_pos);
+ if (thd->slave_thread)
+ free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
+ }
+
DBUG_RETURN(error);
err:
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 0649a8f05e8..17c9029e1a5 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -170,6 +170,8 @@ void Master_info::clear_in_memory_info(bool all)
{
port= MYSQL_PORT;
host[0] = 0; user[0] = 0; password[0] = 0;
+ domain_id_filter.clear_ids();
+ reset_dynamic(&ignore_server_ids);
}
}
@@ -1797,6 +1799,12 @@ void Domain_id_filter::reset_filter()
m_filter= false;
}
+void Domain_id_filter::clear_ids()
+{
+ reset_dynamic(&m_domain_ids[DO_DOMAIN_IDS]);
+ reset_dynamic(&m_domain_ids[IGNORE_DOMAIN_IDS]);
+}
+
/**
Update the do/ignore domain id filter lists.
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 4d47689ac18..b6ff69d1f64 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -79,6 +79,11 @@ public:
void reset_filter();
/*
+ Clear do_ids and ignore_ids to disable domain id filtering
+ */
+ void clear_ids();
+
+ /*
Update the do/ignore domain id filter lists.
@param do_ids [IN] domain ids to be kept
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 65f053aa09b..9d8d678b052 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -3359,21 +3359,22 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
const Account_options &options,
const privilege_t privileges)
{
+ ACL_USER_PARAM::AUTH *work_copy= NULL;
if (nauth)
{
- if (acl_user->nauth >= nauth)
- acl_user->nauth= nauth;
- else
- acl_user->alloc_auth(&acl_memroot, nauth);
+ if (!(work_copy= (ACL_USER_PARAM::AUTH*)
+ alloc_root(thd->mem_root, nauth * sizeof(ACL_USER_PARAM::AUTH))))
+ return 1;
USER_AUTH *auth= combo.auth;
for (uint i= 0; i < nauth; i++, auth= auth->next)
{
- acl_user->auth[i].plugin= auth->plugin;
- acl_user->auth[i].auth_string= safe_lexcstrdup_root(&acl_memroot, auth->auth_str);
- if (fix_user_plugin_ptr(acl_user->auth + i))
- acl_user->auth[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin);
- if (set_user_auth(thd, acl_user->user, acl_user->auth + i, auth->pwtext))
+ work_copy[i].plugin= auth->plugin;
+ work_copy[i].auth_string= safe_lexcstrdup_root(&acl_memroot,
+ auth->auth_str);
+ if (fix_user_plugin_ptr(work_copy + i))
+ work_copy[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin);
+ if (set_user_auth(thd, acl_user->user, work_copy + i, auth->pwtext))
return 1;
}
}
@@ -3401,11 +3402,34 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
if (options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
acl_user->account_locked= options.account_locked == ACCOUNTLOCK_LOCKED;
- /* Unexpire the user password */
+ if (thd->is_error())
+ {
+ // If something went wrong (including OOM) we will not spoil acl cache
+ return 1;
+ }
+ /* Unexpire the user password and copy AUTH (when no more errors possible)*/
if (nauth)
{
acl_user->password_expired= false;
- acl_user->password_last_changed= thd->query_start();;
+ acl_user->password_last_changed= thd->query_start();
+
+ if (acl_user->nauth >= nauth)
+ {
+ acl_user->nauth= nauth;
+ }
+ else
+ {
+ if (acl_user->alloc_auth(&acl_memroot, nauth))
+ {
+ /*
+ acl_user is a copy, so NULL assigned in case of an error do not
+ change the acl cache
+ */
+ return 1;
+ }
+ }
+ DBUG_ASSERT(work_copy); // allocated under the same condinition
+ memcpy(acl_user->auth, work_copy, nauth * sizeof(ACL_USER_PARAM::AUTH));
}
switch (options.password_expire) {
@@ -10195,8 +10219,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
LEX_USER *user_from, LEX_USER *user_to)
{
int result= 0;
- int idx;
int elements;
+ bool restart;
const char *UNINIT_VAR(user);
const char *UNINIT_VAR(host);
ACL_USER *acl_user= NULL;
@@ -10304,82 +10328,98 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
DBUG_RETURN(-1);
}
+
#ifdef EXTRA_DEBUG
DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
struct_no, user_from->user.str, user_from->host.str));
#endif
- /* Loop over all elements *backwards* (see the comment below). */
- for (idx= elements - 1; idx >= 0; idx--)
- {
- /*
- Get a pointer to the element.
- */
- switch (struct_no) {
- case USER_ACL:
- acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
- user= acl_user->user.str;
- host= acl_user->host.hostname;
- break;
+ /* Loop over elements backwards as it may reduce the number of mem-moves
+ for dynamic arrays.
- case DB_ACL:
- acl_db= &acl_dbs.at(idx);
- user= acl_db->user;
- host= acl_db->host.hostname;
+ We restart the loop, if we deleted or updated anything in a hash table
+ because calling my_hash_delete or my_hash_update shuffles elements indices
+ and we can miss some if we do only one scan.
+ */
+ do {
+ restart= false;
+ for (int idx= elements - 1; idx >= 0; idx--)
+ {
+ /*
+ Get a pointer to the element.
+ */
+ switch (struct_no) {
+ case USER_ACL:
+ acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
+ user= acl_user->user.str;
+ host= acl_user->host.hostname;
break;
- case COLUMN_PRIVILEGES_HASH:
- case PROC_PRIVILEGES_HASH:
- case FUNC_PRIVILEGES_HASH:
- case PACKAGE_SPEC_PRIVILEGES_HASH:
- case PACKAGE_BODY_PRIVILEGES_HASH:
- grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
- user= grant_name->user;
- host= grant_name->host.hostname;
- break;
+ case DB_ACL:
+ acl_db= &acl_dbs.at(idx);
+ user= acl_db->user;
+ host= acl_db->host.hostname;
+ break;
- case PROXY_USERS_ACL:
- acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
- user= acl_proxy_user->get_user();
- host= acl_proxy_user->get_host();
- break;
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
+ user= grant_name->user;
+ host= grant_name->host.hostname;
+ break;
- case ROLES_MAPPINGS_HASH:
- role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
- user= role_grant_pair->u_uname;
- host= role_grant_pair->u_hname;
- break;
+ case PROXY_USERS_ACL:
+ acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
+ user= acl_proxy_user->get_user();
+ host= acl_proxy_user->get_host();
+ break;
- default:
- DBUG_ASSERT(0);
- }
- if (! host)
- host= "";
+ case ROLES_MAPPINGS_HASH:
+ role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
+ user= role_grant_pair->u_uname;
+ host= role_grant_pair->u_hname;
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ }
+ if (! host)
+ host= "";
#ifdef EXTRA_DEBUG
- DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
- struct_no, idx, user, host));
+ DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
+ struct_no, idx, user, host));
#endif
- if (struct_no == ROLES_MAPPINGS_HASH)
- {
- const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
- if (user_from->is_role())
+ if (struct_no == ROLES_MAPPINGS_HASH)
{
- /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
- to check both the user field as well as the role field for a match.
+ const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
+ if (user_from->is_role())
+ {
+ /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
+ to check both the user field as well as the role field for a match.
- It is possible to have a role granted to a role. If we are going
- to modify the mapping entry, it needs to be done on either on the
- "user" end (here represented by a role) or the "role" end. At least
- one part must match.
+ It is possible to have a role granted to a role. If we are going
+ to modify the mapping entry, it needs to be done on either on the
+ "user" end (here represented by a role) or the "role" end. At least
+ one part must match.
- If the "user" end has a not-empty host string, it can never match
- as we are searching for a role here. A role always has an empty host
- string.
- */
- if ((*host || strcmp(user_from->user.str, user)) &&
- strcmp(user_from->user.str, role))
- continue;
+ If the "user" end has a not-empty host string, it can never match
+ as we are searching for a role here. A role always has an empty host
+ string.
+ */
+ if ((*host || strcmp(user_from->user.str, user)) &&
+ strcmp(user_from->user.str, role))
+ continue;
+ }
+ else
+ {
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
+ }
}
else
{
@@ -10387,157 +10427,134 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
my_strcasecmp(system_charset_info, user_from->host.str, host))
continue;
}
- }
- else
- {
- if (strcmp(user_from->user.str, user) ||
- my_strcasecmp(system_charset_info, user_from->host.str, host))
- continue;
- }
- result= 1; /* At least one element found. */
- if ( drop )
- {
- elements--;
- switch ( struct_no ) {
- case USER_ACL:
- free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
- delete_dynamic_element(&acl_users, idx);
- break;
+ result= 1; /* At least one element found. */
+ if ( drop )
+ {
+ elements--;
+ switch ( struct_no ) {
+ case USER_ACL:
+ free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
+ delete_dynamic_element(&acl_users, idx);
+ break;
- case DB_ACL:
- acl_dbs.del(idx);
- break;
+ case DB_ACL:
+ acl_dbs.del(idx);
+ break;
- case COLUMN_PRIVILEGES_HASH:
- case PROC_PRIVILEGES_HASH:
- case FUNC_PRIVILEGES_HASH:
- case PACKAGE_SPEC_PRIVILEGES_HASH:
- case PACKAGE_BODY_PRIVILEGES_HASH:
- my_hash_delete(grant_name_hash, (uchar*) grant_name);
- /*
- In our HASH implementation on deletion one elements
- is moved into a place where a deleted element was,
- and the last element is moved into the empty space.
- Thus we need to re-examine the current element, but
- we don't have to restart the search from the beginning.
- */
- if (idx != elements)
- idx++;
- break;
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ my_hash_delete(grant_name_hash, (uchar*) grant_name);
+ restart= true;
+ break;
- case PROXY_USERS_ACL:
- delete_dynamic_element(&acl_proxy_users, idx);
- break;
+ case PROXY_USERS_ACL:
+ delete_dynamic_element(&acl_proxy_users, idx);
+ break;
- case ROLES_MAPPINGS_HASH:
- my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
- if (idx != elements)
- idx++;
- break;
+ case ROLES_MAPPINGS_HASH:
+ my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
+ restart= true;
+ break;
- default:
- DBUG_ASSERT(0);
- break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
}
- }
- else if ( user_to )
- {
- switch ( struct_no ) {
- case USER_ACL:
- acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
- update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
- acl_user->hostname_length= strlen(acl_user->host.hostname);
- break;
-
- case DB_ACL:
- acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
- update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
- break;
+ else if ( user_to )
+ {
+ switch ( struct_no ) {
+ case USER_ACL:
+ acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
+ update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
+ acl_user->hostname_length= strlen(acl_user->host.hostname);
+ break;
- case COLUMN_PRIVILEGES_HASH:
- case PROC_PRIVILEGES_HASH:
- case FUNC_PRIVILEGES_HASH:
- case PACKAGE_SPEC_PRIVILEGES_HASH:
- case PACKAGE_BODY_PRIVILEGES_HASH:
- {
- /*
- Save old hash key and its length to be able to properly update
- element position in hash.
- */
- char *old_key= grant_name->hash_key;
- size_t old_key_length= grant_name->key_length;
+ case DB_ACL:
+ acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
+ update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
+ break;
- /*
- Update the grant structure with the new user name and host name.
- */
- grant_name->set_user_details(user_to->host.str, grant_name->db,
- user_to->user.str, grant_name->tname,
- TRUE);
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
+ case PACKAGE_SPEC_PRIVILEGES_HASH:
+ case PACKAGE_BODY_PRIVILEGES_HASH:
+ {
+ /*
+ Save old hash key and its length to be able to properly update
+ element position in hash.
+ */
+ char *old_key= grant_name->hash_key;
+ size_t old_key_length= grant_name->key_length;
+
+ /*
+ Update the grant structure with the new user name and host name.
+ */
+ grant_name->set_user_details(user_to->host.str, grant_name->db,
+ user_to->user.str, grant_name->tname,
+ TRUE);
+
+ /*
+ Since username is part of the hash key, when the user name
+ is renamed, the hash key is changed. Update the hash to
+ ensure that the position matches the new hash key value
+ */
+ my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
+ old_key_length);
+ restart= true;
+ break;
+ }
- /*
- Since username is part of the hash key, when the user name
- is renamed, the hash key is changed. Update the hash to
- ensure that the position matches the new hash key value
- */
- my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
- old_key_length);
- /*
- hash_update() operation could have moved element from the tail or
- the head of the hash to the current position. But it can never
- move an element from the head to the tail or from the tail to the
- head over the current element.
- So we need to examine the current element once again, but
- we don't need to restart the search from the beginning.
- */
- idx++;
+ case PROXY_USERS_ACL:
+ acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
+ acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
break;
- }
- case PROXY_USERS_ACL:
- acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
- acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
- break;
+ case ROLES_MAPPINGS_HASH:
+ {
+ /*
+ Save old hash key and its length to be able to properly update
+ element position in hash.
+ */
+ char *old_key= role_grant_pair->hashkey.str;
+ size_t old_key_length= role_grant_pair->hashkey.length;
+ bool oom;
+
+ if (user_to->is_role())
+ oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
+ role_grant_pair->u_hname,
+ user_to->user.str, false);
+ else
+ oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
+ user_to->host.str,
+ role_grant_pair->r_uname, false);
+ if (oom)
+ DBUG_RETURN(-1);
+
+ my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
+ (uchar*) old_key, old_key_length);
+ restart= true;
+ break;
+ }
- case ROLES_MAPPINGS_HASH:
- {
- /*
- Save old hash key and its length to be able to properly update
- element position in hash.
- */
- char *old_key= role_grant_pair->hashkey.str;
- size_t old_key_length= role_grant_pair->hashkey.length;
- bool oom;
-
- if (user_to->is_role())
- oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
- role_grant_pair->u_hname,
- user_to->user.str, false);
- else
- oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
- user_to->host.str,
- role_grant_pair->r_uname, false);
- if (oom)
- DBUG_RETURN(-1);
-
- my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
- (uchar*) old_key, old_key_length);
- idx++; // see the comment above
+ default:
+ DBUG_ASSERT(0);
break;
}
- default:
- DBUG_ASSERT(0);
+ }
+ else
+ {
+ /* If search is requested, we do not need to search further. */
break;
}
-
}
- else
- {
- /* If search is requested, we do not need to search further. */
- break;
- }
- }
+ } while (restart);
#ifdef EXTRA_DEBUG
DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
#endif
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 73bfb8227e1..eab33a79b38 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -784,6 +784,9 @@ retry:
if (!(handler= mysql_ha_find_handler(thd, &tables->alias)))
goto err0;
+ if (thd->transaction->xid_state.check_has_uncommitted_xa())
+ goto err0;
+
table= handler->table;
tables->table= table; // This is used by fix_fields
table->pos_in_table_list= tables;
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index c49ea7d5908..e15980e9b3c 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -3949,6 +3949,16 @@ err:
mi->unlock_slave_threads();
if (ret == FALSE)
my_ok(thd);
+ else
+ {
+ /*
+ Depending on where CHANGE MASTER failed, the logs may be waiting to be
+ reopened. This would break future log updates and CHANGE MASTER calls.
+ `try_fix_log_state()` allows the relay log to fix its state to no longer
+ expect to be reopened.
+ */
+ mi->rli.relay_log.try_fix_log_state();
+ }
DBUG_RETURN(ret);
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index ec1630b01b0..e90fe4079fd 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -27971,6 +27971,11 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
//Item List
bool first= 1;
+ /*
+ outer_select() can not be used here because it is for name resolution
+ and will return NULL at any end of name resolution chain (view/derived)
+ */
+ bool top_level= (get_master()->get_master() == 0);
List_iterator_fast<Item> it(item_list);
Item *item;
while ((item= it++))
@@ -27980,7 +27985,8 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
else
str->append(',');
- if (is_subquery_function() && item->is_autogenerated_name())
+ if ((is_subquery_function() && item->is_autogenerated_name()) ||
+ !item->name.str)
{
/*
Do not print auto-generated aliases in subqueries. It has no purpose
@@ -27989,7 +27995,20 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
item->print(str, query_type);
}
else
- item->print_item_w_name(str, query_type);
+ {
+ /*
+ Do not print illegal names (if it is not top level SELECT).
+ Top level view checked (and correct name are assigned),
+ other cases of top level SELECT are not important, because
+ it is not "table field".
+ */
+ if (top_level ||
+ !item->is_autogenerated_name() ||
+ !check_column_name(item->name.str))
+ item->print_item_w_name(str, query_type);
+ else
+ item->print(str, query_type);
+ }
}
/*
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 04c2e959a3c..f4fc8d2decf 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -4450,7 +4450,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names,
if (!lookup_field_vals->wild_table_value &&
lookup_field_vals->table_value.str)
{
- if (lookup_field_vals->table_value.length > NAME_LEN)
+ if (check_table_name(lookup_field_vals->table_value.str,
+ lookup_field_vals->table_value.length,
+ false))
{
/*
Impossible value for a table name,
@@ -4487,6 +4489,9 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names,
return (schema_tables_add(thd, table_names,
lookup_field_vals->table_value.str));
+ if (check_db_name((LEX_STRING*)db_name))
+ return 0; // Impossible TABLE_SCHEMA name
+
find_files_result res= find_files(thd, table_names, db_name, path,
&lookup_field_vals->table_value);
if (res != FIND_FILES_OK)
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index d28ba06e963..92fd6dd3ea6 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4229,8 +4229,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
/* Align key length to multibyte char boundary */
key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
}
- else
- is_hash_field_needed= true;
}
}
// Catch invalid use of partial keys
@@ -4276,11 +4274,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
else
{
- if (key->type == Key::UNIQUE)
- {
- is_hash_field_needed= true;
- }
- else
+ if (key->type != Key::UNIQUE)
{
key_part_length= MY_MIN(max_key_length, file->max_key_part_length());
my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
@@ -4289,6 +4283,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
+ if (key->type == Key::UNIQUE
+ && key_part_length > MY_MIN(max_key_length,
+ file->max_key_part_length()))
+ is_hash_field_needed= true;
+
/* We can not store key_part_length more then 2^16 - 1 in frm */
if (is_hash_field_needed && column->length > UINT_MAX16)
{
@@ -4323,7 +4322,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
unique_key=1;
key_info->key_length=(uint16) key_length;
if (key_info->key_length > max_key_length && key->type == Key::UNIQUE)
- is_hash_field_needed= true;
+ is_hash_field_needed= true; // for case "a BLOB UNIQUE"
if (key_length > max_key_length && key->type != Key::FULLTEXT &&
!is_hash_field_needed)
{
diff --git a/sql/table.cc b/sql/table.cc
index ba7461ed7a5..e06c4db81f0 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4946,6 +4946,21 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
if (check_for_path_chars &&
(*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR))
return 1;
+ /*
+ We don't allow zero byte in table/schema names:
+ - Some code still uses NULL-terminated strings.
+ Zero bytes will confuse this code.
+ - There is a little practical use of zero bytes in names anyway.
+ Note, if the string passed as "name" comes here
+ from the parser as an identifier, it does not contain zero bytes,
+ as the parser rejects zero bytes in identifiers.
+ But "name" can also come here from queries like this:
+ SELECT * FROM I_S.TABLES WHERE TABLE_NAME='str';
+ In this case "name" is a general string expression
+ and it can have any arbitrary bytes, including zero bytes.
+ */
+ if (*name == 0x00)
+ return 1;
name++;
name_length++;
}