summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Nesterenko <brandon.nesterenko@mariadb.com>2021-11-03 08:18:48 -0600
committerBrandon Nesterenko <brandon.nesterenko@mariadb.com>2021-11-03 08:18:48 -0600
commitfa12a418bec21ae98c0d177d7065c1284a5c08b0 (patch)
treeaf836c424bbe90d71edc5737180cd14189791dff
parentf591888fdfe2b4ea49bad6c5521cac33a154c786 (diff)
downloadmariadb-git-fa12a418bec21ae98c0d177d7065c1284a5c08b0.tar.gz
MDEV-20119 stash
-rw-r--r--client/client_priv.h4
-rw-r--r--client/mysqlbinlog.cc305
-rw-r--r--man/mysqlbinlog.167
-rw-r--r--mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc2
-rw-r--r--mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore-master.opt2
-rw-r--r--mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test242
-rw-r--r--sql/rpl_gtid.cc129
-rw-r--r--sql/rpl_gtid.h96
8 files changed, 826 insertions, 21 deletions
diff --git a/client/client_priv.h b/client/client_priv.h
index 23b2e6782a1..d32690c5abb 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -105,6 +105,10 @@ enum options_client
OPT_COPY_S3_TABLES,
OPT_PRINT_TABLE_METADATA,
OPT_ASOF_TIMESTAMP,
+ OPT_IGNORE_DOMAIN_IDS,
+ OPT_DO_DOMAIN_IDS,
+ OPT_IGNORE_SERVER_IDS,
+ OPT_DO_SERVER_IDS,
OPT_MAX_CLIENT_OPTION /* should be always the last */
};
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index a1e5d07b11d..ec1469b0c46 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -82,7 +82,7 @@ extern "C" {
char server_version[SERVER_VERSION_LENGTH];
}
-ulong server_id = 0;
+static char *server_id_str;
// needed by net_serv.c
ulong bytes_sent = 0L, bytes_received = 0L;
@@ -144,13 +144,18 @@ static char *charset= 0;
static uint verbose= 0;
+static char *ignore_domain_ids_str, *do_domain_ids_str;
+static char *ignore_server_ids_str, *do_server_ids_str;
+
static char *start_pos_str, *stop_pos_str;
static ulonglong start_position= BIN_LOG_HEADER_SIZE,
stop_position= (longlong)(~(my_off_t)0) ;
#define start_position_mot ((my_off_t)start_position)
#define stop_position_mot ((my_off_t)stop_position)
+static Gtid_event_filter *gtid_event_filter= NULL;
static Domain_gtid_event_filter *domain_gtid_filter= NULL;
+static Server_gtid_event_filter *server_gtid_filter= NULL;
static char *start_datetime_str, *stop_datetime_str;
static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX;
@@ -986,11 +991,27 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
return result;
}
+/*
+ The presence of the domain filter indicates whether or not GTID filtering is
+ enabled or not (a server filter is still valid using log position)
+*/
static inline my_bool is_gtid_filtering_enabled()
{
return domain_gtid_filter != NULL;
}
+/*
+ In the absence of a domain id, filtering by server id can still be valid.
+*/
+static inline my_bool is_server_id_excluded(uint32 server_id)
+{
+ static rpl_gtid server_tester_gtid;
+ server_tester_gtid.server_id= server_id;
+ return server_gtid_filter == NULL
+ ? FALSE // No server id filter exists
+ : server_gtid_filter->exclude(&server_tester_gtid);
+}
+
/**
Print the given event, and either delete it or delegate the deletion
to someone else.
@@ -1079,18 +1100,18 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
If the binlog output should be filtered using GTIDs, test the new event
group to see if its events should be written or ignored.
*/
- if (ev_type == GTID_EVENT && domain_gtid_filter)
+ if (ev_type == GTID_EVENT && gtid_event_filter)
{
Gtid_log_event *gle= (Gtid_log_event*) ev;
rpl_gtid gtid;
set_rpl_gtid(&gtid, gle->domain_id, gle->server_id, gle->seq_no);
- if (domain_gtid_filter->has_finished())
+ if (gtid_event_filter->has_finished())
{
retval= OK_STOP;
goto end;
}
- else if (!domain_gtid_filter->exclude(&gtid))
+ else if (!gtid_event_filter->exclude(&gtid))
{
ev->activate_current_event_group();
}
@@ -1127,7 +1148,8 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
events.
*/
if (ev_type != ROTATE_EVENT &&
- server_id && (server_id != ev->server_id))
+ !ev->is_event_group_active() && is_server_id_excluded(ev->server_id))
+ //server_id && (server_id != ev->server_id))
goto end;
}
if ((ev->when >= stop_datetime)
@@ -1722,9 +1744,42 @@ static struct my_option my_options[] =
"Print row event positions",
&print_row_event_positions, &print_row_event_positions, 0, GET_BOOL,
NO_ARG, 1, 0, 0, 0, 0, 0},
- {"server-id", 0,
- "Extract only binlog entries created by the server having the given id.",
- &server_id, &server_id, 0, GET_ULONG,
+ {"ignore-domain-ids", OPT_IGNORE_DOMAIN_IDS,
+ "A list of positive integers, separated by commas, that form a blacklist "
+ "of domain ids. Any log event with a GTID that originates from a domain id "
+ "specified in this list is hidden. Cannot be used with "
+ "--do-domain-ids. When used with --(ignore|do)-server-ids, the result is the "
+ "intersection between the two datasets.",
+ &ignore_domain_ids_str, &ignore_domain_ids_str, 0, GET_STR_ALLOC,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"do-domain-ids", OPT_DO_DOMAIN_IDS,
+ "A list of positive integers, separated by commas, that form a whitelist "
+ "of domain ids. Any log event with a GTID that originates from a domain id "
+ "specified in this list is displayed. Cannot be used with "
+ "--ignore-domain-ids. When used with --(ignore|do)-server-ids, the result "
+ "is the intersection between the two datasets.",
+ &do_domain_ids_str, &do_domain_ids_str, 0, GET_STR_ALLOC, REQUIRED_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"ignore-server-ids", OPT_IGNORE_SERVER_IDS,
+ "A list of positive integers, separated by commas, that form a blacklist "
+ "of server ids. Any log event originating from a server id "
+ "specified in this list is hidden. Cannot be used with "
+ "--do-server-ids. When used with --(ignore|do)-domain-ids, the result is "
+ "the intersection between the two datasets.",
+ &ignore_server_ids_str, &ignore_server_ids_str, 0, GET_STR_ALLOC,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"do-server-ids", OPT_DO_SERVER_IDS,
+ "A list of positive integers, separated by commas, that form a whitelist "
+ "of server ids. Any log event originating from a server id "
+ "specified in this list is displayed. Cannot be used with "
+ "--ignore-server-ids. When used with --(ignore|do)-domain-ids, the result "
+ "is the intersection between the two datasets. Synonymous to --server-id.",
+ &do_server_ids_str, &do_server_ids_str, 0, GET_STR_ALLOC,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"server-id", OPT_SERVER_ID,
+ "Extract only binlog entries created by the server having the given id. "
+ "Synonymous with --do-server-ids.",
+ &server_id_str, &server_id_str, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"set-charset", OPT_SET_CHARSET,
"Add 'SET NAMES character_set' to the output.", &charset,
@@ -1927,9 +1982,14 @@ static void cleanup()
my_free(stop_datetime_str);
my_free(start_pos_str);
my_free(stop_pos_str);
+ my_free(ignore_domain_ids_str);
+ my_free(do_domain_ids_str);
+ my_free(ignore_server_ids_str);
+ my_free(do_server_ids_str);
+ my_free(server_id_str);
free_root(&glob_root, MYF(0));
- delete domain_gtid_filter;
+ delete gtid_event_filter;
delete binlog_filter;
delete glob_description_event;
@@ -1996,6 +2056,98 @@ static my_time_t convert_str_to_timestamp(const char* str)
my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap);
}
+/*
+ Parse a list of positive numbers separated by commas.
+ Returns a list of numbers on success, NULL on parsing/resource error
+*/
+static uint32 *parse_u32_list(const char *str, size_t str_len, uint32 *n_vals)
+{
+ const char *str_begin= const_cast<char *>(str);
+ const char *str_end= str_begin + str_len;
+ const char *p = str_begin;
+ uint32 len= 0, alloc_len= 4;
+ uint32 *list= NULL;
+ int err;
+
+ for (;;)
+ {
+ uint32 val;
+
+ /*
+ Set it to the end of the string overall, but when parsing, it will be
+ moved to the end of the element
+ */
+ char *el_end= (char*) str_begin + str_len;
+
+ if (len >= (((uint32)1 << 28)-1))
+ {
+ my_free(list);
+ list= NULL;
+ goto end;
+ }
+
+ val= (uint32)my_strtoll10(p, &el_end, &err);
+ if (err)
+ {
+ my_free(list);
+ list= NULL;
+ goto end;
+ }
+ p = el_end;
+
+ if ((!list || len >= alloc_len) &&
+ !(list=
+ (uint32 *)my_realloc(PSI_INSTRUMENT_ME, list,
+ (alloc_len= alloc_len*2) * sizeof(uint32),
+ MYF(MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR))))
+ return NULL;
+ list[len++]= val;
+
+ if (el_end == str_end)
+ break;
+ if (*p != ',')
+ {
+ my_free(list);
+ return NULL;
+ }
+ ++p;
+ }
+ *n_vals= len;
+
+end:
+ return list;
+}
+
+/*
+ If multiple different types of Gtid_event_filters are used, the result
+ should be the intersection between the filter types.
+*/
+static void extend_main_gtid_event_filter(Gtid_event_filter *new_filter)
+{
+ if (gtid_event_filter == NULL)
+ gtid_event_filter= new_filter;
+ else
+ gtid_event_filter=
+ new Intersecting_gtid_event_filter(gtid_event_filter, new_filter);
+}
+
+static void ensure_server_filter_initialized()
+{
+ if (server_gtid_filter == NULL)
+ {
+ server_gtid_filter= new Server_gtid_event_filter();
+ extend_main_gtid_event_filter(server_gtid_filter);
+ }
+}
+
+static void ensure_domain_filter_initialized()
+{
+ if (domain_gtid_filter == NULL)
+ {
+ domain_gtid_filter= new Domain_gtid_event_filter();
+ extend_main_gtid_event_filter(domain_gtid_filter);
+ }
+}
extern "C" my_bool
get_one_option(const struct my_option *opt, const char *argument, const char *filename)
@@ -2210,10 +2362,7 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
else if (n_stop_gtid_ranges > 0)
{
uint32 gtid_idx;
-
- if (domain_gtid_filter == NULL)
- domain_gtid_filter= new Domain_gtid_event_filter();
-
+ ensure_domain_filter_initialized();
for (gtid_idx = 0; gtid_idx < n_stop_gtid_ranges; gtid_idx++)
{
rpl_gtid *stop_gtid= &stop_gtids[gtid_idx];
@@ -2262,10 +2411,7 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
else if (n_start_gtid_ranges > 0)
{
uint32 gtid_idx;
-
- if (domain_gtid_filter == NULL)
- domain_gtid_filter= new Domain_gtid_event_filter();
-
+ ensure_domain_filter_initialized();
for (gtid_idx = 0; gtid_idx < n_start_gtid_ranges; gtid_idx++)
{
rpl_gtid *start_gtid= &start_gtids[gtid_idx];
@@ -2283,6 +2429,131 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
}
break;
}
+ case OPT_IGNORE_DOMAIN_IDS:
+ {
+ uint32 n_ids= 0;
+ uint32 *domain_ids= parse_u32_list(ignore_domain_ids_str,
+ strlen(ignore_domain_ids_str), &n_ids);
+
+ if (domain_ids == NULL || n_ids == 0)
+ {
+ sql_print_error("Value for --ignore-domain-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ ensure_domain_filter_initialized();
+ if (domain_gtid_filter->set_blacklist(domain_ids, n_ids))
+ {
+ my_free(domain_ids);
+ /* TODO Set_blacklist should write the specific error*/
+ sql_print_error(
+ "Cannot combine --ignore-domain-ids with --do-domain-ids");
+ return 1;
+ }
+ my_free(domain_ids);
+ break;
+ }
+ case OPT_DO_DOMAIN_IDS:
+ {
+ uint32 n_ids= 0;
+ uint32 *domain_ids= parse_u32_list(do_domain_ids_str,
+ strlen(do_domain_ids_str), &n_ids);
+
+ if (domain_ids == NULL || n_ids == 0)
+ {
+ sql_print_error("Value for --do-domain-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ ensure_domain_filter_initialized();
+ if (domain_gtid_filter->set_whitelist(domain_ids, n_ids))
+ {
+ my_free(domain_ids);
+ /* TODO set_whitelist should write the specific error*/
+ sql_print_error(
+ "Cannot combine --ignore-domain-ids with --do-domain-ids");
+ return 1;
+ }
+ my_free(domain_ids);
+ break;
+ }
+ case OPT_IGNORE_SERVER_IDS:
+ {
+ uint32 n_ids= 0;
+ uint32 *server_ids= parse_u32_list(ignore_server_ids_str,
+ strlen(ignore_server_ids_str), &n_ids);
+
+ if (server_ids == NULL || n_ids == 0)
+ {
+ sql_print_error("Value for --ignore-server-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ ensure_server_filter_initialized();
+ if (server_gtid_filter->set_blacklist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ /* TODO set_whitelist should write the specific error*/
+ sql_print_error(
+ "An id specified in --ignore-server-ids already has a filtering rule");
+ return 1;
+ }
+ my_free(server_ids);
+ break;
+ }
+ case OPT_DO_SERVER_IDS:
+ {
+ uint32 n_ids= 0;
+ uint32 *server_ids=
+ parse_u32_list(do_server_ids_str, strlen(do_server_ids_str), &n_ids);
+
+ if (server_ids == NULL || n_ids == 0)
+ {
+ sql_print_error("Value for --do-server-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ ensure_server_filter_initialized();
+ if (server_gtid_filter->set_whitelist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ /* TODO set_whitelist should write the specific error*/
+ sql_print_error(
+ "An id specified in --do-server-ids already has a filtering rule");
+ return 1;
+ }
+ my_free(server_ids);
+ break;
+ }
+ case OPT_SERVER_ID:
+ {
+ uint32 n_ids= 0;
+ uint32 *server_ids=
+ parse_u32_list(server_id_str, strlen(server_id_str), &n_ids);
+
+ if (server_ids == NULL || n_ids == 0)
+ {
+ sql_print_error("Value for --server-id is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ ensure_server_filter_initialized();
+ if (server_gtid_filter->set_whitelist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ /* TODO set_whitelist should write the specific error*/
+ sql_print_error(
+ "An id specified in --server-id already has a filtering rule");
+ return 1;
+ }
+ my_free(server_ids);
+ break;
+ }
case '?':
usage();
opt_version= 1;
diff --git a/man/mysqlbinlog.1 b/man/mysqlbinlog.1
index 17c81809ce2..1e37d8b6830 100644
--- a/man/mysqlbinlog.1
+++ b/man/mysqlbinlog.1
@@ -882,11 +882,78 @@ quote the value (e.g. \fB--rewrite-db="oldname->newname"\fR\&.
.sp -1
.IP \(bu 2.3
.\}
+.\" mysqlbinlog: ignore-domain-ids option
+.\" ignore-domain-ids option: mysqlbinlog
+\fB\-\-ignore\-domain\-ids=\fR\fB\fIIDs\fR\fR
+.sp
+Hide events which are a part of any stream identified by the domain ID
+list \fIIDs\fR\&. Cannot be used with \-\-do\-domain\-ids\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+.\" mysqlbinlog: do-domain-ids option
+.\" do-domain-ids option: mysqlbinlog
+\fB\-\-do\-domain\-ids=\fR\fB\fIIDs\fR\fR
+.sp
+Display only the events which exist in a stream identified by \fIIDs\fR\&.
+Cannot be used with \-\-ignore\-domain\-ids\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+.\" mysqlbinlog: ignore-server-ids option
+.\" ignore-server-ids option: mysqlbinlog
+\fB\-\-ignore\-server\-ids=\fR\fB\fIIDs\fR\fR
+.sp
+Hide events which originated from any server whose ID is specified in
+\fIIDs\fR\&. Cannot be used with \-\-do\-server\-ids\ or \-\-server\-id\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+.\" mysqlbinlog: do-server-ids option
+.\" do-server-ids option: mysqlbinlog
+\fB\-\-do\-server\-ids=\fR\fB\fIIDs\fR\fR
+.sp
+Display only the events that originated from a server identified in
+\fIIDs\fR\&. Cannot be used with \-\-ignore\-server\-ids, and
+synonymous with \-\-server\-id\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
.\" mysqlbinlog: server-id option
.\" server-id option: mysqlbinlog
\fB\-\-server\-id=\fR\fB\fIid\fR\fR
.sp
Display only those events created by the server having the given server ID\&.
+Cannot be used with \-\-ignore\-server\-ids, and synonymous with
+\-\-do\-server\-ids\&.
.RE
.sp
.RS 4
diff --git a/mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc b/mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc
index a80507d6e42..baaf492d007 100644
--- a/mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc
+++ b/mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc
@@ -64,7 +64,7 @@ DROP TABLE t2;
--echo #
--echo # Test Case 1:
--echo # The end of the binlog file resets the server and domain id of the
---echo # session
+--echo # session-
# As gtid_domain_id and server_id should not change after reading binlog in GTID
# mode, change variables to otherwise-unused values to ensure they remain
diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore-master.opt b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore-master.opt
new file mode 100644
index 00000000000..c94389cfd56
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore-master.opt
@@ -0,0 +1,2 @@
+--timezone=GMT-8
+
diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test
new file mode 100644
index 00000000000..93a1b370e46
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test
@@ -0,0 +1,242 @@
+#
+# Purpose:
+# This test ensures that the mariadb-binlog CLI tool can use GTIDS to filter
+# log events by blacklisting or whitelisting domain ids or server ids.
+# Specifically, it tests the new arguments --do-domain-ids, --ignore-domain-ids,
+# --ignore-server-ids, and --do-server-ids to ensure they exclude the correct
+# events.
+#
+# Methodology:
+# This test validates that the new arguments produce the correct output
+# through the following test cases:
+# Test Case 1) --ignore-domain-ids standalone
+# Test Case 2) --do-domain-ids standalone
+# Test Case 3) --ignore-server-ids standalone
+# Test Case 4) --do-server-ids standalone
+# Test Case 5) --ignore-domain-ids with --ignore-server-ids intersects the
+# datasets
+# Test Case 6) --ignore-domain-ids with GTID range unions the datasets
+# Test Case 7) --ignore-server-ids with GTID range intersects the datasets
+# Test Case 8) --ignore-domain-ids, --ignore-server-ids, and GTID range
+# Test Case 9) --server-id and --do-server-ids union results together
+#
+# Additionally, this test validates the following error scenarios:
+# Error Case 1) --ignore-domain-ids and --do-domain-ids both specified
+# Error Case 1) --ignore-server-ids and --do-server-ids both specified
+# Error Case 2) --(ignore|do)-domain-ids and GTID range specified on the same
+# domain
+#
+# References:
+# MDEV-20119: Implement the --do-domain-ids, --ignore-domain-ids, and
+# --ignore-server-ids options for mysqlbinlog
+#
+
+--source include/have_log_bin.inc
+
+--echo ###############################
+--echo # Test Setup
+--echo ###############################
+
+## Fix timestamp to avoid varying results.
+#
+SET timestamp=1000000000;
+RESET MASTER;
+
+## Save old state
+#
+let $ORIG_GTID_DOMAIN_ID = `select @@session.gtid_domain_id`;
+let $ORIG_SERVER_ID = `select @@session.server_id`;
+
+## Configure test variables
+#
+--let $MYSQLD_DATADIR=`select @@datadir`
+--let OUT_FILE=$MYSQLTEST_VARDIR/tmp/binlog.out
+--let SEARCH_OUTPUT=matches
+--let SEARCH_FILE=$OUT_FILE
+--let BINLOG_FILENAME= query_get_value(SHOW MASTER STATUS, File, 1)
+--let BINLOG_FILE_PARAM= $MYSQLD_DATADIR/$BINLOG_FILENAME.orig
+
+## Initialize test data
+#
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+CREATE TABLE t1 (a int);
+INSERT INTO t1 values (1);
+--let t1_checksum= `CHECKSUM TABLE t1`
+
+SET @@session.server_id= 2;
+CREATE TABLE t2 (a int);
+INSERT INTO t2 values (2);
+--let t2_checksum= `CHECKSUM TABLE t2`
+
+SET @@session.gtid_domain_id= 1;
+SET @@session.server_id= 1;
+CREATE TABLE t3 (a int);
+INSERT INTO t3 values (3);
+--let t3_checksum= `CHECKSUM TABLE t3`
+
+SET @@session.gtid_domain_id= 1;
+SET @@session.server_id= 3;
+CREATE TABLE t4 (a int);
+INSERT INTO t4 values (4);
+--let t4_checksum= `CHECKSUM TABLE t4`
+
+SET @@session.gtid_domain_id= 2;
+SET @@session.server_id= 1;
+CREATE TABLE t5 (a int);
+INSERT INTO t5 values (5);
+--let t5_checksum= `CHECKSUM TABLE t5`
+
+
+FLUSH LOGS;
+
+--copy_file $MYSQLD_DATADIR/$BINLOG_FILENAME $BINLOG_FILE_PARAM
+
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE t4;
+DROP TABLE t5;
+RESET MASTER;
+
+--echo ###############################
+--echo # Test Cases
+--echo ###############################
+
+--echo #
+--echo # Test Case 1:
+--echo # --ignore-domain-ids standalone
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0,1 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-domain-ids=0,1 | $MYSQL
+if ($t5_checksum != `CHECKSUM TABLE t5`)
+{
+ die "Dead";
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die "t1 should not exist as binlog replay should exclude domains 0 and 1 from results";
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die "t2 should not exist as binlog replay should exclude domains 0 and 1 from results";
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die "t3 should not exist as binlog replay should exclude domains 0 and 1 from results";
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die "t4 should not exist as binlog replay should exclude domains 0 and 1 from results";
+}
+
+SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4';
+
+#--echo #
+#--echo # Test Case 2:
+#--echo # --do-domain-ids standalone
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=2 > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=2 > $OUT_FILE
+#
+#--echo #
+#--echo # Test Case 3:
+#--echo # --ignore-server-ids standalone
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1,3 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1,3 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 4:
+#--echo # --do-server-ids standalone
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --do-server-ids=1 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --do-server-ids=1 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 5:
+#--echo # --ignore-domain-ids with --ignore-server-ids intersects the datasets
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1,3 --ignore-domain-ids=1,2 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1,3 --ignore-domain-ids=1,2 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 6:
+#--echo # --ignore-domain-ids with GTID range unions the datasets
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=2 --start-position=1-1-1 --stop-position=1-2-2 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=2 --start-position=1-1-1 --stop-position=1-2-2 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 7:
+#--echo # --ignore-server-ids with GTID range intersects the datasets
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=2 --stop-position=1-3-3 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=2 --stop-position=1-3-3 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 8:
+#--echo # --ignore-domain-ids, --ignore-server-ids, and GTID range
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=0 --ignore-server-ids=2 --stop-position=1-3-3 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --do-domain-ids=0 --ignore-server-ids=2 --stop-position=1-3-3 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+#
+#--echo #
+#--echo # Test Case 9:
+#--echo # --server-id and --do-server-ids union results together
+#--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --do-server-ids=1,2 --server-id=1 --base64-output=NEVER > OUT_FILE
+#--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --do-server-ids=1,2 --server-id=1 --base64-output=NEVER > $OUT_FILE
+#--let SEARCH_PATTERN=end_log_pos[^\n]+
+#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+#--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /xid=[0-9]*/xid=#/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /CRC32 0x[0-9a-f]*/CRC32 XXX/ /collation_server=[0-9]+/collation_server=X/ /character_set_client=[0-9]+/character_set_client=X/ /collation_connection=[0-9]+/collation_connection=X/
+#--source include/search_pattern_in_file.inc
+
+
+--echo ##############################
+--echo # Error Cases
+--echo ##############################
+
+--echo #
+--echo # Error Case 1:
+--echo # --ignore-domain-ids and --do-domain-ids both specified
+--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --ignore-domain-ids=0 --do-domain-ids=1 --base64-output=NEVER > OUT_FILE
+--error 1
+--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --ignore-domain-ids=0 --do-domain-ids=1 --base64-output=NEVER > $OUT_FILE
+
+--echo #
+--echo # Error Case 2:
+--echo # --ignore-server-ids and --do-server-ids both specified
+--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1 --do-server-ids=2 --base64-output=NEVER > OUT_FILE
+--error 1
+--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --ignore-server-ids=1 --do-server-ids=2 --base64-output=NEVER > $OUT_FILE
+
+--echo #
+--echo # Error Case 3:
+--echo # --(ignore|do)-domain-ids and GTID range specified on the same domain
+--echo # MYSQL_BINLOG MYSQLD_DATADIR/master-bin.000001 --start-position=0-1-1 --ignore-domain-ids=0 --base64-output=NEVER > OUT_FILE
+--error 1
+--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 --start-position=0-1-1 --ignore-domain-ids=0 --base64-output=NEVER > $OUT_FILE
+
+--echo ##############################
+--echo # Cleanup
+--echo ##############################
+#DROP TABLE t1;
+--eval SET @@global.gtid_domain_id= $ORIG_GTID_DOMAIN_ID
+--eval SET @@global.server_id= $ORIG_SERVER_ID
+
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 27ee073795e..d19628a0395 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -3232,7 +3232,8 @@ void free_gtid_filter_element(void *p)
}
Id_delegating_gtid_event_filter::Id_delegating_gtid_event_filter()
- : m_num_explicit_filters(0), m_num_completed_filters(0)
+ : m_num_explicit_filters(0), m_num_completed_filters(0),
+ m_whitelist_set(FALSE), m_blacklist_set(FALSE)
{
my_hash_init(PSI_INSTRUMENT_ME, &m_filters_by_id_hash, &my_charset_bin, 32,
offsetof(gtid_filter_element, identifier), sizeof(uint32), NULL,
@@ -3330,6 +3331,126 @@ my_bool Id_delegating_gtid_event_filter::exclude(rpl_gtid *gtid)
return ret;
}
+int Id_delegating_gtid_event_filter::set_blacklist(
+ gtid_filter_identifier *id_list, size_t n_ids)
+{
+ size_t id_ctr;
+ int err;
+
+ if (m_whitelist_set)
+ {
+ /*
+ Whitelist is already set, we can't do a blacklist and whitelist
+ together.
+ */
+ err= 1;
+ goto err;
+ }
+
+ for (id_ctr= 0; id_ctr < n_ids; id_ctr++)
+ {
+ gtid_filter_identifier filter_id= id_list[id_ctr];
+ gtid_filter_element *map_element=
+ find_or_create_filter_element_for_id(filter_id);
+
+ if(map_element == NULL)
+ {
+ err= 1;
+ goto err;
+ }
+ else if (map_element->filter == NULL)
+ {
+ map_element->filter= new Reject_all_gtid_filter();
+ m_num_explicit_filters++;
+ //Identifiable_gtid_event_filter *rejecting_filter=
+ // new Identifiable_gtid_event_filter(filter_id,
+ // new Reject_all_gtid_filter());
+ //map_element->filter= rejecting_filter;
+ }
+ else if (map_element->filter->get_filter_type() !=
+ REJECT_ALL_GTID_FILTER_TYPE)
+ {
+ /*
+ There is a different filter placed on an id that should be blacklisted.
+ Error.
+ */
+ err= 1;
+ goto err;
+ }
+ }
+
+ /*
+ With a blacklist, we by default want to accept everything that is not
+ specified in the list
+ */
+ set_default_filter(new Accept_all_gtid_filter());
+ m_blacklist_set= TRUE;
+ err= 0;
+
+err:
+ return err;
+}
+
+int Id_delegating_gtid_event_filter::set_whitelist(
+ gtid_filter_identifier *id_list, size_t n_ids)
+{
+ size_t id_ctr;
+ int err;
+
+ if (m_blacklist_set)
+ {
+ /*
+ Blacklist is already set, we can't do a blacklist and whitelist
+ together.
+ */
+ err= 1;
+ goto err;
+ }
+
+ for (id_ctr= 0; id_ctr < n_ids; id_ctr++)
+ {
+ gtid_filter_identifier filter_id= id_list[id_ctr];
+ gtid_filter_element *map_element=
+ find_or_create_filter_element_for_id(filter_id);
+
+ if(map_element == NULL)
+ {
+ err= 1;
+ goto err;
+ }
+ else if (map_element->filter == NULL)
+ {
+ map_element->filter= new Accept_all_gtid_filter();
+ m_num_explicit_filters++;
+ //Identifiable_gtid_event_filter *accepting_filter=
+ // new Identifiable_gtid_event_filter(filter_id,
+ // new Accept_all_gtid_filter());
+ //map_element->filter= accepting_filter;
+ }
+ else if (map_element->filter->get_filter_type() !=
+ ACCEPT_ALL_GTID_FILTER_TYPE)
+ {
+ /*
+ There is a different filter placed on an id that should be whitelisted.
+ Error.
+ */
+ err= 1;
+ goto err;
+ }
+ }
+
+ /*
+ With a whitelist, we by only want to accept the ids which are specified.
+ Everything else should be denied.
+ */
+ set_default_filter(new Reject_all_gtid_filter());
+ m_whitelist_set= TRUE;
+ err= 0;
+
+err:
+ return err;
+}
+
Window_gtid_event_filter *
Domain_gtid_event_filter::find_or_create_window_filter_for_id(
uint32 domain_id)
@@ -3525,3 +3646,9 @@ void Domain_gtid_event_filter::clear_stop_gtids()
reset_dynamic(&m_stop_filters);
}
+
+my_bool Intersecting_gtid_event_filter::exclude(rpl_gtid *gtid)
+{
+ DBUG_ASSERT(m_filter1 && m_filter2);
+ return m_filter1->exclude(gtid) || m_filter2->exclude(gtid);
+}
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index 86751d3c371..8eba3c549a5 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -399,7 +399,9 @@ public:
{
DELEGATING_GTID_FILTER_TYPE = 1,
WINDOW_GTID_FILTER_TYPE = 2,
- ACCEPT_ALL_GTID_FILTER_TYPE = 3
+ ACCEPT_ALL_GTID_FILTER_TYPE = 3,
+ REJECT_ALL_GTID_FILTER_TYPE = 4,
+ INTERSECTING_GTID_FILTER_TYPE = 5
};
/*
@@ -452,6 +454,20 @@ public:
};
/*
+ TODO
+*/
+class Reject_all_gtid_filter : public Gtid_event_filter
+{
+public:
+ Reject_all_gtid_filter() {}
+ ~Reject_all_gtid_filter() {}
+ my_bool exclude(rpl_gtid *gtid) { return TRUE; }
+ uint32 get_filter_type() { return REJECT_ALL_GTID_FILTER_TYPE; }
+ my_bool has_finished() { return FALSE; }
+ void write_warnings(FILE *out) {}
+};
+
+/*
A filter implementation that passes through events between two GTIDs, m_start
(exclusive) and m_stop (inclusive).
@@ -617,6 +633,19 @@ public:
virtual gtid_filter_identifier get_id_from_gtid(rpl_gtid *) = 0;
+ /*
+ Set the default behavior to include all ids except for the ones that are
+ provided in the input list or overridden with another filter.
+ Returns 0 on ok, non-zero on error
+ */
+ int set_blacklist(gtid_filter_identifier *id_list, size_t n_ids);
+
+ /*
+ Set the default behavior to exclude all ids except for the ones that are
+ provided in the input list or overridden with another filter.
+ Returns 0 on ok, non-zero on error
+ */
+ int set_whitelist(gtid_filter_identifier *id_list, size_t n_ids);
protected:
@@ -626,6 +655,8 @@ protected:
HASH m_filters_by_id_hash;
+ my_bool m_whitelist_set, m_blacklist_set;
+
gtid_filter_element *find_or_create_filter_element_for_id(gtid_filter_identifier);
};
@@ -728,7 +759,68 @@ private:
*/
my_bool m_is_gtid_strict_mode;
- Window_gtid_event_filter *find_or_create_window_filter_for_id(gtid_filter_identifier);
+ Window_gtid_event_filter *
+ find_or_create_window_filter_for_id(gtid_filter_identifier);
+};
+
+/*
+ A subclass of Id_delegating_gtid_event_filter which identifies filters using the
+ server id of a GTID.
+*/
+class Server_gtid_event_filter : public Id_delegating_gtid_event_filter
+{
+public:
+ /*
+ Returns the server id of from the input GTID
+ */
+ gtid_filter_identifier get_id_from_gtid(rpl_gtid *gtid)
+ {
+ return gtid->server_id;
+ }
+};
+
+/*
+ A Gtid_event_filter implementation that delegates the filtering to two
+ other filters, where the result is the intersection between the two.
+*/
+class Intersecting_gtid_event_filter : public Gtid_event_filter
+{
+public:
+ Intersecting_gtid_event_filter(Gtid_event_filter *filter1,
+ Gtid_event_filter *filter2)
+ : m_filter1(filter1), m_filter2(filter2) {}
+ ~Intersecting_gtid_event_filter()
+ {
+ delete m_filter1;
+ delete m_filter2;
+ }
+
+ /*
+ Returns TRUE if either m_filter1 or m_filter1 exclude the gtid, returns
+ FALSE otherwise, i.e. both m_filter1 and m_filter2 allow the gtid
+ */
+ my_bool exclude(rpl_gtid *gtid);
+ uint32 get_filter_type() { return INTERSECTING_GTID_FILTER_TYPE; }
+
+ Gtid_event_filter *get_filter_1() { return m_filter1; }
+ Gtid_event_filter *get_filter_2() { return m_filter2; }
+
+ my_bool has_finished()
+ {
+ DBUG_ASSERT(m_filter1 && m_filter2);
+ return m_filter1->has_finished() && m_filter2->has_finished();
+ }
+
+ void write_warnings(FILE *out)
+ {
+ DBUG_ASSERT(m_filter1 && m_filter2);
+ m_filter1->write_warnings(out);
+ m_filter2->write_warnings(out);
+ }
+
+ protected:
+ Gtid_event_filter *m_filter1;
+ Gtid_event_filter *m_filter2;
};
#endif /* RPL_GTID_H */