diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/log.cc | 40 | ||||
-rw-r--r-- | sql/log.h | 5 | ||||
-rw-r--r-- | sql/rpl_gtid.cc | 74 | ||||
-rw-r--r-- | sql/rpl_gtid.h | 3 | ||||
-rw-r--r-- | sql/rpl_rli.cc | 2 | ||||
-rw-r--r-- | sql/share/errmsg-utf8.txt | 4 | ||||
-rw-r--r-- | sql/sql_reload.cc | 2 | ||||
-rw-r--r-- | sql/sql_repl.cc | 4 | ||||
-rw-r--r-- | sql/sql_repl.h | 2 | ||||
-rw-r--r-- | sql/sys_vars.cc | 101 | ||||
-rw-r--r-- | sql/sys_vars.h | 41 |
11 files changed, 269 insertions, 9 deletions
diff --git a/sql/log.cc b/sql/log.cc index ddea3805fee..041a0b555d1 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3703,7 +3703,8 @@ err: 1 error */ -bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) +bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log, + rpl_gtid *init_state, uint32 init_state_len) { LOG_INFO linfo; bool error=0; @@ -3722,6 +3723,14 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) if (!is_relay_log) { + if (init_state && !is_empty_state()) + { + my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0)); + mysql_mutex_unlock(&LOCK_index); + mysql_mutex_unlock(&LOCK_log); + DBUG_RETURN(1); + } + /* Mark that a RESET MASTER is in progress. This ensures that a binlog checkpoint will not try to write binlog @@ -3839,7 +3848,10 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) if (!is_relay_log) { - rpl_global_gtid_binlog_state.reset(); + if (init_state) + rpl_global_gtid_binlog_state.load(init_state, init_state_len); + else + rpl_global_gtid_binlog_state.reset(); } /* Start logging with a new file */ @@ -5525,6 +5537,30 @@ MYSQL_BIN_LOG::append_state_pos(String *str) bool +MYSQL_BIN_LOG::append_state(String *str) +{ + bool err; + + mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); + err= rpl_global_gtid_binlog_state.append_state(str); + mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); + return err; +} + + +bool +MYSQL_BIN_LOG::is_empty_state() +{ + bool res; + + mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); + res= (rpl_global_gtid_binlog_state.count() == 0); + mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); + return res; +} + + +bool MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id, rpl_gtid *out_gtid) { diff --git a/sql/log.h b/sql/log.h index 018ac64eff7..11b9cd289f7 100644 --- a/sql/log.h +++ b/sql/log.h @@ -751,7 +751,8 @@ public: int register_create_index_entry(const char* entry); int purge_index_entry(THD *thd, ulonglong *decrease_log_space, bool need_mutex); - bool reset_logs(THD* thd, bool create_new_log); + bool reset_logs(THD* thd, bool create_new_log, + rpl_gtid *init_state, uint32 init_state_len); void close(uint exiting); void clear_inuse_flag_when_closing(File file); @@ -780,6 +781,8 @@ public: int write_state_to_file(); int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size); bool append_state_pos(String *str); + bool append_state(String *str); + bool is_empty_state(); bool find_in_binlog_state(uint32 domain_id, uint32 server_id, rpl_gtid *out_gtid); bool lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid); diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index 96dade4d390..62d2d6026ce 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -719,6 +719,45 @@ gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid) } +rpl_gtid * +gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len) +{ + char *p= const_cast<char *>(str); + char *end= p + str_len; + uint32 len= 0, alloc_len= 5; + rpl_gtid *list= NULL; + + for (;;) + { + rpl_gtid gtid; + + if (len >= (((uint32)1 << 28)-1) || gtid_parser_helper(&p, end, >id)) + { + my_free(list); + return NULL; + } + if ((!list || len >= alloc_len) && + !(list= + (rpl_gtid *)my_realloc(list, + (alloc_len= alloc_len*2) * sizeof(rpl_gtid), + MYF(MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR)))) + return NULL; + list[len++]= gtid; + + if (p == end) + break; + if (*p != ',') + { + my_free(list); + return NULL; + } + ++p; + } + *out_len= len; + return list; +} + + /* Update the slave replication state with the GTID position obtained from master when connecting with old-style (filename,offset) position. @@ -1234,6 +1273,41 @@ rpl_binlog_state::append_pos(String *str) } +bool +rpl_binlog_state::append_state(String *str) +{ + uint32 i, j; + bool first= true; + + for (i= 0; i < hash.records; ++i) + { + element *e= (element *)my_hash_element(&hash, i); + if (!e->last_gtid) + { + DBUG_ASSERT(e->hash.records==0); + continue; + } + for (j= 0; j <= e->hash.records; ++j) + { + const rpl_gtid *gtid; + if (j < e->hash.records) + { + gtid= (rpl_gtid *)my_hash_element(&e->hash, j); + if (gtid == e->last_gtid) + continue; + } + else + gtid= e->last_gtid; + + if (rpl_slave_state_tostring_helper(str, gtid, &first)) + return true; + } + } + + return false; +} + + slave_connection_state::slave_connection_state() { my_hash_init(&hash, &my_charset_bin, 32, diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index 8d2c98d54d3..d6da2295abe 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -163,6 +163,7 @@ struct rpl_binlog_state int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size); int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size); bool append_pos(String *str); + bool append_state(String *str); rpl_gtid *find(uint32 domain_id, uint32 server_id); rpl_gtid *find_most_recent(uint32 domain_id); }; @@ -205,5 +206,7 @@ struct slave_connection_state extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid, bool *first); extern int gtid_check_rpl_slave_state_table(TABLE *table); +extern rpl_gtid *gtid_parse_string_to_list(const char *p, size_t len, + uint32 *out_len); #endif /* RPL_GTID_H */ diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 7b55688ce3c..dd2a151108d 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1011,7 +1011,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, rli->cur_log_fd= -1; } - if (rli->relay_log.reset_logs(thd, !just_reset)) + if (rli->relay_log.reset_logs(thd, !just_reset, NULL, 0)) { *errmsg = "Failed during log reset"; error=1; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index abafade698d..97cff25fa31 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6527,7 +6527,7 @@ ER_SQL_DISCOVER_ERROR ER_FAILED_GTID_STATE_INIT eng "Failed initializing replication GTID state" ER_INCORRECT_GTID_STATE - eng "Could not parse GTID list for GTID_POS" + eng "Could not parse GTID list" ER_CANNOT_UPDATE_GTID_STATE eng "Could not update replication slave gtid state" ER_DUPLICATE_GTID_DOMAIN @@ -6557,3 +6557,5 @@ ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a stored function or trigger" ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2 eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog. Since the master's binlog contains GTIDs with higher sequence numbers, it probably means that the slave has diverged due to executing extra errorneous transactions" +ER_BINLOG_MUST_BE_EMPTY + eng "This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log" diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index e9c9dc86e41..8a05732173d 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -322,7 +322,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, { DBUG_ASSERT(thd); tmp_write_to_binlog= 0; - if (reset_master(thd)) + if (reset_master(thd, NULL, 0)) { /* NOTE: my_error() has been already called by reset_master(). */ result= 1; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 34b2b90c8b2..3e4939b7fa8 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3474,7 +3474,7 @@ err: @retval 0 success @retval 1 error */ -int reset_master(THD* thd) +int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len) { if (!mysql_bin_log.is_open()) { @@ -3483,7 +3483,7 @@ int reset_master(THD* thd) return 1; } - if (mysql_bin_log.reset_logs(thd, 1)) + if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len)) return 1; RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */)); return 0; diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 917da9b598e..da55e3e863f 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -46,7 +46,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report); bool change_master(THD* thd, Master_info* mi, bool *master_info_added); bool mysql_show_binlog_events(THD* thd); int reset_slave(THD *thd, Master_info* mi); -int reset_master(THD* thd); +int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len); bool purge_master_logs(THD* thd, const char* to_log); bool purge_master_logs_before_date(THD* thd, time_t purge_time); bool log_in_use(const char* log_name); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index b35e8bb383c..3827e3f4b67 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1434,6 +1434,107 @@ static Sys_var_mybool Sys_gtid_strict_mode( "generate an out-of-order binlog if executed.", GLOBAL_VAR(opt_gtid_strict_mode), CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + + +struct gtid_binlog_state_data { rpl_gtid *list; uint32 list_len; }; + +bool +Sys_var_gtid_binlog_state::do_check(THD *thd, set_var *var) +{ + String str, *res; + struct gtid_binlog_state_data *data; + rpl_gtid *list; + uint32 list_len; + + DBUG_ASSERT(var->type == OPT_GLOBAL); + + if (!(res= var->value->val_str(&str))) + return true; + if (thd->in_active_multi_stmt_transaction()) + { + my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0)); + return true; + } + if (!mysql_bin_log.is_open()) + { + my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(0)); + return true; + } + if (!mysql_bin_log.is_empty_state()) + { + my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0)); + return true; + } + if (res->length() == 0) + list= NULL; + else if (!(list= gtid_parse_string_to_list(res->ptr(), res->length(), + &list_len))) + { + my_error(ER_INCORRECT_GTID_STATE, MYF(0)); + return true; + } + if (!(data= (gtid_binlog_state_data *)my_malloc(sizeof(*data), MYF(0)))) + { + my_free(list); + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return true; + } + data->list= list; + data->list_len= list_len; + var->save_result.ptr= data; + return false; +} + + +bool +Sys_var_gtid_binlog_state::global_update(THD *thd, set_var *var) +{ + bool res; + + DBUG_ASSERT(var->type == OPT_GLOBAL); + + if (!var->value) + { + my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str); + return true; + } + + struct gtid_binlog_state_data *data= + (struct gtid_binlog_state_data *)var->save_result.ptr; + mysql_mutex_unlock(&LOCK_global_system_variables); + res= (0 != reset_master(thd, data->list, data->list_len)); + mysql_mutex_lock(&LOCK_global_system_variables); + my_free(data->list); + my_free(data); + return res; +} + + +uchar * +Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, LEX_STRING *base) +{ + char buf[512]; + String str(buf, sizeof(buf), system_charset_info); + char *p; + + str.length(0); + if ((opt_bin_log && mysql_bin_log.append_state(&str)) || + !(p= thd->strmake(str.ptr(), str.length()))) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return NULL; + } + + return (uchar *)p; +} + + +static unsigned char opt_gtid_binlog_state_dummy; +static Sys_var_gtid_binlog_state Sys_gtid_binlog_state( + "gtid_binlog_state", + "The internal GTID state of the binlog, used to keep track of all " + "GTIDs ever logged to the binlog.", + GLOBAL_VAR(opt_gtid_binlog_state_dummy), NO_CMD_LINE); #endif diff --git a/sql/sys_vars.h b/sql/sys_vars.h index 260a8af8384..e1c2201da31 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -2167,3 +2167,44 @@ public: } uchar *global_value_ptr(THD *thd, LEX_STRING *base); }; + + +/** + Class for @@global.gtid_binlog_state. +*/ +class Sys_var_gtid_binlog_state: public sys_var +{ +public: + Sys_var_gtid_binlog_state(const char *name_arg, + const char *comment, int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt) + : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id, + getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG, + NULL, NULL, NULL) + { + option.var_type= GET_STR; + } + bool do_check(THD *thd, set_var *var); + bool session_update(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + return true; + } + bool global_update(THD *thd, set_var *var); + bool check_update_type(Item_result type) { return type != STRING_RESULT; } + void session_save_default(THD *thd, set_var *var) + { + DBUG_ASSERT(false); + } + void global_save_default(THD *thd, set_var *var) + { + /* Record the attempt to use default so we can error. */ + var->value= 0; + } + uchar *session_value_ptr(THD *thd, LEX_STRING *base) + { + DBUG_ASSERT(false); + return NULL; + } + uchar *global_value_ptr(THD *thd, LEX_STRING *base); +}; |