summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/client_priv.h4
-rw-r--r--client/mysqlbinlog.cc364
-rw-r--r--man/mysqlbinlog.167
-rw-r--r--mysql-test/suite/binlog/r/binlog_mysqlbinlog_gtid_ignore.result122
-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.test448
-rw-r--r--sql/rpl_gtid.cc143
-rw-r--r--sql/rpl_gtid.h101
8 files changed, 1210 insertions, 41 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 d4e10a7141a..ec01e556259 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,6 +144,9 @@ 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) ;
@@ -151,7 +154,10 @@ static ulonglong start_position= BIN_LOG_HEADER_SIZE,
#define stop_position_mot ((my_off_t)stop_position)
static Gtid_stream_auditor *gtid_stream_auditor= NULL;
-static Domain_gtid_event_filter *domain_gtid_filter= NULL;
+static Gtid_event_filter *gtid_event_filter= NULL;
+static Domain_gtid_event_filter *domain_window_gtid_filter= NULL;
+static Domain_gtid_event_filter *domain_id_exclusivity_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;
@@ -987,9 +993,25 @@ 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;
+ return domain_window_gtid_filter || domain_id_exclusivity_gtid_filter;
+}
+
+/*
+ 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);
}
/**
@@ -1029,11 +1051,11 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
#endif
/* Run time estimation of the output window configuration. */
- if (ev_type == GTID_LIST_EVENT && is_gtid_filtering_enabled())
+ if (ev_type == GTID_LIST_EVENT && domain_window_gtid_filter)
{
Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
- size_t n_start_gtid_ranges= domain_gtid_filter->get_num_start_gtids();
- rpl_gtid *start_gtids= domain_gtid_filter->get_start_gtids();
+ size_t n_start_gtid_ranges= domain_window_gtid_filter->get_num_start_gtids();
+ rpl_gtid *start_gtids= domain_window_gtid_filter->get_start_gtids();
for (size_t i= 0; i < n_start_gtid_ranges; i++)
{
for (size_t k= 0; k < glev->count; k++)
@@ -1052,8 +1074,8 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
}
}
}
- size_t n_stop_gtid_ranges= domain_gtid_filter->get_num_stop_gtids();
- rpl_gtid *stop_gtids= domain_gtid_filter->get_stop_gtids();
+ size_t n_stop_gtid_ranges= domain_window_gtid_filter->get_num_stop_gtids();
+ rpl_gtid *stop_gtids= domain_window_gtid_filter->get_stop_gtids();
for (size_t i= 0; i < n_stop_gtid_ranges; i++)
{
for (size_t k= 0; k < glev->count; k++)
@@ -1080,21 +1102,21 @@ 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 || gtid_stream_auditor))
+ if (ev_type == GTID_EVENT && (is_gtid_filtering_enabled() || gtid_stream_auditor))
{
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)
+ if (gtid_event_filter)
{
- if (domain_gtid_filter->has_finished())
+ if (gtid_event_filter->has_finished())
{
retval= OK_STOP;
goto end;
}
- if (!domain_gtid_filter->exclude(&gtid))
+ if (!gtid_event_filter->exclude(&gtid))
ev->activate_current_event_group();
else
ev->deactivate_current_event_group();
@@ -1131,8 +1153,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
the format_description event so that we can parse subsequent
events.
*/
- if (ev_type != ROTATE_EVENT &&
- server_id && (server_id != ev->server_id))
+ if (ev_type != ROTATE_EVENT && is_server_id_excluded(ev->server_id))
goto end;
}
if ((ev->when >= stop_datetime)
@@ -1719,9 +1740,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,
@@ -1924,9 +1978,31 @@ 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;
+ if (gtid_event_filter)
+ {
+ delete gtid_event_filter;
+ }
+ else
+ {
+ /*
+ If there was an error during input parsing, gtid_event_filter will not
+ be set, so we need to ensure the comprising filters are cleaned up
+ properly.
+ */
+ if (domain_id_exclusivity_gtid_filter)
+ delete domain_id_exclusivity_gtid_filter;
+ if (domain_window_gtid_filter)
+ delete domain_window_gtid_filter;
+ if (server_gtid_filter)
+ delete server_gtid_filter;
+ }
delete gtid_stream_auditor;
delete binlog_filter;
@@ -1994,6 +2070,80 @@ 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);
+}
extern "C" my_bool
get_one_option(const struct my_option *opt, const char *argument, const char *filename)
@@ -2181,8 +2331,9 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
case OPT_STOP_POSITION:
{
/* Stop position was already specified, so reset it and use the new list */
- if (domain_gtid_filter && domain_gtid_filter->get_num_stop_gtids() > 0)
- domain_gtid_filter->clear_stop_gtids();
+ if (domain_window_gtid_filter &&
+ domain_window_gtid_filter->get_num_stop_gtids() > 0)
+ domain_window_gtid_filter->clear_stop_gtids();
uint32 n_stop_gtid_ranges= 0;
rpl_gtid *stop_gtids= gtid_parse_string_to_list(
@@ -2208,14 +2359,13 @@ 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();
+ if (domain_window_gtid_filter == NULL)
+ domain_window_gtid_filter= new Domain_gtid_event_filter();
for (gtid_idx = 0; gtid_idx < n_stop_gtid_ranges; gtid_idx++)
{
rpl_gtid *stop_gtid= &stop_gtids[gtid_idx];
- if (domain_gtid_filter->add_stop_gtid(stop_gtid))
+ if (domain_window_gtid_filter->add_stop_gtid(stop_gtid))
{
my_free(stop_gtids);
return 1;
@@ -2232,8 +2382,9 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
case 'j':
{
/* Start position was already specified, so reset it and use the new list */
- if (domain_gtid_filter && domain_gtid_filter->get_num_start_gtids() > 0)
- domain_gtid_filter->clear_start_gtids();
+ if (domain_window_gtid_filter &&
+ domain_window_gtid_filter->get_num_start_gtids() > 0)
+ domain_window_gtid_filter->clear_start_gtids();
uint32 n_start_gtid_ranges= 0;
rpl_gtid *start_gtids= gtid_parse_string_to_list(
@@ -2247,6 +2398,9 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
No GTIDs specified in OPT_START_POSITION specification. Treat the value
as a singular index.
*/
+ if (domain_window_gtid_filter == NULL)
+ domain_window_gtid_filter= new Domain_gtid_event_filter();
+
start_position= my_strtoll10(start_pos_str, &end_ptr, &err);
if (err || *end_ptr)
@@ -2260,14 +2414,13 @@ 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();
+ if (domain_window_gtid_filter == NULL)
+ domain_window_gtid_filter= new Domain_gtid_event_filter();
for (gtid_idx = 0; gtid_idx < n_start_gtid_ranges; gtid_idx++)
{
rpl_gtid *start_gtid= &start_gtids[gtid_idx];
- if (domain_gtid_filter->add_start_gtid(start_gtid))
+ if (domain_window_gtid_filter->add_start_gtid(start_gtid))
{
my_free(start_gtids);
return 1;
@@ -2281,6 +2434,129 @@ 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("Input for --ignore-domain-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ if (domain_id_exclusivity_gtid_filter == NULL)
+ domain_id_exclusivity_gtid_filter= new Domain_gtid_event_filter();
+
+ if (domain_id_exclusivity_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("Input for --do-domain-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ if (domain_id_exclusivity_gtid_filter == NULL)
+ domain_id_exclusivity_gtid_filter= new Domain_gtid_event_filter();
+
+ if (domain_id_exclusivity_gtid_filter->set_whitelist(domain_ids, n_ids))
+ {
+ my_free(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("Input for --ignore-server-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ if (server_gtid_filter == NULL)
+ server_gtid_filter= new Server_gtid_event_filter();
+
+ if (server_gtid_filter->set_blacklist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ 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("Input for --do-server-ids is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ if (server_gtid_filter == NULL)
+ server_gtid_filter= new Server_gtid_event_filter();
+
+ if (server_gtid_filter->set_whitelist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ 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("Input for --server-id is invalid. Should be a "
+ "list of positive integers");
+ return 1;
+ }
+
+ if (server_gtid_filter == NULL)
+ server_gtid_filter= new Server_gtid_event_filter();
+
+ if (server_gtid_filter->set_whitelist(server_ids, n_ids))
+ {
+ my_free(server_ids);
+ return 1;
+ }
+ my_free(server_ids);
+ break;
+ }
case '?':
usage();
opt_version= 1;
@@ -2292,7 +2568,6 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
return 0;
}
-
static int parse_args(int *argc, char*** argv)
{
int ho_error;
@@ -2314,12 +2589,10 @@ static int parse_args(int *argc, char*** argv)
start_position= UINT_MAX32;
}
- if (domain_gtid_filter)
- Log_event::enable_event_group_filtering();
-
if (opt_gtid_strict_mode)
{
- if (domain_gtid_filter && domain_gtid_filter->validate_window_filters())
+ if (domain_window_gtid_filter &&
+ domain_window_gtid_filter->validate_window_filters())
{
/*
In strict mode, if any --start/stop-position GTID ranges are invalid,
@@ -2331,6 +2604,21 @@ static int parse_args(int *argc, char*** argv)
gtid_stream_auditor= new Gtid_stream_auditor();
}
+ if (domain_window_gtid_filter)
+ {
+ Log_event::enable_event_group_filtering();
+ extend_main_gtid_event_filter(domain_window_gtid_filter);
+ }
+
+ if(domain_id_exclusivity_gtid_filter)
+ {
+ Log_event::enable_event_group_filtering();
+ extend_main_gtid_event_filter(domain_id_exclusivity_gtid_filter);
+ }
+
+ if(server_gtid_filter)
+ extend_main_gtid_event_filter(server_gtid_filter);
+
return 0;
}
@@ -2512,7 +2800,7 @@ static Exit_status check_master_version()
goto err;
}
- if (domain_gtid_filter && domain_gtid_filter->get_num_start_gtids() > 0)
+ if (domain_window_gtid_filter && domain_window_gtid_filter->get_num_start_gtids() > 0)
{
char str_buf[256];
String query_str(str_buf, sizeof(str_buf), system_charset_info);
@@ -2520,8 +2808,8 @@ static Exit_status check_master_version()
query_str.append(STRING_WITH_LEN("SET @slave_connect_state='"),
system_charset_info);
- size_t n_start_gtids= domain_gtid_filter->get_num_start_gtids();
- rpl_gtid *start_gtids= domain_gtid_filter->get_start_gtids();
+ size_t n_start_gtids= domain_window_gtid_filter->get_num_start_gtids();
+ rpl_gtid *start_gtids= domain_window_gtid_filter->get_start_gtids();
for (size_t gtid_idx = 0; gtid_idx < n_start_gtids; gtid_idx++)
{
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/r/binlog_mysqlbinlog_gtid_ignore.result b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_gtid_ignore.result
new file mode 100644
index 00000000000..a0ca7c2f943
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_gtid_ignore.result
@@ -0,0 +1,122 @@
+###############################
+# Test Setup
+###############################
+SET timestamp=1000000000;
+RESET MASTER;
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+CREATE TABLE t1 (a int);
+SET @@session.server_id= 2;
+CREATE TABLE t2 (a int);
+INSERT INTO t2 values (3);
+SET @@session.gtid_domain_id= 1;
+SET @@session.server_id= 1;
+CREATE TABLE t3 (a int);
+INSERT INTO t3 values (4);
+SET @@session.server_id= 3;
+CREATE TABLE t4 (a int);
+INSERT INTO t4 values (5);
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+INSERT INTO t1 values (1);
+SET @@session.gtid_domain_id= 2;
+SET @@session.server_id= 1;
+CREATE TABLE t5 (a int);
+INSERT INTO t5 values (6);
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+INSERT INTO t1 values (2);
+FLUSH LOGS;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE t4;
+DROP TABLE t5;
+RESET MASTER;
+###############################
+# Test Cases
+###############################
+#
+# Test Case 1:
+# --ignore-domain-ids standalone
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0,1 | MYSQL
+DROP TABLE t5;
+#
+# Test Case 2:
+# --do-domain-ids standalone
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0,2 | MYSQL
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t5;
+#
+# Test Case 3:
+# --ignore-server-ids standalone
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=2,3 | MYSQL
+DROP TABLE t1;
+DROP TABLE t3;
+DROP TABLE t5;
+#
+# Test Case 4:
+# --do-server-ids standalone
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=2,3 | MYSQL
+DROP TABLE t2;
+DROP TABLE t4;
+#
+# Test Case 5:
+# --ignore-domain-ids with --ignore-server-ids intersects the datasets
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=1,3 --ignore-domain-ids=1,2 | MYSQL
+DROP TABLE t2;
+#
+# Test Case 6:
+# --do-domain-ids with GTID range intersects the datasets
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0 --start-position=0-1-0 --stop-position=0-1-4 | MYSQL
+DROP TABLE t1;
+DROP TABLE t2;
+#
+# Test Case 7:
+# --ignore-server-ids with GTID range intersects the datasets
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=2,3 --start-position=0-1-0 --stop-position=0-1-4 | MYSQL
+DROP TABLE t1;
+DROP TABLE t3;
+#
+# Test Case 8:
+# --ignore-domain-ids, --ignore-server-ids, and GTID range
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --stop-position=0-1-4 | MYSQL
+DROP TABLE t3;
+#
+# Test Case 9:
+# If the same domain id rule is specified multiple times, only the values
+# from the last specification will be used
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0,1 --do-domain-ids=2 | MYSQL
+DROP TABLE t5;
+#
+# Test Case 10:
+# --server-id and --do-server-ids are aliases and the latter will
+# override the former
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=1,2 --server-id=3 | MYSQL
+DROP TABLE t4;
+#
+# Test Case 11:
+# --ignore-domain-ids, --ignore-server-ids, and multiple GTID ranges
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --start-position=0-1-0,1-1-0 --stop-position=1-1-2,0-1-4| MYSQL
+DROP TABLE t3;
+##############################
+# Error Cases
+##############################
+#
+# Error Case 1:
+# --ignore-domain-ids and --do-domain-ids both specified
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --do-domain-ids=1
+#
+# Error Case 2:
+# --ignore-server-ids and --do-server-ids both specified
+# MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=1 --ignore-server-ids=2
+#
+# Error Case 3:
+# Invalid ID number provided
+# MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=-1
+##############################
+# Cleanup
+##############################
+SET @@global.gtid_domain_id= 0;
+SET @@global.server_id= 1;
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..816ec50c55d
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test
@@ -0,0 +1,448 @@
+#
+# 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 BINLOG_FILENAME= query_get_value(SHOW MASTER STATUS, File, 1)
+--let BINLOG_FILE_PARAM= $MYSQLD_DATADIR/$BINLOG_FILENAME.orig
+
+--let table_inconsistent_err= "table data is inconsistent after replaying binlog events";
+--let table_exists_error= "table exists but binlog playback should have excluded its creation";
+
+# Initialize test data
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+CREATE TABLE t1 (a int);
+
+SET @@session.server_id= 2;
+CREATE TABLE t2 (a int);
+INSERT INTO t2 values (3);
+--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 (4);
+--let t3_checksum= `CHECKSUM TABLE t3`
+
+SET @@session.server_id= 3;
+CREATE TABLE t4 (a int);
+INSERT INTO t4 values (5);
+--let t4_checksum= `CHECKSUM TABLE t4`
+
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+INSERT INTO t1 values (1);
+--let t1_partial_checksum= `CHECKSUM TABLE t1`
+
+SET @@session.gtid_domain_id= 2;
+SET @@session.server_id= 1;
+CREATE TABLE t5 (a int);
+INSERT INTO t5 values (6);
+--let t5_checksum= `CHECKSUM TABLE t5`
+
+SET @@session.gtid_domain_id= 0;
+SET @@session.server_id= 1;
+INSERT INTO t1 values (2);
+--let t1_checksum= `CHECKSUM TABLE t1`
+
+
+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 $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t5;
+
+--echo #
+--echo # Test Case 2:
+--echo # --do-domain-ids standalone
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0,2 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-domain-ids=0,2 | $MYSQL
+if ($t1_checksum != `CHECKSUM TABLE t1`)
+{
+ die $table_inconsistent_err;
+}
+if ($t2_checksum != `CHECKSUM TABLE t2`)
+{
+ die $table_inconsistent_err;
+}
+if ($t5_checksum != `CHECKSUM TABLE t5`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t5;
+
+--echo #
+--echo # Test Case 3:
+--echo # --ignore-server-ids standalone
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=2,3 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-server-ids=2,3 | $MYSQL
+if ($t1_checksum != `CHECKSUM TABLE t1`)
+{
+ die $table_inconsistent_err;
+}
+if ($t3_checksum != `CHECKSUM TABLE t3`)
+{
+ die $table_inconsistent_err;
+}
+if ($t5_checksum != `CHECKSUM TABLE t5`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t1;
+DROP TABLE t3;
+DROP TABLE t5;
+
+--echo #
+--echo # Test Case 4:
+--echo # --do-server-ids standalone
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=2,3 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-server-ids=2,3 | $MYSQL
+if ($t2_checksum != `CHECKSUM TABLE t2`)
+{
+ die $table_inconsistent_err;
+}
+if ($t4_checksum != `CHECKSUM TABLE t4`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t2;
+DROP TABLE t4;
+
+--echo #
+--echo # Test Case 5:
+--echo # --ignore-domain-ids with --ignore-server-ids intersects the datasets
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=1,3 --ignore-domain-ids=1,2 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-server-ids=1,3 --ignore-domain-ids=1,2 | $MYSQL
+if ($t2_checksum != `CHECKSUM TABLE t2`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t2;
+
+
+--echo #
+--echo # Test Case 6:
+--echo # --do-domain-ids with GTID range intersects the datasets
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0 --start-position=0-1-0 --stop-position=0-1-4 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-domain-ids=0 --start-position=0-1-0 --stop-position=0-1-4 | $MYSQL
+if ($t1_partial_checksum != `CHECKSUM TABLE t1`)
+{
+ die $table_inconsistent_err;
+}
+if ($t2_checksum != `CHECKSUM TABLE t2`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t1;
+DROP TABLE t2;
+
+--echo #
+--echo # Test Case 7:
+--echo # --ignore-server-ids with GTID range intersects the datasets
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=2,3 --start-position=0-1-0 --stop-position=0-1-4 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-server-ids=2,3 --start-position=0-1-0 --stop-position=0-1-4 | $MYSQL
+if ($t1_partial_checksum != `CHECKSUM TABLE t1`)
+{
+ die $table_inconsistent_err;
+}
+if ($t3_checksum != `CHECKSUM TABLE t3`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t1;
+DROP TABLE t3;
+
+
+--echo #
+--echo # Test Case 8:
+--echo # --ignore-domain-ids, --ignore-server-ids, and GTID range
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --stop-position=0-1-4 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --stop-position=0-1-4 | $MYSQL
+if ($t3_checksum != `CHECKSUM TABLE t3`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t3;
+
+--echo #
+--echo # Test Case 9:
+--echo # If the same domain id rule is specified multiple times, only the values
+--echo # from the last specification will be used
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-domain-ids=0,1 --do-domain-ids=2 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-domain-ids=0,1 --do-domain-ids=2 | $MYSQL
+if ($t5_checksum != `CHECKSUM TABLE t5`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t5;
+
+--echo #
+--echo # Test Case 10:
+--echo # --server-id and --do-server-ids are aliases and the latter will
+--echo # override the former
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=1,2 --server-id=3 | MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-server-ids=1,2 --server-id=3 | $MYSQL
+if ($t4_checksum != `CHECKSUM TABLE t4`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't3'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t4;
+
+--echo #
+--echo # Test Case 11:
+--echo # --ignore-domain-ids, --ignore-server-ids, and multiple GTID ranges
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --start-position=0-1-0,1-1-0 --stop-position=1-1-2,0-1-4| MYSQL
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-domain-ids=0 --ignore-server-ids=2,3 --start-position=0-1-0,1-1-0 --stop-position=1-1-2,0-1-4 | $MYSQL
+if ($t3_checksum != `CHECKSUM TABLE t3`)
+{
+ die $table_inconsistent_err;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't1'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't4'`)
+{
+ die $table_exists_error;
+}
+if (`SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't5'`)
+{
+ die $table_exists_error;
+}
+DROP TABLE t3;
+
+--echo ##############################
+--echo # Error Cases
+--echo ##############################
+
+--echo #
+--echo # Error Case 1:
+--echo # --ignore-domain-ids and --do-domain-ids both specified
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-domain-ids=0 --do-domain-ids=1
+--error 1
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-domain-ids=0 --do-domain-ids=1
+
+--echo #
+--echo # Error Case 2:
+--echo # --ignore-server-ids and --do-server-ids both specified
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --do-server-ids=1 --ignore-server-ids=2
+--error 1
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --do-server-ids=1 --ignore-server-ids=2
+
+--echo #
+--echo # Error Case 3:
+--echo # Invalid ID number provided
+--echo # MYSQL_BINLOG BINLOG_FILE_PARAM --ignore-server-ids=-1
+--error 1
+--exec $MYSQL_BINLOG $BINLOG_FILE_PARAM --ignore-server-ids=-1
+
+--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 a7f8298506f..367c8b7a8c5 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -3308,7 +3308,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),
@@ -3392,6 +3393,140 @@ 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;
+ sql_print_error("Cannot create exclusion rule for %s ids because an "
+ "inclusion rule already exists (possibly from specifying "
+ "both --do-%s-ids and --ignore-%s-ids)",
+ get_id_type_name(), get_id_type_name(),
+ get_id_type_name());
+ goto err;
+ }
+
+ /* If a blacklist is specified more than once, only use the latest values */
+ if (m_blacklist_set)
+ my_hash_reset(&m_filters_by_id_hash);
+
+ 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++;
+ }
+ 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;
+ sql_print_error("Cannot set filter blacklist on %s id %lu because it "
+ "already has a filtering rule",
+ get_id_type_name(), filter_id);
+ 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;
+ sql_print_error("Cannot create inclusion rule for %s ids because an "
+ "exclusion rule already exists (possibly from specifying "
+ "both --ignore-%s-ids and --do-%s-ids)",
+ get_id_type_name(), get_id_type_name(),
+ get_id_type_name());
+ goto err;
+ }
+
+ /* If a whitelist is specified more than once, only use the latest values */
+ if (m_whitelist_set)
+ my_hash_reset(&m_filters_by_id_hash);
+
+ 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++;
+ }
+ 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;
+ sql_print_error("Cannot set filter whitelist on %s id %lu because it "
+ "already has a filtering rule",
+ get_id_type_name(), filter_id);
+ 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)
@@ -3608,3 +3743,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 927937d5162..fe5286943d9 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -458,7 +458,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
};
/*
@@ -504,6 +506,20 @@ public:
};
/*
+ Filter implementation to exclude all tested GTIDs.
+*/
+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).
@@ -657,6 +673,21 @@ public:
uint32 get_filter_type() { return DELEGATING_GTID_FILTER_TYPE; }
virtual gtid_filter_identifier get_id_from_gtid(rpl_gtid *) = 0;
+ virtual const char* get_id_type_name() = 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:
@@ -666,6 +697,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);
};
@@ -709,6 +742,8 @@ public:
return gtid->domain_id;
}
+ const char* get_id_type_name() { return "domain"; }
+
/*
Validates that window filters with both a start and stop GTID satisfy
stop_gtid > start_gtid
@@ -759,7 +794,69 @@ private:
DYNAMIC_ARRAY m_start_filters;
DYNAMIC_ARRAY m_stop_filters;
- 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;
+ }
+
+ const char* get_id_type_name() { return "server"; }
+};
+
+/*
+ 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; }
+
+ /*
+ Returns true if either filter has finished. To elaborate, as this filter
+ performs an intersection, if either filter has finished, the result would
+ be excluded regardless.
+ */
+ my_bool has_finished()
+ {
+ DBUG_ASSERT(m_filter1 && m_filter2);
+ return m_filter1->has_finished() || m_filter2->has_finished();
+ }
+
+ protected:
+ Gtid_event_filter *m_filter1;
+ Gtid_event_filter *m_filter2;
};
#endif /* RPL_GTID_H */