diff options
author | Brandon Nesterenko <brandon.nesterenko@mariadb.com> | 2021-11-03 08:18:48 -0600 |
---|---|---|
committer | Brandon Nesterenko <brandon.nesterenko@mariadb.com> | 2021-11-03 08:18:48 -0600 |
commit | fa12a418bec21ae98c0d177d7065c1284a5c08b0 (patch) | |
tree | af836c424bbe90d71edc5737180cd14189791dff | |
parent | f591888fdfe2b4ea49bad6c5521cac33a154c786 (diff) | |
download | mariadb-git-fa12a418bec21ae98c0d177d7065c1284a5c08b0.tar.gz |
MDEV-20119 stash
-rw-r--r-- | client/client_priv.h | 4 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 305 | ||||
-rw-r--r-- | man/mysqlbinlog.1 | 67 | ||||
-rw-r--r-- | mysql-test/suite/binlog/include/mysqlbinlog_gtid_window_test_cases.inc | 2 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore-master.opt | 2 | ||||
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_mysqlbinlog_gtid_ignore.test | 242 | ||||
-rw-r--r-- | sql/rpl_gtid.cc | 129 | ||||
-rw-r--r-- | sql/rpl_gtid.h | 96 |
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(>id, 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(>id)) + else if (!gtid_event_filter->exclude(>id)) { 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 */ |