1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Bug #48321 CURRENT_USER() incorrectly replicated for DROP/RENAME USER;

REVOKE/GRANT; ALTER EVENT.

The following statements support the CURRENT_USER() where a user is needed.
  DROP USER 
  RENAME USER CURRENT_USER() ...
  GRANT ... TO CURRENT_USER()
  REVOKE ... FROM CURRENT_USER()
  ALTER DEFINER = CURRENT_USER() EVENT
but, When these statements are binlogged, CURRENT_USER() just is binlogged
as 'CURRENT_USER()', it is not expanded to the real user name. When slave 
executes the log event, 'CURRENT_USER()' is expand to the user of slave 
SQL thread, but SQL thread's user name always NULL. This breaks the replication.

After this patch, All above statements are rewritten when they are binlogged.
The CURRENT_USER() is expanded to the real user's name and host.
This commit is contained in:
2010-01-30 20:49:25 +08:00
parent fe7ad16bb4
commit 788c28aceb
13 changed files with 2319 additions and 104 deletions

View File

@ -194,6 +194,7 @@ static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static bool acl_write_bin_log(THD *thd, List <LEX_USER> &list, bool clear_error);
/*
Convert scrambled password to binary form, according to scramble type,
@ -3225,7 +3226,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (!result) /* success */
{
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
if (acl_write_bin_log(thd, user_list, TRUE))
result= -1;
}
rw_unlock(&LOCK_grant);
@ -3401,8 +3403,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
if (write_to_binlog)
{
if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
result= TRUE;
result|= acl_write_bin_log(thd, user_list, FALSE);
}
rw_unlock(&LOCK_grant);
@ -3531,7 +3532,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (!result)
{
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
result= acl_write_bin_log(thd, list, TRUE);
}
rw_unlock(&LOCK_grant);
@ -5663,9 +5664,9 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
}
static void append_user(String *str, LEX_USER *user)
static void append_user(String *str, LEX_USER *user, bool comma= TRUE)
{
if (str->length())
if (comma && str->length())
str->append(',');
str->append('\'');
str->append(user->user.str);
@ -5674,6 +5675,65 @@ static void append_user(String *str, LEX_USER *user)
str->append('\'');
}
/*
The operations(DROP, RENAME, REVOKE, GRANT) will cause inconsistency between
master and slave, when CURRENT_USER() is used. To solve this problem, we
construct a new binlog statement in which CURRENT_USER() is replaced by
the real user name and host name.
*/
static bool acl_write_bin_log(THD *thd, List <LEX_USER> &list, bool clear_error)
{
String log_query;
LEX *lex= thd->lex;
List_iterator <LEX_USER> user_list(list);
LEX_USER *user, *tmp_user;
if (!mysql_bin_log.is_open())
return FALSE;
if (log_query.append(lex->stmt_begin, lex->stmt_user_begin - lex->stmt_begin))
return TRUE;
while ((tmp_user= user_list++))
{
if (!(user= get_current_user(thd, tmp_user)))
continue;
/*
No User, but a password?
They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
Get the current user, and shallow-copy the new password to them!
*/
if (!tmp_user->user.str && tmp_user->password.str)
user->password= tmp_user->password;
if (log_query.append(" ", 1))
return TRUE;
append_user(&log_query, user, FALSE);
/* Only 'GRANT' have password */
if (user->password.str)
{
if (log_query.append(STRING_WITH_LEN(" IDENTIFIED BY ")) ||
log_query.append(STRING_WITH_LEN("PASSWORD ")) ||
log_query.append("'", 1) ||
log_query.append(user->password.str,
user->password.length) ||
log_query.append("'", 1))
return TRUE;
}
if (log_query.append(",", 1))
return TRUE;
}
/* It is binlogged only when at least one user is in the query */
if (log_query.c_ptr()[log_query.length()-1] == ',')
{
log_query.length(log_query.length()-1);
if (log_query.append(lex->stmt_user_end, lex->stmt_end - lex->stmt_user_end))
return TRUE;
return write_bin_log(thd, clear_error, log_query.c_ptr_safe(),
log_query.length()) != 0;
}
return FALSE;
}
/*
Create a list of users.
@ -5780,6 +5840,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
{
int result;
String wrong_users;
String log_query;
LEX_USER *user_name, *tmp_user_name;
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
@ -5809,6 +5870,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
log_query.append(STRING_WITH_LEN("DROP USER"));
while ((tmp_user_name= user_list++))
{
if (!(user_name= get_current_user(thd, tmp_user_name)))
@ -5816,6 +5878,17 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
result= TRUE;
continue;
}
/*
The operation will cause inconsistency between master and slave, when
CURRENT_USER() is used. To solve this problem, we construct a new
binlog statement in which CURRENT_USER() is replaced by the real user
name and host name.
*/
log_query.append(STRING_WITH_LEN(" "));
append_user(&log_query, user_name, FALSE);
log_query.append(STRING_WITH_LEN(","));
if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
{
append_user(&wrong_users, user_name);
@ -5834,7 +5907,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
if (some_users_deleted)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
{
if (log_query.c_ptr()[log_query.length()-1] == ',')
{
log_query.length(log_query.length()-1);
result|= write_bin_log(thd, FALSE, log_query.c_ptr_safe(), log_query.length());
}
}
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@ -5862,6 +5941,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
{
int result;
String wrong_users;
String log_query;
LEX_USER *user_from, *tmp_user_from;
LEX_USER *user_to, *tmp_user_to;
List_iterator <LEX_USER> user_list(list);
@ -5889,6 +5969,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
log_query.append(STRING_WITH_LEN("RENAME USER"));
while ((tmp_user_from= user_list++))
{
if (!(user_from= get_current_user(thd, tmp_user_from)))
@ -5904,6 +5985,18 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
}
DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
/*
The operation will cause inconsistency between master and slave, when
CURRENT_USER() is used. To solve this problem, we construct a new
binlog statement in which CURRENT_USER() is replaced by the real user
name and host name.
*/
log_query.append(STRING_WITH_LEN(" "));
append_user(&log_query, user_from, FALSE);
log_query.append(STRING_WITH_LEN(" TO "));
append_user(&log_query, user_to, FALSE);
log_query.append(STRING_WITH_LEN(","));
/*
Search all in-memory structures and grant tables
for a mention of the new user name.
@ -5925,9 +6018,15 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
if (result)
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
if (some_users_renamed && mysql_bin_log.is_open())
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
if (some_users_renamed)
{
if (log_query.c_ptr()[log_query.length()-1] == ',')
{
log_query.length(log_query.length()-1);
result|= write_bin_log(thd, FALSE, log_query.c_ptr_safe(), log_query.length());
}
}
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
@ -6117,8 +6216,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
VOID(pthread_mutex_unlock(&acl_cache->lock));
int binlog_error=
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
int binlog_error= 0;
if (acl_write_bin_log(thd, list, FALSE))
binlog_error= 1;
rw_unlock(&LOCK_grant);
close_thread_tables(thd);