diff options
37 files changed, 1501 insertions, 653 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 8b79266d749..8de096e5ec1 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -59,7 +59,8 @@ static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; #endif static const char *load_default_groups[]= { "mysqlbinlog","client",0 }; -void sql_print_error(const char *format, ...); +static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; static bool opt_hexdump= 0; @@ -92,24 +93,33 @@ static ulonglong rec_count= 0; static short binlog_flags = 0; static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; -static bool stop_passed= 0; -static my_bool file_not_closed_error= 0; -/* - check_header() will set the pointer below. - Why do we need here a pointer on an event instead of an event ? - This is because the event will be created (alloced) in read_log_event() - (which returns a pointer) in check_header(). +/** + Pointer to the Format_description_log_event of the currently active binlog. + + This will be changed each time a new Format_description_log_event is + found in the binlog. It is finally destroyed at program termination. +*/ +static Format_description_log_event* glob_description_event= NULL; + +/** + Exit status for functions in this file. */ -static Format_description_log_event* glob_description_event; +enum Exit_status { + /** No error occurred and execution should continue. */ + OK_CONTINUE= 0, + /** An error occurred and execution should stop. */ + ERROR_STOP, + /** No error occurred but execution should stop. */ + OK_STOP +}; -static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); -static int dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); -static int dump_log_entries(const char* logname); -static void die(const char* fmt, ...) __attribute__ ((__noreturn__)); -static MYSQL* safe_connect(); +static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, + const char* logname); +static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, + const char* logname); +static Exit_status dump_log_entries(const char* logname); +static Exit_status safe_connect(); class Load_log_processor @@ -132,22 +142,29 @@ class Load_log_processor char *fname; Create_file_log_event *event; }; + /* + @todo Should be a map (e.g., a hash map), not an array. With the + present implementation, the number of elements in this array is + about the number of files loaded since the server started, which + may be big after a few years. We should be able to use existing + library data structures for this. /Sven + */ DYNAMIC_ARRAY file_names; - /* - Looking for new uniquie filename that doesn't exist yet by - adding postfix -%x - - SYNOPSIS - create_unique_file() - - filename buffer for filename - file_name_end tail of buffer that should be changed - should point to a memory enough to printf("-%x",..) - - RETURN VALUES - values less than 0 - can't find new filename - values great or equal 0 - created file with found filename + /** + Looks for a non-existing filename by adding a numerical suffix to + the given base name, creates the generated file, and returns the + filename by modifying the filename argument. + + @param[in,out] filename Base filename + + @param[in,out] file_name_end Pointer to last character of + filename. The numerical suffix will be written to this position. + Note that there must be a least five bytes of allocated memory + after file_name_end. + + @retval -1 Error (can't find new filename). + @retval >=0 Found file. */ File create_unique_file(char *filename, char *file_name_end) { @@ -201,22 +218,20 @@ public: delete_dynamic(&file_names); } - /* - Obtain Create_file event for LOAD DATA statement by its file_id. + /** + Obtain Create_file event for LOAD DATA statement by its file_id + and remove it from this Load_log_processor's list of events. - SYNOPSIS - grab_event() - file_id - file_id identifiying LOAD DATA statement + Checks whether we have already seen a Create_file_log_event with + the given file_id. If yes, returns a pointer to the event and + removes the event from array describing active temporary files. + From this moment, the caller is responsible for freeing the memory + occupied by the event. - DESCRIPTION - Checks whenever we have already seen Create_file event for this file_id. - If yes then returns pointer to it and removes it from array describing - active temporary files. Since this moment caller is responsible for - freeing memory occupied by this event and associated file name. + @param[in] file_id File id identifying LOAD DATA statement. - RETURN VALUES - Pointer to Create_file event or 0 if there was no such event - with this file_id. + @return Pointer to Create_file_log_event, or NULL if we have not + seen any Create_file_log_event with this file_id. */ Create_file_log_event *grab_event(uint file_id) { @@ -231,23 +246,20 @@ public: return res; } - /* - Obtain file name of temporary file for LOAD DATA statement by its file_id. - - SYNOPSIS - grab_fname() - file_id - file_id identifiying LOAD DATA statement - - DESCRIPTION - Checks whenever we have already seen Begin_load_query event for this - file_id. If yes then returns file name of corresponding temporary file. - Removes record about this file from the array of active temporary files. - Since this moment caller is responsible for freeing memory occupied by - this name. - - RETURN VALUES - String with name of temporary file or 0 if we have not seen Begin_load_query - event with this file_id. + /** + Obtain file name of temporary file for LOAD DATA statement by its + file_id and remove it from this Load_log_processor's list of events. + + @param[in] file_id Identifier for the LOAD DATA statement. + + Checks whether we have already seen Begin_load_query event for + this file_id. If yes, returns the file name of the corresponding + temporary file and removes the filename from the array of active + temporary files. From this moment, the caller is responsible for + freeing the memory occupied by this name. + + @return String with the name of the temporary file, or NULL if we + have not seen any Begin_load_query_event with this file_id. */ char *grab_fname(uint file_id) { @@ -264,19 +276,29 @@ public: } return res; } - int process(Create_file_log_event *ce); - int process(Begin_load_query_log_event *ce); - int process(Append_block_log_event *ae); + Exit_status process(Create_file_log_event *ce); + Exit_status process(Begin_load_query_log_event *ce); + Exit_status process(Append_block_log_event *ae); File prepare_new_file_for_old_format(Load_log_event *le, char *filename); - int load_old_format_file(NET* net, const char *server_fname, - uint server_fname_len, File file); - int process_first_event(const char *bname, uint blen, const uchar *block, - uint block_len, uint file_id, - Create_file_log_event *ce); + Exit_status load_old_format_file(NET* net, const char *server_fname, + uint server_fname_len, File file); + Exit_status process_first_event(const char *bname, uint blen, + const uchar *block, + uint block_len, uint file_id, + Create_file_log_event *ce); }; +/** + Creates and opens a new temporary file in the directory specified by previous call to init_by_dir_name() or init_by_cur_dir(). + + @param[in] le The basename of the created file will start with the + basename of the file pointed to by this Load_log_event. + + @param[out] filename Buffer to save the filename in. + @return File handle >= 0 on success, -1 on error. +*/ File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le, char *filename) { @@ -284,13 +306,13 @@ File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le, char *tail; File file; - fn_format(filename, le->fname, target_dir_name, "", 1); + fn_format(filename, le->fname, target_dir_name, "", MY_REPLACE_DIR); len= strlen(filename); tail= filename + len; if ((file= create_unique_file(filename,tail)) < 0) { - sql_print_error("Could not construct local filename %s",filename); + error("Could not construct local filename %s.",filename); return -1; } @@ -300,16 +322,33 @@ File Load_log_processor::prepare_new_file_for_old_format(Load_log_event *le, } -int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, - uint server_fname_len, File file) +/** + Reads a file from a server and saves it locally. + + @param[in,out] net The server to read from. + + @param[in] server_fname The name of the file that the server should + read. + + @param[in] server_fname_len The length of server_fname. + + @param[in,out] file The file to write to. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. +*/ +Exit_status Load_log_processor::load_old_format_file(NET* net, + const char*server_fname, + uint server_fname_len, + File file) { uchar buf[FN_REFLEN+1]; buf[0] = 0; memcpy(buf + 1, server_fname, server_fname_len + 1); if (my_net_write(net, buf, server_fname_len +2) || net_flush(net)) { - sql_print_error("Failed requesting the remote dump of %s", server_fname); - return -1; + error("Failed requesting the remote dump of %s.", server_fname); + return ERROR_STOP; } for (;;) @@ -319,8 +358,8 @@ int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, { if (my_net_write(net, (uchar*) "", 0) || net_flush(net)) { - sql_print_error("Failed sending the ack packet"); - return -1; + error("Failed sending the ack packet."); + return ERROR_STOP; } /* we just need to send something, as the server will read but @@ -331,63 +370,63 @@ int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, } else if (packet_len == packet_error) { - sql_print_error("Failed reading a packet during the dump of %s ", - server_fname); - return -1; + error("Failed reading a packet during the dump of %s.", server_fname); + return ERROR_STOP; } if (packet_len > UINT_MAX) { - sql_print_error("Illegal length of packet read from net"); - return -1; + error("Illegal length of packet read from net."); + return ERROR_STOP; } if (my_write(file, (uchar*) net->read_pos, (uint) packet_len, MYF(MY_WME|MY_NABP))) - return -1; + return ERROR_STOP; } - return 0; + return OK_CONTINUE; } -/* - Process first event in the sequence of events representing LOAD DATA - statement. - - SYNOPSIS - process_first_event() - bname - base name for temporary file to be created - blen - base name length - block - first block of data to be loaded - block_len - first block length - file_id - identifies LOAD DATA statement - ce - pointer to Create_file event object if we are processing - this type of event. - - DESCRIPTION - Creates temporary file to be used in LOAD DATA and writes first block of - data to it. Registers its file name (and optional Create_file event) - in the array of active temporary files. - - RETURN VALUES - 0 - success - non-0 - error +/** + Process the first event in the sequence of events representing a + LOAD DATA statement. + + Creates a temporary file to be used in LOAD DATA and writes first + block of data to it. Registers its file name (and optional + Create_file event) in the array of active temporary files. + + @param bname Base name for temporary file to be created. + @param blen Base name length. + @param block First block of data to be loaded. + @param block_len First block length. + @param file_id Identifies the LOAD DATA statement. + @param ce Pointer to Create_file event object if we are processing + this type of event. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. */ - -int Load_log_processor::process_first_event(const char *bname, uint blen, - const uchar *block, uint block_len, - uint file_id, - Create_file_log_event *ce) +Exit_status Load_log_processor::process_first_event(const char *bname, + uint blen, + const uchar *block, + uint block_len, + uint file_id, + Create_file_log_event *ce) { uint full_len= target_dir_name_len + blen + 9 + 9 + 1; - int error= 0; + Exit_status retval= OK_CONTINUE; char *fname, *ptr; File file; File_name_record rec; DBUG_ENTER("Load_log_processor::process_first_event"); if (!(fname= (char*) my_malloc(full_len,MYF(MY_WME)))) - DBUG_RETURN(-1); + { + error("Out of memory."); + delete ce; + DBUG_RETURN(ERROR_STOP); + } memcpy(fname, target_dir_name, target_dir_name_len); ptr= fname + target_dir_name_len; @@ -397,9 +436,10 @@ int Load_log_processor::process_first_event(const char *bname, uint blen, if ((file= create_unique_file(fname,ptr)) < 0) { - sql_print_error("Could not construct local filename %s%s", - target_dir_name,bname); - DBUG_RETURN(-1); + error("Could not construct local filename %s%s.", + target_dir_name,bname); + delete ce; + DBUG_RETURN(ERROR_STOP); } rec.fname= fname; @@ -407,23 +447,39 @@ int Load_log_processor::process_first_event(const char *bname, uint blen, if (set_dynamic(&file_names, (uchar*)&rec, file_id)) { - sql_print_error("Could not construct local filename %s%s", - target_dir_name, bname); - DBUG_RETURN(-1); + error("Out of memory."); + delete ce; + DBUG_RETURN(ERROR_STOP); } if (ce) ce->set_fname_outside_temp_buf(fname, strlen(fname)); if (my_write(file, (uchar*)block, block_len, MYF(MY_WME|MY_NABP))) - error= -1; + { + error("Failed writing to file."); + retval= ERROR_STOP; + } if (my_close(file, MYF(MY_WME))) - error= -1; - DBUG_RETURN(error); + { + error("Failed closing file."); + retval= ERROR_STOP; + } + DBUG_RETURN(retval); } -int Load_log_processor::process(Create_file_log_event *ce) +/** + Process the given Create_file_log_event. + + @see Load_log_processor::process_first_event(const char*,uint,const char*,uint,uint,Create_file_log_event*) + + @param ce Create_file_log_event to process. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. +*/ +Exit_status Load_log_processor::process(Create_file_log_event *ce) { const char *bname= ce->fname + dirname_length(ce->fname); uint blen= ce->fname_len - (bname-ce->fname); @@ -433,14 +489,46 @@ int Load_log_processor::process(Create_file_log_event *ce) } -int Load_log_processor::process(Begin_load_query_log_event *blqe) +/** + Process the given Begin_load_query_log_event. + + @see Load_log_processor::process_first_event(const char*,uint,const char*,uint,uint,Create_file_log_event*) + + @param ce Begin_load_query_log_event to process. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. +*/ +Exit_status Load_log_processor::process(Begin_load_query_log_event *blqe) { return process_first_event("SQL_LOAD_MB", 11, blqe->block, blqe->block_len, blqe->file_id, 0); } -int Load_log_processor::process(Append_block_log_event *ae) +/** + Process the given Append_block_log_event. + + Appends the chunk of the file contents specified by the event to the + file created by a previous Begin_load_query_log_event or + Create_file_log_event. + + If the file_id for the event does not correspond to any file + previously registered through a Begin_load_query_log_event or + Create_file_log_event, this member function will print a warning and + return OK_CONTINUE. It is safe to return OK_CONTINUE, because no + query will be written for this event. We should not print an error + and fail, since the missing file_id could be because a (valid) + --start-position has been specified after the Begin/Create event but + before this Append event. + + @param ae Append_block_log_event to process. + + @retval ERROR_STOP An error occurred - the program should terminate. + + @retval OK_CONTINUE No error, the program should continue. +*/ +Exit_status Load_log_processor::process(Append_block_log_event *ae) { DBUG_ENTER("Load_log_processor::process"); const char* fname= ((ae->file_id < file_names.elements) ? @@ -450,15 +538,24 @@ int Load_log_processor::process(Append_block_log_event *ae) if (fname) { File file; - int error= 0; + Exit_status retval= OK_CONTINUE; if (((file= my_open(fname, O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0)) - DBUG_RETURN(-1); + { + error("Failed opening file %s", fname); + DBUG_RETURN(ERROR_STOP); + } if (my_write(file,(uchar*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP))) - error= -1; + { + error("Failed writing to file %s", fname); + retval= ERROR_STOP; + } if (my_close(file,MYF(MY_WME))) - error= -1; - DBUG_RETURN(error); + { + error("Failed closing file %s", fname); + retval= ERROR_STOP; + } + DBUG_RETURN(retval); } /* @@ -466,16 +563,50 @@ int Load_log_processor::process(Append_block_log_event *ae) --start-position). Assuming it's a big --start-position, we just do nothing and print a warning. */ - fprintf(stderr,"Warning: ignoring Append_block as there is no \ -Create_file event for file_id: %u\n",ae->file_id); - DBUG_RETURN(-1); + warning("Ignoring Append_block as there is no " + "Create_file event for file_id: %u", ae->file_id); + DBUG_RETURN(OK_CONTINUE); +} + + +static Load_log_processor load_processor; + + +/** + Replace windows-style backslashes by forward slashes so it can be + consumed by the mysql client, which requires Unix path. + + @todo This is only useful under windows, so may be ifdef'ed out on + other systems. /Sven + + @todo If a Create_file_log_event contains a filename with a + backslash (valid under unix), then we have problems under windows. + /Sven + + @param[in,out] fname Filename to modify. The filename is modified + in-place. +*/ +static void convert_path_to_forward_slashes(char *fname) +{ + while (*fname) + { + if (*fname == '\\') + *fname= '/'; + fname++; + } } -Load_log_processor load_processor; +/** + Indicates whether the given database should be filtered out, + according to the --database=X option. + @param log_dbname Name of database. -static bool check_database(const char *log_dbname) + @return nonzero if the database with the given name should be + filtered out, 0 otherwise. +*/ +static bool shall_skip_database(const char *log_dbname) { return one_database && (log_dbname != NULL) && @@ -483,8 +614,23 @@ static bool check_database(const char *log_dbname) } +/** + Prints the given event in base64 format. + + The header is printed to the head cache and the body is printed to + the body cache of the print_event_info structure. This allows all + base64 events corresponding to the same statement to be joined into + one BINLOG statement. + + @param[in] ev Log_event to print. + @param[in,out] result_file FILE to which the output will be written. + @param[in,out] print_event_info Parameters and context state + determining how to print. -static int + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. +*/ +static Exit_status write_event_header_and_base64(Log_event *ev, FILE *result_file, PRINT_EVENT_INFO *print_event_info) { @@ -497,35 +643,44 @@ write_event_header_and_base64(Log_event *ev, FILE *result_file, ev->print_base64(body, print_event_info, FALSE); /* Read data from cache and write to result file */ - DBUG_RETURN(copy_event_cache_to_file_and_reinit(head, result_file) || - copy_event_cache_to_file_and_reinit(body, result_file)); + if (copy_event_cache_to_file_and_reinit(head, result_file) || + copy_event_cache_to_file_and_reinit(body, result_file)) + { + error("Error writing event to file."); + DBUG_RETURN(ERROR_STOP); + } + DBUG_RETURN(OK_CONTINUE); } -/* - Process an event - - SYNOPSIS - process_event() - - RETURN - 0 ok and continue - 1 error and terminate - -1 ok and terminate - - TODO - This function returns 0 even in some error cases. This should be changed. +/** + Print the given event, and either delete it or delegate the deletion + to someone else. + + The deletion may be delegated in two cases: (1) the event is a + Format_description_log_event, and is saved in + glob_description_event; (2) the event is a Create_file_log_event, + and is saved in load_processor. + + @param[in,out] print_event_info Parameters and context state + determining how to print. + @param[in] ev Log_event to process. + @param[in] pos Offset from beginning of binlog file. + @param[in] logname Name of input binlog. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. + @retval OK_STOP No error, but the end of the specified range of + events to process has been reached and the program should terminate. */ - - - -int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, - my_off_t pos) +Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, + my_off_t pos, const char *logname) { char ll_buff[21]; Log_event_type ev_type= ev->get_type_code(); DBUG_ENTER("process_event"); print_event_info->short_form= short_form; + Exit_status retval= OK_CONTINUE; /* Format events are not concerned by --offset and such, we always need to @@ -545,14 +700,15 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, start_datetime= 0; offset= 0; // print everything and protect against cycling rec_count } - if (server_id && (server_id != ev->server_id)) { - DBUG_RETURN(0); - } + if (server_id && (server_id != ev->server_id)) + /* skip just this event, continue processing the log. */ + goto end; if (((my_time_t)(ev->when) >= stop_datetime) || (pos >= stop_position_mot)) { - stop_passed= 1; // skip all next binlogs - DBUG_RETURN(-1); + /* end the program */ + retval= OK_STOP; + goto end; } if (!short_form) fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); @@ -568,10 +724,15 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, switch (ev_type) { case QUERY_EVENT: - if (check_database(((Query_log_event*)ev)->db)) + if (shall_skip_database(((Query_log_event*)ev)->db)) goto end; if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS) - write_event_header_and_base64(ev, result_file, print_event_info); + { + if ((retval= write_event_header_and_base64(ev, result_file, + print_event_info)) != + OK_CONTINUE) + goto end; + } else ev->print(result_file, print_event_info); break; @@ -585,7 +746,7 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, related Append_block and Exec_load. Note that Load event from 3.23 is not tested. */ - if (check_database(ce->db)) + if (shall_skip_database(ce->db)) goto end; // Next event /* We print the event, but with a leading '#': this is just to inform @@ -596,7 +757,10 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, */ if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS) { - write_event_header_and_base64(ce, result_file, print_event_info); + if ((retval= write_event_header_and_base64(ce, result_file, + print_event_info)) != + OK_CONTINUE) + goto end; } else ce->print(result_file, print_event_info, TRUE); @@ -604,17 +768,29 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, // If this binlog is not 3.23 ; why this test?? if (glob_description_event->binlog_version >= 3) { - if (load_processor.process(ce)) - break; // Error - ev= 0; + /* + transfer the responsibility for destroying the event to + load_processor + */ + ev= NULL; + if ((retval= load_processor.process(ce)) != OK_CONTINUE) + goto end; } break; } + case APPEND_BLOCK_EVENT: + /* + Append_block_log_events can safely print themselves even if + the subsequent call load_processor.process fails, because the + output of Append_block_log_event::print is only a comment. + */ ev->print(result_file, print_event_info); - if (load_processor.process((Append_block_log_event*) ev)) - break; // Error + if ((retval= load_processor.process((Append_block_log_event*) ev)) != + OK_CONTINUE) + goto end; break; + case EXEC_LOAD_EVENT: { ev->print(result_file, print_event_info); @@ -627,13 +803,18 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, */ if (ce) { + /* + We must not convert earlier, since the file is used by + my_open() in Load_log_processor::append(). + */ + convert_path_to_forward_slashes((char*) ce->fname); ce->print(result_file, print_event_info, TRUE); my_free((char*)ce->fname,MYF(MY_WME)); delete ce; } else - fprintf(stderr,"Warning: ignoring Exec_load as there is no \ -Create_file event for file_id: %u\n",exv->file_id); + warning("Ignoring Execute_load_log_event as there is no " + "Create_file event for file_id: %u", exv->file_id); break; } case FORMAT_DESCRIPTION_EVENT: @@ -653,41 +834,37 @@ Create_file event for file_id: %u\n",exv->file_id); if (!force_if_open_opt && (glob_description_event->flags & LOG_EVENT_BINLOG_IN_USE_F)) { - file_not_closed_error= 1; - DBUG_RETURN(1); + error("Attempting to dump binlog '%s', which was not closed properly. " + "Most probably, mysqld is still writing it, or it crashed. " + "Rerun with --force-if-open to ignore this problem.", logname); + DBUG_RETURN(ERROR_STOP); } break; case BEGIN_LOAD_QUERY_EVENT: ev->print(result_file, print_event_info); - load_processor.process((Begin_load_query_log_event*) ev); + if ((retval= load_processor.process((Begin_load_query_log_event*) ev)) != + OK_CONTINUE) + goto end; break; case EXECUTE_LOAD_QUERY_EVENT: { Execute_load_query_log_event *exlq= (Execute_load_query_log_event*)ev; char *fname= load_processor.grab_fname(exlq->file_id); - if (check_database(exlq->db)) + if (!shall_skip_database(exlq->db)) { if (fname) - my_free(fname, MYF(MY_WME)); - goto end; + { + convert_path_to_forward_slashes(fname); + exlq->print(result_file, print_event_info, fname); + } + else + warning("Ignoring Execute_load_query since there is no " + "Begin_load_query event for file_id: %u", exlq->file_id); } if (fname) - { - /* - Fix the path so it can be consumed by mysql client (requires Unix path). - */ - int stop= strlen(fname); - for (int i= 0; i < stop; i++) - if (fname[i] == '\\') - fname[i]= '/'; - exlq->print(result_file, print_event_info, fname); my_free(fname, MYF(MY_WME)); - } - else - fprintf(stderr,"Warning: ignoring Execute_load_query as there is no \ -Begin_load_query event for file_id: %u\n", exlq->file_id); break; } case TABLE_MAP_EVENT: @@ -706,20 +883,18 @@ Begin_load_query event for file_id: %u\n", exlq->file_id); */ if (!print_event_info->printed_fd_event && !short_form) { - /* - todo: a lot to clean up here - */ const char* type_str= ev->get_type_str(); - delete ev; if (opt_base64_output_mode == BASE64_OUTPUT_NEVER) - die("--base64-output=never specified, but binlog contains a " - "%s event which must be printed in base64.", - type_str); + error("--base64-output=never specified, but binlog contains a " + "%s event which must be printed in base64.", + type_str); else - die("malformed binlog: it does not contain any " - "Format_description_log_event. I now found a %s event, which is " - "not safe to process without a Format_description_log_event.", - type_str); + error("malformed binlog: it does not contain any " + "Format_description_log_event. I now found a %s event, which " + "is not safe to process without a " + "Format_description_log_event.", + type_str); + goto err; } /* FALL THROUGH */ default: @@ -727,6 +902,10 @@ Begin_load_query event for file_id: %u\n", exlq->file_id); } } + goto end; + +err: + retval= ERROR_STOP; end: rec_count++; /* @@ -739,7 +918,7 @@ end: ev->temp_buf= 0; delete ev; } - DBUG_RETURN(0); + DBUG_RETURN(retval); } @@ -894,16 +1073,71 @@ that may lead to an endless loop.", }; -void sql_print_error(const char *format,...) +/** + Auxiliary function used by error() and warning(). + + Prints the given text (normally "WARNING: " or "ERROR: "), followed + by the given vprintf-style string, followed by a newline. + + @param format Printf-style format string. + @param args List of arguments for the format string. + @param msg Text to print before the string. +*/ +static void error_or_warning(const char *format, va_list args, const char *msg) { - va_list args; - va_start(args, format); - fprintf(stderr, "ERROR: "); + fprintf(stderr, "%s: ", msg); vfprintf(stderr, format, args); fprintf(stderr, "\n"); +} + +/** + Prints a message to stderr, prefixed with the text "ERROR: " and + suffixed with a newline. + + @param format Printf-style format string, followed by printf + varargs. +*/ +static void error(const char *format,...) +{ + va_list args; + va_start(args, format); + error_or_warning(format, args, "ERROR"); + va_end(args); +} + + +/** + This function is used in log_event.cc to report errors. + + @param format Printf-style format string, followed by printf + varargs. +*/ +static void sql_print_error(const char *format,...) +{ + va_list args; + va_start(args, format); + error_or_warning(format, args, "ERROR"); + va_end(args); +} + +/** + Prints a message to stderr, prefixed with the text "WARNING: " and + suffixed with a newline. + + @param format Printf-style format string, followed by printf + varargs. +*/ +static void warning(const char *format,...) +{ + va_list args; + va_start(args, format); + error_or_warning(format, args, "WARNING"); va_end(args); } +/** + Frees memory for global variables in this file. +*/ static void cleanup() { my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); @@ -911,20 +1145,10 @@ static void cleanup() my_free((char*) host, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) user, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) dirname_for_local_load, MYF(MY_ALLOW_ZERO_PTR)); -} -static void die(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - va_end(args); - cleanup(); - /* We cannot free DBUG, it is used in global destructors after exit(). */ - my_end(my_end_arg | MY_DONT_FREE_DBUG); - exit(1); + delete glob_description_event; + if (mysql) + mysql_close(mysql); } #include <help_start.h> @@ -962,7 +1186,7 @@ static my_time_t convert_str_to_timestamp(const char* str) if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) != MYSQL_TIMESTAMP_DATETIME || was_cut) { - fprintf(stderr, "Incorrect date and time argument: %s\n", str); + error("Incorrect date and time argument: %s", str); exit(1); } /* @@ -1063,34 +1287,56 @@ static int parse_args(int *argc, char*** argv) return 0; } -static MYSQL* safe_connect() + +/** + Create and initialize the global mysql object, and connect to the + server. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. +*/ +static Exit_status safe_connect() { - MYSQL *local_mysql= mysql_init(NULL); + mysql= mysql_init(NULL); - if (!local_mysql) - die("Failed on mysql_init"); + if (!mysql) + { + error("Failed on mysql_init."); + return ERROR_STOP; + } if (opt_protocol) - mysql_options(local_mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); - if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0)) + mysql_options(mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); + if (!mysql_real_connect(mysql, host, user, pass, 0, port, sock, 0)) { - char errmsg[256]; - strmake(errmsg, mysql_error(local_mysql), sizeof(errmsg)-1); - mysql_close(local_mysql); - die("failed on connect: %s", errmsg); + error("Failed on connect: %s", mysql_error(mysql)); + return ERROR_STOP; } - local_mysql->reconnect= 1; - return local_mysql; + mysql->reconnect= 1; + return OK_CONTINUE; } -static int dump_log_entries(const char* logname) +/** + High-level function for dumping a named binlog. + + This function calls dump_remote_log_entries() or + dump_local_log_entries() to do the job. + + @param[in] logname Name of input binlog. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. + @retval OK_STOP No error, but the end of the specified range of + events to process has been reached and the program should terminate. +*/ +static Exit_status dump_log_entries(const char* logname) { - int rc; + Exit_status rc; PRINT_EVENT_INFO print_event_info; if (!print_event_info.init_ok()) - return 1; + return ERROR_STOP; /* Set safe delimiter, to dump things like CREATE PROCEDURE safely @@ -1108,51 +1354,50 @@ static int dump_log_entries(const char* logname) } -/* - This is not as smart as check_header() (used for local log); it will not work - for a binlog which mixes format. TODO: fix this. +/** + When reading a remote binlog, this function is used to grab the + Format_description_log_event in the beginning of the stream. + + This is not as smart as check_header() (used for local log); it will + not work for a binlog which mixes format. TODO: fix this. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. */ -static int check_master_version(MYSQL *mysql_arg, - Format_description_log_event - **description_event) +static Exit_status check_master_version() { MYSQL_RES* res = 0; MYSQL_ROW row; const char* version; - if (mysql_query(mysql_arg, "SELECT VERSION()") || - !(res = mysql_store_result(mysql_arg))) + if (mysql_query(mysql, "SELECT VERSION()") || + !(res = mysql_store_result(mysql))) { - /* purecov: begin inspected */ - char errmsg[256]; - strmake(errmsg, mysql_error(mysql_arg), sizeof(errmsg)-1); - mysql_close(mysql_arg); - die("Error checking master version: %s", errmsg); - /* purecov: end */ + error("Could not find server version: " + "Query failed when checking master version: %s", mysql_error(mysql)); + return ERROR_STOP; } if (!(row = mysql_fetch_row(res))) { - /* purecov: begin inspected */ - mysql_free_result(res); - mysql_close(mysql); - die("Master returned no rows for SELECT VERSION()"); - /* purecov: end */ + error("Could not find server version: " + "Master returned no rows for SELECT VERSION()."); + goto err; } + if (!(version = row[0])) { - /* purecov: begin inspected */ - mysql_free_result(res); - mysql_close(mysql_arg); - die("Master reported NULL for the version"); - /* purecov: end */ + error("Could not find server version: " + "Master reported NULL for the version."); + goto err; } + delete glob_description_event; switch (*version) { case '3': - *description_event= new Format_description_log_event(1); + glob_description_event= new Format_description_log_event(1); break; case '4': - *description_event= new Format_description_log_event(3); + glob_description_event= new Format_description_log_event(3); break; case '5': /* @@ -1161,31 +1406,53 @@ static int check_master_version(MYSQL *mysql_arg, So we first assume that this is 4.0 (which is enough to read the Format_desc event if one comes). */ - *description_event= new Format_description_log_event(3); + glob_description_event= new Format_description_log_event(3); break; default: - /* purecov: begin inspected */ - mysql_free_result(res); - mysql_close(mysql_arg); - die("Master reported unrecognized MySQL version '%s'", version); - /* purecov: end */ + glob_description_event= NULL; + error("Could not find server version: " + "Master reported unrecognized MySQL version '%s'.", version); + goto err; + } + if (!glob_description_event || !glob_description_event->is_valid()) + { + error("Failed creating Format_description_log_event; out of memory?"); + goto err; } + mysql_free_result(res); - return 0; + return OK_CONTINUE; + +err: + mysql_free_result(res); + return ERROR_STOP; } -static int dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname) +/** + Requests binlog dump from a remote server and prints the events it + receives. + + @param[in,out] print_event_info Parameters and context state + determining how to print. + @param[in] logname Name of input binlog. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. + @retval OK_STOP No error, but the end of the specified range of + events to process has been reached and the program should terminate. +*/ +static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, + const char* logname) { uchar buf[128]; ulong len; uint logname_len; NET* net; - int error= 0; my_off_t old_off= start_position_mot; char fname[FN_REFLEN+1]; + Exit_status retval= OK_CONTINUE; DBUG_ENTER("dump_remote_log_entries"); /* @@ -1193,20 +1460,12 @@ static int dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, we cannot re-use the same connection as before, because it is now dead (COM_BINLOG_DUMP kills the thread when it finishes). */ - mysql= safe_connect(); + if ((retval= safe_connect()) != OK_CONTINUE) + DBUG_RETURN(retval); net= &mysql->net; - if (check_master_version(mysql, &glob_description_event)) - { - fprintf(stderr, "Could not find server version"); - DBUG_RETURN(1); - } - if (!glob_description_event || !glob_description_event->is_valid()) - { - fprintf(stderr, "Invalid Format_description log event; \ -could be out of memory"); - DBUG_RETURN(1); - } + if ((retval= check_master_version()) != OK_CONTINUE) + DBUG_RETURN(retval); /* COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to @@ -1218,18 +1477,16 @@ could be out of memory"); size_t tlen = strlen(logname); if (tlen > UINT_MAX) { - fprintf(stderr,"Log name too long\n"); - error= 1; - goto err; + error("Log name too long."); + DBUG_RETURN(ERROR_STOP); } logname_len = (uint) tlen; int4store(buf + 6, 0); memcpy(buf + 10, logname, logname_len); if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1)) { - fprintf(stderr,"Got fatal error sending the log dump command\n"); - error= 1; - goto err; + error("Got fatal error sending the log dump command."); + DBUG_RETURN(ERROR_STOP); } for (;;) @@ -1240,10 +1497,8 @@ could be out of memory"); len= cli_safe_read(mysql); if (len == packet_error) { - fprintf(stderr, "Got error reading packet from server: %s\n", - mysql_error(mysql)); - error= 1; - goto err; + error("Got error reading packet from server: %s", mysql_error(mysql)); + DBUG_RETURN(ERROR_STOP); } if (len < 8 && net->read_pos[0] == 254) break; // end of data @@ -1253,9 +1508,8 @@ could be out of memory"); len - 1, &error_msg, glob_description_event))) { - fprintf(stderr, "Could not construct log event object\n"); - error= 1; - goto err; + error("Could not construct log event object: %s", error_msg); + DBUG_RETURN(ERROR_STOP); } /* If reading from a remote host, ensure the temp_buf for the @@ -1294,8 +1548,7 @@ could be out of memory"); if ((rev->ident_len != logname_len) || memcmp(rev->new_log_ident, logname, logname_len)) { - error= 0; - goto err; + DBUG_RETURN(OK_CONTINUE); } /* Otherwise, this is a fake Rotate for our log, at the very @@ -1320,11 +1573,9 @@ could be out of memory"); if (old_off != BIN_LOG_HEADER_SIZE) len= 1; // fake event, don't increment old_off } - if ((error= process_event(print_event_info, ev, old_off))) - { - error= ((error < 0) ? 0 : 1); - goto err; - } + Exit_status retval= process_event(print_event_info, ev, old_off, logname); + if (retval != OK_CONTINUE) + DBUG_RETURN(retval); } else { @@ -1332,26 +1583,21 @@ could be out of memory"); const char *old_fname= le->fname; uint old_len= le->fname_len; File file; + Exit_status retval; if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) - { - error= 1; - goto err; - } + DBUG_RETURN(ERROR_STOP); - if ((error= process_event(print_event_info, ev, old_off))) + retval= process_event(print_event_info, ev, old_off, logname); + if (retval != OK_CONTINUE) { my_close(file,MYF(MY_WME)); - error= ((error < 0) ? 0 : 1); - goto err; + DBUG_RETURN(retval); } - error= load_processor.load_old_format_file(net,old_fname,old_len,file); + retval= load_processor.load_old_format_file(net,old_fname,old_len,file); my_close(file,MYF(MY_WME)); - if (error) - { - error= 1; - goto err; - } + if (retval != OK_CONTINUE) + DBUG_RETURN(retval); } /* Let's adjust offset for remote log as for local log to produce @@ -1360,15 +1606,13 @@ could be out of memory"); old_off+= len-1; } -err: - mysql_close(mysql); - DBUG_RETURN(error); + DBUG_RETURN(OK_CONTINUE); } /** - Reads the @c Format_description_log_event from the beginning of the - input file. + Reads the @c Format_description_log_event from the beginning of a + local input file. The @c Format_description_log_event is only read if it is outside the range specified with @c --start-position; otherwise, it will be @@ -1382,32 +1626,42 @@ err: @param file The file to which a @c Format_description_log_event will be printed. - @param description_event Pointer to the global @c - Format_description_log_event pointer. This will be updated if a new - Format_description_log_event is found. + @param[in,out] print_event_info Parameters and context state + determining how to print. - @param print_event_info Context state needed to print events. + @param[in] logname Name of input binlog. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. + @retval OK_STOP No error, but the end of the specified range of + events to process has been reached and the program should terminate. */ -static void check_header(IO_CACHE* file, - Format_description_log_event **description_event, - PRINT_EVENT_INFO *print_event_info) +static Exit_status check_header(IO_CACHE* file, + PRINT_EVENT_INFO *print_event_info, + const char* logname) { uchar header[BIN_LOG_HEADER_SIZE]; uchar buf[PROBE_HEADER_LEN]; my_off_t tmp_pos, pos; - *description_event= new Format_description_log_event(3); + delete glob_description_event; + if (!(glob_description_event= new Format_description_log_event(3))) + { + error("Failed creating Format_description_log_event; out of memory?"); + return ERROR_STOP; + } + pos= my_b_tell(file); my_b_seek(file, (my_off_t)0); if (my_b_read(file, header, sizeof(header))) { - delete *description_event; - die("Failed reading header; Probably an empty file"); + error("Failed reading header; probably an empty file."); + return ERROR_STOP; } if (memcmp(header, BINLOG_MAGIC, sizeof(header))) { - delete *description_event; - die("File is not a binary log file"); + error("File is not a binary log file."); + return ERROR_STOP; } /* @@ -1430,10 +1684,9 @@ static void check_header(IO_CACHE* file, { if (file->error) { - delete *description_event; - die("\ -Could not read entry at offset %lu : Error in log format or read error", - tmp_pos); + error("Could not read entry at offset %llu: " + "Error in log format or read error.", (ulonglong)tmp_pos); + return ERROR_STOP; } /* Otherwise this is just EOF : this log currently contains 0-2 @@ -1463,8 +1716,13 @@ Could not read entry at offset %lu : Error in log format or read error", (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN)) { /* This is 3.23 (format 1) */ - delete *description_event; - *description_event= new Format_description_log_event(1); + delete glob_description_event; + if (!(glob_description_event= new Format_description_log_event(1))) + { + error("Failed creating Format_description_log_event; " + "out of memory?"); + return ERROR_STOP; + } } break; } @@ -1476,26 +1734,32 @@ Could not read entry at offset %lu : Error in log format or read error", Format_description_log_event *new_description_event; my_b_seek(file, tmp_pos); /* seek back to event's start */ if (!(new_description_event= (Format_description_log_event*) - Log_event::read_log_event(file, *description_event))) + Log_event::read_log_event(file, glob_description_event))) /* EOF can't be hit here normally, so it's a real error */ { - delete *description_event; - die("Could not read a Format_description_log_event event \ -at offset %lu ; this could be a log format error or read error", - tmp_pos); + error("Could not read a Format_description_log_event event at " + "offset %llu; this could be a log format error or read error.", + (ulonglong)tmp_pos); + return ERROR_STOP; } if (opt_base64_output_mode == BASE64_OUTPUT_AUTO || opt_base64_output_mode == BASE64_OUTPUT_ALWAYS) + { /* process_event will delete *description_event and set it to the new one, so we should not do it ourselves in this case. */ - process_event(print_event_info, new_description_event, tmp_pos); + Exit_status retval= process_event(print_event_info, + new_description_event, tmp_pos, + logname); + if (retval != OK_CONTINUE) + return retval; + } else { - delete *description_event; - *description_event= new_description_event; + delete glob_description_event; + glob_description_event= new_description_event; } DBUG_PRINT("info",("Setting description_event")); } @@ -1503,12 +1767,13 @@ at offset %lu ; this could be a log format error or read error", { Log_event *ev; my_b_seek(file, tmp_pos); /* seek back to event's start */ - if (!(ev= Log_event::read_log_event(file, *description_event))) - /* EOF can't be hit here normally, so it's a real error */ + if (!(ev= Log_event::read_log_event(file, glob_description_event))) { - delete *description_event; - die("Could not read a Rotate_log_event event at offset %lu ;" - " this could be a log format error or read error", tmp_pos); + /* EOF can't be hit here normally, so it's a real error */ + error("Could not read a Rotate_log_event event at offset %llu;" + " this could be a log format error or read error.", + (ulonglong)tmp_pos); + return ERROR_STOP; } delete ev; } @@ -1517,31 +1782,48 @@ at offset %lu ; this could be a log format error or read error", } } my_b_seek(file, pos); + return OK_CONTINUE; } -static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname) +/** + Reads a local binlog and prints the events it sees. + + @param[in] logname Name of input binlog. + + @param[in,out] print_event_info Parameters and context state + determining how to print. + + @retval ERROR_STOP An error occurred - the program should terminate. + @retval OK_CONTINUE No error, the program should continue. + @retval OK_STOP No error, but the end of the specified range of + events to process has been reached and the program should terminate. +*/ +static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, + const char* logname) { File fd = -1; IO_CACHE cache,*file= &cache; uchar tmp_buff[BIN_LOG_HEADER_SIZE]; - int error= 0; + Exit_status retval= OK_CONTINUE; if (logname && strcmp(logname, "-") != 0) { + /* read from normal file */ if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) - return 1; + return ERROR_STOP; if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0, MYF(MY_WME | MY_NABP))) { my_close(fd, MYF(MY_WME)); - return 1; + return ERROR_STOP; } - check_header(file, &glob_description_event, print_event_info); + if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE) + goto end; } - else // reading from stdin; + else { + /* read from stdin */ /* Windows opens stdin in text mode by default. Certain characters such as CTRL-Z are interpeted as events and the read() method @@ -1553,14 +1835,18 @@ static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, #if defined (__WIN__) || (_WIN64) if (_setmode(fileno(stdin), O_BINARY) == -1) { - fprintf(stderr, "Could not set binary mode on stdin.\n"); - return 1; + error("Could not set binary mode on stdin."); + return ERROR_STOP; } #endif if (init_io_cache(file, fileno(stdin), 0, READ_CACHE, (my_off_t) 0, 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) - return 1; - check_header(file, &glob_description_event, print_event_info); + { + error("Failed to init IO cache."); + return ERROR_STOP; + } + if ((retval= check_header(file, print_event_info, logname)) != OK_CONTINUE) + goto end; if (start_position) { /* skip 'start_position' characters from stdin */ @@ -1571,8 +1857,8 @@ static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) { - error= 1; - goto end; + error("Failed reading from file."); + goto err; } } } @@ -1580,14 +1866,14 @@ static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, if (!glob_description_event || !glob_description_event->is_valid()) { - delete glob_description_event; - die("Invalid Format_description log event; could be out of memory"); + error("Invalid Format_description log event; could be out of memory."); + goto err; } if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) { - error= 1; - goto end; + error("Failed reading from file."); + goto err; } for (;;) { @@ -1605,36 +1891,36 @@ static int dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, file->error= 0; else if (file->error) { - fprintf(stderr, - "Could not read entry at offset %s:" - "Error in log format or read error\n", - llstr(old_off,llbuff)); - error= 1; + error("Could not read entry at offset %s: " + "Error in log format or read error.", + llstr(old_off,llbuff)); + goto err; } // file->error == 0 means EOF, that's OK, we break in this case - break; - } - if ((error= process_event(print_event_info, ev, old_off))) - { - if (error < 0) - error= 0; - break; + goto end; } + if ((retval= process_event(print_event_info, ev, old_off, logname)) != + OK_CONTINUE) + goto end; } + /* NOTREACHED */ + +err: + retval= ERROR_STOP; + end: if (fd >= 0) my_close(fd, MYF(MY_WME)); end_io_cache(file); - delete glob_description_event; - return error; + return retval; } int main(int argc, char** argv) { char **defaults_argv; - int exit_value= 0; + Exit_status retval= OK_CONTINUE; ulonglong save_stop_position; MY_INIT(argv[0]); DBUG_ENTER("main"); @@ -1696,15 +1982,13 @@ int main(int argc, char** argv) "\n/*!40101 SET NAMES %s */;\n", charset); for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; - (--argc >= 0) && !stop_passed ; ) + (--argc >= 0) ; ) { if (argc == 0) // last log, --stop-position applies stop_position= save_stop_position; - if (dump_log_entries(*(argv++))) - { - exit_value=1; + if ((retval= dump_log_entries(*argv++)) != OK_CONTINUE) break; - } + // For next log, --start-position does not apply start_position= BIN_LOG_HEADER_SIZE; } @@ -1736,17 +2020,9 @@ int main(int argc, char** argv) /* We cannot free DBUG, it is used in global destructors after exit(). */ my_end(my_end_arg | MY_DONT_FREE_DBUG); - if (file_not_closed_error) - { - fprintf(stderr, -"\nError: attempting to dump binlog '%s' which was not closed properly.\n" -"Most probably mysqld is still writting it, or crashed.\n" -"Your current options specify --disable-force-if-open\n" -"which means to abort on this problem.\n" -"You can rerun using --force-if-open to ignore this problem.\n\n", argv[-1]); - } - exit(exit_value); - DBUG_RETURN(exit_value); // Keep compilers happy + exit(retval == ERROR_STOP ? 1 : 0); + /* Keep compilers happy. */ + DBUG_RETURN(retval == ERROR_STOP ? 1 : 0); } /* diff --git a/include/my_base.h b/include/my_base.h index 04de6f55f44..ddefb5be7b4 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -434,13 +434,15 @@ enum ha_base_keytype { #define HA_ERR_RECORD_IS_THE_SAME 169 /* It is not possible to log this statement */ #define HA_ERR_LOGGING_IMPOSSIBLE 170 -#define HA_ERR_CORRUPT_EVENT 171 /* The event was corrupt, leading to - illegal data being read */ +#define HA_ERR_CORRUPT_EVENT 171 /* The event was corrupt, leading to */ + /* illegal data being read */ #define HA_ERR_NEW_FILE 172 /* New file format */ #define HA_ERR_INITIALIZATION 173 /* Error during initialization */ #define HA_ERR_FILE_TOO_SHORT 174 /* File too short */ #define HA_ERR_WRONG_CRC 175 /* Wrong CRC on page */ -#define HA_ERR_LAST 175 /* Copy of last error nr */ +#define HA_ERR_ROWS_EVENT_APPLY 176 /* The event could not be processed */ + /* no other hanlder error happened */ +#define HA_ERR_LAST 176 /* Copy of last error nr */ /* Number of different errors */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/mysql-test/extra/rpl_tests/rpl_ndb_2multi_eng.test b/mysql-test/extra/rpl_tests/rpl_ndb_2multi_eng.test index 7f19245c0b9..1677aaf8277 100644 --- a/mysql-test/extra/rpl_tests/rpl_ndb_2multi_eng.test +++ b/mysql-test/extra/rpl_tests/rpl_ndb_2multi_eng.test @@ -342,5 +342,6 @@ SHOW CREATE TABLE t1; --echo --- Do Cleanup --- DROP TABLE IF EXISTS t1; +sync_slave_with_master; # End of 5.1 test case diff --git a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test index b5795cf525d..0e391cb0f37 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test +++ b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test @@ -46,7 +46,7 @@ ALTER TABLE t1_bit ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test'; # ... and add one non-nullable INT column last in table 't1_text' # with no default, -ALTER TABLE t1_nodef ADD x INT NOT NULL; +ALTER TABLE t1_nodef ADD x INT NOT NULL, ADD y INT NOT NULL, ADD z INT NOT NULL; # ... and remove the last column in t2 ALTER TABLE t2 DROP b; # ... change the type of the single column in table 't4' @@ -222,8 +222,8 @@ sync_slave_with_master; --echo **** On Slave **** connection slave; -INSERT INTO t1_nodef VALUES (1,2,3); -INSERT INTO t1_nodef VALUES (2,4,6); +INSERT INTO t1_nodef VALUES (1,2,3,4,5); +INSERT INTO t1_nodef VALUES (2,4,6,8,10); --echo **** On Master **** connection master; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 0a64232730c..488ec644b74 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3898,14 +3898,6 @@ sub mysqld_arguments ($$$$) { mtr_add_arg($args, "%s--user=root"); } - # When mysqld is run by a root user(euid is 0), it will fail - # to start unless we specify what user to run as, see BUG#30630 - my $euid= $>; - if (!$glob_win32 and $euid == 0 and - (grep(/^--user/, @$extra_opt, @opt_extra_mysqld_opt)) == 0) { - mtr_add_arg($args, "%s--user=root", $prefix); - } - if ( $opt_valgrind_mysqld ) { mtr_add_arg($args, "%s--skip-safemalloc", $prefix); diff --git a/mysql-test/suite/binlog/r/binlog_base64_flag.result b/mysql-test/suite/binlog/r/binlog_base64_flag.result index aa801346d9f..8e5d7def823 100644 --- a/mysql-test/suite/binlog/r/binlog_base64_flag.result +++ b/mysql-test/suite/binlog/r/binlog_base64_flag.result @@ -40,8 +40,13 @@ SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.uniq SET @@session.sql_mode=0/*!*/; /*!\C latin1 *//*!*/; SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; -create table t1 (a int) engine= myisam/*!*/; +create table t1 (a int) engine= myisam +/*!*/; # at 203 +DELIMITER ; +# End of log file +ROLLBACK /* added by mysqlbinlog */; +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; ==== Test non-matching FD event and Row event ==== BINLOG ' 4CdYRw8BAAAAYgAAAGYAAAAAAAQANS4xLjE1LW5kYi02LjEuMjQtZGVidWctbG9nAAAAAAAAAAAA diff --git a/mysql-test/suite/binlog/r/binlog_old_versions.result b/mysql-test/suite/binlog/r/binlog_old_versions.result index a514f9278a6..77289252b4c 100644 --- a/mysql-test/suite/binlog/r/binlog_old_versions.result +++ b/mysql-test/suite/binlog/r/binlog_old_versions.result @@ -29,6 +29,16 @@ SELECT COUNT(*) FROM t3; COUNT(*) 17920 DROP TABLE t1, t2, t3; +==== Read binlog from version 4.1 ==== +SELECT * FROM t1 ORDER BY a; +a b +0 last_insert_id +4 four +190243 random +SELECT COUNT(*) FROM t3; +COUNT(*) +17920 +DROP TABLE t1, t3; ==== Read binlog from alcatel tree (mysql-5.1-wl2325-5.0-drop6) ==== SELECT * FROM t1 ORDER BY a; a b diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_4_1.000001 b/mysql-test/suite/binlog/std_data/binlog_old_version_4_1.000001 Binary files differnew file mode 100644 index 00000000000..66db9668d46 --- /dev/null +++ b/mysql-test/suite/binlog/std_data/binlog_old_version_4_1.000001 diff --git a/mysql-test/suite/binlog/std_data/binlog-bug32407.000001 b/mysql-test/suite/binlog/std_data/bug32407.001 Binary files differindex c73243707ef..c73243707ef 100644 --- a/mysql-test/suite/binlog/std_data/binlog-bug32407.000001 +++ b/mysql-test/suite/binlog/std_data/bug32407.001 diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-telco.000001 b/mysql-test/suite/binlog/std_data/ver_5_1-telco.001 Binary files differindex 76856cb04a2..76856cb04a2 100644 --- a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-telco.000001 +++ b/mysql-test/suite/binlog/std_data/ver_5_1-telco.001 diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-wl2325_row.000001 b/mysql-test/suite/binlog/std_data/ver_5_1-wl2325_r.001 Binary files differindex 47071c011f9..47071c011f9 100644 --- a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-wl2325_row.000001 +++ b/mysql-test/suite/binlog/std_data/ver_5_1-wl2325_r.001 diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-wl2325_stm.000001 b/mysql-test/suite/binlog/std_data/ver_5_1-wl2325_s.001 Binary files differindex 4302bfed879..4302bfed879 100644 --- a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1-wl2325_stm.000001 +++ b/mysql-test/suite/binlog/std_data/ver_5_1-wl2325_s.001 diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1_17.000001 b/mysql-test/suite/binlog/std_data/ver_5_1_17.001 Binary files differindex 9b6e200e492..9b6e200e492 100644 --- a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1_17.000001 +++ b/mysql-test/suite/binlog/std_data/ver_5_1_17.001 diff --git a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1_23.000001 b/mysql-test/suite/binlog/std_data/ver_5_1_23.001 Binary files differindex 0e9a9d1470a..0e9a9d1470a 100644 --- a/mysql-test/suite/binlog/std_data/binlog_old_version_5_1_23.000001 +++ b/mysql-test/suite/binlog/std_data/ver_5_1_23.001 diff --git a/mysql-test/suite/binlog/t/binlog_base64_flag.test b/mysql-test/suite/binlog/t/binlog_base64_flag.test index 32319460ab8..01f98b8a134 100644 --- a/mysql-test/suite/binlog/t/binlog_base64_flag.test +++ b/mysql-test/suite/binlog/t/binlog_base64_flag.test @@ -6,6 +6,10 @@ # See also BUG#32407. +# BINLOG statement does not work in embedded mode. +source include/not_embedded.inc; + + # Test to show BUG#32407. This reads a binlog created with the # mysql-5.1-telco-6.1 tree, specifically at the tag # mysql-5.1.15-ndb-6.1.23, and applies it to the database. The test @@ -15,7 +19,7 @@ # The binlog contains row events equivalent to: # CREATE TABLE t1 (a int) engine = myisam # INSERT INTO t1 VALUES (1), (1) -exec $MYSQL_BINLOG suite/binlog/std_data/binlog-bug32407.000001 | $MYSQL; +exec $MYSQL_BINLOG suite/binlog/std_data/bug32407.001 | $MYSQL; # The above line should succeed and t1 should contain two ones select * from t1; @@ -68,7 +72,7 @@ select * from t1; # mysqlbinlog should fail --replace_regex /#[0-9][0-9][0-9][0-9][0-9][0-9] .*/#/ error 1; -exec $MYSQL_BINLOG --base64-output=never suite/binlog/std_data/binlog-bug32407.000001; +exec $MYSQL_BINLOG --base64-output=never suite/binlog/std_data/bug32407.001; # the above line should output the query log event and then stop @@ -78,7 +82,7 @@ exec $MYSQL_BINLOG --base64-output=never suite/binlog/std_data/binlog-bug32407.0 --echo ==== Test non-matching FD event and Row event ==== # This is the Format_description_log_event from -# binlog-bug32407.000001, encoded in base64. It contains only the old +# bug32407.001, encoded in base64. It contains only the old # row events (number of event types is 22) BINLOG ' 4CdYRw8BAAAAYgAAAGYAAAAAAAQANS4xLjE1LW5kYi02LjEuMjQtZGVidWctbG9nAAAAAAAAAAAA diff --git a/mysql-test/suite/binlog/t/binlog_killed.test b/mysql-test/suite/binlog/t/binlog_killed.test index e5f7288b17c..ab8a8cd59bd 100644 --- a/mysql-test/suite/binlog/t/binlog_killed.test +++ b/mysql-test/suite/binlog/t/binlog_killed.test @@ -39,7 +39,7 @@ connection con2; reap; let $rows= `select count(*) from t2 /* must be 2 or 0 */`; ---exec $MYSQL_BINLOG --start-position=134 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog +--exec $MYSQL_BINLOG --force-if-open --start-position=134 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval select (@a:=load_file("$MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog")) @@ -250,7 +250,7 @@ source include/show_binlog_events.inc; # a proof the query is binlogged with an error ---exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--exec $MYSQL_BINLOG --force-if-open --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval select (@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) @@ -296,7 +296,7 @@ source include/show_binlog_events.inc; # a proof the query is binlogged with an error ---exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--exec $MYSQL_BINLOG --force-if-open --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval select (@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) diff --git a/mysql-test/suite/binlog/t/binlog_killed_simulate.test b/mysql-test/suite/binlog/t/binlog_killed_simulate.test index 2121a90dc8c..cb3b5a6e827 100644 --- a/mysql-test/suite/binlog/t/binlog_killed_simulate.test +++ b/mysql-test/suite/binlog/t/binlog_killed_simulate.test @@ -23,7 +23,7 @@ update t1 set a=2 /* will be "killed" after work has been done */; #todo: introduce a suite private macro that provides numeric values # for some constants like the offset of the first real event # that is different between severs versions. ---exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--exec $MYSQL_BINLOG --force-if-open --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval select (@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) @@ -51,7 +51,7 @@ load data infile '../std_data_ln/rpl_loaddata.dat' into table t2 /* will be "kil source include/show_binlog_events.inc; ---exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--exec $MYSQL_BINLOG --force-if-open --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR eval select (@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) diff --git a/mysql-test/suite/binlog/t/binlog_old_versions.test b/mysql-test/suite/binlog/t/binlog_old_versions.test index 2d56ebd588d..0ccea406a82 100644 --- a/mysql-test/suite/binlog/t/binlog_old_versions.test +++ b/mysql-test/suite/binlog/t/binlog_old_versions.test @@ -31,7 +31,7 @@ DROP TABLE IF EXISTS t1, t2, t3; --echo ==== Read modern binlog (version 5.1.23) ==== # Read binlog. ---exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_5_1_23.000001 | $MYSQL --local-infile=1 +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/ver_5_1_23.001 | $MYSQL --local-infile=1 # Show result. SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; @@ -43,7 +43,7 @@ DROP TABLE t1, t2, t3; --echo ==== Read binlog from version 5.1.17 ==== # Read binlog. ---exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_5_1_17.000001 | $MYSQL --local-infile=1 +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/ver_5_1_17.001 | $MYSQL --local-infile=1 # Show result. SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; @@ -52,6 +52,21 @@ SELECT COUNT(*) FROM t3; DROP TABLE t1, t2, t3; +--echo ==== Read binlog from version 4.1 ==== + +# In this version, neither row-based binlogging nor Xid events +# existed, so the binlog was generated without the "row-based tests" +# part and the "get xid event" part, and it does not create table t2. + +# Read binlog. +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_4_1.000001 | $MYSQL --local-infile=1 +# Show result. +SELECT * FROM t1 ORDER BY a; +SELECT COUNT(*) FROM t3; +# Reset. +DROP TABLE t1, t3; + + --echo ==== Read binlog from alcatel tree (mysql-5.1-wl2325-5.0-drop6) ==== # In this version, it was not possible to switch between row-based and @@ -60,9 +75,9 @@ DROP TABLE t1, t2, t3; # replication. # Read rbr binlog. ---exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_5_1-wl2325_row.000001 | $MYSQL --local-infile=1 +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/ver_5_1-wl2325_r.001 | $MYSQL --local-infile=1 # Read stm binlog. ---exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_5_1-wl2325_stm.000001 | $MYSQL --local-infile=1 +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/ver_5_1-wl2325_s.001 | $MYSQL --local-infile=1 # Show result. SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; @@ -74,7 +89,7 @@ DROP TABLE t1, t2, t3; --echo ==== Read binlog from ndb tree (mysql-5.1-telco-6.1) ==== # Read binlog. ---exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/binlog_old_version_5_1-telco.000001 | $MYSQL --local-infile=1 +--exec $MYSQL_BINLOG --local-load=$MYSQLTEST_VARDIR/tmp/ suite/binlog/std_data/ver_5_1-telco.001 | $MYSQL --local-infile=1 # Show resulting tablea. SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; diff --git a/mysql-test/suite/binlog/t/disabled.def b/mysql-test/suite/binlog/t/disabled.def index c93bc2a158e..a6e73fa31d8 100644 --- a/mysql-test/suite/binlog/t/disabled.def +++ b/mysql-test/suite/binlog/t/disabled.def @@ -10,4 +10,3 @@ # ############################################################################## binlog_multi_engine : Bug#32663 binlog_multi_engine.test fails randomly -binlog_base64_flag : BUG#33247 2007-12-14 Sven: mysqlbinlog does not clean up after itself on termination. When compiled in debug mode, this test generates lots of warnings for memory leaks. diff --git a/mysql-test/suite/bugs/data/rpl_bug12691.dat b/mysql-test/suite/bugs/data/rpl_bug12691.dat new file mode 100644 index 00000000000..de980441c3a --- /dev/null +++ b/mysql-test/suite/bugs/data/rpl_bug12691.dat @@ -0,0 +1,3 @@ +a +b +c diff --git a/mysql-test/suite/bugs/r/rpl_bug12691.result b/mysql-test/suite/bugs/r/rpl_bug12691.result new file mode 100644 index 00000000000..69d5e8009b0 --- /dev/null +++ b/mysql-test/suite/bugs/r/rpl_bug12691.result @@ -0,0 +1,34 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; + +**** On Master **** +CREATE TABLE t1 (b CHAR(10)); + +**** On Slave **** +STOP SLAVE; + +**** On Master **** +LOAD DATA INFILE FILENAME +SELECT COUNT(*) FROM t1; +COUNT(*) +3 +SHOW BINLOG EVENTS; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Format_desc 1 # Server ver: # +master-bin.000001 # Query 1 # use `test`; CREATE TABLE t1 (b CHAR(10)) +master-bin.000001 # Begin_load_query 1 # ;file_id=#;block_len=# +master-bin.000001 # Execute_load_query 1 # use `test`; LOAD DATA INFILE FILENAME ;file_id=# + +**** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT COUNT(*) FROM t1; +COUNT(*) +0 + +**** On Master **** +DROP TABLE t1; diff --git a/mysql-test/suite/bugs/t/rpl_bug12691.test b/mysql-test/suite/bugs/t/rpl_bug12691.test new file mode 100644 index 00000000000..b29c85584a5 --- /dev/null +++ b/mysql-test/suite/bugs/t/rpl_bug12691.test @@ -0,0 +1,53 @@ +# Bug#12691: Exec_master_log_pos corrupted with SQL_SLAVE_SKIP_COUNTER +# Date: 01/31/2008 +# Added: Serge Kozlov <skozlov@mysql.com> + +--source include/master-slave.inc +--connection master +--source include/have_binlog_format_mixed_or_statement.inc + +--echo +--echo **** On Master **** +CREATE TABLE t1 (b CHAR(10)); +--echo +--echo **** On Slave **** +--sync_slave_with_master +STOP SLAVE; +--source include/wait_for_slave_to_stop.inc + +--connection master + +--echo +--echo **** On Master **** +--exec cp $MYSQL_TEST_DIR/suite/bugs/data/rpl_bug12691.dat $MYSQLTEST_VARDIR/tmp/ +--echo LOAD DATA INFILE FILENAME +--disable_query_log +--eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/rpl_bug12691.dat' INTO TABLE t1 FIELDS TERMINATED BY '|' +--enable_query_log +--remove_file $MYSQLTEST_VARDIR/tmp/rpl_bug12691.dat + +SELECT COUNT(*) FROM t1; + +--replace_column 2 # 5 # +--replace_regex /Server ver: .+/Server ver: #/ /table_id: [0-9]+/table_id: #/ /COMMIT.+xid=[0-9]+.+/#/ /file_id=[0-9]+/file_id=#/ /block_len=[0-9]+/block_len=#/ /'.+'/FILENAME/ +SHOW BINLOG EVENTS; + +--save_master_pos + +--connection slave +--echo +--echo **** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +--source include/wait_for_slave_to_start.inc +--sync_with_master + +SELECT COUNT(*) FROM t1; + +# Clean up +--connection master +--echo +--echo **** On Master **** +DROP TABLE t1; +--sync_slave_with_master + diff --git a/mysql-test/suite/rpl/r/rpl_loaddata_map.result b/mysql-test/suite/rpl/r/rpl_loaddata_map.result index e6ddc1ebedc..7078389c987 100644 --- a/mysql-test/suite/rpl/r/rpl_loaddata_map.result +++ b/mysql-test/suite/rpl/r/rpl_loaddata_map.result @@ -12,13 +12,13 @@ load data infile 'MYSQLTEST_VARDIR/tmp/bug30435_5k.txt' into table t2; select count(*) from t2 /* 5 000 */; count(*) 5000 -show binlog events in 'master-bin.000002' from 106; +show binlog events in 'master-bin.000002' from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000002 106 Query 1 # use `test`; create table t2 (id int not null primary key auto_increment) -master-bin.000002 229 Begin_load_query 1 # ;file_id=#;block_len=8192 -master-bin.000002 8444 Append_block 1 # ;file_id=#;block_len=8192 -master-bin.000002 16659 Append_block 1 # ;file_id=#;block_len=7509 -master-bin.000002 24191 Execute_load_query 1 # use `test`; load data infile 'MYSQLTEST_VARDIR/tmp/bug30435_5k.txt' into table t2 ;file_id=# +master-bin.000002 # Query # # use `test`; create table t2 (id int not null primary key auto_increment) +master-bin.000002 # Begin_load_query # # ;file_id=#;block_len=# +master-bin.000002 # Append_block # # ;file_id=#;block_len=# +master-bin.000002 # Append_block # # ;file_id=#;block_len=# +master-bin.000002 # Execute_load_query # # use `test`; load data infile 'MYSQLTEST_VARDIR/tmp/bug30435_5k.txt' into table t2 ;file_id=# select count(*) from t2 /* 5 000 */; count(*) 5000 diff --git a/mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result b/mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result index 6859a406b16..e81d4f7454e 100644 --- a/mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result +++ b/mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result @@ -26,7 +26,7 @@ ADD x BIT(3) DEFAULT b'011', ADD y BIT(5) DEFAULT b'10101', ADD z BIT(2) DEFAULT b'10'; ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test'; -ALTER TABLE t1_nodef ADD x INT NOT NULL; +ALTER TABLE t1_nodef ADD x INT NOT NULL, ADD y INT NOT NULL, ADD z INT NOT NULL; ALTER TABLE t2 DROP b; ALTER TABLE t4 MODIFY a FLOAT; ALTER TABLE t5 MODIFY b FLOAT; @@ -393,8 +393,8 @@ INSERT INTO t1_nodef VALUES (1,2); INSERT INTO t1_nodef VALUES (2,4); SET SQL_LOG_BIN=1; **** On Slave **** -INSERT INTO t1_nodef VALUES (1,2,3); -INSERT INTO t1_nodef VALUES (2,4,6); +INSERT INTO t1_nodef VALUES (1,2,3,4,5); +INSERT INTO t1_nodef VALUES (2,4,6,8,10); **** On Master **** UPDATE t1_nodef SET b=2*b WHERE a=1; SELECT * FROM t1_nodef ORDER BY a; @@ -403,9 +403,9 @@ a b 2 4 **** On Slave **** SELECT * FROM t1_nodef ORDER BY a; -a b x -1 4 3 -2 4 6 +a b x y z +1 4 3 4 5 +2 4 6 8 10 **** On Master **** DELETE FROM t1_nodef WHERE a=2; SELECT * FROM t1_nodef ORDER BY a; @@ -413,8 +413,8 @@ a b 1 4 **** On Slave **** SELECT * FROM t1_nodef ORDER BY a; -a b x -1 4 3 +a b x y z +1 4 3 4 5 **** Cleanup **** DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef; DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t7,t8,t9; diff --git a/mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result b/mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result index 17b2a2f7b52..a6834be5a86 100644 --- a/mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result +++ b/mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result @@ -26,7 +26,7 @@ ADD x BIT(3) DEFAULT b'011', ADD y BIT(5) DEFAULT b'10101', ADD z BIT(2) DEFAULT b'10'; ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test'; -ALTER TABLE t1_nodef ADD x INT NOT NULL; +ALTER TABLE t1_nodef ADD x INT NOT NULL, ADD y INT NOT NULL, ADD z INT NOT NULL; ALTER TABLE t2 DROP b; ALTER TABLE t4 MODIFY a FLOAT; ALTER TABLE t5 MODIFY b FLOAT; @@ -393,8 +393,8 @@ INSERT INTO t1_nodef VALUES (1,2); INSERT INTO t1_nodef VALUES (2,4); SET SQL_LOG_BIN=1; **** On Slave **** -INSERT INTO t1_nodef VALUES (1,2,3); -INSERT INTO t1_nodef VALUES (2,4,6); +INSERT INTO t1_nodef VALUES (1,2,3,4,5); +INSERT INTO t1_nodef VALUES (2,4,6,8,10); **** On Master **** UPDATE t1_nodef SET b=2*b WHERE a=1; SELECT * FROM t1_nodef ORDER BY a; @@ -403,9 +403,9 @@ a b 2 4 **** On Slave **** SELECT * FROM t1_nodef ORDER BY a; -a b x -1 4 3 -2 4 6 +a b x y z +1 4 3 4 5 +2 4 6 8 10 **** On Master **** DELETE FROM t1_nodef WHERE a=2; SELECT * FROM t1_nodef ORDER BY a; @@ -413,8 +413,8 @@ a b 1 4 **** On Slave **** SELECT * FROM t1_nodef ORDER BY a; -a b x -1 4 3 +a b x y z +1 4 3 4 5 **** Cleanup **** DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef; DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t7,t8,t9; diff --git a/mysql-test/suite/rpl/t/rpl_loaddata_map.test b/mysql-test/suite/rpl/t/rpl_loaddata_map.test index 3d6f09844b6..6a8378c8fdc 100644 --- a/mysql-test/suite/rpl/t/rpl_loaddata_map.test +++ b/mysql-test/suite/rpl/t/rpl_loaddata_map.test @@ -7,6 +7,7 @@ # BUG#33413 show binlog events fails if binlog has event size of close # to max_allowed_packet +source include/have_binlog_format_mixed_or_statement.inc; source include/master-slave.inc; source include/have_innodb.inc; source include/have_binlog_format_mixed_or_statement.inc; @@ -35,9 +36,9 @@ select count(*) from t2 /* 5 000 */; # the binglog will show fragmented Append_block events --let $binlog_start=106 ---replace_column 5 # ---replace_regex /\/\* xid=.* \*\//\/* XID *\// /file_id=[0-9]+/file_id=#/ ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--replace_column 2 # 4 # 5 # +--replace_regex /\/\* xid=.* \*\//\/* XID *\// /file_id=[0-9]+/file_id=#/ /block_len=[0-9]+/block_len=#/ +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $binlog_start <binlog_start> --eval show binlog events in 'master-bin.000002' from $binlog_start diff --git a/sql/log_event.cc b/sql/log_event.cc index a4b32a84b42..2c89f8cbba6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -99,27 +99,51 @@ static const char *HA_ERR(int i) case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME"; case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE"; case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT"; + case HA_ERR_ROWS_EVENT_APPLY : return "HA_ERR_ROWS_EVENT_APPLY"; } return 0; } /** - macro to call from different branches of Rows_log_event::do_apply_event + Error reporting facility for Rows_log_event::do_apply_event + + @param level error, warning or info + @param ha_error HA_ERR_ code + @param rli pointer to the active Relay_log_info instance + @param thd pointer to the slave thread's thd + @param table pointer to the event's table object + @param type the type of the event + @param log_name the master binlog file name + @param pos the master binlog file pos (the next after the event) + */ static void inline slave_rows_error_report(enum loglevel level, int ha_error, Relay_log_info const *rli, THD *thd, TABLE *table, const char * type, const char *log_name, ulong pos) { - const char *handler_error= HA_ERR(ha_error); + const char *handler_error= HA_ERR(ha_error); + char buff[MAX_SLAVE_ERRMSG], *slider; + const char *buff_end= buff + sizeof(buff); + uint len; + List_iterator_fast<MYSQL_ERROR> it(thd->warn_list); + MYSQL_ERROR *err; + buff[0]= 0; + + for (err= it++, slider= buff; err && slider < buff_end - 1; + slider += len, err= it++) + { + len= my_snprintf(slider, buff_end - slider, + " %s, Error_code: %d;", err->msg, err->code); + } + rli->report(level, thd->net.client_last_errno, "Could not execute %s event on table %s.%s;" - "%s%s handler error %s; " + "%s handler error %s; " "the event's master log %s, end_log_pos %lu", type, table->s->db.str, table->s->table_name.str, - thd->net.client_last_error[0] != 0 ? thd->net.client_last_error : "", - thd->net.client_last_error[0] != 0 ? ";" : "", + buff, handler_error == NULL? "<unknown>" : handler_error, log_name, pos); } @@ -212,9 +236,9 @@ uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation */ #ifdef MYSQL_CLIENT -static void pretty_print_str(IO_CACHE* cache, char* str, int len) +static void pretty_print_str(IO_CACHE* cache, const char* str, int len) { - char* end = str + len; + const char* end = str + len; my_b_printf(cache, "\'"); while (str < end) { @@ -277,9 +301,9 @@ inline int ignored_error_code(int err_code) */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -static char *pretty_print_str(char *packet, char *str, int len) +static char *pretty_print_str(char *packet, const char *str, int len) { - char *end= str + len; + const char *end= str + len; char *pos= packet; *pos++= '\''; while (str < end) @@ -385,7 +409,7 @@ static void cleanup_load_tmpdir() write_str() */ -static bool write_str(IO_CACHE *file, char *str, uint length) +static bool write_str(IO_CACHE *file, const char *str, uint length) { uchar tmp[1]; tmp[0]= (uchar) length; @@ -2957,18 +2981,63 @@ Format_description_log_event(const char* buf, If post_header_len is null, it means malloc failed, and is_valid will fail, so there is no need to do anything. - The trees which have wrong event id's are: - mysql-5.1-wl2325-5.0-drop6p13-alpha, mysql-5.1-wl2325-5.0-drop6, - mysql-5.1-wl2325-5.0, mysql-5.1-wl2325-no-dd (`grep -C2 - BEGIN_LOAD_QUERY_EVENT /home/bk/ * /sql/log_event.h`). The - corresponding version (`grep mysql, configure.in` in those trees) - strings are 5.2.2-a_drop6p13-alpha, 5.2.2-a_drop6p13c, - 5.1.5-a_drop5p20, 5.1.2-a_drop5p5. + The trees in which events have wrong id's are: + + mysql-5.1-wl1012.old mysql-5.1-wl2325-5.0-drop6p13-alpha + mysql-5.1-wl2325-5.0-drop6 mysql-5.1-wl2325-5.0 + mysql-5.1-wl2325-no-dd + + (this was found by grepping for two lines in sequence where the + first matches "FORMAT_DESCRIPTION_EVENT," and the second matches + "TABLE_MAP_EVENT," in log_event.h in all trees) + + In these trees, the following server_versions existed since + TABLE_MAP_EVENT was introduced: + + 5.1.1-a_drop5p3 5.1.1-a_drop5p4 5.1.1-alpha + 5.1.2-a_drop5p10 5.1.2-a_drop5p11 5.1.2-a_drop5p12 + 5.1.2-a_drop5p13 5.1.2-a_drop5p14 5.1.2-a_drop5p15 + 5.1.2-a_drop5p16 5.1.2-a_drop5p16b 5.1.2-a_drop5p16c + 5.1.2-a_drop5p17 5.1.2-a_drop5p4 5.1.2-a_drop5p5 + 5.1.2-a_drop5p6 5.1.2-a_drop5p7 5.1.2-a_drop5p8 + 5.1.2-a_drop5p9 5.1.3-a_drop5p17 5.1.3-a_drop5p17b + 5.1.3-a_drop5p17c 5.1.4-a_drop5p18 5.1.4-a_drop5p19 + 5.1.4-a_drop5p20 5.1.4-a_drop6p0 5.1.4-a_drop6p1 + 5.1.4-a_drop6p2 5.1.5-a_drop5p20 5.2.0-a_drop6p3 + 5.2.0-a_drop6p4 5.2.0-a_drop6p5 5.2.0-a_drop6p6 + 5.2.1-a_drop6p10 5.2.1-a_drop6p11 5.2.1-a_drop6p12 + 5.2.1-a_drop6p6 5.2.1-a_drop6p7 5.2.1-a_drop6p8 + 5.2.2-a_drop6p13 5.2.2-a_drop6p13-alpha 5.2.2-a_drop6p13b + 5.2.2-a_drop6p13c + + (this was found by grepping for "mysql," in all historical + versions of configure.in in the trees listed above). + + There are 5.1.1-alpha versions that use the new event id's, so we + do not test that version string. So replication from 5.1.1-alpha + with the other event id's to a new version does not work. + Moreover, we can safely ignore the part after drop[56]. This + allows us to simplify the big list above to the following regexes: + + 5\.1\.[1-5]-a_drop5.* + 5\.1\.4-a_drop6.* + 5\.2\.[0-2]-a_drop6.* + + This is what we test for in the 'if' below. */ if (post_header_len && - (strncmp(server_version, "5.1.2-a_drop5", 13) == 0 || - strncmp(server_version, "5.1.5-a_drop5", 13) == 0 || - strncmp(server_version, "5.2.2-a_drop6", 13) == 0)) + server_version[0] == '5' && server_version[1] == '.' && + server_version[3] == '.' && + strncmp(server_version + 5, "-a_drop", 7) == 0 && + ((server_version[2] == '1' && + server_version[4] >= '1' && server_version[4] <= '5' && + server_version[12] == '5') || + (server_version[2] == '1' && + server_version[4] == '4' && + server_version[12] == '6') || + (server_version[2] == '2' && + server_version[4] >= '0' && server_version[4] <= '2' && + server_version[12] == '6'))) { if (number_of_event_types != 22) { @@ -6026,7 +6095,8 @@ bool sql_ex_info::write_data(IO_CACHE* file) sql_ex_info::init() */ -char *sql_ex_info::init(char *buf, char *buf_end, bool use_new_format) +const char *sql_ex_info::init(const char *buf, const char *buf_end, + bool use_new_format) { cached_new_format = use_new_format; if (use_new_format) @@ -6039,12 +6109,11 @@ char *sql_ex_info::init(char *buf, char *buf_end, bool use_new_format) the case when we have old format because we will be reusing net buffer to read the actual file before we write out the Create_file event. */ - const char *ptr= buf; - if (read_str(&ptr, buf_end, (const char **) &field_term, &field_term_len) || - read_str(&ptr, buf_end, (const char **) &enclosed, &enclosed_len) || - read_str(&ptr, buf_end, (const char **) &line_term, &line_term_len) || - read_str(&ptr, buf_end, (const char **) &line_start, &line_start_len) || - read_str(&ptr, buf_end, (const char **) &escaped, &escaped_len)) + if (read_str(&buf, buf_end, &field_term, &field_term_len) || + read_str(&buf, buf_end, &enclosed, &enclosed_len) || + read_str(&buf, buf_end, &line_term, &line_term_len) || + read_str(&buf, buf_end, &line_start, &line_start_len) || + read_str(&buf, buf_end, &escaped, &escaped_len)) return 0; opt_flags = *buf++; } @@ -7646,7 +7715,7 @@ Rows_log_event::write_row(const Relay_log_info *const rli, /* fill table->record[0] with default values */ - if ((error= prepare_record(rli, table, m_width, + if ((error= prepare_record(table, m_width, TRUE /* check if columns have def. values */))) DBUG_RETURN(error); @@ -7964,13 +8033,17 @@ int Rows_log_event::find_row(const Relay_log_info *rli) DBUG_ASSERT(m_table && m_table->in_use != NULL); TABLE *table= m_table; - int error; - - /* unpack row - missing fields get default values */ + int error= 0; - // TODO: shall we check and report errors here? - prepare_record(NULL,table,m_width,FALSE /* don't check errors */); - error= unpack_current_row(rli); + /* + rpl_row_tabledefs.test specifies that + if the extra field on the slave does not have a default value + and this is okay with Delete or Update events. + Todo: fix wl3228 hld that requires defauls for all types of events + */ + + prepare_record(table, m_width, FALSE); + error= unpack_current_row(rli); #ifndef DBUG_OFF DBUG_PRINT("info",("looking for the following record")); diff --git a/sql/log_event.h b/sql/log_event.h index efb8675780e..4e151d6cde9 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -152,11 +152,11 @@ struct old_sql_ex struct sql_ex_info { sql_ex_info() {} /* Remove gcc warning */ - char* field_term; - char* enclosed; - char* line_term; - char* line_start; - char* escaped; + const char* field_term; + const char* enclosed; + const char* line_term; + const char* line_start; + const char* escaped; int cached_new_format; uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len; char opt_flags; @@ -171,7 +171,7 @@ struct sql_ex_info line_start_len + escaped_len + 6 : 7); } bool write_data(IO_CACHE* file); - char* init(char* buf,char* buf_end,bool use_new_format); + const char* init(const char* buf, const char* buf_end, bool use_new_format); bool new_format() { return ((cached_new_format != -1) ? cached_new_format : @@ -667,34 +667,35 @@ typedef struct st_print_event_info @section Log_event_binary_format Binary Format - Any Log_event saved on disk consists of the following three + Any @c Log_event saved on disk consists of the following three components. - @li Common-Header - @li Post-Header - @li Body + * Common-Header + * Post-Header + * Body - The Common-Header, documented below, always has the same form and - length within one version of MySQL. Each event type specifies a - form and length of the Post-Header common to all events of the type. - The Body may be of different form and length even for different - events of the same type. The binary formats of Post-Header and Body - are documented separately in each subclass. The binary format of - Common-Header is as follows. + The Common-Header, documented in the table @ref Table_common_header + "below", always has the same form and length within one version of + MySQL. Each event type specifies a form and length of the + Post-Header common to all events of the type. The Body may be of + different form and length even for different events of the same + type. The binary formats of Post-Header and Body are documented + separately in each subclass. The binary format of Common-Header is + as follows. <table> <caption>Common-Header</caption> <tr> <th>Name</th> - <th>Format<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> <td>timestamp</td> <td>4 byte unsigned integer</td> - <td>The number of seconds since 1970. + <td>The time when the query started, in seconds since 1970. </td> </tr> @@ -705,14 +706,14 @@ typedef struct st_print_event_info </tr> <tr> - <td>master_id</td> - <td>4 byte integer</td> + <td>server_id</td> + <td>4 byte unsigned integer</td> <td>Server ID of the server that created the event.</td> </tr> <tr> <td>total_size</td> - <td>4 byte integer</td> + <td>4 byte unsigned integer</td> <td>The total size of this event, in bytes. In other words, this is the sum of the sizes of Common-Header, Post-Header, and Body. </td> @@ -720,9 +721,12 @@ typedef struct st_print_event_info <tr> <td>master_position</td> - <td>4 byte integer</td> + <td>4 byte unsigned integer</td> <td>The position of the next event in the master binary log, in - bytes from the beginning of the file. + bytes from the beginning of the file. In a binlog that is not a + relay log, this is just the position of the next event, in bytes + from the beginning of the file. In a relay log, this is + the position of the next event in the master's binlog. </td> </tr> @@ -736,13 +740,55 @@ typedef struct st_print_event_info Summing up the numbers above, we see that the total size of the common header is 19 bytes. - @subsection Log_event_endianness_and_string_formats Endianness and String Formats + @subsection Log_event_format_of_atomic_primitives Format of Atomic Primitives + + - All numbers, whether they are 16-, 24-, 32-, or 64-bit numbers, + are stored in little endian, i.e., the least significant byte first, + unless otherwise specified. + + @anchor packed_integer + - Some events use a special format for efficient representation of + unsigned integers, called Packed Integer. A Packed Integer has the + capacity of storing up to 8-byte integers, while small integers + still can use 1, 3, or 4 bytes. The first byte indicates how many + bytes are used by the integer, according to the following table: + + <table> + <caption>Format of Packed Integer</caption> + + <tr> + <th>First byte</th> + <th>Format</th> + </tr> + + <tr> + <td>0-250</td> + <td>The first byte is the number (in range 0-250), and no more + bytes are used.</td> + </tr> + + <tr> + <td>252</td> + <td>Two more bytes are used. The number is in the range + 251-0xffff.</td> + </tr> - All numbers, whether they are 16-, 32-, or 64-bit, are stored in - little endian, i.e., the least significant byte first. + <tr> + <td>253</td> + <td>Three more bytes are used. The number is in the range + 0xffff-0xffffff.</td> + </tr> - Strings are stored in various formats. The format of each string is - documented separately. + <tr> + <td>254</td> + <td>Eight more bytes are used. The number is in the range + 0xffffff-0xffffffffffffffff.</td> + </tr> + + </table> + + - Strings are stored in various formats. The format of each string + is documented separately. */ class Log_event { @@ -1123,7 +1169,8 @@ protected: /** @class Query_log_event - Logs SQL queries. + A @c Query_log_event is created for each query that modifies the + database, unless the query is logged row-based. @section Query_log_event_binary_format Binary format @@ -1134,60 +1181,49 @@ protected: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> <td>slave_proxy_id</td> <td>4 byte unsigned integer</td> - <td>An integer identifying the client thread, which is unique on - the server. (Note, however, that two threads on different servers - may have the same slave_proxy_id.) This is used when a client - thread creates a temporary table. Temporary tables are local to - the client, and the slave_proxy_id is used to distinguish - temporary tables belonging to different clients. + <td>An integer identifying the client thread that issued the + query. The id is unique per server. (Note, however, that two + threads on different servers may have the same slave_proxy_id.) + This is used when a client thread creates a temporary table local + to the client. The slave_proxy_id is used to distinguish + temporary tables that belong to different clients. </td> </tr> <tr> <td>exec_time</td> - <td>4 byte integer</td> - <td>???TODO</td> + <td>4 byte unsigned integer</td> + <td>The time from when the query started to when it was logged in + the binlog, in seconds.</td> </tr> <tr> <td>db_len</td> <td>1 byte integer</td> - <td>The length of the name of the currently selected - database. - </td> + <td>The length of the name of the currently selected database.</td> </tr> <tr> <td>error_code</td> - <td>2 byte integer</td> + <td>2 byte unsigned integer</td> <td>Error code generated by the master. If the master fails, the slave will fail with the same error code, except for the error - codes ER_DB_CREATE_EXISTS==1007 and ER_DB_DROP_EXISTS==1008. + codes ER_DB_CREATE_EXISTS == 1007 and ER_DB_DROP_EXISTS == 1008. </td> </tr> <tr> <td>status_vars_len</td> - <td>2 byte integer</td> + <td>2 byte unsigned integer</td> <td>The length of the status_vars block of the Body, in bytes. See - <a href="#query_log_event_status_vars">below</a>. - </td> - </tr> - - <tr> - <td>Post-Header-For-Derived</td> - <td>0 bytes</td> - <td>This field is only written by the subclass - Execute_load_query_log_event. In this base class, it takes 0 - bytes. See separate documentation for - Execute_load_query_log_event. + @ref query_log_event_status_vars "below". </td> </tr> </table> @@ -1199,19 +1235,19 @@ protected: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> - <td><a name="query_log_event_status_vars" /> status_vars</td> - <td>variable length</td> + <td>@anchor query_log_event_status_vars status_vars</td> + <td>status_vars_len bytes</td> <td>Zero or more status variables. Each status variable consists of one byte identifying the variable stored, followed by the value of the variable. The possible variables are listed separately in - the table below. MySQL always writes events in the order defined - below; however, it is capable of reading them in any order. - </td> + the table @ref Table_query_log_event_status_vars "below". MySQL + always writes events in the order defined below; however, it is + capable of reading them in any order. </td> </tr> <tr> @@ -1237,13 +1273,14 @@ protected: The following table lists the status variables that may appear in the status_vars field. + @anchor Table_query_log_event_status_vars <table> <caption>Status variables for Query_log_event</caption> <tr> <th>Status variable</th> - <th>1-byte identifier</th> - <th>Size<br/></th> + <th>1 byte identifier</th> + <th>Format</th> <th>Description</th> </tr> @@ -1251,13 +1288,13 @@ protected: <td>flags2</td> <td>Q_FLAGS2_CODE == 0</td> <td>4 byte bitfield</td> - <td>The flags in thd->options, binary AND-ed with - OPTIONS_WRITTEN_TO_BIN_LOG. The thd->options bitfield contains - options for SELECT. OPTIONS_WRITTEN identifies those options that - need to be written to the binlog (not all do). Specifically, - OPTIONS_WRITTEN_TO_BIN_LOG equals (OPTION_AUTO_IS_NULL | - OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS | - OPTION_NOT_AUTOCOMMIT), or 0x0c084000 in hex. + <td>The flags in @c thd->options, binary AND-ed with @c + OPTIONS_WRITTEN_TO_BIN_LOG. The @c thd->options bitfield contains + options for "SELECT". @c OPTIONS_WRITTEN identifies those options + that need to be written to the binlog (not all do). Specifically, + @c OPTIONS_WRITTEN_TO_BIN_LOG equals (@c OPTION_AUTO_IS_NULL | @c + OPTION_NO_FOREIGN_KEY_CHECKS | @c OPTION_RELAXED_UNIQUE_CHECKS | + @c OPTION_NOT_AUTOCOMMIT), or 0x0c084000 in hex. These flags correspond to the SQL variables SQL_AUTO_IS_NULL, FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, and AUTOCOMMIT, documented in @@ -1271,8 +1308,8 @@ protected: <tr> <td>sql_mode</td> <td>Q_SQL_MODE_CODE == 1</td> - <td>8 byte integer</td> - <td>The sql_mode variable. See the section "SQL Modes" in the + <td>8 byte bitfield</td> + <td>The @c sql_mode variable. See the section "SQL Modes" in the MySQL manual, and see mysql_priv.h for a list of the possible flags. Currently (2007-10-04), the following flags are available: <pre> @@ -1310,10 +1347,10 @@ protected: MODE_PAD_CHAR_TO_FULL_LENGTH==0x80000000 </pre> All these flags are replicated from the server. However, all - flags except MODE_NO_DIR_IN_CREATE are honored by the slave; the - slave always preserves its old value of MODE_NO_DIR_IN_CREATE. - For a rationale, see comment in Query_log_event::do_apply_event in - log_event.cc. + flags except @c MODE_NO_DIR_IN_CREATE are honored by the slave; + the slave always preserves its old value of @c + MODE_NO_DIR_IN_CREATE. For a rationale, see comment in + @c Query_log_event::do_apply_event in @c log_event.cc. This field is always written to the binlog. </td> @@ -1327,7 +1364,7 @@ protected: </td> <td>Stores the client's current catalog. Every database belongs to a catalog, the same way that every table belongs to a - database. Currently, there is only one catalog, 'std'. + database. Currently, there is only one catalog, "std". This field is written if the length of the catalog is > 0; otherwise it is not written. @@ -1343,7 +1380,7 @@ protected: auto_increment_offset, in that order. For more information, see "System variables" in the MySQL manual. - This field is written if auto_increment>1; otherwise it is not + This field is written if auto_increment > 1. Otherwise, it is not written. </td> </tr> @@ -1351,14 +1388,14 @@ protected: <tr> <td>charset</td> <td>Q_CHARSET_CODE == 4</td> - <td>three 2-byte unsigned integers (i.e., 6 bytes)</td> + <td>three 2 byte unsigned integers, totally 2+2+2=6 bytes</td> <td>The three variables character_set_client, collation_connection, and collation_server, in that order. - `character_set_client' is a code identifying the character set and + character_set_client is a code identifying the character set and collation used by the client to encode the query. - `collation_connection' identifies the character set and collation + collation_connection identifies the character set and collation that the master converts the query to when it receives it; this is - useful when comparing literal strings. `collation_server' is the + useful when comparing literal strings. collation_server is the default character set and collation used when a new database is created. @@ -1396,9 +1433,9 @@ protected: <td>Q_LC_TIME_NAMES_CODE == 7</td> <td>2 byte integer</td> <td>A code identifying a table of month and day names. The - mapping from codes to languages is defined in sql_locale.cc. + mapping from codes to languages is defined in @c sql_locale.cc. - This field is written if it is != 0, i.e., if the locale is not + This field is written if it is not 0, i.e., if the locale is not en_US. </td> </tr> @@ -1409,14 +1446,14 @@ protected: <td>2 byte integer</td> <td>The value of the collation_database system variable (in the - source code stored in thd->variables.collation_database), which + source code stored in @c thd->variables.collation_database), which holds the code for a (character set, collation) pair as described above (see Q_CHARSET_CODE). - `collation_database' was used in old versions (???WHEN). Its - value was loaded when issuing a "use db" command and could be - changed by issuing a "SET collation_database=xxx" command. It - used to affect the "LOAD DATA INFILE" and "CREATE TABLE" commands. + collation_database was used in old versions (???WHEN). Its value + was loaded when issuing a "use db" query and could be changed by + issuing a "SET collation_database=xxx" query. It used to affect + the "LOAD DATA INFILE" and "CREATE TABLE" commands. In newer versions, "CREATE TABLE" has been changed to take the character set from the database of the created table, rather than @@ -1433,17 +1470,17 @@ protected: @subsection Query_log_event_notes_on_previous_versions Notes on Previous Versions - @li Status vars were introduced in version 5.0. To read earlier + * Status vars were introduced in version 5.0. To read earlier versions correctly, check the length of the Post-Header. - @li The status variable Q_CATALOG_CODE == 2 existed in MySQL 5.0.x, + * The status variable Q_CATALOG_CODE == 2 existed in MySQL 5.0.x, where 0<=x<=3. It was identical to Q_CATALOG_CODE, except that the string had a trailing '\0'. The '\0' was removed in 5.0.4 since it was redundant (the string length is stored before the string). The Q_CATALOG_CODE will never be written by a new master, but can still be understood by a new slave. - @li See Q_CHARSET_DATABASE_NUMBER in the table above. + * See Q_CHARSET_DATABASE_NUMBER in the table above. */ class Query_log_event: public Log_event @@ -1576,7 +1613,8 @@ public: /* !!! Public in this patch to allow old usage */ /** @class Muted_query_log_event - Pretends to log SQL queries, but doesn't actually do so. + Pretends to log SQL queries, but doesn't actually do so. This is + used internally only and never written to any binlog. @section Muted_query_log_event_binary_format Binary Format @@ -1603,7 +1641,7 @@ public: @class Slave_log_event Note that this class is currently not used at all; no code writes a - Slave_log_event (though some code in repl_failsafe.cc reads + @c Slave_log_event (though some code in @c repl_failsafe.cc reads @c Slave_log_event). So it's not a problem if this code is not maintained. @@ -1617,7 +1655,7 @@ public: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> @@ -1717,26 +1755,27 @@ private: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> <td>slave_proxy_id</td> <td>4 byte unsigned integer</td> - <td>An integer identifying the client thread, which is unique on - the server. (Note, however, that the same slave_proxy_id may - appear on different servers.) This is used when a client thread - creates a temporary table. Temporary tables are local to the - client, and the slave_proxy_id is used to distinguish temporary - tables belonging to different clients. + <td>An integer identifying the client thread that issued the + query. The id is unique per server. (Note, however, that two + threads on different servers may have the same slave_proxy_id.) + This is used when a client thread creates a temporary table local + to the client. The slave_proxy_id is used to distinguish + temporary tables that belong to different clients. </td> </tr> <tr> <td>exec_time</td> <td>4 byte unsigned integer</td> - <td>???TODO</td> + <td>The time from when the query started to when it was logged in + the binlog, in seconds.</td> </tr> <tr> @@ -1773,7 +1812,7 @@ private: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> @@ -1813,7 +1852,7 @@ private: <li> In the old format, we know that each string has length 0 or 1. Therefore, only the first byte of each string is stored. The order of the strings is the same as in the new format. These five - bytes are followed by the same 1-byte bitfield as in the new + bytes are followed by the same 1 byte bitfield as in the new format. Finally, a 1 byte bitfield called empty_flags is stored. The low 5 bits of empty_flags indicate which of the five strings have length 0. For each of the following flags that is set, the @@ -1831,7 +1870,7 @@ private: <tr> <td>field_lens</td> - <td>num_fields 1-byte unsigned integers</td> + <td>num_fields 1 byte unsigned integers</td> <td>An array of num_fields integers representing the length of each field in the query. (num_fields is from the Post-Header). </td> @@ -1992,11 +2031,13 @@ extern char server_version[SERVER_VERSION_LENGTH]; Start_log_event_v3 is the Start_log_event of binlog format 3 (MySQL 3.23 and 4.x). - Format_description_log_event derives from Start_log_event_v3; it is the - Start_log_event of binlog format 4 (MySQL 5.0), that is, the event that - describes the other events' header/postheader lengths. This event is sent by - MySQL 5.0 whenever it starts sending a new binlog if the requested position - is >4 (otherwise if ==4 the event will be sent naturally). + + Format_description_log_event derives from Start_log_event_v3; it is + the Start_log_event of binlog format 4 (MySQL 5.0), that is, the + event that describes the other events' Common-Header/Post-Header + lengths. This event is sent by MySQL 5.0 whenever it starts sending + a new binlog if the requested position is >4 (otherwise if ==4 the + event will be sent naturally). @section Start_log_event_v3_binary_format Binary Format */ @@ -2150,7 +2191,9 @@ protected: /** @class Intvar_log_event - Logs special variables related to auto_increment values. + An Intvar_log_event will be created just before a Query_log_event, + if the query uses one of the variables LAST_INSERT_ID or INSERT_ID. + Each Intvar_log_event holds the value of one of these variables. @section Intvar_log_event_binary_format Binary Format @@ -2161,12 +2204,12 @@ protected: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> - <td>Type</td> + <td>type</td> <td>1 byte enumeration</td> <td>One byte identifying the type of variable stored. Currently, two identifiers are supported: LAST_INSERT_ID_EVENT==1 and @@ -2182,7 +2225,6 @@ protected: </table> */ - class Intvar_log_event: public Log_event { public: @@ -2228,15 +2270,34 @@ private: written in 4.1.1 for PASSWORD() (but the fact that it is written is just a waste, it does not cause bugs). + The state of the random number generation consists of 128 bits, + which are stored internally as two 64-bit numbers. + @section Rand_log_event_binary_format Binary Format This event type has no Post-Header. The Body of this event type has two components: - @li seed1 (8 bytes): 64 bit random seed1. - @li seed2 (8 bytes): 64 bit random seed2. + <table> + <caption>Post-Header for Intvar_log_event</caption> - The state of the random number generation consists of 128 bits, - which are stored internally as two 64-bit numbers. + <tr> + <th>Name</th> + <th>Format</th> + <th>Description</th> + </tr> + + <tr> + <td>seed1</td> + <td>8 byte unsigned integer</td> + <td>64 bit random seed1.</td> + </tr> + + <tr> + <td>seed2</td> + <td>8 byte unsigned integer</td> + <td>64 bit random seed2.</td> + </tr> + </table> */ class Rand_log_event: public Log_event @@ -2423,14 +2484,14 @@ private: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> - <td>pos</td> + <td>position</td> <td>8 byte integer</td> - <td>???TODO</td> + <td>The position within the binlog to rotate to.</td> </tr> </table> @@ -2442,17 +2503,17 @@ private: <tr> <th>Name</th> - <th>Size<br/></th> + <th>Format</th> <th>Description</th> </tr> <tr> - <td>new_log_ident</td> + <td>new_log</td> <td>variable length string without trailing zero, extending to the end of the event (determined by the length field of the Common-Header) </td> - <td>???TODO</td> + <td>Name of the binlog to rotate to.</td> </tr> </table> @@ -2841,10 +2902,316 @@ char *str_to_hex(char *to, const char *from, uint len); /** @class Table_map_log_event - Create a mapping from a (database name, table name) couple to a table - identifier (an integer number). + In row-based mode, every row operation event is preceded by a + Table_map_log_event which maps a table definition to a number. The + table definition consists of database name, table name, and column + definitions. @section Table_map_log_event_binary_format Binary Format + + The Post-Header has the following components: + + <table> + <caption>Post-Header for Table_map_log_event</caption> + + <tr> + <th>Name</th> + <th>Format</th> + <th>Description</th> + </tr> + + <tr> + <td>table_id</td> + <td>6 bytes unsigned integer</td> + <td>The number that identifies the table.</td> + </tr> + + <tr> + <td>flags</td> + <td>2 byte bitfield</td> + <td>Reserved for future use; currently always 0.</td> + </tr> + + </table> + + The Body has the following components: + + <table> + <caption>Body for Table_map_log_event</caption> + + <tr> + <th>Name</th> + <th>Format</th> + <th>Description</th> + </tr> + + <tr> + <td>database_name</td> + <td>one byte string length, followed by null-terminated string</td> + <td>The name of the database in which the table resides. The name + is represented as a one byte unsigned integer representing the + number of bytes in the name, followed by length bytes containing + the database name, followed by a terminating 0 byte. (Note the + redundancy in the representation of the length.) </td> + </tr> + + <tr> + <td>table_name</td> + <td>one byte string length, followed by null-terminated string</td> + <td>The name of the table, encoded the same way as the database + name above.</td> + </tr> + + <tr> + <td>column_count</td> + <td>@ref packed_integer "Packed Integer"</td> + <td>The number of columns in the table, represented as a packed + variable-length integer.</td> + </tr> + + <tr> + <td>column_type</td> + <td>List of column_count 1 byte enumeration values</td> + <td>The type of each column in the table, listed from left to + right. Each byte is mapped to a column type according to the + enumeration type enum_field_types defined in mysql_com.h. The + mapping of types to numbers is listed in the table @ref + Table_table_map_log_event_column_types "below" (along with + description of the associated metadata field). </td> + </tr> + + <tr> + <td>metadata_length</td> + <td>@ref packed_integer "Packed Integer"</td> + <td>The length of the following metadata block</td> + </tr> + + <tr> + <td>metadata</td> + <td>list of metadata for each column</td> + <td>For each column from left to right, a chunk of data who's + length and semantics depends on the type of the column. The + length and semantics for the metadata for each column are listed + in the table @ref Table_table_map_log_event_column_types + "below".</td> + </tr> + + <tr> + <td>null_bits</td> + <td>column_count bits, rounded up to nearest byte</td> + <td>For each column, a bit indicating whether data in the column + can be NULL or not. The number of bytes needed for this is + int((column_count+7)/8). The flag for the first column from the + left is in the least-significant bit of the first byte, the second + is in the second least significant bit of the first byte, the + ninth is in the least significant bit of the second byte, and so + on. </td> + </tr> + + </table> + + The table below lists all column types, along with the numerical + identifier for it and the size and interpretation of meta-data used + to describe the type. + + @anchor Table_table_map_log_event_column_types + <table> + <caption>Table_map_log_event column types: numerical identifier and + metadata</caption> + <tr> + <th>Name</th> + <th>Identifier</th> + <th>Size of metadata in bytes</th> + <th>Description of metadata</th> + </tr> + + <tr> + <td>MYSQL_TYPE_DECIMAL</td><td>0</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_TINY</td><td>1</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_SHORT</td><td>2</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_LONG</td><td>3</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_FLOAT</td><td>4</td> + <td>1 byte</td> + <td>1 byte unsigned integer, representing the "pack_length", which + is equal to sizeof(float) on the server from which the event + originates.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_DOUBLE</td><td>5</td> + <td>1 byte</td> + <td>1 byte unsigned integer, representing the "pack_length", which + is equal to sizeof(double) on the server from which the event + originates.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_NULL</td><td>6</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_TIMESTAMP</td><td>7</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_LONGLONG</td><td>8</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_INT24</td><td>9</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_DATE</td><td>10</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_TIME</td><td>11</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_DATETIME</td><td>12</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_YEAR</td><td>13</td> + <td>0</td> + <td>No column metadata.</td> + </tr> + + <tr> + <td><i>MYSQL_TYPE_NEWDATE</i></td><td><i>14</i></td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td>MYSQL_TYPE_VARCHAR</td><td>15</td> + <td>2 bytes</td> + <td>2 byte unsigned integer representing the maximum length of + the string.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_BIT</td><td>16</td> + <td>2 bytes</td> + <td>A 1 byte unsigned int representing the length in bits of the + bitfield (0 to 64), followed by a 1 byte unsigned int + representing the number of bytes occupied by the bitfield. The + number of bytes is either int((length+7)/8) or int(length/8).</td> + </tr> + + <tr> + <td>MYSQL_TYPE_NEWDECIMAL</td><td>246</td> + <td>2 bytes</td> + <td>A 1 byte unsigned int representing the precision, followed + by a 1 byte unsigned int representing the number of decimals.</td> + </tr> + + <tr> + <td><i>MYSQL_TYPE_ENUM</i></td><td><i>247</i></td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td><i>MYSQL_TYPE_SET</i></td><td><i>248</i></td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td>MYSQL_TYPE_TINY_BLOB</td><td>249</td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td><i>MYSQL_TYPE_MEDIUM_BLOB</i></td><td><i>250</i></td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td><i>MYSQL_TYPE_LONG_BLOB</i></td><td><i>251</i></td> + <td>–</td> + <td><i>This enumeration value is only used internally and cannot + exist in a binlog.</i></td> + </tr> + + <tr> + <td>MYSQL_TYPE_BLOB</td><td>252</td> + <td>1 byte</td> + <td>The pack length, i.e., the number of bytes needed to represent + the length of the blob: 1, 2, 3, or 4.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_VAR_STRING</td><td>253</td> + <td>2 bytes</td> + <td>This is used to store both strings and enumeration values. + The first byte is a enumeration value storing the <i>real + type</i>, which may be either MYSQL_TYPE_VAR_STRING or + MYSQL_TYPE_ENUM. The second byte is a 1 byte unsigned integer + representing the field size, i.e., the number of bytes needed to + store the length of the string.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_STRING</td><td>254</td> + <td>2 bytes</td> + <td>The first byte is always MYSQL_TYPE_VAR_STRING (i.e., 253). + The second byte is the field size, i.e., the number of bytes in + the representation of size of the string: 3 or 4.</td> + </tr> + + <tr> + <td>MYSQL_TYPE_GEOMETRY</td><td>255</td> + <td>1 byte</td> + <td>The pack length, i.e., the number of bytes needed to represent + the length of the geometry: 1, 2, 3, or 4.</td> + </tr> + + </table> */ class Table_map_log_event : public Log_event { @@ -3131,6 +3498,8 @@ protected: ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT); int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols, &m_curr_row_end, &m_master_reclength); + if (m_curr_row_end > m_rows_end) + my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0)); ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT); return result; } @@ -3408,7 +3777,7 @@ protected: <caption>Incident event format</caption> <tr> <th>Symbol</th> - <th>Size<br/>(bytes)</th> + <th>Format</th> <th>Description</th> </tr> <tr> diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 7621bdc6291..13f9763debe 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -2078,7 +2078,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli, /* fill table->record[0] with default values */ - if ((error= prepare_record(rli, table, m_width, + if ((error= prepare_record(table, m_width, TRUE /* check if columns have def. values */))) DBUG_RETURN(error); @@ -2289,7 +2289,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) /* unpack row - missing fields get default values */ // TODO: shall we check and report errors here? - prepare_record(NULL,table,m_width,FALSE /* don't check errors */); + prepare_record(table, m_width, FALSE /* don't check errors */); error= unpack_current_row(rli); #ifndef DBUG_OFF diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index eb32897f937..7c74dcba5a0 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -307,17 +307,15 @@ unpack_row(Relay_log_info const *rli, If @c check is true, fields are explicitly initialized only if they have default value or can be NULL. Otherwise error is reported. - @param log Used to report errors. @param table Table whose record[0] buffer is prepared. @param skip Number of columns for which default value initialization should be skipped. @param check Indicates if errors should be checked when setting default values. - @returns 0 on success. + @returns 0 on success or a handler level error code */ -int prepare_record(const Slave_reporting_capability *const log, - TABLE *const table, +int prepare_record(TABLE *const table, const uint skip, const bool check) { DBUG_ENTER("prepare_record"); @@ -337,14 +335,8 @@ int prepare_record(const Slave_reporting_capability *const log, if (check && ((f->flags & mask) == mask)) { - DBUG_ASSERT(log); - error= ER_NO_DEFAULT_FOR_FIELD; - log->report(ERROR_LEVEL, error, - "Field `%s` of table `%s`.`%s` " - "has no default value and cannot be NULL", - f->field_name, table->s->db.str, - table->s->table_name.str); - my_error(error, MYF(0), f->field_name); + my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), f->field_name); + error = HA_ERR_ROWS_EVENT_APPLY; } else f->set_default(); diff --git a/sql/rpl_record.h b/sql/rpl_record.h index 0d6ceda7433..f9e64f0ab1d 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -30,8 +30,7 @@ int unpack_row(Relay_log_info const *rli, uchar const **const row_end, ulong *const master_reclength); // Fill table's record[0] with default values. -int prepare_record(const Slave_reporting_capability *const, TABLE *const, - const uint =0, const bool =FALSE); +int prepare_record(TABLE *const, const uint =0, const bool =FALSE); #endif #endif diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 3e9a484126a..03f790b934f 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -33,7 +33,10 @@ Relay_log_info::Relay_log_info() :Slave_reporting_capability("SQL"), no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), - group_relay_log_pos(0), + group_relay_log_pos(0), event_relay_log_pos(0), +#if HAVE_purify + is_fake(FALSE), +#endif cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_thd(0), diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index a3a57ad4ce9..36daffae1af 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -154,6 +154,10 @@ public: ulonglong event_relay_log_pos; ulonglong future_event_relay_log_pos; +#ifdef HAVE_purify + bool is_fake; /* Mark that this is a fake relay log info structure */ +#endif + /* Original log name and position of the group we're currently executing (whose coordinates are group_relay_log_name/pos in the relay log) diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 026a0023660..ee3a7e6080a 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6119,3 +6119,5 @@ ER_SLAVE_AMBIGOUS_EXEC_MODE ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement." +ER_SLAVE_CORRUPT_EVENT + eng "Corrupted replication event was detected" diff --git a/sql/slave.cc b/sql/slave.cc index 4ffc2023e85..ea0dde942da 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1337,14 +1337,15 @@ bool show_master_info(THD* thd, Master_info* mi) protocol->prepare_for_resend(); /* - TODO: we read slave_running without run_lock, whereas these variables - are updated under run_lock and not data_lock. In 5.0 we should lock - run_lock on top of data_lock (with good order). + slave_running can be accessed without run_lock but not other + non-volotile members like mi->io_thd, which is guarded by the mutex. */ + pthread_mutex_lock(&mi->run_lock); + protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin); + pthread_mutex_unlock(&mi->run_lock); + pthread_mutex_lock(&mi->data_lock); pthread_mutex_lock(&mi->rli.data_lock); - - protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin); protocol->store(mi->host, &my_charset_bin); protocol->store(mi->user, &my_charset_bin); protocol->store((uint32) mi->port); @@ -1892,14 +1893,21 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli, 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)); +#ifdef HAVE_purify + if (!rli->is_fake) +#endif + { +#ifndef DBUG_OFF + char buf[22]; +#endif + 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. @@ -1909,6 +1917,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli, */ if (error) { + char buf[22]; rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, "It was not possible to update the positions" " of the relay log information: the slave may" diff --git a/sql/slave.h b/sql/slave.h index f1772bbc1fc..b4f1b7f7467 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -80,8 +80,8 @@ class Master_info; mi->rli does not either. In Master_info: run_lock, data_lock - run_lock protects all information about the run state: slave_running, and the - existence of the I/O thread (to stop/start it, you need this mutex). + run_lock protects all information about the run state: slave_running, thd + and the existence of the I/O thread to stop/start it, you need this mutex). data_lock protects some moving members of the struct: counters (log name, position) and relay log (MYSQL_BIN_LOG object). diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 04f408453ea..cff4ceeccf9 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -56,6 +56,9 @@ void mysql_client_binlog_statement(THD* thd) if (!thd->rli_fake) { thd->rli_fake= new Relay_log_info; +#ifdef HAVE_purify + thd->rli_fake->is_fake= TRUE; +#endif have_fd_event= FALSE; } if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec) @@ -152,14 +155,13 @@ void mysql_client_binlog_statement(THD* thd) */ if (!have_fd_event) { - if (bufptr[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) + int type = bufptr[EVENT_TYPE_OFFSET]; + if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3) 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])); + MYF(0), Log_event::get_type_str((Log_event_type)type)); goto end; } } |