summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorsven@riska.(none) <>2007-12-14 19:02:02 +0100
committersven@riska.(none) <>2007-12-14 19:02:02 +0100
commit5cc348a6a6d4e93bf6f0401996d029b6f249e66d (patch)
treef82037782aea390667096dae3f5f214d28a217eb /sql
parentab396c1d091a4836c8c6abca9c34b7869ce76055 (diff)
downloadmariadb-git-5cc348a6a6d4e93bf6f0401996d029b6f249e66d.tar.gz
BUG#32407: Impossible to do point-in-time recovery from older binlog
Problem: it is unsafe to read base64-printed events without first reading the Format_description_log_event (FD). Currently, mysqlbinlog cannot print the FD. As a side effect, another bug has also been fixed: When mysqlbinlog --start-position=X was specified, no ROLLBACK was printed. I changed this, so that ROLLBACK is always printed. This patch does several things: - Format_description_log_event (FD) now print themselves in base64 format. - mysqlbinlog is now able to print FD events. It has three modes: --base64-output=auto Print row events in base64 output, and print FD event. The FD event is printed even if it is outside the range specified with --start-position, because it would not be safe to read row events otherwise. This is the default. --base64-output=always Like --base64-output=auto, but also print base64 output for query events. This is like the old --base64-output flag, which is also a shorthand for --base64-output=always --base64-output=never Never print base64 output, generate error if row events occur in binlog. This is useful to suppress the FD event in binlogs known not to contain row events (e.g., because BINLOG statement is unsafe, requires root privileges, is not SQL, etc) - the BINLOG statement now handles FD events correctly, by setting the thread's rli's relay log's description_event_for_exec to the loaded event. In fact, executing a BINLOG statement is almost the same as reading an event from a relay log. Before my patch, the code for this was separated (exec_relay_log_event in slave.cc executes events from the relay log, mysql_client_binlog_statement in sql_binlog.cc executes BINLOG statements). I needed to augment mysql_client_binlog_statement to do parts of what exec_relay_log_event does. Hence, I did a small refactoring and moved parts of exec_relay_log_event to a new function, which I named apply_event_and_update_pos. apply_event_and_update_pos is called both from exec_relay_log_event and from mysql_client_binlog_statement. - When a non-FD event is executed in a BINLOG statement, without previously executing a FD event in a BINLOG statement, it generates an error, because that's unsafe. I took a new error code for that: ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENTS. In order to get a decent error message containing the name of the event, I added the class method char* Log_event::get_type_str(Log_event_type type), which returns a string name for the given Log_event_type. This is just like the existing char* Log_event::get_type_str(), except it is a class method that takes the log event type as parameter. I also added PRE_GA_*_ROWS_LOG_EVENT to Log_event::get_type_str(), so that names of old rows event are properly printed. - When reading an event, I added a check that the event type is known by the current Format_description_log_event. Without this, it may crash on bad input (and I was struck by this several times). - I patched the following test cases, which all contain BINLOG statements for row events which must be preceded by BINLOG statements for FD events: - rpl_bug31076 While I was here, I fixed some small things in log_event.cc: - replaced hard-coded 4 by EVENT_TYPE_OFFSET in 3 places - replaced return by DBUG_VOID_RETURN in one place - The name of the logfile can be '-' to indicate stdin. Before my patch, the code just checked if the first character is '-'; now it does a full strcmp(). Probably, all arguments that begin with a - are already handled somewhere else as flags, but I still think it is better that the code reflects what it is supposed to do, with as little dependencies as possible on other parts of the code. If we one day implement that all command line arguments after -- are files (as most unix tools do), then we need this. I also fixed the following in slave.cc: - next_event() was declared twice, and queue_event was not static but should be static (not used outside the file).
Diffstat (limited to 'sql')
-rw-r--r--sql/log_event.cc204
-rw-r--r--sql/log_event.h29
-rw-r--r--sql/share/errmsg.txt3
-rw-r--r--sql/slave.cc306
-rw-r--r--sql/slave.h2
-rw-r--r--sql/sql_binlog.cc74
6 files changed, 403 insertions, 215 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc
index cdfd58535fb..8a4fabde3e3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -491,9 +491,9 @@ static void print_set_option(IO_CACHE* file, uint32 bits_changed,
Log_event::get_type_str()
*/
-const char* Log_event::get_type_str()
+const char* Log_event::get_type_str(Log_event_type type)
{
- switch(get_type_code()) {
+ switch(type) {
case START_EVENT_V3: return "Start_v3";
case STOP_EVENT: return "Stop";
case QUERY_EVENT: return "Query";
@@ -524,6 +524,11 @@ const char* Log_event::get_type_str()
}
}
+const char* Log_event::get_type_str()
+{
+ return get_type_str(get_type_code());
+}
+
/*
Log_event::Log_event()
@@ -1050,94 +1055,111 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
}
- switch(buf[EVENT_TYPE_OFFSET]) {
- case QUERY_EVENT:
- ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
- break;
- case LOAD_EVENT:
- ev = new Load_log_event(buf, event_len, description_event);
- break;
- case NEW_LOAD_EVENT:
- ev = new Load_log_event(buf, event_len, description_event);
- break;
- case ROTATE_EVENT:
- ev = new Rotate_log_event(buf, event_len, description_event);
- break;
+ uint event_type= buf[EVENT_TYPE_OFFSET];
+ if (event_type > description_event->number_of_event_types &&
+ event_type != FORMAT_DESCRIPTION_EVENT)
+ {
+ /*
+ It is unsafe to use the description_event if its post_header_len
+ array does not include the event type.
+ */
+ DBUG_PRINT("error", ("event type %d found, but the current "
+ "Format_description_log_event supports only %d event "
+ "types", event_type,
+ description_event->number_of_event_types));
+ ev= NULL;
+ }
+ else
+ {
+ switch(event_type) {
+ case QUERY_EVENT:
+ ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
+ break;
+ case LOAD_EVENT:
+ ev = new Load_log_event(buf, event_len, description_event);
+ break;
+ case NEW_LOAD_EVENT:
+ ev = new Load_log_event(buf, event_len, description_event);
+ break;
+ case ROTATE_EVENT:
+ ev = new Rotate_log_event(buf, event_len, description_event);
+ break;
#ifdef HAVE_REPLICATION
- case SLAVE_EVENT: /* can never happen (unused event) */
- ev = new Slave_log_event(buf, event_len);
- break;
+ case SLAVE_EVENT: /* can never happen (unused event) */
+ ev = new Slave_log_event(buf, event_len);
+ break;
#endif /* HAVE_REPLICATION */
- case CREATE_FILE_EVENT:
- ev = new Create_file_log_event(buf, event_len, description_event);
- break;
- case APPEND_BLOCK_EVENT:
- ev = new Append_block_log_event(buf, event_len, description_event);
- break;
- case DELETE_FILE_EVENT:
- ev = new Delete_file_log_event(buf, event_len, description_event);
- break;
- case EXEC_LOAD_EVENT:
- ev = new Execute_load_log_event(buf, event_len, description_event);
- break;
- case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
- ev = new Start_log_event_v3(buf, description_event);
- break;
- case STOP_EVENT:
- ev = new Stop_log_event(buf, description_event);
- break;
- case INTVAR_EVENT:
- ev = new Intvar_log_event(buf, description_event);
- break;
- case XID_EVENT:
- ev = new Xid_log_event(buf, description_event);
- break;
- case RAND_EVENT:
- ev = new Rand_log_event(buf, description_event);
- break;
- case USER_VAR_EVENT:
- ev = new User_var_log_event(buf, description_event);
- break;
- case FORMAT_DESCRIPTION_EVENT:
- ev = new Format_description_log_event(buf, event_len, description_event);
- break;
+ case CREATE_FILE_EVENT:
+ ev = new Create_file_log_event(buf, event_len, description_event);
+ break;
+ case APPEND_BLOCK_EVENT:
+ ev = new Append_block_log_event(buf, event_len, description_event);
+ break;
+ case DELETE_FILE_EVENT:
+ ev = new Delete_file_log_event(buf, event_len, description_event);
+ break;
+ case EXEC_LOAD_EVENT:
+ ev = new Execute_load_log_event(buf, event_len, description_event);
+ break;
+ case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
+ ev = new Start_log_event_v3(buf, description_event);
+ break;
+ case STOP_EVENT:
+ ev = new Stop_log_event(buf, description_event);
+ break;
+ case INTVAR_EVENT:
+ ev = new Intvar_log_event(buf, description_event);
+ break;
+ case XID_EVENT:
+ ev = new Xid_log_event(buf, description_event);
+ break;
+ case RAND_EVENT:
+ ev = new Rand_log_event(buf, description_event);
+ break;
+ case USER_VAR_EVENT:
+ ev = new User_var_log_event(buf, description_event);
+ break;
+ case FORMAT_DESCRIPTION_EVENT:
+ ev = new Format_description_log_event(buf, event_len, description_event);
+ break;
#if defined(HAVE_REPLICATION)
- case PRE_GA_WRITE_ROWS_EVENT:
- ev = new Write_rows_log_event_old(buf, event_len, description_event);
- break;
- case PRE_GA_UPDATE_ROWS_EVENT:
- ev = new Update_rows_log_event_old(buf, event_len, description_event);
- break;
- case PRE_GA_DELETE_ROWS_EVENT:
- ev = new Delete_rows_log_event_old(buf, event_len, description_event);
- break;
- case WRITE_ROWS_EVENT:
- ev = new Write_rows_log_event(buf, event_len, description_event);
- break;
- case UPDATE_ROWS_EVENT:
- ev = new Update_rows_log_event(buf, event_len, description_event);
- break;
- case DELETE_ROWS_EVENT:
- ev = new Delete_rows_log_event(buf, event_len, description_event);
- break;
- case TABLE_MAP_EVENT:
- ev = new Table_map_log_event(buf, event_len, description_event);
- break;
+ case PRE_GA_WRITE_ROWS_EVENT:
+ ev = new Write_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case PRE_GA_UPDATE_ROWS_EVENT:
+ ev = new Update_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case PRE_GA_DELETE_ROWS_EVENT:
+ ev = new Delete_rows_log_event_old(buf, event_len, description_event);
+ break;
+ case WRITE_ROWS_EVENT:
+ ev = new Write_rows_log_event(buf, event_len, description_event);
+ break;
+ case UPDATE_ROWS_EVENT:
+ ev = new Update_rows_log_event(buf, event_len, description_event);
+ break;
+ case DELETE_ROWS_EVENT:
+ ev = new Delete_rows_log_event(buf, event_len, description_event);
+ break;
+ case TABLE_MAP_EVENT:
+ ev = new Table_map_log_event(buf, event_len, description_event);
+ break;
#endif
- case BEGIN_LOAD_QUERY_EVENT:
- ev = new Begin_load_query_log_event(buf, event_len, description_event);
- break;
- case EXECUTE_LOAD_QUERY_EVENT:
- ev= new Execute_load_query_log_event(buf, event_len, description_event);
- break;
- case INCIDENT_EVENT:
- ev = new Incident_log_event(buf, event_len, description_event);
- break;
- default:
- DBUG_PRINT("error",("Unknown event code: %d",
- (int) buf[EVENT_TYPE_OFFSET]));
- ev= NULL;
- break;
+ case BEGIN_LOAD_QUERY_EVENT:
+ ev = new Begin_load_query_log_event(buf, event_len, description_event);
+ break;
+ case EXECUTE_LOAD_QUERY_EVENT:
+ ev= new Execute_load_query_log_event(buf, event_len, description_event);
+ break;
+ case INCIDENT_EVENT:
+ ev = new Incident_log_event(buf, event_len, description_event);
+ break;
+ default:
+ DBUG_PRINT("error",("Unknown event code: %d",
+ (int) buf[EVENT_TYPE_OFFSET]));
+ ev= NULL;
+ break;
+ }
}
DBUG_PRINT("read_event", ("%s(type_code: %d; event_len: %d)",
@@ -2604,6 +2626,14 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter);
#endif
}
+ if (temp_buf &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
+ !print_event_info->short_form)
+ {
+ my_b_printf(&cache, "BINLOG '\n");
+ print_base64(&cache, print_event_info, FALSE);
+ print_event_info->printed_fd_event= TRUE;
+ }
DBUG_VOID_RETURN;
}
#endif /* MYSQL_CLIENT */
@@ -5050,7 +5080,7 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
Load_log_event::get_data_size() +
create_file_header_len + 1);
if (len < block_offset)
- return;
+ DBUG_VOID_RETURN;
block = (char*)buf + block_offset;
block_len = len - block_offset;
}
diff --git a/sql/log_event.h b/sql/log_event.h
index 0275b02dd49..4a75f330203 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -567,6 +567,15 @@ class Format_description_log_event;
class Relay_log_info;
#ifdef MYSQL_CLIENT
+enum enum_base64_output_mode {
+ BASE64_OUTPUT_NEVER= 0,
+ BASE64_OUTPUT_AUTO= 1,
+ BASE64_OUTPUT_ALWAYS= 2,
+ BASE64_OUTPUT_UNSPEC= 3,
+ /* insert new output modes here */
+ BASE64_OUTPUT_MODE_COUNT
+};
+
/*
A structure for mysqlbinlog to know how to print events
@@ -600,7 +609,8 @@ typedef struct st_print_event_info
st_print_event_info()
:flags2_inited(0), sql_mode_inited(0),
auto_increment_increment(1),auto_increment_offset(1), charset_inited(0),
- lc_time_names_number(0), charset_database_number(0)
+ lc_time_names_number(0), charset_database_number(0),
+ base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
{
/*
Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
@@ -627,7 +637,14 @@ typedef struct st_print_event_info
/* Settings on how to print the events */
bool short_form;
- bool base64_output;
+ enum_base64_output_mode base64_output_mode;
+ /*
+ This is set whenever a Format_description_event is printed.
+ Later, when an event is printed in base64, this flag is tested: if
+ no Format_description_event has been seen, it is unsafe to print
+ the base64 event, so an error message is generated.
+ */
+ bool printed_fd_event;
my_off_t hexdump_from;
uint8 common_header_len;
char delimiter[16];
@@ -936,7 +953,13 @@ public:
const char **error,
const Format_description_log_event
*description_event);
- /* returns the human readable name of the event's type */
+ /**
+ Returns the human readable name of the given event type.
+ */
+ static const char* get_type_str(Log_event_type type);
+ /**
+ Returns the human readable name of this event's type.
+ */
const char* get_type_str();
/* Return start of query time or current time */
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index dce1dd785bf..026a0023660 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -6116,3 +6116,6 @@ ER_CANT_CREATE_SROUTINE
eng "Cannot create stored routine `%-.64s`. Check warnings"
ER_SLAVE_AMBIGOUS_EXEC_MODE
eng "Ambiguous slave modes combination. %s"
+
+ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT
+ eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement."
diff --git a/sql/slave.cc b/sql/slave.cc
index b6611d44723..14fa1c45c36 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -13,6 +13,17 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @addtogroup Replication
+ @{
+
+ @file
+
+ @brief Code to run the io thread and the sql thread on the
+ replication slave.
+*/
+
#include "mysql_priv.h"
#include <mysql.h>
@@ -33,10 +44,6 @@
#include "rpl_tblmap.h"
-int queue_event(Master_info* mi,const char* buf,ulong event_len);
-static Log_event* next_event(Relay_log_info* rli);
-
-
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
#define MAX_SLAVE_RETRY_PAUSE 5
@@ -132,6 +139,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
const char* table_name, bool overwrite);
static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi);
static Log_event* next_event(Relay_log_info* rli);
+static int queue_event(Master_info* mi,const char* buf,ulong event_len);
static int terminate_slave_thread(THD *thd,
pthread_mutex_t* term_lock,
pthread_cond_t* term_cond,
@@ -1757,6 +1765,175 @@ static int has_temporary_error(THD *thd)
DBUG_RETURN(0);
}
+
+/**
+ Applies the given event and advances the relay log position.
+
+ In essence, this function does:
+
+ @code
+ ev->apply_event(rli);
+ ev->update_pos(rli);
+ @endcode
+
+ But it also does some maintainance, such as skipping events if
+ needed and reporting errors.
+
+ If the @c skip flag is set, then it is tested whether the event
+ should be skipped, by looking at the slave_skip_counter and the
+ server id. The skip flag should be set when calling this from a
+ replication thread but not set when executing an explicit BINLOG
+ statement.
+
+ @retval 0 OK.
+
+ @retval 1 Error calling ev->apply_event().
+
+ @retval 2 No error calling ev->apply_event(), but error calling
+ ev->update_pos().
+*/
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
+ bool skip)
+{
+ int exec_res= 0;
+
+ DBUG_ENTER("apply_event_and_update_pos");
+
+ DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)",
+ ev->get_type_str(), ev->get_type_code(),
+ ev->server_id));
+ DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu",
+ FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
+ FLAGSTR(thd->options, OPTION_BEGIN),
+ rli->last_event_start_time));
+
+ /*
+ Execute the event to change the database and update the binary
+ log coordinates, but first we set some data that is needed for
+ the thread.
+
+ The event will be executed unless it is supposed to be skipped.
+
+ Queries originating from this server must be skipped. Low-level
+ events (Format_description_log_event, Rotate_log_event,
+ Stop_log_event) from this server must also be skipped. But for
+ those we don't want to modify 'group_master_log_pos', because
+ these events did not exist on the master.
+ Format_description_log_event is not completely skipped.
+
+ Skip queries specified by the user in 'slave_skip_counter'. We
+ can't however skip events that has something to do with the log
+ files themselves.
+
+ Filtering on own server id is extremely important, to ignore
+ execution of events created by the creation/rotation of the relay
+ log (remember that now the relay log starts with its Format_desc,
+ has a Rotate etc).
+ */
+
+ thd->server_id = ev->server_id; // use the original server id for logging
+ thd->set_time(); // time the query
+ thd->lex->current_select= 0;
+ if (!ev->when)
+ ev->when= my_time(0);
+ ev->thd = thd; // because up to this point, ev->thd == 0
+
+ if (skip)
+ {
+ int reason= ev->shall_skip(rli);
+ if (reason == Log_event::EVENT_SKIP_COUNT)
+ --rli->slave_skip_counter;
+ pthread_mutex_unlock(&rli->data_lock);
+ if (reason == Log_event::EVENT_SKIP_NOT)
+ exec_res= ev->apply_event(rli);
+#ifndef DBUG_OFF
+ /*
+ This only prints information to the debug trace.
+
+ TODO: Print an informational message to the error log?
+ */
+ static const char *const explain[] = {
+ // EVENT_SKIP_NOT,
+ "not skipped",
+ // EVENT_SKIP_IGNORE,
+ "skipped because event should be ignored",
+ // EVENT_SKIP_COUNT
+ "skipped because event skip counter was non-zero"
+ };
+ DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
+ thd->options & OPTION_BEGIN ? 1 : 0,
+ rli->get_flag(Relay_log_info::IN_STMT)));
+ DBUG_PRINT("skip_event", ("%s event was %s",
+ ev->get_type_str(), explain[reason]));
+#endif
+ }
+ else
+ exec_res= ev->apply_event(rli);
+
+ DBUG_PRINT("info", ("apply_event error = %d", exec_res));
+ if (exec_res == 0)
+ {
+ int error= ev->update_pos(rli);
+ char buf[22];
+ DBUG_PRINT("info", ("update_pos error = %d", error));
+ DBUG_PRINT("info", ("group %s %s",
+ llstr(rli->group_relay_log_pos, buf),
+ rli->group_relay_log_name));
+ DBUG_PRINT("info", ("event %s %s",
+ llstr(rli->event_relay_log_pos, buf),
+ rli->event_relay_log_name));
+ /*
+ The update should not fail, so print an error message and
+ return an error code.
+
+ TODO: Replace this with a decent error message when merged
+ with BUG#24954 (which adds several new error message).
+ */
+ if (error)
+ {
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
+ "It was not possible to update the positions"
+ " of the relay log information: the slave may"
+ " be in an inconsistent state."
+ " Stopped in %s position %s",
+ rli->group_relay_log_name,
+ llstr(rli->group_relay_log_pos, buf));
+ DBUG_RETURN(2);
+ }
+ }
+
+ DBUG_RETURN(exec_res ? 1 : 0);
+}
+
+
+/**
+ Top-level function for executing the next event from the relay log.
+
+ This function reads the event from the relay log, executes it, and
+ advances the relay log position. It also handles errors, etc.
+
+ This function may fail to apply the event for the following reasons:
+
+ - The position specfied by the UNTIL condition of the START SLAVE
+ command is reached.
+
+ - It was not possible to read the event from the log.
+
+ - The slave is killed.
+
+ - An error occurred when applying the event, and the event has been
+ tried slave_trans_retries times. If the event has been retried
+ fewer times, 0 is returned.
+
+ - init_master_info or init_relay_log_pos failed. (These are called
+ if a failure occurs when applying the event.)</li>
+
+ - An error occurred when updating the binlog position.
+
+ @retval 0 The event was applied.
+
+ @retval 1 The event was not applied.
+*/
static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
{
DBUG_ENTER("exec_relay_log_event");
@@ -1802,117 +1979,26 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
}
if (ev)
{
- int const type_code= ev->get_type_code();
- int exec_res= 0;
-
- DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)",
- ev->get_type_str(), type_code, ev->server_id));
- DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu",
- FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
- FLAGSTR(thd->options, OPTION_BEGIN),
- rli->last_event_start_time));
-
+ int exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
/*
- Execute the event to change the database and update the binary
- log coordinates, but first we set some data that is needed for
- the thread.
-
- The event will be executed unless it is supposed to be skipped.
-
- Queries originating from this server must be skipped. Low-level
- events (Format_description_log_event, Rotate_log_event,
- Stop_log_event) from this server must also be skipped. But for
- those we don't want to modify 'group_master_log_pos', because
- these events did not exist on the master.
- Format_description_log_event is not completely skipped.
-
- Skip queries specified by the user in 'slave_skip_counter'. We
- can't however skip events that has something to do with the log
- files themselves.
-
- Filtering on own server id is extremely important, to ignore
- execution of events created by the creation/rotation of the relay
- log (remember that now the relay log starts with its Format_desc,
- has a Rotate etc).
+ Format_description_log_event should not be deleted because it will be
+ used to read info about the relay log's format; it will be deleted when
+ the SQL thread does not need it, i.e. when this thread terminates.
*/
-
- thd->server_id = ev->server_id; // use the original server id for logging
- thd->set_time(); // time the query
- thd->lex->current_select= 0;
- if (!ev->when)
- ev->when= my_time(0);
- ev->thd = thd; // because up to this point, ev->thd == 0
-
- int reason= ev->shall_skip(rli);
- if (reason == Log_event::EVENT_SKIP_COUNT)
- --rli->slave_skip_counter;
- pthread_mutex_unlock(&rli->data_lock);
- if (reason == Log_event::EVENT_SKIP_NOT)
- exec_res= ev->apply_event(rli);
-#ifndef DBUG_OFF
- /*
- This only prints information to the debug trace.
-
- TODO: Print an informational message to the error log?
- */
- static const char *const explain[] = {
- // EVENT_SKIP_NOT,
- "not skipped",
- // EVENT_SKIP_IGNORE,
- "skipped because event should be ignored",
- // EVENT_SKIP_COUNT
- "skipped because event skip counter was non-zero"
- };
- DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
- thd->options & OPTION_BEGIN ? 1 : 0,
- rli->get_flag(Relay_log_info::IN_STMT)));
- DBUG_PRINT("skip_event", ("%s event was %s",
- ev->get_type_str(), explain[reason]));
-#endif
-
- DBUG_PRINT("info", ("apply_event error = %d", exec_res));
- if (exec_res == 0)
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
{
- int error= ev->update_pos(rli);
- char buf[22];
- DBUG_PRINT("info", ("update_pos error = %d", error));
- DBUG_PRINT("info", ("group %s %s",
- llstr(rli->group_relay_log_pos, buf),
- rli->group_relay_log_name));
- DBUG_PRINT("info", ("event %s %s",
- llstr(rli->event_relay_log_pos, buf),
- rli->event_relay_log_name));
- /*
- The update should not fail, so print an error message and
- return an error code.
-
- TODO: Replace this with a decent error message when merged
- with BUG#24954 (which adds several new error message).
- */
- if (error)
- {
- rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
- "It was not possible to update the positions"
- " of the relay log information: the slave may"
- " be in an inconsistent state."
- " Stopped in %s position %s",
- rli->group_relay_log_name,
- llstr(rli->group_relay_log_pos, buf));
- DBUG_RETURN(1);
- }
+ DBUG_PRINT("info", ("Deleting the event after it has been executed"));
+ delete ev;
}
/*
- Format_description_log_event should not be deleted because it will be
- used to read info about the relay log's format; it will be deleted when
- the SQL thread does not need it, i.e. when this thread terminates.
+ update_log_pos failed: this should not happen, so we don't
+ retry.
*/
- if (type_code != FORMAT_DESCRIPTION_EVENT)
- {
- DBUG_PRINT("info", ("Deleting the event after it has been executed"));
- delete ev;
- }
+ if (exec_res == 2)
+ DBUG_RETURN(1);
+
if (slave_trans_retries)
{
int temp_err;
@@ -3051,7 +3137,7 @@ static int queue_old_event(Master_info *mi, const char *buf,
any >=5.0.0 format.
*/
-int queue_event(Master_info* mi,const char* buf, ulong event_len)
+static int queue_event(Master_info* mi,const char* buf, ulong event_len)
{
int error= 0;
ulong inc_pos;
@@ -3937,4 +4023,8 @@ template class I_List_iterator<i_string>;
template class I_List_iterator<i_string_pair>;
#endif
+/**
+ @} (end of group Replication)
+*/
+
#endif /* HAVE_REPLICATION */
diff --git a/sql/slave.h b/sql/slave.h
index 2cd9ea352ba..f1772bbc1fc 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -187,6 +187,8 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
void set_slave_thread_options(THD* thd);
void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
void rotate_relay_log(Master_info* mi);
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
+ bool skip);
pthread_handler_t handle_slave_io(void *arg);
pthread_handler_t handle_slave_sql(void *arg);
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index fa6aa8f5881..543b1af9fc0 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -17,15 +17,14 @@
#include "rpl_rli.h"
#include "base64.h"
-/*
+/**
Execute a BINLOG statement
- TODO: This currently assumes a MySQL 5.x binlog.
- When we'll have binlog with a different format, to execute the
- BINLOG command properly the server will need to know which format
- the BINLOG command's event is in. mysqlbinlog should then send
- the Format_description_log_event of the binlog it reads and the
- server thread should cache this format into
+ To execute the BINLOG command properly the server needs to know
+ which format the BINLOG command's event is in. Therefore, the first
+ BINLOG statement seen must be a base64 encoding of the
+ Format_description_log_event, as outputted by mysqlbinlog. This
+ Format_description_log_event is cached in
rli->description_event_for_exec.
*/
@@ -54,11 +53,24 @@ void mysql_client_binlog_statement(THD* thd)
/*
Allocation
*/
+
+ /*
+ If we do not have a Format_description_event, we create a dummy
+ one here. In this case, the first event we read must be a
+ Format_description_event.
+ */
+ my_bool have_fd_event= TRUE;
if (!thd->rli_fake)
+ {
thd->rli_fake= new Relay_log_info;
-
- const Format_description_log_event *desc=
- new Format_description_log_event(4);
+ have_fd_event= FALSE;
+ }
+ if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec)
+ {
+ thd->rli_fake->relay_log.description_event_for_exec=
+ new Format_description_log_event(4);
+ have_fd_event= FALSE;
+ }
const char *error= 0;
char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME));
@@ -67,7 +79,9 @@ void mysql_client_binlog_statement(THD* thd)
/*
Out of memory check
*/
- if (!(thd->rli_fake && desc && buf))
+ if (!(thd->rli_fake &&
+ thd->rli_fake->relay_log.description_event_for_exec &&
+ buf))
{
my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
goto end;
@@ -138,7 +152,28 @@ void mysql_client_binlog_statement(THD* thd)
goto end;
}
- ev= Log_event::read_log_event(bufptr, event_len, &error, desc);
+ /*
+ If we have not seen any Format_description_event, then we must
+ see one; it is the only statement that can be read in base64
+ without a prior Format_description_event.
+ */
+ if (!have_fd_event)
+ {
+ if (bufptr[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT)
+ have_fd_event= TRUE;
+ else
+ {
+ my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
+ MYF(0),
+ Log_event::get_type_str(
+ (Log_event_type)bufptr[EVENT_TYPE_OFFSET]));
+ goto end;
+ }
+ }
+
+ ev= Log_event::read_log_event(bufptr, event_len, &error,
+ thd->rli_fake->relay_log.
+ description_event_for_exec);
DBUG_PRINT("info",("binlog base64 err=%s", error));
if (!ev)
@@ -174,11 +209,10 @@ void mysql_client_binlog_statement(THD* thd)
Neither do we have to update the log positions, since that is
not used at all: the rli_fake instance is used only for error
reporting.
- */
+ */
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- if (IF_DBUG(int err= ) ev->apply_event(thd->rli_fake))
+ if (apply_event_and_update_pos(ev, thd, thd->rli_fake, FALSE))
{
- DBUG_PRINT("info", ("apply_event() returned: %d", err));
/*
TODO: Maybe a better error message since the BINLOG statement
now contains several events.
@@ -188,7 +222,14 @@ void mysql_client_binlog_statement(THD* thd)
}
#endif
- delete ev;
+ /*
+ Format_description_log_event should not be deleted because it
+ will be used to read info about the relay log's format; it
+ will be deleted when the SQL thread does not need it,
+ i.e. when this thread terminates.
+ */
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
ev= 0;
}
}
@@ -207,7 +248,6 @@ end:
*/
thd->net.no_send_ok= nsok;
- delete desc;
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_VOID_RETURN;
}