Commit 788c28ac authored by 's avatar

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.
parent fe7ad16b
--let $count=1
connection master;
if (`SELECT '$diff_table' = ''`)
{
let $diff_table= mysql.user;
}
--echo
--echo
--echo
--echo TEST STATEMENT: '$statement'
--echo --------------------------------------------------------------------------
if (`SELECT '$diff_columns' = ''`)
{
eval CREATE VIEW test.bug48321_v1 AS SELECT user FROM $diff_table
WHERE user LIKE 'bug48321%';
}
if (`SELECT '$diff_columns' <> ''`)
{
eval CREATE VIEW test.bug48321_v1 AS SELECT user, $diff_columns
FROM $diff_table WHERE user LIKE 'bug48321%';
}
while (`SELECT $count < 6`)
{
--echo
--echo TEST STATEMENT: '$statement'
--echo CASE $count:
--echo -------
let $user2= 'bug48321_2'@'localhost';
let $user3= 'bug48321_3'@'localhost';
let $user1= CURRENT_USER();
if (`SELECT '$action'='RENAME'`)
{
let $user1= $user1 TO 'bug48321_4'@'localhost';
let $user2= $user2 TO 'bug48321_5'@'localhost';
let $user3= $user3 TO 'bug48321_6'@'localhost';
}
if (`SELECT '$action'='GRANT'`)
{
let $user1= $user1 IDENTIFIED BY 'user1';
let $user3= $user3 IDENTIFIED BY '';
}
if (`SELECT $count=1`)
{
--echo # Only CURRENT_USER() in the user list of the test statement.
let $users_list= $user1;
}
if (`SELECT $count=2`)
{
--echo # Two users are in the test statement, CURRENT_USER is the first one.
let $users_list= $user1, $user2;
}
if (`SELECT $count=3`)
{
--echo # Two users are in the test statement, CURRENT_USER is the last one.
let $users_list= $user2, $user1;
}
if (`SELECT $count=4`)
{
--echo # Three users are in the test statement, CURRENT_USER is the second one.
let $users_list= $user2, $user1, $user3;
}
if (`SELECT $count=5`)
{
--echo # CURRENT_USER is not in the test statement.
let $users_list= $user2, $user3;
}
--echo users_list= $users_list
--echo
--echo # Connect to master with user1, so user1 always is the current user,
--echo # when test statement is runing.
eval GRANT ALL PRIVILEGES ON *.* TO 'bug48321_1'@'localhost'
WITH GRANT OPTION;
eval CREATE USER 'bug48321_2'@'localhost', 'bug48321_3'@'localhost'
IDENTIFIED BY 'user3';
if (`SELECT '$action'='REVOKE'`)
{
--echo
--echo # Grant some privileges to users at first when testing
--echo # 'REVOKE ...' statement.
eval GRANT ALL PRIVILEGES ON *.* TO 'bug48321_2'@'localhost',
'bug48321_3'@'localhost' WITH GRANT OPTION;
eval GRANT ALTER ROUTINE, EXECUTE ON PROCEDURE p1 TO 'bug48321_1'@'localhost',
'bug48321_2'@'localhost', 'bug48321_3'@'localhost';
}
connect (conn1, 127.0.0.1, 'bug48321_1'@'localhost',,);
connection conn1;
--echo
let $temp= `SELECT "$statement"`;
eval $temp;
--echo
disconnect conn1;
connection master;
sync_slave_with_master;
connection master;
let $diff_table_1= master:test.bug48321_v1;
let $diff_table_2= slave:test.bug48321_v1;
source include/diff_tables.inc;
--echo
--echo # Delete all bug48321% users
connection master;
DELETE FROM mysql.user WHERE user LIKE 'bug48321%';
DELETE FROM mysql.procs_priv WHERE user LIKE 'bug48321%';
FLUSH PRIVILEGES;
inc $count;
}
DROP VIEW test.bug48321_v1;
......@@ -17,16 +17,15 @@ show grants for x@y;
Grants for x@y
GRANT USAGE ON *.* TO 'x'@'y'
GRANT SELECT ON `d1`.`t` TO 'x'@'y'
show binlog events;
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 4 Format_desc 1 106 Server ver: VERSION, Binlog ver: 4
master-bin.000001 106 Query 1 193 drop database if exists d1
master-bin.000001 193 Query 1 272 create database d1
master-bin.000001 272 Query 1 370 use `d1`; create table t (s1 int) engine=innodb
master-bin.000001 370 Query 1 436 BEGIN
master-bin.000001 436 Query 1 521 use `d1`; insert into t values (1)
master-bin.000001 521 Xid 1 548 COMMIT /* XID */
master-bin.000001 548 Query 1 633 use `d1`; grant select on t to x@y
master-bin.000001 # Query # # drop database if exists d1
master-bin.000001 # Query # # create database d1
master-bin.000001 # Query # # use `d1`; create table t (s1 int) engine=innodb
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `d1`; insert into t values (1)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Query # # use `d1`; grant select on t to 'x'@'y'
start transaction;
insert into t values (2);
revoke select on t from x@y;
......@@ -38,19 +37,18 @@ s1
show grants for x@y;
Grants for x@y
GRANT USAGE ON *.* TO 'x'@'y'
show binlog events;
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 4 Format_desc 1 106 Server ver: VERSION, Binlog ver: 4
master-bin.000001 106 Query 1 193 drop database if exists d1
master-bin.000001 193 Query 1 272 create database d1
master-bin.000001 272 Query 1 370 use `d1`; create table t (s1 int) engine=innodb
master-bin.000001 370 Query 1 436 BEGIN
master-bin.000001 436 Query 1 521 use `d1`; insert into t values (1)
master-bin.000001 521 Xid 1 548 COMMIT /* XID */
master-bin.000001 548 Query 1 633 use `d1`; grant select on t to x@y
master-bin.000001 633 Query 1 699 BEGIN
master-bin.000001 699 Query 1 784 use `d1`; insert into t values (2)
master-bin.000001 784 Xid 1 811 COMMIT /* XID */
master-bin.000001 811 Query 1 899 use `d1`; revoke select on t from x@y
master-bin.000001 # Query # # drop database if exists d1
master-bin.000001 # Query # # create database d1
master-bin.000001 # Query # # use `d1`; create table t (s1 int) engine=innodb
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `d1`; insert into t values (1)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Query # # use `d1`; grant select on t to 'x'@'y'
master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `d1`; insert into t values (2)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Query # # use `d1`; revoke select on t from 'x'@'y'
drop user x@y;
drop database d1;
......@@ -251,3 +251,45 @@ DROP EVENT event44331_1;
DROP EVENT event44331_2;
DROP EVENT event44331_3;
DROP EVENT event44331_4;
DROP VIEW IF EXISTS events_view;
DROP EVENT IF EXISTS event48321_1;
DROP EVENT IF EXISTS event48321_2;
DROP EVENT IF EXISTS event48321_3;
DROP EVENT IF EXISTS event48321_4;
CREATE VIEW events_view AS
SELECT EVENT_SCHEMA, EVENT_NAME, DEFINER FROM INFORMATION_SCHEMA.EVENTS
WHERE EVENT_NAME LIKE 'event48321%';
CREATE DEFINER=CURRENT_USER() /*!50000 EVENT event48321_1 */
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
CREATE DEFINER=CURRENT_USER() EVENT event48321_2
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
CREATE /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
Comparing tables master:test.events_view and slave:test.events_view
ALTER DEFINER=CURRENT_USER() EVENT event48321_1 RENAME TO event48321_4;
ALTER DEFINER=CURRENT_USER() EVENT event48321_2
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
ALTER /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
Comparing tables master:test.events_view and slave:test.events_view
ALTER /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG; ALTER EVENT event48321_2 ENABLE |
Comparing tables master:test.events_view and slave:test.events_view
ALTER EVENT event48321_3 ENABLE;
Comparing tables master:test.events_view and slave:test.events_view
DROP EVENT event48321_4;
DROP EVENT event48321_2;
DROP EVENT event48321_3;
DROP VIEW events_view;
......@@ -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(*)
......@@ -1079,7 +1079,7 @@ master-bin.000001 # Query 1 # BEGIN
master-bin.000001 # Query 1 # use `test_rpl`; INSERT INTO t1 VALUES(1, 'test1')
master-bin.000001 # Xid 1 # #
master-bin.000001 # Query 1 # use `test_rpl`; CREATE DEFINER=`root`@`localhost` EVENT e1 ON SCHEDULE EVERY '1' SECOND COMMENT 'e_second_comment' DO DELETE FROM t1
master-bin.000001 # Query 1 # use `test_rpl`; ALTER EVENT e1 RENAME TO e2
master-bin.000001 # Query 1 # use `test_rpl`; ALTER DEFINER=`root`@`localhost` EVENT e1 RENAME TO e2
master-bin.000001 # Query 1 # use `test_rpl`; DROP EVENT e2
master-bin.000001 # Query 1 # BEGIN
master-bin.000001 # Query 1 # use `test_rpl`; DELETE FROM t1
......
......@@ -433,9 +433,9 @@ master-bin.000001 # Query 1 # use `mysqltest1`; create table t2 like t1
master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` PROCEDURE `foo3`()
DETERMINISTIC
insert into t1 values (15)
master-bin.000001 # Query 1 # use `mysqltest1`; grant CREATE ROUTINE, EXECUTE on mysqltest1.* to "zedjzlcsjhd"@127.0.0.1
master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT on mysqltest1.t1 to "zedjzlcsjhd"@127.0.0.1
master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT, INSERT on mysqltest1.t2 to "zedjzlcsjhd"@127.0.0.1
master-bin.000001 # Query 1 # use `mysqltest1`; grant CREATE ROUTINE, EXECUTE on mysqltest1.* to 'zedjzlcsjhd'@'127.0.0.1'
master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT on mysqltest1.t1 to 'zedjzlcsjhd'@'127.0.0.1'
master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT, INSERT on mysqltest1.t2 to 'zedjzlcsjhd'@'127.0.0.1'
master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` PROCEDURE `foo4`()
DETERMINISTIC
begin
......@@ -510,7 +510,7 @@ select * from t1
master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo
master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1
master-bin.000001 # Query 1 # drop database mysqltest1
master-bin.000001 # Query 1 # drop user "zedjzlcsjhd"@127.0.0.1
master-bin.000001 # Query 1 # DROP USER 'zedjzlcsjhd'@'127.0.0.1'
master-bin.000001 # Query 1 # use `test`; drop function if exists f1
master-bin.000001 # Query 1 # use `test`; CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
READS SQL DATA
......@@ -675,13 +675,13 @@ CREATE DEFINER=`root`@`localhost` PROCEDURE `foo3`()
insert into t1 values (15)
/*!*/;
SET TIMESTAMP=t/*!*/;
grant CREATE ROUTINE, EXECUTE on mysqltest1.* to "zedjzlcsjhd"@127.0.0.1
grant CREATE ROUTINE, EXECUTE on mysqltest1.* to 'zedjzlcsjhd'@'127.0.0.1'
/*!*/;
SET TIMESTAMP=t/*!*/;
grant SELECT on mysqltest1.t1 to "zedjzlcsjhd"@127.0.0.1
grant SELECT on mysqltest1.t1 to 'zedjzlcsjhd'@'127.0.0.1'
/*!*/;
SET TIMESTAMP=t/*!*/;
grant SELECT, INSERT on mysqltest1.t2 to "zedjzlcsjhd"@127.0.0.1
grant SELECT, INSERT on mysqltest1.t2 to 'zedjzlcsjhd'@'127.0.0.1'
/*!*/;
SET TIMESTAMP=t/*!*/;
CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` PROCEDURE `foo4`()
......@@ -842,7 +842,7 @@ SET TIMESTAMP=t/*!*/;
drop database mysqltest1
/*!*/;
SET TIMESTAMP=t/*!*/;
drop user "zedjzlcsjhd"@127.0.0.1
DROP USER 'zedjzlcsjhd'@'127.0.0.1'
/*!*/;
use test/*!*/;
SET TIMESTAMP=t/*!*/;
......
This diff is collapsed.
......@@ -25,9 +25,7 @@ grant select on t to x@y;
#
rollback;
show grants for x@y;
--replace_result $VERSION VERSION
--replace_regex /\/\* xid=.* \*\//\/* XID *\//
show binlog events;
--source include/show_binlog_events.inc
start transaction;
insert into t values (2);
revoke select on t from x@y;
......@@ -37,9 +35,7 @@ revoke select on t from x@y;
commit;
select * from t;
show grants for x@y;
--replace_result $VERSION VERSION
--replace_regex /\/\* xid=.* \*\//\/* XID *\//
show binlog events;
--source include/show_binlog_events.inc
drop user x@y;
drop database d1;
--sync_slave_with_master
......@@ -105,3 +105,85 @@ DROP EVENT event44331_2;
DROP EVENT event44331_3;
DROP EVENT event44331_4;
sync_slave_with_master;
#
# BUG#48321
# This test verifies if the definer is consistent between master and slave,
# when the event is created or altered with the DEFINER clause that the
# DEFINER is set to CURRENT_USER()
#
connection master;
--disable_warnings
DROP VIEW IF EXISTS events_view;
DROP EVENT IF EXISTS event48321_1;
DROP EVENT IF EXISTS event48321_2;
DROP EVENT IF EXISTS event48321_3;
DROP EVENT IF EXISTS event48321_4;
--enable_warnings
CREATE VIEW events_view AS
SELECT EVENT_SCHEMA, EVENT_NAME, DEFINER FROM INFORMATION_SCHEMA.EVENTS
WHERE EVENT_NAME LIKE 'event48321%';
let $diff_table_1= master:test.events_view;
let $diff_table_2= slave:test.events_view;
CREATE DEFINER=CURRENT_USER() /*!50000 EVENT event48321_1 */
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
CREATE DEFINER=CURRENT_USER() EVENT event48321_2
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
CREATE /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
sync_slave_with_master;
--source include/diff_tables.inc
connection master;
ALTER DEFINER=CURRENT_USER() EVENT event48321_1 RENAME TO event48321_4;
ALTER DEFINER=CURRENT_USER() EVENT event48321_2
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
ALTER /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG;
sync_slave_with_master;
--source include/diff_tables.inc
# Two statements in on query
connection master;
DELIMITER |;
ALTER /*!50000 DEFINER=CURRENT_USER() */ EVENT event48321_3
ON SCHEDULE AT CURRENT_TIMESTAMP
ON COMPLETION PRESERVE DISABLE
DO SELECT 48321 as BUG; ALTER EVENT event48321_2 ENABLE |
DELIMITER ;|
sync_slave_with_master;
--source include/diff_tables.inc
#No Event boday
connection master;
ALTER EVENT event48321_3 ENABLE;
sync_slave_with_master;
--source include/diff_tables.inc
connection master;
DROP EVENT event48321_4;
DROP EVENT event48321_2;
DROP EVENT event48321_3;
DROP VIEW events_view;
--source include/master-slave-end.inc
......@@ -54,8 +54,85 @@ drop user 'not_exist_user1'@'fakehost', 'not_exist_user2'@'fakehost';
sync_slave_with_master;
select Host,User from mysql.user where Host='fakehost';
#
# show the binlog events on the master
#
connection master;
source include/show_binlog_events.inc;
#
# BUG#48321
#
let $action= RENAME;
let $statement= RENAME USER \$users_list;
source extra/rpl_tests/rpl_current_user.test;
let $action= DROP;
let $statement= DROP USER \$users_list;
source extra/rpl_tests/rpl_current_user.test;
--disable_warnings
DROP PROCEDURE IF EXISTS f1;
--enable_warnings
CREATE PROCEDURE p1() SELECT 1;
#REVOKE ALL PRIVILEGES
let $action= REVOKE;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Grant_priv;
let $statement= REVOKE ALL PRIVILEGES, GRANT OPTION FROM \$users_list;
source extra/rpl_tests/rpl_current_user.test;
#REVOKE ALL PRIVILEGES with comment
let $action= REVOKE;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Grant_priv;
let $statement= REVOKE ALL PRIVILEGES, GRANT OPTION FROM \$users_list /*With comment*/;
source extra/rpl_tests/rpl_current_user.test;
#REVOKE ALL PRIVILEGES with comment
let $action= REVOKE;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Grant_priv;
let $statement= REVOKE ALL PRIVILEGES, GRANT OPTION FROM /*With comment*/ \$users_list;
source extra/rpl_tests/rpl_current_user.test;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv;
#REVOKE ON TABLE
let $statement= REVOKE SELECT, UPDATE, CREATE, DROP ON TABLE *.* FROM \$users_list;
source extra/rpl_tests/rpl_current_user.test;
#REVOKE ON CREATE ROUTINE
let $diff_columns= Create_routine_priv;
let $statement= REVOKE CREATE ROUTINE ON *.* FROM \$users_list;
source extra/rpl_tests/rpl_current_user.test;
#REVOKE ON ROUTINE
let $diff_table= mysql.procs_priv;
let $diff_columns= Routine_name, Proc_priv;
let $statement= REVOKE ALTER ROUTINE, EXECUTE ON PROCEDURE p1 FROM \$users_list;
source extra/rpl_tests/rpl_current_user.test;
let $diff_table= mysql.user;
#GRANT ALL PRIVILEGES
let $action= GRANT;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Grant_priv, Password;
let $statement= GRANT ALL PRIVILEGES ON *.* TO \$users_list WITH GRANT OPTION;
source extra/rpl_tests/rpl_current_user.test;
#GRANT ALL PRIVILEGES with comment
let $action= GRANT;
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Grant_priv, Password;
let $statement= GRANT ALL PRIVILEGES ON *.* TO \$users_list /* With Comment */ WITH GRANT OPTION;
source extra/rpl_tests/rpl_current_user.test;
#GRANT ON TABLE
let $diff_columns= Select_priv, Update_priv, Create_priv, Drop_priv, Password;
let $statement= GRANT SELECT, UPDATE, CREATE, DROP ON TABLE *.* TO \$users_list;
source extra/rpl_tests/rpl_current_user.test;
#GRANT ON CREATE ROUTINE
let $diff_columns= Create_routine_priv;
let $statement= GRANT CREATE ROUTINE ON *.* TO \$users_list;
source extra/rpl_tests/rpl_current_user.test;
#GRANT ON ROUTINE
let $diff_table= mysql.procs_priv;
let $diff_columns= Routine_name, Proc_priv;
let $statement= GRANT ALTER ROUTINE, EXECUTE ON PROCEDURE p1 TO \$users_list;
DROP PROCEDURE p1;
source extra/rpl_tests/rpl_current_user.test;
......@@ -341,31 +341,48 @@ Events::reconstruct_interval_expression(String *buf, interval_type interval,
}
/**
Create a new query string for removing executable comments
for avoiding leak and keeping consistency of the execution
on master and slave.
/*
Binlog '{CREATE|ALTER} EVENT' statements.
Definer part is always rewritten, for definer can be CURRENT_USER() function.
@param[in] thd Thread handler
@param[in] buf Query string
@param[in] create CREATE or ALTER statement
@return
0 ok
1 error
FASE ok
TRUE error
*/
static int
create_query_string(THD *thd, String *buf)
static bool event_write_bin_log(THD *thd, bool create)
{
/* Append the "CREATE" part of the query */
if (buf->append(STRING_WITH_LEN("CREATE ")))
return 1;
/* Append definer */
append_definer(thd, buf, &(thd->lex->definer->user), &(thd->lex->definer->host));
String log_query;
if (create)
{
/* Append the "CREATE" part of the query */
if (log_query.append(STRING_WITH_LEN("CREATE ")))
return TRUE;
}
else
{
/* Append the "ALETR " part of the query */
if (log_query.append(STRING_WITH_LEN("ALTER ")))
return TRUE;
}
/* Append definer
If the definer is not set or set to CURRENT_USER, the value of CURRENT_USER
will be written into the binary log as the definer for the SQL thread.
*/
append_definer(thd, &log_query, &(thd->lex->definer->user),
&(thd->lex->definer->host));
/* Append the left part of thd->query after "DEFINER" part */
if (buf->append(thd->lex->stmt_definition_begin))
return 1;
return 0;
if (log_query.append(thd->lex->stmt_definition_begin,
thd->lex->stmt_definition_end -
thd->lex->stmt_definition_begin))
return TRUE;
return write_bin_log(thd, TRUE, log_query.c_ptr_safe(), log_query.length())
!= 0;
}
/**
......@@ -380,8 +397,7 @@ create_query_string(THD *thd, String *buf)
@sa Events::drop_event for the notes about locking, pre-locking
and Events DDL.
@retval FALSE OK
@retval TRUE Error (reported)
@retval FALSE OK @retval TRUE Error (reported)
*/
bool
......@@ -465,22 +481,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
binlog the create event unless it's been successfully dropped
*/
if (!dropped)
{
/* Binlog the create event. */
DBUG_ASSERT(thd->query() && thd->query_length());
String log_query;
if (create_query_string(thd, &log_query))
{
sql_print_error("Event Error: An error occurred while creating query string, "
"before writing it into binary log.");
/* Restore the state of binlog format */
thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(TRUE);
}
/* If the definer is not set or set to CURRENT_USER, the value of CURRENT_USER
will be written into the binary log as the definer for the SQL thread. */
ret= write_bin_log(thd, TRUE, log_query.c_ptr(), log_query.length());
}
ret= event_write_bin_log(thd, TRUE);
}
pthread_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
......@@ -602,9 +603,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
if (event_queue)
event_queue->update_event(thd, parse_data->dbname, parse_data->name,
new_element);
/* Binlog the alter event. */
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
ret= event_write_bin_log(thd, FALSE);
}
}
pthread_mutex_unlock(&LOCK_event_metadata);
......
......@@ -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);
......
......@@ -1727,6 +1727,8 @@ typedef struct st_lex : public Query_tables_list
- CREATE TRIGGER (points to "TRIGGER");
- CREATE PROCEDURE (points to "PROCEDURE");
- CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE");
- CREATE VIEW(points to "VIEW");
- CREATE EVENT(points to "EVENT");
This pointer is required to add possibly omitted DEFINER-clause to the
DDL-statement before dumping it to the binlog.
......@@ -1735,6 +1737,29 @@ typedef struct st_lex : public Query_tables_list
const char *stmt_definition_end;
/*
stmt_begin is intended to point to the begin of every statement.
It is now used in the following statements:
- GRANT ALL PRIVELEGES ON *.* (points to "GRANT");
- REVOKE ALL PRIVELEGES ON *.* (points to "REVOKE");
*/
const char *stmt_begin;
const char *stmt_end;
/*
stmt_user_begin is intended to point to the begin of the user list in
the following statements:
- GRANT ALL PRIVELEGES ON *.* TO 'username'@'hostname'
(points to "'username'");
- REVOKE ALL PRIVELEGES ON *.* FROM 'username'@'hostname'
(points to "'username'");
these pointers are required to replace the CURRENT_USER()
function by the real user before dumping it to the binlog.
*/
const char *stmt_user_begin;
const char *stmt_user_end;
/**
During name resolution search only in the table list given by
Name_resolution_context::first_name_resolution_table and
......
......@@ -1562,7 +1562,11 @@ opt_end_of_input:
;
verb_clause:
statement
remember_name statement remember_end
{
Lex->stmt_begin= $1;
Lex->stmt_end= $3;
}
| begin
;
......@@ -5743,7 +5747,7 @@ alter:
}
view_tail
{}
| ALTER definer_opt EVENT_SYM sp_name
| ALTER definer_opt remember_name EVENT_SYM sp_name
{
/*
It is safe to use Lex->spname because
......@@ -5755,7 +5759,8 @@ alter:
if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
MYSQL_YYABORT;
Lex->event_parse_data->identifier= $4;
Lex->event_parse_data->identifier= $5;
Lex->stmt_definition_begin= $3;
Lex->sql_command= SQLCOM_ALTER_EVENT;
}
......@@ -5765,7 +5770,7 @@ alter:
opt_ev_comment
opt_ev_sql_stmt
{
if (!($6 || $7 || $8 || $9 || $10))
if (!($7 || $8 || $9 || $10 || $11))
{
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
......@@ -5826,7 +5831,16 @@ opt_ev_rename_to:
;
opt_ev_sql_stmt:
/* empty*/ { $$= 0;}
/* empty*/
{
$$= 0;
/*
Lex->sp_head is not initialized when event body is empty.
So we can not use Lex->sp_head->set_stmt_end() to set
stmt_definition_end.
*/
Lex->stmt_definition_end= (char*) YYLIP->get_cpp_tok_end();
}
| DO_SYM ev_sql_stmt { $$= 1; }
;
......@@ -11516,6 +11530,7 @@ user:
$$->user = $1;
$$->host.str= (char *) "%";
$$->host.length= 1;
Lex->stmt_user_end= YYLIP->get_cpp_ptr();
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
......@@ -11528,6 +11543,7 @@ user:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
Lex->stmt_user_end= YYLIP->get_cpp_ptr();
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
......@@ -11537,6 +11553,7 @@ user:
}
| CURRENT_USER optional_braces
{
Lex->stmt_user_end= YYLIP->get_cpp_ptr();
if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
/*
......@@ -12727,9 +12744,10 @@ user_list:
;
grant_list:
{ Lex->stmt_user_begin= YYLIP->get_cpp_ptr(); }
grant_user
{
if (Lex->users_list.push_back($1))
if (Lex->users_list.push_back($2))
MYSQL_YYABORT;
}
| grant_list ',' grant_user
......@@ -12742,6 +12760,7 @@ grant_list:
grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
Lex->stmt_user_end= YYLIP->get_cpp_ptr();
$$=$1; $1->password=$4;
if ($4.length)
{
......@@ -12768,7 +12787,10 @@ grant_user:
}
}
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
{ $$= $1; $1->password= $5; }
{
Lex->stmt_user_end= YYLIP->get_cpp_ptr();
$$= $1; $1->password= $5;
}
| user
{ $$= $1; $1->password= null_lex_str; }
;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment