From c4d69f17753f375e8cfd18c33de291cdf13504f9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 Aug 2011 11:38:52 +0200 Subject: [PATCH 1/4] MWL#234: Support for marking binlog events to not be replicated, and for telling slaves not to replicate events with such mark --- client/mysqlbinlog.cc | 38 +++ configure.in | 2 +- .../suite/rpl/r/rpl_do_not_replicate.result | 162 ++++++++++++ .../suite/rpl/t/rpl_do_not_replicate.test | 245 ++++++++++++++++++ sql/log_event.cc | 18 +- sql/log_event.h | 26 +- sql/mysql_priv.h | 2 + sql/mysqld.cc | 10 +- sql/set_var.cc | 56 ++++ sql/set_var.h | 19 ++ sql/slave.cc | 52 ++++ sql/sql_binlog.cc | 9 + sql/sql_repl.cc | 77 +++--- 13 files changed, 677 insertions(+), 39 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_do_not_replicate.result create mode 100644 mysql-test/suite/rpl/t/rpl_do_not_replicate.test diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 5d458090352..40f0348d3a2 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -670,6 +670,31 @@ print_use_stmt(PRINT_EVENT_INFO* pinfo, const Query_log_event *ev) } +/** + Print "SET do_not_replicate=..." statement when needed. + + Not all servers support this (only MariaDB from some version on). So we + mark the SET to only execute from the version of MariaDB that supports it, + and also only output it if we actually see events with the flag set, to not + get spurious errors on MySQL@Oracle servers of higher version that do not + support the flag. + + So we start out assuming @@do_not_replicate is 0, and only output a SET + statement when it changes. +*/ +static void +print_do_not_replicate_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev) +{ + int cur_val; + + cur_val= (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F) != 0; + if (cur_val == pinfo->do_not_replicate) + return; /* Not changed. */ + fprintf(result_file, "/*!50400 SET do_not_replicate=%d*/%s\n", + cur_val, pinfo->delimiter); + pinfo->do_not_replicate= cur_val; +} + /** Prints the given event in base64 format. @@ -802,7 +827,10 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, goto end; } else + { + print_do_not_replicate_statement(print_event_info, ev); ev->print(result_file, print_event_info); + } break; } @@ -832,7 +860,10 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, goto end; } else + { + print_do_not_replicate_statement(print_event_info, ev); ce->print(result_file, print_event_info, TRUE); + } // If this binlog is not 3.23 ; why this test?? if (glob_description_event->binlog_version >= 3) @@ -927,6 +958,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, if (!shall_skip_database(exlq->db)) { print_use_stmt(print_event_info, exlq); + print_do_not_replicate_statement(print_event_info, ev); if (fname) { convert_path_to_forward_slashes(fname); @@ -1030,6 +1062,12 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, } /* FALL THROUGH */ } + case INTVAR_EVENT: + case RAND_EVENT: + case USER_VAR_EVENT: + case XID_EVENT: + print_do_not_replicate_statement(print_event_info, ev); + /* Fall through ... */ default: ev->print(result_file, print_event_info); } diff --git a/configure.in b/configure.in index c9b097d2f84..0c22291d3bc 100644 --- a/configure.in +++ b/configure.in @@ -13,7 +13,7 @@ dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MariaDB Server], [5.2.7-MariaDB], [], [mysql]) +AC_INIT([MariaDB Server], [5.4.0-MariaDB], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM diff --git a/mysql-test/suite/rpl/r/rpl_do_not_replicate.result b/mysql-test/suite/rpl/r/rpl_do_not_replicate.result new file mode 100644 index 00000000000..d0f1df310c1 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_do_not_replicate.result @@ -0,0 +1,162 @@ +include/master-slave.inc +[connection master] +CREATE USER 'nonsuperuser'@'127.0.0.1'; +GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, +SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; +SET GLOBAL replicate_ignore_do_not_replicate=1; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +DROP USER'nonsuperuser'@'127.0.0.1'; +SET GLOBAL replicate_ignore_do_not_replicate=1; +ERROR HY000: This operation cannot be performed with a running slave; run STOP SLAVE first +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=1; +START SLAVE; +SET do_not_replicate=0; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; +INSERT INTO t1(a) VALUES (1); +INSERT INTO t2(a) VALUES (1); +SET do_not_replicate=1; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t1(a) VALUES (2); +INSERT INTO t2(a) VALUES (2); +FLUSH NO_WRITE_TO_BINLOG LOGS; +SHOW TABLES; +Tables_in_test +t1 +t2 +SELECT * FROM t1; +a b +1 NULL +SELECT * FROM t2; +a b +1 NULL +DROP TABLE t3; +FLUSH NO_WRITE_TO_BINLOG LOGS; +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=0; +START SLAVE; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t3(a) VALUES(2); +SELECT * FROM t3; +a b +2 NULL +DROP TABLE t3; +TRUNCATE t1; +RESET MASTER; +SET do_not_replicate=0; +INSERT INTO t1 VALUES (1,0); +SET do_not_replicate=1; +INSERT INTO t1 VALUES (2,0); +SET do_not_replicate=0; +INSERT INTO t1 VALUES (3,0); +SELECT * FROM t1 ORDER by a; +a b +1 0 +2 0 +3 0 +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=1; +TRUNCATE t1; +SELECT * FROM t1 ORDER by a; +a b +1 0 +2 0 +3 0 +START SLAVE; +SELECT * FROM t1 ORDER by a; +a b +1 0 +3 0 +TRUNCATE t1; +STOP SLAVE; +SET GLOBAL sql_slave_skip_counter=2; +SET GLOBAL replicate_ignore_do_not_replicate=1; +START SLAVE; +SET @old_binlog_format= @@binlog_format; +SET binlog_format= statement; +SET do_not_replicate=0; +INSERT INTO t1 VALUES (1,5); +SET do_not_replicate=1; +INSERT INTO t1 VALUES (2,5); +SET do_not_replicate=0; +INSERT INTO t1 VALUES (3,5); +INSERT INTO t1 VALUES (4,5); +SET binlog_format= @old_binlog_format; +SELECT * FROM t1; +a b +4 5 +TRUNCATE t1; +BINLOG '66I6Tg8BAAAAZgAAAGoAAAABAAQANS40LjAtTWFyaWFEQi12YWxncmluZC1tYXgtZGVidWctbG9n +AAAAAAAAAAAAAAAAAADrojpOEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'; +BINLOG 'HaM6ThMBAAAAKgAAANgAAAAAgA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC +HaM6ThcBAAAAJgAAAP4AAAAAgA8AAAAAAAEAAv/8AQAAAAgAAAA='; +BINLOG 'JqM6ThMBAAAAKgAAALEBAAAAAA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC +JqM6ThcBAAAAJgAAANcBAAAAAA8AAAAAAAEAAv/8AgAAAAgAAAA='; +SELECT * FROM t1 ORDER BY a; +a b +1 8 +2 8 +SELECT * FROM t1 ORDER by a; +a b +2 8 +SET do_not_replicate=0; +BEGIN; +SET do_not_replicate=0; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SET do_not_replicate=1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +ROLLBACK; +SET do_not_replicate=1; +BEGIN; +SET do_not_replicate=0; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SET do_not_replicate=1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +COMMIT; +SET autocommit=0; +INSERT INTO t2(a) VALUES(100); +SET do_not_replicate=1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +ROLLBACK; +SET autocommit=1; +SET do_not_replicate=1; +CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION do_not_replicate=x; RETURN x; END| +CREATE PROCEDURE bar(x INT) BEGIN SET SESSION do_not_replicate=x; END| +CREATE FUNCTION baz (x INT) RETURNS INT BEGIN CALL bar(x); RETURN x; END| +SELECT foo(0); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT baz(0); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SET @a= foo(1); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SET @a= baz(1); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UPDATE t2 SET b=foo(0); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +UPDATE t2 SET b=baz(0); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +INSERT INTO t1 VALUES (101, foo(1)); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +INSERT INTO t1 VALUES (101, baz(0)); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT @@do_not_replicate; +@@do_not_replicate +1 +CALL bar(0); +SELECT @@do_not_replicate; +@@do_not_replicate +0 +CALL bar(1); +SELECT @@do_not_replicate; +@@do_not_replicate +1 +DROP FUNCTION foo; +DROP PROCEDURE bar; +DROP FUNCTION baz; +SET do_not_replicate=0; +DROP TABLE t1,t2; +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=0; +START SLAVE; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_do_not_replicate.test b/mysql-test/suite/rpl/t/rpl_do_not_replicate.test new file mode 100644 index 00000000000..5d3984db0bd --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_do_not_replicate.test @@ -0,0 +1,245 @@ +--source include/master-slave.inc +--source include/have_innodb.inc + +connection slave; +# Test that SUPER is required to change @@replicate_ignore_do_not_replicate. +CREATE USER 'nonsuperuser'@'127.0.0.1'; +GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, + SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; +connect(nonpriv, 127.0.0.1, nonsuperuser,, test, $SLAVE_MYPORT,); +connection nonpriv; +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +SET GLOBAL replicate_ignore_do_not_replicate=1; +disconnect nonpriv; +connection slave; +DROP USER'nonsuperuser'@'127.0.0.1'; + +--error ER_SLAVE_MUST_STOP +SET GLOBAL replicate_ignore_do_not_replicate=1; +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=1; +START SLAVE; + +connection master; +SET do_not_replicate=0; + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; +INSERT INTO t1(a) VALUES (1); +INSERT INTO t2(a) VALUES (1); + +SET do_not_replicate=1; + +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t1(a) VALUES (2); +INSERT INTO t2(a) VALUES (2); + +# Inject a rotate event in the binlog stream sent to slave (otherwise we will +# fail sync_slave_with_master as the last event on the master is not present +# on the slave). +FLUSH NO_WRITE_TO_BINLOG LOGS; + +sync_slave_with_master; +connection slave; +SHOW TABLES; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; +DROP TABLE t3; + +FLUSH NO_WRITE_TO_BINLOG LOGS; +sync_slave_with_master; +connection slave; +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=0; +START SLAVE; + +connection master; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t3(a) VALUES(2); +sync_slave_with_master; +connection slave; +SELECT * FROM t3; +connection master; +DROP TABLE t3; + +# +# Test that the slave will preserve the @@do_not_replicate flag in its +# own binlog. +# + +TRUNCATE t1; +sync_slave_with_master; +connection slave; +RESET MASTER; + +connection master; +SET do_not_replicate=0; +INSERT INTO t1 VALUES (1,0); +SET do_not_replicate=1; +INSERT INTO t1 VALUES (2,0); +SET do_not_replicate=0; +INSERT INTO t1 VALUES (3,0); + +sync_slave_with_master; +connection slave; +# Since slave has @@replicate_ignore_do_not_replicate=0, it should have +# applied all events. +SELECT * FROM t1 ORDER by a; + +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=1; +let $SLAVE_DATADIR= `select @@datadir`; + +connection master; +TRUNCATE t1; + +# Now apply the slave binlog to the master, to check that both the slave +# and mysqlbinlog will preserve the @@do_not_replicate flag. +--exec $MYSQL_BINLOG $SLAVE_DATADIR/slave-bin.000001 > $MYSQLTEST_VARDIR/tmp/rpl_do_not_replicate.binlog +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/rpl_do_not_replicate.binlog + +# The master should have all three events. +SELECT * FROM t1 ORDER by a; + +# The slave should be missing event 2, which is marked with the +# @@do_not_replicate flag. + +connection slave; +START SLAVE; + +connection master; +sync_slave_with_master; + +connection slave; +SELECT * FROM t1 ORDER by a; + +# +# Test that @@sql_slave_skip_counter does not count skipped @@do_not_replicate +# events. +# + +connection master; +TRUNCATE t1; + +sync_slave_with_master; +connection slave; +STOP SLAVE; +SET GLOBAL sql_slave_skip_counter=2; +SET GLOBAL replicate_ignore_do_not_replicate=1; +START SLAVE; + +connection master; +# Need to fix @@binlog_format to get consistent event count. +SET @old_binlog_format= @@binlog_format; +SET binlog_format= statement; +SET do_not_replicate=0; +INSERT INTO t1 VALUES (1,5); +SET do_not_replicate=1; +INSERT INTO t1 VALUES (2,5); +SET do_not_replicate=0; +INSERT INTO t1 VALUES (3,5); +INSERT INTO t1 VALUES (4,5); +SET binlog_format= @old_binlog_format; + +sync_slave_with_master; +connection slave; + +# The slave should have skipped the first three inserts (number 1 and 3 due +# to @@sql_slave_skip_counter=2, number 2 due to +# @@replicate_ignore_do_not_replicate=1). So only number 4 should be left. +SELECT * FROM t1; + + +# +# Check that BINLOG statement preserves the @@do_not_replicate flag. +# +connection master; +TRUNCATE t1; + +# Format description log event. +BINLOG '66I6Tg8BAAAAZgAAAGoAAAABAAQANS40LjAtTWFyaWFEQi12YWxncmluZC1tYXgtZGVidWctbG9n +AAAAAAAAAAAAAAAAAADrojpOEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'; +# INSERT INTO t1 VALUES (1,8) # with @@do_not_replicate=1 +BINLOG 'HaM6ThMBAAAAKgAAANgAAAAAgA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC +HaM6ThcBAAAAJgAAAP4AAAAAgA8AAAAAAAEAAv/8AQAAAAgAAAA='; +# INSERT INTO t1 VALUES (2,8) # with @@do_not_replicate=0 +BINLOG 'JqM6ThMBAAAAKgAAALEBAAAAAA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC +JqM6ThcBAAAAJgAAANcBAAAAAA8AAAAAAAEAAv/8AgAAAAgAAAA='; + +SELECT * FROM t1 ORDER BY a; +sync_slave_with_master; +connection slave; +# Slave should have only the second insert, the first should be ignored due to +# the @@do_not_replicate flag. +SELECT * FROM t1 ORDER by a; + + +# Test that it is not possible to d change @@do_not_replicate inside a +# transaction or statement, thereby replicating only parts of statements +# or transactions. +connection master; +SET do_not_replicate=0; + +BEGIN; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET do_not_replicate=0; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET do_not_replicate=1; +ROLLBACK; +SET do_not_replicate=1; +BEGIN; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET do_not_replicate=0; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET do_not_replicate=1; +COMMIT; +SET autocommit=0; +INSERT INTO t2(a) VALUES(100); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET do_not_replicate=1; +ROLLBACK; +SET autocommit=1; + +SET do_not_replicate=1; +--delimiter | +CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION do_not_replicate=x; RETURN x; END| +CREATE PROCEDURE bar(x INT) BEGIN SET SESSION do_not_replicate=x; END| +CREATE FUNCTION baz (x INT) RETURNS INT BEGIN CALL bar(x); RETURN x; END| +--delimiter ; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SELECT foo(0); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SELECT baz(0); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET @a= foo(1); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +SET @a= baz(1); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +UPDATE t2 SET b=foo(0); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +UPDATE t2 SET b=baz(0); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +INSERT INTO t1 VALUES (101, foo(1)); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +INSERT INTO t1 VALUES (101, baz(0)); +SELECT @@do_not_replicate; +CALL bar(0); +SELECT @@do_not_replicate; +CALL bar(1); +SELECT @@do_not_replicate; +DROP FUNCTION foo; +DROP PROCEDURE bar; +DROP FUNCTION baz; + +# Clean up. +connection master; +SET do_not_replicate=0; +DROP TABLE t1,t2; +connection slave; +STOP SLAVE; +SET GLOBAL replicate_ignore_do_not_replicate=0; +START SLAVE; + +--source include/rpl_end.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index 217adce4f66..05313dc3337 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -665,11 +665,13 @@ const char* Log_event::get_type_str() #ifndef MYSQL_CLIENT Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) - :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg), thd(thd_arg) + :log_pos(0), temp_buf(0), exec_time(0), thd(thd_arg) { server_id= thd->server_id; when= thd->start_time; cache_stmt= using_trans; + flags= flags_arg | + (thd->options & OPTION_DO_NOT_REPLICATE ? LOG_EVENT_DO_NOT_REPLICATE_F : 0); } @@ -825,7 +827,9 @@ Log_event::do_shall_skip(Relay_log_info *rli) rli->replicate_same_server_id, rli->slave_skip_counter)); if ((server_id == ::server_id && !rli->replicate_same_server_id) || - (rli->slave_skip_counter == 1 && rli->is_in_group())) + (rli->slave_skip_counter == 1 && rli->is_in_group()) || + (flags & LOG_EVENT_DO_NOT_REPLICATE_F + && opt_replicate_ignore_do_not_replicate)) return EVENT_SKIP_IGNORE; if (rli->slave_skip_counter > 0) return EVENT_SKIP_COUNT; @@ -3483,6 +3487,14 @@ Query_log_event::do_shall_skip(Relay_log_info *rli) DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len)); DBUG_ASSERT(query && q_len > 0); + /* + An event skipped due to @@do_not_replicate must not be counted towards the + number of events to be skipped due to @@sql_slave_skip_counter. + */ + if (flags & LOG_EVENT_DO_NOT_REPLICATE_F && + opt_replicate_ignore_do_not_replicate) + DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE); + if (rli->slave_skip_counter > 0) { if (strcmp("BEGIN", query) == 0) @@ -9780,7 +9792,7 @@ st_print_event_info::st_print_event_info() auto_increment_increment(0),auto_increment_offset(0), charset_inited(0), lc_time_names_number(~0), charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER), - thread_id(0), thread_id_printed(false), + thread_id(0), thread_id_printed(false), do_not_replicate(0), base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE) { /* diff --git a/sql/log_event.h b/sql/log_event.h index 4ea511f45b5..b3a6d0c9d48 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -490,6 +490,19 @@ struct sql_ex_info */ #define LOG_EVENT_RELAY_LOG_F 0x40 +/** + @def LOG_EVENT_DO_NOT_REPLICATE_F + + Flag set by application creating the event (with @@do_not_replicate); the + slave will skip replication of such events if + --replicate-ignore-do-not-replicate is set. + + This is a MariaDB flag; we allocate it from the end of the available + values to reduce risk of conflict with new MySQL flags. +*/ +#define LOG_EVENT_DO_NOT_REPLICATE_F 0x8000 + + /** @def OPTIONS_WRITTEN_TO_BIN_LOG @@ -656,6 +669,11 @@ typedef struct st_print_event_info uint charset_database_number; uint thread_id; bool thread_id_printed; + /* + Track when @@do_not_replicate changes so we need to output a SET + statement for it. + */ + int do_not_replicate; st_print_event_info(); @@ -910,8 +928,8 @@ public: /** Some 16 flags. See the definitions above for LOG_EVENT_TIME_F, - LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F, and - LOG_EVENT_SUPPRESS_USE_F for notes. + LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F, + LOG_EVENT_SUPPRESS_USE_F, and LOG_EVENT_DO_NOT_REPLICATE_F for notes. */ uint16 flags; @@ -3915,6 +3933,8 @@ public: DBUG_PRINT("enter", ("m_incident: %d", m_incident)); m_message.str= NULL; /* Just as a precaution */ m_message.length= 0; + /* Replicate the incident irregardless of @@do_not_replicate. */ + flags&= ~LOG_EVENT_DO_NOT_REPLICATE_F; DBUG_VOID_RETURN; } @@ -3924,6 +3944,8 @@ public: DBUG_ENTER("Incident_log_event::Incident_log_event"); DBUG_PRINT("enter", ("m_incident: %d", m_incident)); m_message= msg; + /* Replicate the incident irregardless of @@do_not_replicate. */ + flags&= ~LOG_EVENT_DO_NOT_REPLICATE_F; DBUG_VOID_RETURN; } #endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 38a29686906..1322ea1b165 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -504,6 +504,7 @@ protected: */ #define TMP_TABLE_FORCE_MYISAM (ULL(1) << 32) #define OPTION_PROFILING (ULL(1) << 33) +#define OPTION_DO_NOT_REPLICATE (ULL(1) << 34) // THD, user @@ -2064,6 +2065,7 @@ extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern my_bool opt_enable_shared_memory; +extern my_bool opt_replicate_ignore_do_not_replicate; extern char *default_tz_name; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3c47fe446ab..e966b35a556 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -553,6 +553,8 @@ uint opt_large_page_size= 0; uint opt_debug_sync_timeout= 0; #endif /* defined(ENABLED_DEBUG_SYNC) */ my_bool opt_old_style_user_limits= 0, trust_function_creators= 0; +my_bool opt_replicate_ignore_do_not_replicate; + /* True if there is at least one per-hour limit for some user, so we should check them before each query (and possibly reset counters when hour is @@ -6085,7 +6087,8 @@ enum options_mysqld OPT_IGNORE_BUILTIN_INNODB, OPT_BINLOG_DIRECT_NON_TRANS_UPDATE, OPT_DEFAULT_CHARACTER_SET_OLD, - OPT_MAX_LONG_DATA_SIZE + OPT_MAX_LONG_DATA_SIZE, + OPT_REPLICATE_IGNORE_DO_NOT_REPLICATE }; @@ -6782,6 +6785,11 @@ each time the SQL thread starts.", "cross database updates. If you need cross database updates to work, " "make sure you have 3.23.28 or later, and use replicate-wild-ignore-" "table=db_name.%. ", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-ignore-do-not-replicate", OPT_REPLICATE_IGNORE_DO_NOT_REPLICATE, + "Tells the slave thread not to replicate events that were created with" + "@@do_not_replicat=1.", &opt_replicate_ignore_do_not_replicate, + &opt_replicate_ignore_do_not_replicate, 0, GET_BOOL, NO_ARG, + 0, 0, 0 ,0, 0, 0}, {"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE, "Tells the slave thread to not replicate to the specified table. To specify " "more than one table to ignore, use the directive multiple times, once for " diff --git a/sql/set_var.cc b/sql/set_var.cc index 4aa30b5634d..dc52ed7baf3 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -117,6 +117,7 @@ static bool set_option_log_bin_bit(THD *thd, set_var *var); static bool set_option_autocommit(THD *thd, set_var *var); static int check_log_update(THD *thd, set_var *var); static bool set_log_update(THD *thd, set_var *var); +static int check_do_not_replicate(THD *thd, set_var *var); static int check_pseudo_thread_id(THD *thd, set_var *var); void fix_binlog_format_after_update(THD *thd, enum_var_type type); static void fix_low_priority_updates(THD *thd, enum_var_type type); @@ -830,6 +831,10 @@ static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL, static sys_var_thd_ulong sys_profiling_history_size(&vars, "profiling_history_size", &SV::profiling_history_size); #endif +static sys_var_thd_bit sys_do_not_replicate(&vars, "do_not_replicate", + check_do_not_replicate, + set_option_bit, + OPTION_DO_NOT_REPLICATE); /* Local state variables */ @@ -906,6 +911,12 @@ static sys_var_thd_set sys_log_slow_verbosity(&vars, "log_slow_verbosity", &SV::log_slow_verbosity, &log_slow_verbosity_typelib); +#ifdef HAVE_REPLICATION +static sys_var_replicate_ignore_do_not_replicate + sys_replicate_ignore_do_not_replicate(&vars, + "replicate_ignore_do_not_replicate", + &opt_replicate_ignore_do_not_replicate); +#endif /* Global read-only variable containing hostname */ static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname); @@ -3268,6 +3279,25 @@ static bool set_log_update(THD *thd, set_var *var) } +static int check_do_not_replicate(THD *thd, set_var *var) +{ + /* + We must not change @@do_not_replicate in the middle of a transaction or + statement, as that could result in only part of the transaction / statement + being replicated. + (This would be particularly serious if we were to replicate eg. + Rows_log_event without Table_map_log_event or transactional updates without + the COMMIT). + */ + if (thd->locked_tables || thd->active_transaction()) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + return 1; + } + return 0; +} + + static int check_pseudo_thread_id(THD *thd, set_var *var) { var->save_result.ulonglong_value= var->value->val_int(); @@ -4412,6 +4442,32 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) } +#ifdef HAVE_REPLICATION +bool sys_var_replicate_ignore_do_not_replicate::update(THD *thd, set_var *var) +{ + bool result; + int thread_mask; + DBUG_ENTER("sys_var_replicate_ignore_do_not_replicate::update"); + + /* Slave threads must be stopped to change the variable. */ + pthread_mutex_lock(&LOCK_active_mi); + lock_slave_threads(active_mi); + init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/); + if (thread_mask) // We refuse if any slave thread is running + { + my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0)); + result= TRUE; + } + else + result= sys_var_bool_ptr::update(thd, var); + + unlock_slave_threads(active_mi); + pthread_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(result); +} +#endif + + uchar *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { diff --git a/sql/set_var.h b/sql/set_var.h index 95885357b83..e2c6f4b95d2 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1283,6 +1283,25 @@ public: bool is_readonly() const; }; + +#ifdef HAVE_REPLICATION +/** + Handler for setting the system variable --replicate-ignore-do-not-replicate. +*/ + +class sys_var_replicate_ignore_do_not_replicate :public sys_var_bool_ptr +{ +public: + sys_var_replicate_ignore_do_not_replicate(sys_var_chain *chain, + const char *name_arg, + my_bool *value_arg) : + sys_var_bool_ptr(chain, name_arg, value_arg) {}; + ~sys_var_replicate_ignore_do_not_replicate() {}; + bool update(THD *thd, set_var *var); +}; +#endif + + /**************************************************************************** Classes for parsing of the SET command ****************************************************************************/ diff --git a/sql/slave.cc b/sql/slave.cc index 435f3d8b95f..bffbfacc40b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1176,6 +1176,38 @@ when it try to get the value of TIME_ZONE global variable from master."; } } + /* + Request the master to filter away events with the @@do_not_replicate flag + set, if we are running with --replicate-ignore-do_not_replicate=1. + */ + if (opt_replicate_ignore_do_not_replicate) + { + if (!mysql_real_query(mysql, STRING_WITH_LEN("SET do_not_replicate=1"))) + { + err_code= mysql_errno(mysql); + if (is_network_error(err_code)) + { + mi->report(ERROR_LEVEL, err_code, + "Setting master-side filtering of @@do_not_replicate failed " + "with error: %s", mysql_error(mysql)); + goto network_err; + } + else if (err_code == ER_UNKNOWN_SYSTEM_VARIABLE) + { + /* + The master is older than the slave and does not support the + @@do_not_replicate feature. + This is not a problem, as such master will not generate events with + the @@do_not_replicate flag set in the first place. We will still + do slave-side filtering of such events though, to handle the (rare) + case of downgrading a master and receiving old events generated from + before the downgrade with the @@do_not_replicate flag set. + */ + DBUG_PRINT("info", ("Old master does not support master-side filtering " + "of @@do_not_replicate events.")); + } + } + } err: if (errmsg) { @@ -2114,6 +2146,8 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli) thd->lex->current_select= 0; if (!ev->when) ev->when= my_time(0); + thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | + (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F ? OPTION_DO_NOT_REPLICATE : 0); ev->thd = thd; // because up to this point, ev->thd == 0 int reason= ev->shall_skip(rli); @@ -3582,6 +3616,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) { int error= 0; ulong inc_pos; + ulong event_pos; Relay_log_info *rli= &mi->rli; pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); DBUG_ENTER("queue_event"); @@ -3666,6 +3701,23 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) break; } + /* + If we filter events master-side (eg. @@do_not_replicate), we will see holes + in the event positions from the master. If we see such a hole, adjust + mi->master_log_pos accordingly so we maintain the correct position (for + reconnect, MASTER_POS_WAIT(), etc.) + */ + if (inc_pos > 0 && + event_len >= LOG_POS_OFFSET+4 && + (event_pos= uint4korr(buf+LOG_POS_OFFSET)) > mi->master_log_pos + inc_pos) + { + inc_pos= event_pos - mi->master_log_pos; + DBUG_PRINT("info", ("Adjust master_log_pos %lu->%lu to account for " + "master-side filtering", + (unsigned long)(mi->master_log_pos + inc_pos), + event_pos)); + } + /* If this event is originating from this server, don't queue it. We don't check this for 3.23 events because it's simpler like this; 3.23 diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 9713ec1ef5c..bb95054a371 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -33,6 +33,7 @@ void mysql_client_binlog_statement(THD* thd) { + ulonglong save_do_not_replicate; DBUG_ENTER("mysql_client_binlog_statement"); DBUG_PRINT("info",("binlog base64: '%*s'", (int) (thd->lex->comment.length < 2048 ? @@ -213,7 +214,15 @@ void mysql_client_binlog_statement(THD* thd) reporting. */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + save_do_not_replicate= thd->options & OPTION_DO_NOT_REPLICATE; + thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | + (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F ? + OPTION_DO_NOT_REPLICATE : 0); + err= ev->apply_event(rli); + + thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | + save_do_not_replicate; #else err= 0; #endif diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 5038d02abca..8de2ce5fcf1 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -337,6 +337,41 @@ Increase max_allowed_packet on master"; } +/* + Helper function for mysql_binlog_send() to write an event down the slave + connection. + + Returns NULL on success, error message string on error. +*/ +static const char * +send_event_to_slave(THD *thd, NET *net, String* const packet) +{ + thd_proc_info(thd, "Sending binlog event to slave"); + + /* + Skip events with the @@do_not_replicate flag set, if slave requested + skipping of such events. + */ + if (thd->options & OPTION_DO_NOT_REPLICATE) + { + uint16 flags= uint2korr(&((*packet)[FLAGS_OFFSET+1])); + if (flags & LOG_EVENT_DO_NOT_REPLICATE_F) + return NULL; + } + + if (my_net_write(net, (uchar*) packet->ptr(), packet->length())) + return "Failed on my_net_write()"; + + DBUG_PRINT("info", ("log event code %d", (*packet)[LOG_EVENT_OFFSET+1] )); + if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) + { + if (send_file(thd)) + return "failed in send_file()"; + } + + return NULL; /* Success */ +} + /* TODO: Clean up loop to only have one call to send_file() */ @@ -349,9 +384,9 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, char search_file_name[FN_REFLEN], *name; IO_CACHE log; File file = -1; - String* packet = &thd->packet; + String* const packet = &thd->packet; int error; - const char *errmsg = "Unknown error"; + const char *errmsg = "Unknown error", *tmp_msg; NET* net = &thd->net; pthread_mutex_t *log_lock; bool binlog_can_be_corrupted= FALSE; @@ -588,9 +623,9 @@ impossible position"; else if ((*packet)[EVENT_TYPE_OFFSET+1] == STOP_EVENT) binlog_can_be_corrupted= FALSE; - if (my_net_write(net, (uchar*) packet->ptr(), packet->length())) + if ((tmp_msg= send_event_to_slave(thd, net, packet))) { - errmsg = "Failed on my_net_write()"; + errmsg = tmp_msg; my_errno= ER_UNKNOWN_ERROR; goto err; } @@ -603,17 +638,6 @@ impossible position"; } }); - DBUG_PRINT("info", ("log event code %d", - (*packet)[LOG_EVENT_OFFSET+1] )); - if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) - { - if (send_file(thd)) - { - errmsg = "failed in send_file()"; - my_errno= ER_UNKNOWN_ERROR; - goto err; - } - } packet->set("\0", 1, &my_charset_bin); } @@ -713,23 +737,12 @@ impossible position"; if (read_packet) { - thd_proc_info(thd, "Sending binlog event to slave"); - if (my_net_write(net, (uchar*) packet->ptr(), packet->length()) ) - { - errmsg = "Failed on my_net_write()"; - my_errno= ER_UNKNOWN_ERROR; - goto err; - } - - if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) - { - if (send_file(thd)) - { - errmsg = "failed in send_file()"; - my_errno= ER_UNKNOWN_ERROR; - goto err; - } - } + if ((tmp_msg= send_event_to_slave(thd, net, packet))) + { + errmsg = tmp_msg; + my_errno= ER_UNKNOWN_ERROR; + goto err; + } packet->set("\0", 1, &my_charset_bin); /* No need to net_flush because we will get to flush later when From b1a13cb15a2cca5dece24c860f64b5ee012ed199 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Aug 2011 13:18:34 +0200 Subject: [PATCH 2/4] MWL#234: After-review fixes, including better names for the new system variables. --- client/mysqlbinlog.cc | 27 +++--- ...ate.result => rpl_skip_replication.result} | 64 +++++++------- ...plicate.test => rpl_skip_replication.test} | 86 +++++++++---------- sql/log_event.cc | 14 +-- sql/log_event.h | 22 ++--- sql/mysql_priv.h | 4 +- sql/mysqld.cc | 17 ++-- sql/set_var.cc | 34 +++++--- sql/set_var.h | 12 +-- sql/slave.cc | 34 +++++--- sql/sql_binlog.cc | 14 +-- sql/sql_repl.cc | 10 ++- 12 files changed, 178 insertions(+), 160 deletions(-) rename mysql-test/suite/rpl/r/{rpl_do_not_replicate.result => rpl_skip_replication.result} (79%) rename mysql-test/suite/rpl/t/{rpl_do_not_replicate.test => rpl_skip_replication.test} (72%) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 40f0348d3a2..4b563090f25 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -671,7 +671,7 @@ print_use_stmt(PRINT_EVENT_INFO* pinfo, const Query_log_event *ev) /** - Print "SET do_not_replicate=..." statement when needed. + Print "SET skip_replication=..." statement when needed. Not all servers support this (only MariaDB from some version on). So we mark the SET to only execute from the version of MariaDB that supports it, @@ -679,20 +679,20 @@ print_use_stmt(PRINT_EVENT_INFO* pinfo, const Query_log_event *ev) get spurious errors on MySQL@Oracle servers of higher version that do not support the flag. - So we start out assuming @@do_not_replicate is 0, and only output a SET + So we start out assuming @@skip_replication is 0, and only output a SET statement when it changes. */ static void -print_do_not_replicate_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev) +print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev) { int cur_val; - cur_val= (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F) != 0; - if (cur_val == pinfo->do_not_replicate) + cur_val= (ev->flags & LOG_EVENT_SKIP_REPLICATION_F) != 0; + if (cur_val == pinfo->skip_replication) return; /* Not changed. */ - fprintf(result_file, "/*!50400 SET do_not_replicate=%d*/%s\n", + fprintf(result_file, "/*!50400 SET skip_replication=%d*/%s\n", cur_val, pinfo->delimiter); - pinfo->do_not_replicate= cur_val; + pinfo->skip_replication= cur_val; } /** @@ -828,7 +828,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, } else { - print_do_not_replicate_statement(print_event_info, ev); + print_skip_replication_statement(print_event_info, ev); ev->print(result_file, print_event_info); } break; @@ -861,7 +861,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, } else { - print_do_not_replicate_statement(print_event_info, ev); + print_skip_replication_statement(print_event_info, ev); ce->print(result_file, print_event_info, TRUE); } @@ -958,10 +958,10 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, if (!shall_skip_database(exlq->db)) { print_use_stmt(print_event_info, exlq); - print_do_not_replicate_statement(print_event_info, ev); if (fname) { convert_path_to_forward_slashes(fname); + print_skip_replication_statement(print_event_info, ev); exlq->print(result_file, print_event_info, fname); } else @@ -1062,13 +1062,8 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, } /* FALL THROUGH */ } - case INTVAR_EVENT: - case RAND_EVENT: - case USER_VAR_EVENT: - case XID_EVENT: - print_do_not_replicate_statement(print_event_info, ev); - /* Fall through ... */ default: + print_skip_replication_statement(print_event_info, ev); ev->print(result_file, print_event_info); } } diff --git a/mysql-test/suite/rpl/r/rpl_do_not_replicate.result b/mysql-test/suite/rpl/r/rpl_skip_replication.result similarity index 79% rename from mysql-test/suite/rpl/r/rpl_do_not_replicate.result rename to mysql-test/suite/rpl/r/rpl_skip_replication.result index d0f1df310c1..4baa5b993a3 100644 --- a/mysql-test/suite/rpl/r/rpl_do_not_replicate.result +++ b/mysql-test/suite/rpl/r/rpl_skip_replication.result @@ -3,20 +3,20 @@ include/master-slave.inc CREATE USER 'nonsuperuser'@'127.0.0.1'; GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; ERROR 42000: Access denied; you need the SUPER privilege for this operation DROP USER'nonsuperuser'@'127.0.0.1'; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; ERROR HY000: This operation cannot be performed with a running slave; run STOP SLAVE first STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; START SLAVE; -SET do_not_replicate=0; +SET skip_replication=0; CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; INSERT INTO t1(a) VALUES (1); INSERT INTO t2(a) VALUES (1); -SET do_not_replicate=1; +SET skip_replication=1; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; INSERT INTO t1(a) VALUES (2); INSERT INTO t2(a) VALUES (2); @@ -34,7 +34,7 @@ a b DROP TABLE t3; FLUSH NO_WRITE_TO_BINLOG LOGS; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=0; +SET GLOBAL replicate_events_marked_for_skip=1; START SLAVE; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; INSERT INTO t3(a) VALUES(2); @@ -44,11 +44,11 @@ a b DROP TABLE t3; TRUNCATE t1; RESET MASTER; -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (1,0); -SET do_not_replicate=1; +SET skip_replication=1; INSERT INTO t1 VALUES (2,0); -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (3,0); SELECT * FROM t1 ORDER by a; a b @@ -56,7 +56,7 @@ a b 2 0 3 0 STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; TRUNCATE t1; SELECT * FROM t1 ORDER by a; a b @@ -71,15 +71,15 @@ a b TRUNCATE t1; STOP SLAVE; SET GLOBAL sql_slave_skip_counter=2; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; START SLAVE; SET @old_binlog_format= @@binlog_format; SET binlog_format= statement; -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (1,5); -SET do_not_replicate=1; +SET skip_replication=1; INSERT INTO t1 VALUES (2,5); -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (3,5); INSERT INTO t1 VALUES (4,5); SET binlog_format= @old_binlog_format; @@ -100,29 +100,29 @@ a b SELECT * FROM t1 ORDER by a; a b 2 8 -SET do_not_replicate=0; +SET skip_replication=0; BEGIN; -SET do_not_replicate=0; +SET skip_replication=0; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction -SET do_not_replicate=1; +SET skip_replication=1; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ROLLBACK; -SET do_not_replicate=1; +SET skip_replication=1; BEGIN; -SET do_not_replicate=0; +SET skip_replication=0; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction -SET do_not_replicate=1; +SET skip_replication=1; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction COMMIT; SET autocommit=0; INSERT INTO t2(a) VALUES(100); -SET do_not_replicate=1; +SET skip_replication=1; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction ROLLBACK; SET autocommit=1; -SET do_not_replicate=1; -CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION do_not_replicate=x; RETURN x; END| -CREATE PROCEDURE bar(x INT) BEGIN SET SESSION do_not_replicate=x; END| +SET skip_replication=1; +CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION skip_replication=x; RETURN x; END| +CREATE PROCEDURE bar(x INT) BEGIN SET SESSION skip_replication=x; END| CREATE FUNCTION baz (x INT) RETURNS INT BEGIN CALL bar(x); RETURN x; END| SELECT foo(0); ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction @@ -140,23 +140,23 @@ INSERT INTO t1 VALUES (101, foo(1)); ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction INSERT INTO t1 VALUES (101, baz(0)); ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction -SELECT @@do_not_replicate; -@@do_not_replicate +SELECT @@skip_replication; +@@skip_replication 1 CALL bar(0); -SELECT @@do_not_replicate; -@@do_not_replicate +SELECT @@skip_replication; +@@skip_replication 0 CALL bar(1); -SELECT @@do_not_replicate; -@@do_not_replicate +SELECT @@skip_replication; +@@skip_replication 1 DROP FUNCTION foo; DROP PROCEDURE bar; DROP FUNCTION baz; -SET do_not_replicate=0; +SET skip_replication=0; DROP TABLE t1,t2; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=0; +SET GLOBAL replicate_events_marked_for_skip=1; START SLAVE; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_do_not_replicate.test b/mysql-test/suite/rpl/t/rpl_skip_replication.test similarity index 72% rename from mysql-test/suite/rpl/t/rpl_do_not_replicate.test rename to mysql-test/suite/rpl/t/rpl_skip_replication.test index 5d3984db0bd..d4657806efe 100644 --- a/mysql-test/suite/rpl/t/rpl_do_not_replicate.test +++ b/mysql-test/suite/rpl/t/rpl_skip_replication.test @@ -2,33 +2,33 @@ --source include/have_innodb.inc connection slave; -# Test that SUPER is required to change @@replicate_ignore_do_not_replicate. +# Test that SUPER is required to change @@replicate_events_marked_for_skip. CREATE USER 'nonsuperuser'@'127.0.0.1'; GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; connect(nonpriv, 127.0.0.1, nonsuperuser,, test, $SLAVE_MYPORT,); connection nonpriv; --error ER_SPECIFIC_ACCESS_DENIED_ERROR -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; disconnect nonpriv; connection slave; DROP USER'nonsuperuser'@'127.0.0.1'; --error ER_SLAVE_MUST_STOP -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; START SLAVE; connection master; -SET do_not_replicate=0; +SET skip_replication=0; CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; INSERT INTO t1(a) VALUES (1); INSERT INTO t2(a) VALUES (1); -SET do_not_replicate=1; +SET skip_replication=1; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; INSERT INTO t1(a) VALUES (2); @@ -52,7 +52,7 @@ FLUSH NO_WRITE_TO_BINLOG LOGS; sync_slave_with_master; connection slave; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=0; +SET GLOBAL replicate_events_marked_for_skip=1; START SLAVE; connection master; @@ -65,7 +65,7 @@ connection master; DROP TABLE t3; # -# Test that the slave will preserve the @@do_not_replicate flag in its +# Test that the slave will preserve the @@skip_replication flag in its # own binlog. # @@ -75,36 +75,36 @@ connection slave; RESET MASTER; connection master; -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (1,0); -SET do_not_replicate=1; +SET skip_replication=1; INSERT INTO t1 VALUES (2,0); -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (3,0); sync_slave_with_master; connection slave; -# Since slave has @@replicate_ignore_do_not_replicate=0, it should have +# Since slave has @@replicate_events_marked_for_skip=1, it should have # applied all events. SELECT * FROM t1 ORDER by a; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; let $SLAVE_DATADIR= `select @@datadir`; connection master; TRUNCATE t1; # Now apply the slave binlog to the master, to check that both the slave -# and mysqlbinlog will preserve the @@do_not_replicate flag. ---exec $MYSQL_BINLOG $SLAVE_DATADIR/slave-bin.000001 > $MYSQLTEST_VARDIR/tmp/rpl_do_not_replicate.binlog ---exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/rpl_do_not_replicate.binlog +# and mysqlbinlog will preserve the @@skip_replication flag. +--exec $MYSQL_BINLOG $SLAVE_DATADIR/slave-bin.000001 > $MYSQLTEST_VARDIR/tmp/rpl_skip_replication.binlog +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/rpl_skip_replication.binlog # The master should have all three events. SELECT * FROM t1 ORDER by a; # The slave should be missing event 2, which is marked with the -# @@do_not_replicate flag. +# @@skip_replication flag. connection slave; START SLAVE; @@ -116,7 +116,7 @@ connection slave; SELECT * FROM t1 ORDER by a; # -# Test that @@sql_slave_skip_counter does not count skipped @@do_not_replicate +# Test that @@sql_slave_skip_counter does not count skipped @@skip_replication # events. # @@ -127,18 +127,18 @@ sync_slave_with_master; connection slave; STOP SLAVE; SET GLOBAL sql_slave_skip_counter=2; -SET GLOBAL replicate_ignore_do_not_replicate=1; +SET GLOBAL replicate_events_marked_for_skip=0; START SLAVE; connection master; # Need to fix @@binlog_format to get consistent event count. SET @old_binlog_format= @@binlog_format; SET binlog_format= statement; -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (1,5); -SET do_not_replicate=1; +SET skip_replication=1; INSERT INTO t1 VALUES (2,5); -SET do_not_replicate=0; +SET skip_replication=0; INSERT INTO t1 VALUES (3,5); INSERT INTO t1 VALUES (4,5); SET binlog_format= @old_binlog_format; @@ -148,12 +148,12 @@ connection slave; # The slave should have skipped the first three inserts (number 1 and 3 due # to @@sql_slave_skip_counter=2, number 2 due to -# @@replicate_ignore_do_not_replicate=1). So only number 4 should be left. +# @@replicate_events_marked_for_skip=0). So only number 4 should be left. SELECT * FROM t1; # -# Check that BINLOG statement preserves the @@do_not_replicate flag. +# Check that BINLOG statement preserves the @@skip_replication flag. # connection master; TRUNCATE t1; @@ -161,10 +161,10 @@ TRUNCATE t1; # Format description log event. BINLOG '66I6Tg8BAAAAZgAAAGoAAAABAAQANS40LjAtTWFyaWFEQi12YWxncmluZC1tYXgtZGVidWctbG9n AAAAAAAAAAAAAAAAAADrojpOEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'; -# INSERT INTO t1 VALUES (1,8) # with @@do_not_replicate=1 +# INSERT INTO t1 VALUES (1,8) # with @@skip_replication=1 BINLOG 'HaM6ThMBAAAAKgAAANgAAAAAgA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC HaM6ThcBAAAAJgAAAP4AAAAAgA8AAAAAAAEAAv/8AQAAAAgAAAA='; -# INSERT INTO t1 VALUES (2,8) # with @@do_not_replicate=0 +# INSERT INTO t1 VALUES (2,8) # with @@skip_replication=0 BINLOG 'JqM6ThMBAAAAKgAAALEBAAAAAA8AAAAAAAEABHRlc3QAAnQxAAIDAwAC JqM6ThcBAAAAJgAAANcBAAAAAA8AAAAAAAEAAv/8AgAAAAgAAAA='; @@ -172,40 +172,40 @@ SELECT * FROM t1 ORDER BY a; sync_slave_with_master; connection slave; # Slave should have only the second insert, the first should be ignored due to -# the @@do_not_replicate flag. +# the @@skip_replication flag. SELECT * FROM t1 ORDER by a; -# Test that it is not possible to d change @@do_not_replicate inside a +# Test that it is not possible to d change @@skip_replication inside a # transaction or statement, thereby replicating only parts of statements # or transactions. connection master; -SET do_not_replicate=0; +SET skip_replication=0; BEGIN; --error ER_LOCK_OR_ACTIVE_TRANSACTION -SET do_not_replicate=0; +SET skip_replication=0; --error ER_LOCK_OR_ACTIVE_TRANSACTION -SET do_not_replicate=1; +SET skip_replication=1; ROLLBACK; -SET do_not_replicate=1; +SET skip_replication=1; BEGIN; --error ER_LOCK_OR_ACTIVE_TRANSACTION -SET do_not_replicate=0; +SET skip_replication=0; --error ER_LOCK_OR_ACTIVE_TRANSACTION -SET do_not_replicate=1; +SET skip_replication=1; COMMIT; SET autocommit=0; INSERT INTO t2(a) VALUES(100); --error ER_LOCK_OR_ACTIVE_TRANSACTION -SET do_not_replicate=1; +SET skip_replication=1; ROLLBACK; SET autocommit=1; -SET do_not_replicate=1; +SET skip_replication=1; --delimiter | -CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION do_not_replicate=x; RETURN x; END| -CREATE PROCEDURE bar(x INT) BEGIN SET SESSION do_not_replicate=x; END| +CREATE FUNCTION foo (x INT) RETURNS INT BEGIN SET SESSION skip_replication=x; RETURN x; END| +CREATE PROCEDURE bar(x INT) BEGIN SET SESSION skip_replication=x; END| CREATE FUNCTION baz (x INT) RETURNS INT BEGIN CALL bar(x); RETURN x; END| --delimiter ; --error ER_LOCK_OR_ACTIVE_TRANSACTION @@ -224,22 +224,22 @@ UPDATE t2 SET b=baz(0); INSERT INTO t1 VALUES (101, foo(1)); --error ER_LOCK_OR_ACTIVE_TRANSACTION INSERT INTO t1 VALUES (101, baz(0)); -SELECT @@do_not_replicate; +SELECT @@skip_replication; CALL bar(0); -SELECT @@do_not_replicate; +SELECT @@skip_replication; CALL bar(1); -SELECT @@do_not_replicate; +SELECT @@skip_replication; DROP FUNCTION foo; DROP PROCEDURE bar; DROP FUNCTION baz; # Clean up. connection master; -SET do_not_replicate=0; +SET skip_replication=0; DROP TABLE t1,t2; connection slave; STOP SLAVE; -SET GLOBAL replicate_ignore_do_not_replicate=0; +SET GLOBAL replicate_events_marked_for_skip=1; START SLAVE; --source include/rpl_end.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index 05313dc3337..ffe4fb2a4fc 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -671,7 +671,7 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) when= thd->start_time; cache_stmt= using_trans; flags= flags_arg | - (thd->options & OPTION_DO_NOT_REPLICATE ? LOG_EVENT_DO_NOT_REPLICATE_F : 0); + (thd->options & OPTION_SKIP_REPLICATION ? LOG_EVENT_SKIP_REPLICATION_F : 0); } @@ -828,8 +828,8 @@ Log_event::do_shall_skip(Relay_log_info *rli) rli->slave_skip_counter)); if ((server_id == ::server_id && !rli->replicate_same_server_id) || (rli->slave_skip_counter == 1 && rli->is_in_group()) || - (flags & LOG_EVENT_DO_NOT_REPLICATE_F - && opt_replicate_ignore_do_not_replicate)) + (flags & LOG_EVENT_SKIP_REPLICATION_F + && !opt_replicate_events_marked_for_skip)) return EVENT_SKIP_IGNORE; if (rli->slave_skip_counter > 0) return EVENT_SKIP_COUNT; @@ -3488,11 +3488,11 @@ Query_log_event::do_shall_skip(Relay_log_info *rli) DBUG_ASSERT(query && q_len > 0); /* - An event skipped due to @@do_not_replicate must not be counted towards the + An event skipped due to @@skip_replication must not be counted towards the number of events to be skipped due to @@sql_slave_skip_counter. */ - if (flags & LOG_EVENT_DO_NOT_REPLICATE_F && - opt_replicate_ignore_do_not_replicate) + if (flags & LOG_EVENT_SKIP_REPLICATION_F && + !opt_replicate_events_marked_for_skip) DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE); if (rli->slave_skip_counter > 0) @@ -9792,7 +9792,7 @@ st_print_event_info::st_print_event_info() auto_increment_increment(0),auto_increment_offset(0), charset_inited(0), lc_time_names_number(~0), charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER), - thread_id(0), thread_id_printed(false), do_not_replicate(0), + thread_id(0), thread_id_printed(false), skip_replication(0), base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE) { /* diff --git a/sql/log_event.h b/sql/log_event.h index b3a6d0c9d48..153c2999bf6 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -491,16 +491,16 @@ struct sql_ex_info #define LOG_EVENT_RELAY_LOG_F 0x40 /** - @def LOG_EVENT_DO_NOT_REPLICATE_F + @def LOG_EVENT_SKIP_REPLICATION_F - Flag set by application creating the event (with @@do_not_replicate); the + Flag set by application creating the event (with @@skip_replication); the slave will skip replication of such events if - --replicate-ignore-do-not-replicate is set. + --replicate-events-marked-for-skip is false. This is a MariaDB flag; we allocate it from the end of the available values to reduce risk of conflict with new MySQL flags. */ -#define LOG_EVENT_DO_NOT_REPLICATE_F 0x8000 +#define LOG_EVENT_SKIP_REPLICATION_F 0x8000 /** @@ -670,10 +670,10 @@ typedef struct st_print_event_info uint thread_id; bool thread_id_printed; /* - Track when @@do_not_replicate changes so we need to output a SET + Track when @@skip_replication changes so we need to output a SET statement for it. */ - int do_not_replicate; + int skip_replication; st_print_event_info(); @@ -929,7 +929,7 @@ public: /** Some 16 flags. See the definitions above for LOG_EVENT_TIME_F, LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F, - LOG_EVENT_SUPPRESS_USE_F, and LOG_EVENT_DO_NOT_REPLICATE_F for notes. + LOG_EVENT_SUPPRESS_USE_F, and LOG_EVENT_SKIP_REPLICATION_F for notes. */ uint16 flags; @@ -3933,8 +3933,8 @@ public: DBUG_PRINT("enter", ("m_incident: %d", m_incident)); m_message.str= NULL; /* Just as a precaution */ m_message.length= 0; - /* Replicate the incident irregardless of @@do_not_replicate. */ - flags&= ~LOG_EVENT_DO_NOT_REPLICATE_F; + /* Replicate the incident irregardless of @@skip_replication. */ + flags&= ~LOG_EVENT_SKIP_REPLICATION_F; DBUG_VOID_RETURN; } @@ -3944,8 +3944,8 @@ public: DBUG_ENTER("Incident_log_event::Incident_log_event"); DBUG_PRINT("enter", ("m_incident: %d", m_incident)); m_message= msg; - /* Replicate the incident irregardless of @@do_not_replicate. */ - flags&= ~LOG_EVENT_DO_NOT_REPLICATE_F; + /* Replicate the incident irregardless of @@skip_replication. */ + flags&= ~LOG_EVENT_SKIP_REPLICATION_F; DBUG_VOID_RETURN; } #endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1322ea1b165..295b913e623 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -504,7 +504,7 @@ protected: */ #define TMP_TABLE_FORCE_MYISAM (ULL(1) << 32) #define OPTION_PROFILING (ULL(1) << 33) -#define OPTION_DO_NOT_REPLICATE (ULL(1) << 34) // THD, user +#define OPTION_SKIP_REPLICATION (ULL(1) << 34) // THD, user @@ -2065,7 +2065,7 @@ extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern my_bool opt_enable_shared_memory; -extern my_bool opt_replicate_ignore_do_not_replicate; +extern my_bool opt_replicate_events_marked_for_skip; extern char *default_tz_name; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e966b35a556..a4477ba58e8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -553,7 +553,7 @@ uint opt_large_page_size= 0; uint opt_debug_sync_timeout= 0; #endif /* defined(ENABLED_DEBUG_SYNC) */ my_bool opt_old_style_user_limits= 0, trust_function_creators= 0; -my_bool opt_replicate_ignore_do_not_replicate; +my_bool opt_replicate_events_marked_for_skip; /* True if there is at least one per-hour limit for some user, so we should @@ -5935,6 +5935,7 @@ enum options_mysqld OPT_SAFEMALLOC_MEM_LIMIT, OPT_REPLICATE_DO_TABLE, OPT_REPLICATE_IGNORE_TABLE, OPT_REPLICATE_WILD_DO_TABLE, OPT_REPLICATE_WILD_IGNORE_TABLE, OPT_REPLICATE_SAME_SERVER_ID, + OPT_REPLICATE_EVENTS_MARKED_FOR_SKIP, OPT_DISCONNECT_SLAVE_EVENT_COUNT, OPT_TC_HEURISTIC_RECOVER, OPT_ABORT_SLAVE_EVENT_COUNT, OPT_LOG_BIN_TRUST_FUNCTION_CREATORS, @@ -6087,8 +6088,7 @@ enum options_mysqld OPT_IGNORE_BUILTIN_INNODB, OPT_BINLOG_DIRECT_NON_TRANS_UPDATE, OPT_DEFAULT_CHARACTER_SET_OLD, - OPT_MAX_LONG_DATA_SIZE, - OPT_REPLICATE_IGNORE_DO_NOT_REPLICATE + OPT_MAX_LONG_DATA_SIZE }; @@ -6785,11 +6785,12 @@ each time the SQL thread starts.", "cross database updates. If you need cross database updates to work, " "make sure you have 3.23.28 or later, and use replicate-wild-ignore-" "table=db_name.%. ", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-ignore-do-not-replicate", OPT_REPLICATE_IGNORE_DO_NOT_REPLICATE, - "Tells the slave thread not to replicate events that were created with" - "@@do_not_replicat=1.", &opt_replicate_ignore_do_not_replicate, - &opt_replicate_ignore_do_not_replicate, 0, GET_BOOL, NO_ARG, - 0, 0, 0 ,0, 0, 0}, + {"replicate-events-marked-for-skip", OPT_REPLICATE_EVENTS_MARKED_FOR_SKIP, + "Tells the slave thread to replicate events that were created with" + "@@skip_replication=1. Default true. If set to false, such events will not" + "be replicated.", &opt_replicate_events_marked_for_skip, + &opt_replicate_events_marked_for_skip, 0, GET_BOOL, NO_ARG, + 1, 0, 0 ,0, 0, 0}, {"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE, "Tells the slave thread to not replicate to the specified table. To specify " "more than one table to ignore, use the directive multiple times, once for " diff --git a/sql/set_var.cc b/sql/set_var.cc index dc52ed7baf3..04d57e50737 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -117,7 +117,7 @@ static bool set_option_log_bin_bit(THD *thd, set_var *var); static bool set_option_autocommit(THD *thd, set_var *var); static int check_log_update(THD *thd, set_var *var); static bool set_log_update(THD *thd, set_var *var); -static int check_do_not_replicate(THD *thd, set_var *var); +static int check_skip_replication(THD *thd, set_var *var); static int check_pseudo_thread_id(THD *thd, set_var *var); void fix_binlog_format_after_update(THD *thd, enum_var_type type); static void fix_low_priority_updates(THD *thd, enum_var_type type); @@ -831,10 +831,20 @@ static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL, static sys_var_thd_ulong sys_profiling_history_size(&vars, "profiling_history_size", &SV::profiling_history_size); #endif -static sys_var_thd_bit sys_do_not_replicate(&vars, "do_not_replicate", - check_do_not_replicate, +/* + When this is set by a connection, binlogged events will be marked with a + corresponding flag. The slave can be configured to not replicate events + so marked. + In the binlog dump thread on the master, this variable is re-used for a + related purpose: The slave sets this flag when connecting to the master to + request that the master filter out (ie. not send) any events with the flag + set, thus saving network traffic on events that would be ignored by the + slave anyway. +*/ +static sys_var_thd_bit sys_skip_replication(&vars, "skip_replication", + check_skip_replication, set_option_bit, - OPTION_DO_NOT_REPLICATE); + OPTION_SKIP_REPLICATION); /* Local state variables */ @@ -912,10 +922,10 @@ static sys_var_thd_set sys_log_slow_verbosity(&vars, &SV::log_slow_verbosity, &log_slow_verbosity_typelib); #ifdef HAVE_REPLICATION -static sys_var_replicate_ignore_do_not_replicate - sys_replicate_ignore_do_not_replicate(&vars, - "replicate_ignore_do_not_replicate", - &opt_replicate_ignore_do_not_replicate); +static sys_var_replicate_events_marked_for_skip + sys_replicate_events_marked_for_skip(&vars, + "replicate_events_marked_for_skip", + &opt_replicate_events_marked_for_skip); #endif /* Global read-only variable containing hostname */ @@ -3279,10 +3289,10 @@ static bool set_log_update(THD *thd, set_var *var) } -static int check_do_not_replicate(THD *thd, set_var *var) +static int check_skip_replication(THD *thd, set_var *var) { /* - We must not change @@do_not_replicate in the middle of a transaction or + We must not change @@skip_replication in the middle of a transaction or statement, as that could result in only part of the transaction / statement being replicated. (This would be particularly serious if we were to replicate eg. @@ -4443,11 +4453,11 @@ sys_var_event_scheduler::update(THD *thd, set_var *var) #ifdef HAVE_REPLICATION -bool sys_var_replicate_ignore_do_not_replicate::update(THD *thd, set_var *var) +bool sys_var_replicate_events_marked_for_skip::update(THD *thd, set_var *var) { bool result; int thread_mask; - DBUG_ENTER("sys_var_replicate_ignore_do_not_replicate::update"); + DBUG_ENTER("sys_var_replicate_events_marked_for_skip::update"); /* Slave threads must be stopped to change the variable. */ pthread_mutex_lock(&LOCK_active_mi); diff --git a/sql/set_var.h b/sql/set_var.h index e2c6f4b95d2..25a9a054ce0 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1286,17 +1286,17 @@ public: #ifdef HAVE_REPLICATION /** - Handler for setting the system variable --replicate-ignore-do-not-replicate. + Handler for setting the system variable --replicate-events-marked-for-skip. */ -class sys_var_replicate_ignore_do_not_replicate :public sys_var_bool_ptr +class sys_var_replicate_events_marked_for_skip :public sys_var_bool_ptr { public: - sys_var_replicate_ignore_do_not_replicate(sys_var_chain *chain, - const char *name_arg, - my_bool *value_arg) : + sys_var_replicate_events_marked_for_skip(sys_var_chain *chain, + const char *name_arg, + my_bool *value_arg) : sys_var_bool_ptr(chain, name_arg, value_arg) {}; - ~sys_var_replicate_ignore_do_not_replicate() {}; + ~sys_var_replicate_events_marked_for_skip() {}; bool update(THD *thd, set_var *var); }; #endif diff --git a/sql/slave.cc b/sql/slave.cc index bffbfacc40b..7e759eecb90 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1177,18 +1177,18 @@ when it try to get the value of TIME_ZONE global variable from master."; } /* - Request the master to filter away events with the @@do_not_replicate flag - set, if we are running with --replicate-ignore-do_not_replicate=1. + Request the master to filter away events with the @@skip_replication flag + set, if we are running with --replicate-events-marked-for-skip=0. */ - if (opt_replicate_ignore_do_not_replicate) + if (!opt_replicate_events_marked_for_skip) { - if (!mysql_real_query(mysql, STRING_WITH_LEN("SET do_not_replicate=1"))) + if (mysql_real_query(mysql, STRING_WITH_LEN("SET skip_replication=1"))) { err_code= mysql_errno(mysql); if (is_network_error(err_code)) { mi->report(ERROR_LEVEL, err_code, - "Setting master-side filtering of @@do_not_replicate failed " + "Setting master-side filtering of @@skip_replication failed " "with error: %s", mysql_error(mysql)); goto network_err; } @@ -1196,15 +1196,24 @@ when it try to get the value of TIME_ZONE global variable from master."; { /* The master is older than the slave and does not support the - @@do_not_replicate feature. + @@skip_replication feature. This is not a problem, as such master will not generate events with - the @@do_not_replicate flag set in the first place. We will still + the @@skip_replication flag set in the first place. We will still do slave-side filtering of such events though, to handle the (rare) case of downgrading a master and receiving old events generated from - before the downgrade with the @@do_not_replicate flag set. + before the downgrade with the @@skip_replication flag set. */ DBUG_PRINT("info", ("Old master does not support master-side filtering " - "of @@do_not_replicate events.")); + "of @@skip_replication events.")); + } + else + { + /* Fatal error */ + errmsg= "The slave I/O thread stops because a fatal error is " + "encountered when it tries to request filtering of events marked " + "with the @@skip_replication flag."; + sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); + goto err; } } } @@ -2146,8 +2155,8 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli) thd->lex->current_select= 0; if (!ev->when) ev->when= my_time(0); - thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | - (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F ? OPTION_DO_NOT_REPLICATE : 0); + thd->options= (thd->options & ~OPTION_SKIP_REPLICATION) | + (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0); ev->thd = thd; // because up to this point, ev->thd == 0 int reason= ev->shall_skip(rli); @@ -3627,7 +3636,6 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */) DBUG_RETURN(queue_old_event(mi,buf,event_len)); - LINT_INIT(inc_pos); pthread_mutex_lock(&mi->data_lock); switch (buf[EVENT_TYPE_OFFSET]) { @@ -3702,7 +3710,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) } /* - If we filter events master-side (eg. @@do_not_replicate), we will see holes + If we filter events master-side (eg. @@skip_replication), we will see holes in the event positions from the master. If we see such a hole, adjust mi->master_log_pos accordingly so we maintain the correct position (for reconnect, MASTER_POS_WAIT(), etc.) diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index bb95054a371..319d08d2a96 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -33,7 +33,7 @@ void mysql_client_binlog_statement(THD* thd) { - ulonglong save_do_not_replicate; + ulonglong save_skip_replication; DBUG_ENTER("mysql_client_binlog_statement"); DBUG_PRINT("info",("binlog base64: '%*s'", (int) (thd->lex->comment.length < 2048 ? @@ -214,15 +214,15 @@ void mysql_client_binlog_statement(THD* thd) reporting. */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) - save_do_not_replicate= thd->options & OPTION_DO_NOT_REPLICATE; - thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | - (ev->flags & LOG_EVENT_DO_NOT_REPLICATE_F ? - OPTION_DO_NOT_REPLICATE : 0); + save_skip_replication= thd->options & OPTION_SKIP_REPLICATION; + thd->options= (thd->options & ~OPTION_SKIP_REPLICATION) | + (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? + OPTION_SKIP_REPLICATION : 0); err= ev->apply_event(rli); - thd->options= (thd->options & ~OPTION_DO_NOT_REPLICATE) | - save_do_not_replicate; + thd->options= (thd->options & ~OPTION_SKIP_REPLICATION) | + save_skip_replication; #else err= 0; #endif diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 8de2ce5fcf1..8c2e6e82cd4 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -349,13 +349,17 @@ send_event_to_slave(THD *thd, NET *net, String* const packet) thd_proc_info(thd, "Sending binlog event to slave"); /* - Skip events with the @@do_not_replicate flag set, if slave requested + Skip events with the @@skip_replication flag set, if slave requested skipping of such events. */ - if (thd->options & OPTION_DO_NOT_REPLICATE) + if (thd->options & OPTION_SKIP_REPLICATION) { + /* + The first byte of the packet is a '\0' to distinguish it from an error + packet. So the actual event starts at offset +1. + */ uint16 flags= uint2korr(&((*packet)[FLAGS_OFFSET+1])); - if (flags & LOG_EVENT_DO_NOT_REPLICATE_F) + if (flags & LOG_EVENT_SKIP_REPLICATION_F) return NULL; } From 50846bf7e5ec60e081b98d2b5d74680e65211d91 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Aug 2011 10:05:14 +0200 Subject: [PATCH 3/4] MWL#234: Add MTR tests for SESSION/GLOBAL semantics of new system variables. --- .../suite/rpl/r/rpl_skip_replication.result | 23 ++++++++++++++++++- .../suite/rpl/t/rpl_skip_replication.test | 11 ++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_skip_replication.result b/mysql-test/suite/rpl/r/rpl_skip_replication.result index 4baa5b993a3..51b8758e209 100644 --- a/mysql-test/suite/rpl/r/rpl_skip_replication.result +++ b/mysql-test/suite/rpl/r/rpl_skip_replication.result @@ -6,12 +6,33 @@ SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; SET GLOBAL replicate_events_marked_for_skip=0; ERROR 42000: Access denied; you need the SUPER privilege for this operation DROP USER'nonsuperuser'@'127.0.0.1'; +SELECT @@global.replicate_events_marked_for_skip; +@@global.replicate_events_marked_for_skip +1 SET GLOBAL replicate_events_marked_for_skip=0; ERROR HY000: This operation cannot be performed with a running slave; run STOP SLAVE first +SELECT @@global.replicate_events_marked_for_skip; +@@global.replicate_events_marked_for_skip +1 STOP SLAVE; +SET SESSION replicate_events_marked_for_skip=0; +ERROR HY000: Variable 'replicate_events_marked_for_skip' is a GLOBAL variable and should be set with SET GLOBAL +SELECT @@global.replicate_events_marked_for_skip; +@@global.replicate_events_marked_for_skip +1 SET GLOBAL replicate_events_marked_for_skip=0; +SELECT @@global.replicate_events_marked_for_skip; +@@global.replicate_events_marked_for_skip +0 START SLAVE; -SET skip_replication=0; +SELECT @@skip_replication; +@@skip_replication +0 +SET GLOBAL skip_replication=1; +ERROR HY000: Variable 'skip_replication' is a SESSION variable and can't be used with SET GLOBAL +SELECT @@skip_replication; +@@skip_replication +0 CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; INSERT INTO t1(a) VALUES (1); diff --git a/mysql-test/suite/rpl/t/rpl_skip_replication.test b/mysql-test/suite/rpl/t/rpl_skip_replication.test index d4657806efe..c78eeef29a4 100644 --- a/mysql-test/suite/rpl/t/rpl_skip_replication.test +++ b/mysql-test/suite/rpl/t/rpl_skip_replication.test @@ -14,14 +14,23 @@ disconnect nonpriv; connection slave; DROP USER'nonsuperuser'@'127.0.0.1'; +SELECT @@global.replicate_events_marked_for_skip; --error ER_SLAVE_MUST_STOP SET GLOBAL replicate_events_marked_for_skip=0; +SELECT @@global.replicate_events_marked_for_skip; STOP SLAVE; +--error ER_GLOBAL_VARIABLE +SET SESSION replicate_events_marked_for_skip=0; +SELECT @@global.replicate_events_marked_for_skip; SET GLOBAL replicate_events_marked_for_skip=0; +SELECT @@global.replicate_events_marked_for_skip; START SLAVE; connection master; -SET skip_replication=0; +SELECT @@skip_replication; +--error ER_LOCAL_VARIABLE +SET GLOBAL skip_replication=1; +SELECT @@skip_replication; CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=myisam; CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; From 9313032283f1650d11fb36066f31d966e8492bdc Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Aug 2011 11:51:02 +0200 Subject: [PATCH 4/4] MWL#234: Implement option to switch between master-side and client-side filtering of @@skip_replication events. --- .../suite/rpl/r/rpl_skip_replication.result | 80 ++++++++++-- .../suite/rpl/t/rpl_skip_replication.test | 123 ++++++++++++++++-- sql/log_event.cc | 6 +- sql/log_event.h | 2 +- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 28 +++- sql/set_var.cc | 6 +- sql/set_var.h | 11 +- sql/slave.cc | 5 +- sql/slave.h | 9 ++ 10 files changed, 230 insertions(+), 42 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_skip_replication.result b/mysql-test/suite/rpl/r/rpl_skip_replication.result index 51b8758e209..6dea2a99fe8 100644 --- a/mysql-test/suite/rpl/r/rpl_skip_replication.result +++ b/mysql-test/suite/rpl/r/rpl_skip_replication.result @@ -3,27 +3,27 @@ include/master-slave.inc CREATE USER 'nonsuperuser'@'127.0.0.1'; GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, SELECT,UPDATE ON *.* TO 'nonsuperuser'@'127.0.0.1'; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; ERROR 42000: Access denied; you need the SUPER privilege for this operation DROP USER'nonsuperuser'@'127.0.0.1'; SELECT @@global.replicate_events_marked_for_skip; @@global.replicate_events_marked_for_skip -1 -SET GLOBAL replicate_events_marked_for_skip=0; +replicate +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; ERROR HY000: This operation cannot be performed with a running slave; run STOP SLAVE first SELECT @@global.replicate_events_marked_for_skip; @@global.replicate_events_marked_for_skip -1 +replicate STOP SLAVE; -SET SESSION replicate_events_marked_for_skip=0; +SET SESSION replicate_events_marked_for_skip=FILTER_ON_MASTER; ERROR HY000: Variable 'replicate_events_marked_for_skip' is a GLOBAL variable and should be set with SET GLOBAL SELECT @@global.replicate_events_marked_for_skip; @@global.replicate_events_marked_for_skip -1 -SET GLOBAL replicate_events_marked_for_skip=0; +replicate +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; SELECT @@global.replicate_events_marked_for_skip; @@global.replicate_events_marked_for_skip -0 +filter_on_master START SLAVE; SELECT @@skip_replication; @@skip_replication @@ -55,8 +55,29 @@ a b DROP TABLE t3; FLUSH NO_WRITE_TO_BINLOG LOGS; STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=1; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; START SLAVE; +SET skip_replication=1; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t1(a) VALUES (3); +INSERT INTO t2(a) VALUES (3); +FLUSH NO_WRITE_TO_BINLOG LOGS; +SHOW TABLES; +Tables_in_test +t1 +t2 +SELECT * FROM t1; +a b +1 NULL +SELECT * FROM t2; +a b +1 NULL +DROP TABLE t3; +FLUSH NO_WRITE_TO_BINLOG LOGS; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; +SET skip_replication=1; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; INSERT INTO t3(a) VALUES(2); SELECT * FROM t3; @@ -77,7 +98,7 @@ a b 2 0 3 0 STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; TRUNCATE t1; SELECT * FROM t1 ORDER by a; a b @@ -92,7 +113,7 @@ a b TRUNCATE t1; STOP SLAVE; SET GLOBAL sql_slave_skip_counter=2; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; START SLAVE; SET @old_binlog_format= @@binlog_format; SET binlog_format= statement; @@ -175,9 +196,44 @@ SELECT @@skip_replication; DROP FUNCTION foo; DROP PROCEDURE bar; DROP FUNCTION baz; +SET skip_replication= 0; +TRUNCATE t1; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; +START SLAVE IO_THREAD; +SET skip_replication= 1; +INSERT INTO t1(a) VALUES (1); +SET skip_replication= 0; +INSERT INTO t1(a) VALUES (2); +include/save_master_pos.inc +include/sync_io_with_master.inc +STOP SLAVE IO_THREAD; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; +SELECT * FROM t1; +a b +2 NULL +SET skip_replication= 0; +TRUNCATE t1; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; +START SLAVE IO_THREAD; +SET skip_replication= 1; +INSERT INTO t1(a) VALUES (1); +SET skip_replication= 0; +INSERT INTO t1(a) VALUES (2); +include/save_master_pos.inc +include/sync_io_with_master.inc +STOP SLAVE IO_THREAD; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; +SELECT * FROM t1 ORDER BY a; +a b +1 NULL +2 NULL SET skip_replication=0; DROP TABLE t1,t2; STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=1; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; START SLAVE; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_skip_replication.test b/mysql-test/suite/rpl/t/rpl_skip_replication.test index c78eeef29a4..49e49745fd9 100644 --- a/mysql-test/suite/rpl/t/rpl_skip_replication.test +++ b/mysql-test/suite/rpl/t/rpl_skip_replication.test @@ -9,20 +9,20 @@ GRANT ALTER,CREATE,DELETE,DROP,EVENT,INSERT,PROCESS,REPLICATION SLAVE, connect(nonpriv, 127.0.0.1, nonsuperuser,, test, $SLAVE_MYPORT,); connection nonpriv; --error ER_SPECIFIC_ACCESS_DENIED_ERROR -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; disconnect nonpriv; connection slave; DROP USER'nonsuperuser'@'127.0.0.1'; SELECT @@global.replicate_events_marked_for_skip; --error ER_SLAVE_MUST_STOP -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; SELECT @@global.replicate_events_marked_for_skip; STOP SLAVE; --error ER_GLOBAL_VARIABLE -SET SESSION replicate_events_marked_for_skip=0; +SET SESSION replicate_events_marked_for_skip=FILTER_ON_MASTER; SELECT @@global.replicate_events_marked_for_skip; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; SELECT @@global.replicate_events_marked_for_skip; START SLAVE; @@ -37,6 +37,8 @@ CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=innodb; INSERT INTO t1(a) VALUES (1); INSERT INTO t2(a) VALUES (1); + +# Test that master-side filtering works. SET skip_replication=1; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; @@ -59,12 +61,46 @@ DROP TABLE t3; FLUSH NO_WRITE_TO_BINLOG LOGS; sync_slave_with_master; + + +# Test that slave-side filtering works. connection slave; STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=1; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; START SLAVE; connection master; +SET skip_replication=1; +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; +INSERT INTO t1(a) VALUES (3); +INSERT INTO t2(a) VALUES (3); + +# Inject a rotate event in the binlog stream sent to slave (otherwise we will +# fail sync_slave_with_master as the last event on the master is not present +# on the slave). +FLUSH NO_WRITE_TO_BINLOG LOGS; + +sync_slave_with_master; +connection slave; +SHOW TABLES; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; +DROP TABLE t3; + +FLUSH NO_WRITE_TO_BINLOG LOGS; +sync_slave_with_master; +connection slave; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; + + +# Test that events with @@skip_replication=1 are not filtered when filtering is +# not set on slave. +connection master; +SET skip_replication=1; CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=myisam; INSERT INTO t3(a) VALUES(2); sync_slave_with_master; @@ -93,12 +129,12 @@ INSERT INTO t1 VALUES (3,0); sync_slave_with_master; connection slave; -# Since slave has @@replicate_events_marked_for_skip=1, it should have +# Since slave has @@replicate_events_marked_for_skip=REPLICATE, it should have # applied all events. SELECT * FROM t1 ORDER by a; STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; let $SLAVE_DATADIR= `select @@datadir`; connection master; @@ -136,7 +172,7 @@ sync_slave_with_master; connection slave; STOP SLAVE; SET GLOBAL sql_slave_skip_counter=2; -SET GLOBAL replicate_events_marked_for_skip=0; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; START SLAVE; connection master; @@ -157,7 +193,8 @@ connection slave; # The slave should have skipped the first three inserts (number 1 and 3 due # to @@sql_slave_skip_counter=2, number 2 due to -# @@replicate_events_marked_for_skip=0). So only number 4 should be left. +# @@replicate_events_marked_for_skip=FILTER_ON_SLAVE). So only number 4 +# should be left. SELECT * FROM t1; @@ -242,13 +279,79 @@ DROP FUNCTION foo; DROP PROCEDURE bar; DROP FUNCTION baz; + +# Test that master-side filtering happens on the master side, and that +# slave-side filtering happens on the slave. + +# First test that events do not reach the slave when master-side filtering +# is configured. Do this by replicating first with only the IO thread running +# and master-side filtering; then change to no filtering and start the SQL +# thread. This should still skip the events, as master-side filtering +# means the events never reached the slave. +connection master; +SET skip_replication= 0; +TRUNCATE t1; +sync_slave_with_master; +connection slave; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_MASTER; +START SLAVE IO_THREAD; +connection master; +SET skip_replication= 1; +INSERT INTO t1(a) VALUES (1); +SET skip_replication= 0; +INSERT INTO t1(a) VALUES (2); +--source include/save_master_pos.inc +connection slave; +--source include/sync_io_with_master.inc +STOP SLAVE IO_THREAD; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; +connection master; +sync_slave_with_master; +connection slave; +# Now only the second insert of (2) should be visible, as the first was +# filtered on the master, so even though the SQL thread ran without skipping +# events, it will never see the event in the first place. +SELECT * FROM t1; + +# Now tests that when slave-side filtering is configured, events _do_ reach +# the slave. +connection master; +SET skip_replication= 0; +TRUNCATE t1; +sync_slave_with_master; +connection slave; +STOP SLAVE; +SET GLOBAL replicate_events_marked_for_skip=FILTER_ON_SLAVE; +START SLAVE IO_THREAD; +connection master; +SET skip_replication= 1; +INSERT INTO t1(a) VALUES (1); +SET skip_replication= 0; +INSERT INTO t1(a) VALUES (2); +--source include/save_master_pos.inc +connection slave; +--source include/sync_io_with_master.inc +STOP SLAVE IO_THREAD; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; +START SLAVE; +connection master; +sync_slave_with_master; +connection slave; +# Now both inserts should be visible. Since filtering was configured to be +# slave-side, the event is in the relay log, and when the SQL thread ran we +# had disabled filtering again. +SELECT * FROM t1 ORDER BY a; + + # Clean up. connection master; SET skip_replication=0; DROP TABLE t1,t2; connection slave; STOP SLAVE; -SET GLOBAL replicate_events_marked_for_skip=1; +SET GLOBAL replicate_events_marked_for_skip=REPLICATE; START SLAVE; --source include/rpl_end.inc diff --git a/sql/log_event.cc b/sql/log_event.cc index ffe4fb2a4fc..cf1e1229c91 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -828,8 +828,8 @@ Log_event::do_shall_skip(Relay_log_info *rli) rli->slave_skip_counter)); if ((server_id == ::server_id && !rli->replicate_same_server_id) || (rli->slave_skip_counter == 1 && rli->is_in_group()) || - (flags & LOG_EVENT_SKIP_REPLICATION_F - && !opt_replicate_events_marked_for_skip)) + (flags & LOG_EVENT_SKIP_REPLICATION_F && + opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE)) return EVENT_SKIP_IGNORE; if (rli->slave_skip_counter > 0) return EVENT_SKIP_COUNT; @@ -3492,7 +3492,7 @@ Query_log_event::do_shall_skip(Relay_log_info *rli) number of events to be skipped due to @@sql_slave_skip_counter. */ if (flags & LOG_EVENT_SKIP_REPLICATION_F && - !opt_replicate_events_marked_for_skip) + opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE) DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE); if (rli->slave_skip_counter > 0) diff --git a/sql/log_event.h b/sql/log_event.h index 153c2999bf6..aeab4bc230c 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -495,7 +495,7 @@ struct sql_ex_info Flag set by application creating the event (with @@skip_replication); the slave will skip replication of such events if - --replicate-events-marked-for-skip is false. + --replicate-events-marked-for-skip is not set to REPLICATE. This is a MariaDB flag; we allocate it from the end of the available values to reduce risk of conflict with new MySQL flags. diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 295b913e623..784756f0061 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2065,7 +2065,7 @@ extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern my_bool opt_enable_shared_memory; -extern my_bool opt_replicate_events_marked_for_skip; +extern uint opt_replicate_events_marked_for_skip; extern char *default_tz_name; #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a4477ba58e8..94882051dfa 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -389,6 +389,16 @@ TYPELIB plugin_maturity_values= const int server_maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN; +/* These names must match RPL_SKIP_XXX #defines in slave.h. */ +static const char *replicate_events_marked_for_skip_names[]= +{ "replicate", "filter_on_slave", "filter_on_master", 0 }; + +TYPELIB replicate_events_marked_for_skip_typelib= +{ + array_elements(replicate_events_marked_for_skip_names) - 1, "", + replicate_events_marked_for_skip_names, 0 +}; + const char *first_keyword= "first", *binary_keyword= "BINARY"; const char *my_localhost= "localhost", *delayed_user= "DELAYED"; #if SIZEOF_OFF_T > 4 && defined(BIG_TABLES) @@ -553,7 +563,7 @@ uint opt_large_page_size= 0; uint opt_debug_sync_timeout= 0; #endif /* defined(ENABLED_DEBUG_SYNC) */ my_bool opt_old_style_user_limits= 0, trust_function_creators= 0; -my_bool opt_replicate_events_marked_for_skip; +uint opt_replicate_events_marked_for_skip; /* True if there is at least one per-hour limit for some user, so we should @@ -6785,12 +6795,6 @@ each time the SQL thread starts.", "cross database updates. If you need cross database updates to work, " "make sure you have 3.23.28 or later, and use replicate-wild-ignore-" "table=db_name.%. ", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-events-marked-for-skip", OPT_REPLICATE_EVENTS_MARKED_FOR_SKIP, - "Tells the slave thread to replicate events that were created with" - "@@skip_replication=1. Default true. If set to false, such events will not" - "be replicated.", &opt_replicate_events_marked_for_skip, - &opt_replicate_events_marked_for_skip, 0, GET_BOOL, NO_ARG, - 1, 0, 0 ,0, 0, 0}, {"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE, "Tells the slave thread to not replicate to the specified table. To specify " "more than one table to ignore, use the directive multiple times, once for " @@ -6807,6 +6811,16 @@ each time the SQL thread starts.", "Can't be set to 1 if --log-slave-updates is used.", &replicate_same_server_id, &replicate_same_server_id, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-events-marked-for-skip", OPT_REPLICATE_EVENTS_MARKED_FOR_SKIP, + "Whether the slave should replicate events that were created with " + "@@skip_replication=1 on the master. Default REPLICATE (no events are " + "skipped). Other values are FILTER_ON_SLAVE (events will be sent by the " + "master but ignored by the slave) and FILTER_ON_MASTER (events marked with " + "@@skip_replication=1 will be filtered on the master and never be sent to " + "the slave).", &opt_replicate_events_marked_for_skip, + &opt_replicate_events_marked_for_skip, + &replicate_events_marked_for_skip_typelib, GET_ENUM, REQUIRED_ARG, + RPL_SKIP_REPLICATE, 0, 0 ,0, 0, 0}, #endif {"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE, "Tells the slave thread to restrict replication to the tables that match " diff --git a/sql/set_var.cc b/sql/set_var.cc index 04d57e50737..2150cd62359 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -925,7 +925,9 @@ static sys_var_thd_set sys_log_slow_verbosity(&vars, static sys_var_replicate_events_marked_for_skip sys_replicate_events_marked_for_skip(&vars, "replicate_events_marked_for_skip", - &opt_replicate_events_marked_for_skip); + &opt_replicate_events_marked_for_skip, + &replicate_events_marked_for_skip_typelib, + NULL); #endif /* Global read-only variable containing hostname */ @@ -4469,7 +4471,7 @@ bool sys_var_replicate_events_marked_for_skip::update(THD *thd, set_var *var) result= TRUE; } else - result= sys_var_bool_ptr::update(thd, var); + result= sys_var_enum::update(thd, var); unlock_slave_threads(active_mi); pthread_mutex_unlock(&LOCK_active_mi); diff --git a/sql/set_var.h b/sql/set_var.h index 25a9a054ce0..2f847ed6df9 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -31,7 +31,8 @@ typedef struct system_variables SV; typedef struct my_locale_st MY_LOCALE; extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib, - optimizer_switch_typelib, slave_exec_mode_typelib; + optimizer_switch_typelib, slave_exec_mode_typelib, + replicate_events_marked_for_skip_typelib; typedef int (*sys_check_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *); @@ -1289,13 +1290,15 @@ public: Handler for setting the system variable --replicate-events-marked-for-skip. */ -class sys_var_replicate_events_marked_for_skip :public sys_var_bool_ptr +class sys_var_replicate_events_marked_for_skip :public sys_var_enum { public: sys_var_replicate_events_marked_for_skip(sys_var_chain *chain, const char *name_arg, - my_bool *value_arg) : - sys_var_bool_ptr(chain, name_arg, value_arg) {}; + uint *value_arg, + TYPELIB *typelib, + sys_after_update_func func) : + sys_var_enum(chain, name_arg, value_arg, typelib, func) {}; ~sys_var_replicate_events_marked_for_skip() {}; bool update(THD *thd, set_var *var); }; diff --git a/sql/slave.cc b/sql/slave.cc index 7e759eecb90..373661075bb 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1178,9 +1178,10 @@ when it try to get the value of TIME_ZONE global variable from master."; /* Request the master to filter away events with the @@skip_replication flag - set, if we are running with --replicate-events-marked-for-skip=0. + set, if we are running with + --replicate-events-marked-for-skip=FILTER_ON_MASTER. */ - if (!opt_replicate_events_marked_for_skip) + if (opt_replicate_events_marked_for_skip == RPL_SKIP_FILTER_ON_MASTER) { if (mysql_real_query(mysql, STRING_WITH_LEN("SET skip_replication=1"))) { diff --git a/sql/slave.h b/sql/slave.h index 1aa5b374e4b..802320446bf 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -134,6 +134,15 @@ extern ulonglong relay_log_space_limit; */ #define SLAVE_FORCE_ALL 4 +/* + Values for the option --replicate-events-marked-for-skip. + Must match the typelib replicate_events_marked_for_skip_typelib in mysqld.cc +*/ +#define RPL_SKIP_REPLICATE 0 +#define RPL_SKIP_FILTER_ON_SLAVE 1 +#define RPL_SKIP_FILTER_ON_MASTER 2 + + int init_slave(); void init_slave_skip_errors(const char* arg); bool flush_relay_log_info(Relay_log_info* rli);