diff options
author | unknown <Li-Bing.Song@sun.com> | 2010-06-27 12:42:06 +0800 |
---|---|---|
committer | unknown <Li-Bing.Song@sun.com> | 2010-06-27 12:42:06 +0800 |
commit | 451cea3f62a1b53c441995c8e1f052875c4fb3bd (patch) | |
tree | 81bce2c4d3e4822ad29ea031ab6cf72a5ecc3413 | |
parent | b4593605e0604f22bec09981709a202c809516fc (diff) | |
download | mariadb-git-451cea3f62a1b53c441995c8e1f052875c4fb3bd.tar.gz |
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() EVENTbut, 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, session's user will be written into query log events
if these statements call CURREN_USER() or 'ALTER EVENT' does not assign a definer.
mysql-test/include/diff_tables.inc:
Expend its abilities.
Now it can diff not only in sessions of 'master' and 'slave', but
other sessions as well.
mysql-test/include/rpl_diff_tables.inc:
Diff the same table between master and slaves.
sql/log_event.cc:
session's user will be written into Query_log_event, if is_current_user_used() is TRUE.
On slave SQL thread, Only thd->variables.current_user is written into Query_log_event,
if it exists.
sql/sql_acl.cc:
On slave SQL thread, grantor should copy from thd->variables.current_user, if it exists
sql/sql_class.h:
On slave SQL thread, thd->variables.current_user is used to store the applying event's
invoker.
-rw-r--r-- | mysql-test/include/diff_tables.inc | 18 | ||||
-rw-r--r-- | mysql-test/include/rpl_diff_tables.inc | 35 | ||||
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_current_user.result | 205 | ||||
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result | 2 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_current_user.cnf | 9 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_current_user.test | 238 | ||||
-rw-r--r-- | sql/log_event.cc | 67 | ||||
-rw-r--r-- | sql/log_event.h | 7 | ||||
-rw-r--r-- | sql/sql_acl.cc | 23 | ||||
-rw-r--r-- | sql/sql_class.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 16 | ||||
-rw-r--r-- | sql/sql_parse.cc | 2 |
12 files changed, 602 insertions, 21 deletions
diff --git a/mysql-test/include/diff_tables.inc b/mysql-test/include/diff_tables.inc index d15dd56b35d..e11e69b3023 100644 --- a/mysql-test/include/diff_tables.inc +++ b/mysql-test/include/diff_tables.inc @@ -64,17 +64,13 @@ let $_diff_table=$diff_table_2; let $_diff_i=2; while ($_diff_i) { - # Parse out any leading "master:" or "slave:" from the table - # specification and connect the appropriate server. - let $_diff_conn_master=`SELECT SUBSTR('$_diff_table', 1, 7) = 'master:'`; - if ($_diff_conn_master) { - let $_diff_table=`SELECT SUBSTR('$_diff_table', 8)`; - connection master; - } - let $_diff_conn_slave=`SELECT SUBSTR('$_diff_table', 1, 6) = 'slave:'`; - if ($_diff_conn_slave) { - let $_diff_table=`SELECT SUBSTR('$_diff_table', 7)`; - connection slave; + # Parse out any leading "master:" or "slave:" from the table specification +# and connect the appropriate server. + let $_pos= `SELECT LOCATE(':', '$_diff_table')`; + let $_diff_conn=`SELECT SUBSTR('$_diff_table', 1, $_pos-1)`; + if (`SELECT "XX$_diff_conn" <> "XX"`) { + let $_diff_table=`SELECT SUBSTR('$_diff_table', $_pos+1)`; + connection $_diff_conn; } # Sanity-check the input. diff --git a/mysql-test/include/rpl_diff_tables.inc b/mysql-test/include/rpl_diff_tables.inc new file mode 100644 index 00000000000..c3a45578a79 --- /dev/null +++ b/mysql-test/include/rpl_diff_tables.inc @@ -0,0 +1,35 @@ +# ############################################################################# +# Check whether the given table is consistent between different master and +# slaves +# +# Usage: +# --let $diff_table= test.t1 +# --let $diff_server_list= master, slave, slave2 +# --source include/rpl_diff_tables.inc +# ############################################################################# + +if (`SELECT "XX$diff_table" = "XX"`) +{ + --die diff_table is null. +} + +--let $_servers= master, slave +if (`SELECT "XX$diff_server_list" <> "XX"`) +{ + --let $_servers= $diff_server_list +} + +--let $_master= `SELECT SUBSTRING_INDEX('$_servers', ',', 1)` +--let $_servers= `SELECT LTRIM(SUBSTRING('$_servers', LENGTH('$_master') + 2))` +connection $_master; +while (`SELECT "XX$_servers" <> "XX"`) +{ + --let $_slave= `SELECT SUBSTRING_INDEX('$_servers', ',', 1)` + --let $_servers= `SELECT LTRIM(SUBSTRING('$_servers', LENGTH('$_slave') + 2))` + + --sync_slave_with_master $_slave + --let $diff_table_1= $_master:$diff_table + --let $diff_table_2= $_slave:$diff_table + --source include/diff_tables.inc + connection $_slave; +} diff --git a/mysql-test/suite/rpl/r/rpl_current_user.result b/mysql-test/suite/rpl/r/rpl_current_user.result new file mode 100644 index 00000000000..85490c2571c --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_current_user.result @@ -0,0 +1,205 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; + +# On slave2 +# Connect slave2 to slave +CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=SLAVE_MYPORT;, +MASTER_LOG_FILE='slave-bin.000001', MASTER_USER='root'; +START SLAVE; + +# [On master] +DROP VIEW IF EXISTS v_user; +DROP VIEW IF EXISTS v_tables_priv; +DROP VIEW IF EXISTS v_procs_priv; +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS my_grant; +DROP PROCEDURE IF EXISTS my_revoke; +DROP FUNCTION IF EXISTS my_user; +DROP EVENT IF EXISTS e1; +CREATE TABLE t1(c1 char(100)); +CREATE VIEW test.v_user AS SELECT * FROM mysql.user WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_tables_priv AS SELECT * FROM mysql.tables_priv WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_procs_priv AS SELECT * FROM mysql.procs_priv WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_event AS SELECT definer FROM mysql.event WHERE name = 'e1'; +CREATE PROCEDURE p1() SELECT 1; +# bug48321_1-01234 has the max length(16) of user. +GRANT ALL PRIVILEGES ON *.* TO 'bug48321_1-01234'@'localhost' WITH GRANT OPTION; + +# Make sure the max lengths of user and host +# the user name is too lengh +GRANT CREATE USER ON *.* TO '01234567890123456'@'fakehost'; +ERROR HY000: String '01234567890123456' is too long for user name (should be no longer than 16) +# the host name is too lengh +GRANT CREATE USER ON *.* TO 'fakename'@'0123456789012345678901234567890123456789012345678901234567890'; +ERROR HY000: String '0123456789012345678901234567890123456789012345678901234567890' is too long for host name (should be no longer than 60) + +# User 'bug48321_1-01234' connects to master by conn1 +# [On conn1] +# Verify 'REVOKE ALL' statement +REVOKE ALL PRIVILEGES, GRANT OPTION FROM CURRENT_USER(); +Comparing tables master:test.v_user and slave:test.v_user +Comparing tables master:test.v_user and slave2:test.v_user + +# Verify 'GRANT ... ON TABLE ...' statement +GRANT CREATE, INSERT, SELECT ON TABLE test.t1 TO CURRENT_USER(); +Comparing tables master:test.v_tables_priv and slave:test.v_tables_priv +Comparing tables master:test.v_tables_priv and slave2:test.v_tables_priv + +# Verify 'GRANT ... ON PROCEDURE...' statement +GRANT ALTER ROUTINE, EXECUTE ON PROCEDURE p1 TO CURRENT_USER(); +Comparing tables master:test.v_procs_priv and slave:test.v_procs_priv +Comparing tables master:test.v_procs_priv and slave2:test.v_procs_priv + +# Verify 'GRANT ... ON *.* ...' statement +GRANT ALL PRIVILEGES ON *.* TO CURRENT_USER() WITH GRANT OPTION; +Comparing tables master:test.v_procs_priv and slave:test.v_procs_priv +Comparing tables master:test.v_procs_priv and slave2:test.v_procs_priv + +# Verify 'REVOKE ... ON TABLE ...' statement +REVOKE CREATE, INSERT, SELECT ON TABLE t1 FROM CURRENT_USER(); +Comparing tables master:test.v_tables_priv and slave:test.v_tables_priv +Comparing tables master:test.v_tables_priv and slave2:test.v_tables_priv + +# Verify 'REVOKE ... ON PROCEDURE...' statement +REVOKE ALTER ROUTINE, EXECUTE ON PROCEDURE p1 FROM CURRENT_USER(); +Comparing tables master:test.v_procs_priv and slave:test.v_procs_priv +Comparing tables master:test.v_procs_priv and slave2:test.v_procs_priv + +# Verify 'REVOKE ... ON *.* ...' statement +REVOKE ALL PRIVILEGES ON *.* FROM CURRENT_USER(); +Comparing tables master:test.v_user and slave:test.v_user +Comparing tables master:test.v_user and slave2:test.v_user + +# Verify 'GRANT ...' statement in the procedure +CREATE PROCEDURE my_grant() +GRANT CREATE, INSERT, SELECT ON TABLE test.t1 TO CURRENT_USER(); +call my_grant; +Comparing tables master:test.v_tables_priv and slave:test.v_tables_priv +Comparing tables master:test.v_tables_priv and slave2:test.v_tables_priv + +# Verify 'REVOKE ... ON TABLE ...' statement in the procedure +CREATE PROCEDURE my_revoke() +REVOKE CREATE, INSERT, SELECT ON TABLE t1 FROM CURRENT_USER(); +call my_revoke; +Comparing tables master:test.v_tables_priv and slave:test.v_tables_priv +Comparing tables master:test.v_tables_priv and slave2:test.v_tables_priv + +# Verify 'RENAME USER ...' statement +RENAME USER CURRENT_USER TO 'bug48321_2'@'localhost'; +Comparing tables master:test.v_user and slave:test.v_user +Comparing tables master:test.v_user and slave2:test.v_user + +# Verify 'DROP USER ...' statement +GRANT CREATE USER ON *.* TO 'bug48321_2'@'localhost'; +DROP USER CURRENT_USER(); +Comparing tables master:test.v_user and slave:test.v_user +Comparing tables master:test.v_user and slave2:test.v_user + +# Verify 'ALTER EVENT...' statement +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT * FROM t1; +# Explicitly assign CURRENT_USER() to definer +ALTER DEFINER=CURRENT_USER() EVENT e1 ENABLE; +Comparing tables master:test.v_event and slave:test.v_event +Comparing tables master:test.v_event and slave2:test.v_event + +# Session user will be set as definer, if the statement does not assign +# a definer +ALTER EVENT e1 ENABLE; +Comparing tables master:test.v_event and slave:test.v_event +Comparing tables master:test.v_event and slave2:test.v_event + +# Verify that this patch does not affect the calling of CURRENT_USER() +# in the other statements +# [On master] +INSERT INTO t1 VALUES(CURRENT_USER()), ('1234'); +Warnings: +Note 1592 Statement may not be safe to log in statement format. +SELECT * FROM t1; +c1 +root@localhost +1234 +# [On slave] +SELECT * FROM t1; +c1 +@ +1234 +# [On slave2] +SELECT * FROM t1; +c1 +@ +1234 +# [On master] +UPDATE t1 SET c1=CURRENT_USER() WHERE c1='1234'; +Warnings: +Note 1592 Statement may not be safe to log in statement format. +SELECT * FROM t1; +c1 +root@localhost +root@localhost +# [On slave] +SELECT * FROM t1; +c1 +@ +@ +# [On slave2] +SELECT * FROM t1; +c1 +@ +@ +# [On master] +DELETE FROM t1 WHERE c1=CURRENT_USER(); +Warnings: +Note 1592 Statement may not be safe to log in statement format. +SELECT * FROM t1; +c1 +# [On slave] +SELECT * FROM t1; +c1 +# [On slave2] +SELECT * FROM t1; +c1 +# [On master] +CREATE TABLE t2(c1 char(100)); +CREATE FUNCTION my_user() RETURNS VARCHAR(64) +SQL SECURITY INVOKER +BEGIN +INSERT INTO t2 VALUES(CURRENT_USER()); +RETURN CURRENT_USER(); +END | +INSERT INTO t1 VALUES(my_user()); +Warnings: +Note 1592 Statement may not be safe to log in statement format. +Note 1592 Statement may not be safe to log in statement format. +SELECT * FROM t1; +c1 +root@localhost +SELECT * FROM t2; +c1 +root@localhost +# [On slave] +SELECT * FROM t1; +c1 +@ +SELECT * FROM t2; +c1 +@ +# [On slave2] +SELECT * FROM t1; +c1 +@ +SELECT * FROM t2; +c1 +@ + +# END +DROP TABLE t1, t2; +DROP VIEW v_user, v_tables_priv, v_procs_priv, v_event; +DROP PROCEDURE p1; +DROP PROCEDURE my_grant; +DROP PROCEDURE my_revoke; +DROP FUNCTION my_user; +DROP EVENT e1; diff --git a/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result b/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result index 26f2545dd72..35f4cd3ecbb 100644 --- a/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result +++ b/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result @@ -750,7 +750,7 @@ test_rpl e2 root@localhost SYSTEM RECURRING NULL 1 # # NULL ENABLED 1 latin1 lat USE test_rpl; SHOW EVENTS; Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation -test_rpl e2 @ SYSTEM RECURRING NULL 1 # # NULL SLAVESIDE_DISABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci +test_rpl e2 root@localhost SYSTEM RECURRING NULL 1 # # NULL SLAVESIDE_DISABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci ==========MASTER========== SELECT COUNT(*) FROM t1; COUNT(*) diff --git a/mysql-test/suite/rpl/t/rpl_current_user.cnf b/mysql-test/suite/rpl/t/rpl_current_user.cnf new file mode 100644 index 00000000000..999ee727a88 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_current_user.cnf @@ -0,0 +1,9 @@ +!include ../my.cnf + +[mysqld.3] +server-id=3 +log-bin=slave-bin + +[ENV] +SLAVE_MYPORT1= @mysqld.3.port +SLAVE_MYSOCK1= @mysqld.3.socket diff --git a/mysql-test/suite/rpl/t/rpl_current_user.test b/mysql-test/suite/rpl/t/rpl_current_user.test new file mode 100644 index 00000000000..72581ed7049 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_current_user.test @@ -0,0 +1,238 @@ +############################################################################## +# BUG#48321 CURRENT_USER() incorrectly replicated for DROP/RENAME USER, +# REVOKE, GRANT, ALTER EVENT +# +# Calling CURRENT_USER() results into inconsistency between slave and master, +# as the slave SQL thread has different user with common users. +# +# After the patch for bug#48321, session's user will be written into query log +# event if CURRENT_USER() is called in 'DROP/RENAME USER', 'REVOKE', 'GRANT', +# 'ALTER EVENT'. +# +############################################################################## +source include/master-slave.inc; +source include/have_binlog_format_statement.inc; + +--echo +--echo # On slave2 +connect (slave2,127.0.0.1,root,,test,$SLAVE_MYPORT1,); +connection slave2; + +--echo # Connect slave2 to slave +--replace_result $SLAVE_MYPORT SLAVE_MYPORT; +eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$SLAVE_MYPORT, + MASTER_LOG_FILE='slave-bin.000001', MASTER_USER='root'; +START SLAVE; +source include/wait_for_slave_to_start.inc; + +--echo +--echo # [On master] +connection master; +--disable_warnings +DROP VIEW IF EXISTS v_user; +DROP VIEW IF EXISTS v_tables_priv; +DROP VIEW IF EXISTS v_procs_priv; +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS my_grant; +DROP PROCEDURE IF EXISTS my_revoke; +DROP FUNCTION IF EXISTS my_user; +DROP EVENT IF EXISTS e1; +--enable_warnings +CREATE TABLE t1(c1 char(100)); +CREATE VIEW test.v_user AS SELECT * FROM mysql.user WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_tables_priv AS SELECT * FROM mysql.tables_priv WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_procs_priv AS SELECT * FROM mysql.procs_priv WHERE User LIKE 'bug48321%'; +CREATE VIEW test.v_event AS SELECT definer FROM mysql.event WHERE name = 'e1'; +CREATE PROCEDURE p1() SELECT 1; +--echo # bug48321_1-01234 has the max length(16) of user. +GRANT ALL PRIVILEGES ON *.* TO 'bug48321_1-01234'@'localhost' WITH GRANT OPTION; + +--echo +--echo # Make sure the max lengths of user and host +--echo # the user name is too lengh +--error 1470 +GRANT CREATE USER ON *.* TO '01234567890123456'@'fakehost'; +--echo # the host name is too lengh +--error 1470 +GRANT CREATE USER ON *.* TO 'fakename'@'0123456789012345678901234567890123456789012345678901234567890'; + +--echo +--echo # User 'bug48321_1-01234' connects to master by conn1 +connect (conn1, 127.0.0.1, 'bug48321_1-01234'@'localhost',,); +connection conn1; +--echo # [On conn1] +--echo # Verify 'REVOKE ALL' statement +REVOKE ALL PRIVILEGES, GRANT OPTION FROM CURRENT_USER(); +let $diff_table= test.v_user; +let $diff_server_list= master, slave, slave2; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'GRANT ... ON TABLE ...' statement +connection conn1; +GRANT CREATE, INSERT, SELECT ON TABLE test.t1 TO CURRENT_USER(); +let $diff_table= test.v_tables_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'GRANT ... ON PROCEDURE...' statement +connection conn1; +GRANT ALTER ROUTINE, EXECUTE ON PROCEDURE p1 TO CURRENT_USER(); +let $diff_table= test.v_procs_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'GRANT ... ON *.* ...' statement +connection conn1; +GRANT ALL PRIVILEGES ON *.* TO CURRENT_USER() WITH GRANT OPTION; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'REVOKE ... ON TABLE ...' statement +connection conn1; +REVOKE CREATE, INSERT, SELECT ON TABLE t1 FROM CURRENT_USER(); +let $diff_table= test.v_tables_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'REVOKE ... ON PROCEDURE...' statement +connection conn1; +REVOKE ALTER ROUTINE, EXECUTE ON PROCEDURE p1 FROM CURRENT_USER(); +let $diff_table= test.v_procs_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'REVOKE ... ON *.* ...' statement +connection conn1; +REVOKE ALL PRIVILEGES ON *.* FROM CURRENT_USER(); +let $diff_table= test.v_user; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'GRANT ...' statement in the procedure +connection conn1; +CREATE PROCEDURE my_grant() + GRANT CREATE, INSERT, SELECT ON TABLE test.t1 TO CURRENT_USER(); +call my_grant; +let $diff_table= test.v_tables_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'REVOKE ... ON TABLE ...' statement in the procedure +connection conn1; +CREATE PROCEDURE my_revoke() + REVOKE CREATE, INSERT, SELECT ON TABLE t1 FROM CURRENT_USER(); +call my_revoke; +let $diff_table= test.v_tables_priv; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'RENAME USER ...' statement +connection conn1; +RENAME USER CURRENT_USER TO 'bug48321_2'@'localhost'; +let $diff_table= test.v_user; +source include/rpl_diff_tables.inc; + +disconnect conn1; + +--echo +--echo # Verify 'DROP USER ...' statement +connection master; +GRANT CREATE USER ON *.* TO 'bug48321_2'@'localhost'; +connect (conn1, 127.0.0.1, 'bug48321_2'@'localhost',,); +connection conn1; +DROP USER CURRENT_USER(); +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify 'ALTER EVENT...' statement +connection master; +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT * FROM t1; + +--echo # Explicitly assign CURRENT_USER() to definer +ALTER DEFINER=CURRENT_USER() EVENT e1 ENABLE; +let $diff_table= test.v_event; +source include/rpl_diff_tables.inc; + +connection master; +--echo +--echo # Session user will be set as definer, if the statement does not assign +--echo # a definer +ALTER EVENT e1 ENABLE; +sync_slave_with_master; +source include/rpl_diff_tables.inc; + +--echo +--echo # Verify that this patch does not affect the calling of CURRENT_USER() +--echo # in the other statements +connection master; +--echo # [On master] +INSERT INTO t1 VALUES(CURRENT_USER()), ('1234'); +SELECT * FROM t1; +sync_slave_with_master; +--echo # [On slave] +SELECT * FROM t1; +--echo # [On slave2] +sync_slave_with_master slave2; +SELECT * FROM t1; + +connection master; +--echo # [On master] +UPDATE t1 SET c1=CURRENT_USER() WHERE c1='1234'; +SELECT * FROM t1; +sync_slave_with_master; +--echo # [On slave] +SELECT * FROM t1; +sync_slave_with_master slave2; +--echo # [On slave2] +SELECT * FROM t1; + +connection master; +--echo # [On master] +DELETE FROM t1 WHERE c1=CURRENT_USER(); +SELECT * FROM t1; +sync_slave_with_master; +--echo # [On slave] +SELECT * FROM t1; +sync_slave_with_master slave2; +--echo # [On slave2] +SELECT * FROM t1; + +connection master; +--echo # [On master] +CREATE TABLE t2(c1 char(100)); + +DELIMITER |; +CREATE FUNCTION my_user() RETURNS VARCHAR(64) + SQL SECURITY INVOKER +BEGIN + INSERT INTO t2 VALUES(CURRENT_USER()); + RETURN CURRENT_USER(); +END | +DELIMITER ;| + +INSERT INTO t1 VALUES(my_user()); +SELECT * FROM t1; +SELECT * FROM t2; +sync_slave_with_master; +--echo # [On slave] +SELECT * FROM t1; +SELECT * FROM t2; +sync_slave_with_master slave2; +--echo # [On slave2] +SELECT * FROM t1; +SELECT * FROM t2; + +--echo +--echo # END +connection master; +DROP TABLE t1, t2; +DROP VIEW v_user, v_tables_priv, v_procs_priv, v_event; +DROP PROCEDURE p1; +DROP PROCEDURE my_grant; +DROP PROCEDURE my_revoke; +DROP FUNCTION my_user; +DROP EVENT e1; +sync_slave_with_master; +sync_slave_with_master slave2; +source include/master-slave-end.inc; diff --git a/sql/log_event.cc b/sql/log_event.cc index 9c6de5bb42e..cdf059b5e1f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2307,6 +2307,53 @@ bool Query_log_event::write(IO_CACHE* file) start+= 4; } + if (thd && thd->is_current_user_used()) + { + LEX_STRING user; + LEX_STRING host; + bzero(&user, sizeof(user)); + bzero(&host, sizeof(host)); + + if (thd->slave_thread) + { + /* user will be null, if master is older than this patch */ + user= thd->variables.current_user.user; + host= thd->variables.current_user.host; + } + else if (thd->security_ctx->priv_user) + { + Security_context *ctx= thd->security_ctx; + + user.length= strlen(ctx->priv_user); + user.str= ctx->priv_user; + if (ctx->priv_host[0] != '\0') + { + host.str= ctx->priv_host; + host.length= strlen(ctx->priv_host); + } + } + + if (user.length > 0) + { + *start++= Q_INVOKER; + + /* + Store user length and user. The max length of use is 16, so 1 byte is + enough to store the user's length. + */ + *start++= (uchar)user.length; + memcpy(start, user.str, user.length); + start+= user.length; + + /* + Store host length and host. The max length of host is 60, so 1 byte is + enough to store the host's length. + */ + *start++= (uchar)host.length; + memcpy(start, host.str, host.length); + start+= host.length; + } + } /* NOTE: When adding new status vars, please don't forget to update the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update the function @@ -2575,6 +2622,8 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, bool catalog_nz= 1; DBUG_ENTER("Query_log_event::Query_log_event(char*,...)"); + bzero(&user, sizeof(user)); + bzero(&host, sizeof(host)); common_header_len= description_event->common_header_len; post_header_len= description_event->post_header_len[event_type-1]; DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d", @@ -2729,6 +2778,20 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, data_written= master_data_written= uint4korr(pos); pos+= 4; break; + case Q_INVOKER: + { + CHECK_SPACE(pos, end, 1); + user.length= *pos++; + CHECK_SPACE(pos, end, user.length); + user.str= my_strndup((const char *)pos, user.length, MYF(0)); + pos+= user.length; + + CHECK_SPACE(pos, end, 1); + host.length= *pos++; + CHECK_SPACE(pos, end, host.length); + host.str= my_strndup((const char *)pos, host.length, MYF(0)); + pos+= host.length; + } default: /* That's why you must write status vars in growing order of code */ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\ @@ -3178,7 +3241,9 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, thd->variables.collation_database= thd->db_charset; thd->table_map_for_update= (table_map)table_map_for_update; - + thd->variables.current_user.user= user; + thd->variables.current_user.host= host; + thd->variables.current_user.password= {0, 0}; /* Execute the query (note that we bypass dispatch_command()) */ const char* found_semicolon= NULL; mysql_parse(thd, thd->query(), thd->query_length(), &found_semicolon); diff --git a/sql/log_event.h b/sql/log_event.h index e3ca4ca3321..816a241e55d 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -264,7 +264,8 @@ struct sql_ex_info 1 + 2 /* type, lc_time_names_number */ + \ 1 + 2 /* type, charset_database_number */ + \ 1 + 8 /* type, table_map_for_update */ + \ - 1 + 4 /* type, master_data_written */) + 1 + 4 /* type, master_data_written */ + \ + 1 + 16 + 1 + 60/* type, user_len, user, host_len, host */) #define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ LOG_EVENT_HEADER_LEN + /* write_header */ \ QUERY_HEADER_LEN + /* write_data */ \ @@ -333,6 +334,8 @@ struct sql_ex_info #define Q_MASTER_DATA_WRITTEN_CODE 10 +#define Q_INVOKER 11 + /* Intvar event post-header */ /* Intvar event data */ @@ -1546,6 +1549,8 @@ protected: */ class Query_log_event: public Log_event { + LEX_STRING user; + LEX_STRING host; protected: Log_event::Byte* data_buf; public: diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index dd256c70ecb..acf14fc91c4 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -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 inline void get_grantor(THD *thd, char* grantor); /* Convert scrambled password to binary form, according to scramble type, @@ -2704,6 +2705,20 @@ end: DBUG_RETURN(result); } +static inline void get_grantor(THD *thd, char *grantor) +{ + const char *user= thd->security_ctx->user; + const char *host= thd->security_ctx->host_or_ip; + +#if defined(HAVE_REPLICATION) + if (thd->slave_thread && thd->variables.current_user.user.length > 0) + { + user= thd->variables.current_user.user.str; + host= thd->variables.current_user.host.str; + } +#endif + strxmov(grantor, user, "@", host, NullS); +} static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, TABLE *table, const LEX_USER &combo, @@ -2718,9 +2733,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, uchar user_key[MAX_KEY_LENGTH]; DBUG_ENTER("replace_table_table"); - strxmov(grantor, thd->security_ctx->user, "@", - thd->security_ctx->host_or_ip, NullS); - + get_grantor(thd, grantor); /* The following should always succeed as new users are created before this function is called! @@ -2850,9 +2863,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, DBUG_RETURN(-1); } - strxmov(grantor, thd->security_ctx->user, "@", - thd->security_ctx->host_or_ip, NullS); - + get_grantor(thd, grantor); /* New users are created before this function is called. diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 93aa6a8268c..cd2bb2a51df 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1236,6 +1236,7 @@ void THD::cleanup_after_query() where= THD::DEFAULT_WHERE; /* reset table map for multi-table update */ table_map_for_update= 0; + clean_current_user_used(); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 4c1d4a98db0..72ffa2e6ba4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -401,6 +401,7 @@ struct system_variables DATE_TIME_FORMAT *datetime_format; DATE_TIME_FORMAT *time_format; my_bool sysdate_is_now; + LEX_USER current_user; }; @@ -2340,6 +2341,19 @@ public: Protected with LOCK_thd_data mutex. */ void set_query(char *query_arg, uint32 query_length_arg); + void set_current_user_used() { current_user_used= TRUE; } + bool is_current_user_used() { return current_user_used; } + void clean_current_user_used() { current_user_used= FALSE; } + void get_definer(LEX_USER *definer) + { + set_current_user_used(); +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + if (slave_thread && variables.current_user.user.length) + *definer= variables.current_user; + else +#endif + get_default_definer(this, definer); + } private: /** The current internal error handler for this thread, or NULL. */ Internal_error_handler *m_internal_handler; @@ -2359,6 +2373,8 @@ private: tree itself is reused between executions and thus is stored elsewhere. */ MEM_ROOT main_mem_root; + + bool current_user_used; }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c4d4f2c9d9d..ec9199b33a5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7650,7 +7650,7 @@ LEX_USER *create_default_definer(THD *thd) if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) return 0; - get_default_definer(thd, definer); + thd->get_definer(definer); return definer; } |