diff options
Diffstat (limited to 'client/mysqlbinlog.cc')
-rw-r--r-- | client/mysqlbinlog.cc | 305 |
1 files changed, 217 insertions, 88 deletions
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index ba030379792..b1fdb3722d8 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -14,11 +14,27 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + + TODO: print the catalog (some USE catalog.db ????). + + Standalone program to read a MySQL binary log (or relay log); + can read files produced by 3.23, 4.x, 5.0 servers. + + Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0. + Should be able to read any file of these categories, even with --position. + An important fact: the Format_desc event of the log is at most the 3rd event + of the log; if it is the 3rd then there is this combination: + Format_desc_of_slave, Rotate_of_master, Format_desc_of_master. +*/ + #define MYSQL_CLIENT #undef MYSQL_SERVER #include "client_priv.h" #include <time.h> #include "log_event.h" +/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ +#include "mysql_priv.h" #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -58,6 +74,14 @@ static short binlog_flags = 0; static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 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(). +*/ +Format_description_log_event* description_event; + static int dump_local_log_entries(const char* logname); static int dump_remote_log_entries(const char* logname); static int dump_log_entries(const char* logname); @@ -303,8 +327,8 @@ Create_file event for file_id: %u\n",ae->file_id); Load_log_processor load_processor; -int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, - my_off_t pos, int old_format) +int process_event(ulonglong *rec_count, LAST_EVENT_INFO *last_event_info, + Log_event *ev, my_off_t pos) { char ll_buff[21]; DBUG_ENTER("process_event"); @@ -322,7 +346,7 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, if ((log_dbname != NULL) && (strcmp(log_dbname, database))) goto end; } - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); break; case CREATE_FILE_EVENT: { @@ -346,8 +370,9 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' below. */ - ce->print(result_file, short_form, last_db, TRUE); - if (!old_format) + ce->print(result_file, short_form, last_event_info, TRUE); + // If this binlog is not 3.23 ; why this test?? + if (description_event->binlog_version >= 3) { if (load_processor.process(ce)) break; // Error @@ -356,13 +381,13 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, break; } case APPEND_BLOCK_EVENT: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); if (load_processor.process((Append_block_log_event*) ev)) break; // Error break; case EXEC_LOAD_EVENT: { - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); Execute_load_log_event *exv= (Execute_load_log_event*)ev; Create_file_log_event *ce= load_processor.grab_event(exv->file_id); /* @@ -372,7 +397,7 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, */ if (ce) { - ce->print(result_file, short_form, last_db, TRUE); + ce->print(result_file, short_form, last_event_info, TRUE); my_free((char*)ce->fname,MYF(MY_WME)); delete ce; } @@ -381,8 +406,32 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, Create_file event for file_id: %u\n",exv->file_id); break; } + case FORMAT_DESCRIPTION_EVENT: + delete description_event; + description_event= (Format_description_log_event*) ev; + ev->print(result_file, short_form, last_event_info); + /* + We don't want this event to be deleted now, so let's hide it (I + (Guilhem) should later see if this triggers a non-serious Valgrind + error). Not serious error, because we will free description_event + later. + */ + ev= 0; + break; + case ROTATE_EVENT: + /* see comments in sql/slave.cc:process_io_rotate() */ + if (description_event->binlog_version >= 4) + { + delete description_event; + /* start from format 3 (MySQL 4.0) again */ + description_event= new Format_description_log_event(3); + if (!description_event || !description_event->is_valid()) + die("Invalid Format_description log event; could be out of memory"); + } + ev->print(result_file, short_form, last_event_info); + break; default: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, last_event_info); } } @@ -493,7 +542,7 @@ static void die(const char* fmt, ...) static void print_version() { - printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); + printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); NETWARE_SET_SCREEN_MODE(1); } @@ -601,7 +650,6 @@ static MYSQL* safe_connect() return local_mysql; } - static int dump_log_entries(const char* logname) { if (remote_opt) @@ -609,13 +657,17 @@ static int dump_log_entries(const char* logname) return dump_local_log_entries(logname); } - -static int check_master_version(MYSQL* mysql) +/* + 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. +*/ +static int check_master_version(MYSQL* mysql, + Format_description_log_event + **description_event) { MYSQL_RES* res = 0; MYSQL_ROW row; const char* version; - int old_format = 0; if (mysql_query(mysql, "SELECT VERSION()") || !(res = mysql_store_result(mysql))) @@ -640,11 +692,18 @@ static int check_master_version(MYSQL* mysql) switch (*version) { case '3': - old_format = 1; + *description_event= new Format_description_log_event(1); break; case '4': + *description_event= new Format_description_log_event(3); case '5': - old_format = 0; + /* + The server is soon going to send us its Format_description log + event, unless it is a 5.0 server with 3.23 or 4.0 binlogs. + 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); break; default: sql_print_error("Master reported unrecognized MySQL version '%s'", @@ -654,27 +713,36 @@ static int check_master_version(MYSQL* mysql) return 1; } mysql_free_result(res); - return old_format; + return 0; } static int dump_remote_log_entries(const char* logname) + { char buf[128]; - char last_db[FN_REFLEN+1] = ""; + LAST_EVENT_INFO last_event_info; uint len, logname_len; NET* net = &mysql->net; - int old_format; DBUG_ENTER("dump_remote_log_entries"); - old_format = check_master_version(mysql); + if (check_master_version(mysql, &description_event)) + { + fprintf(stderr, "Could not find server version"); + DBUG_RETURN(1); + } + if (!description_event || !description_event->is_valid()) + { + fprintf(stderr, "Invalid Format_description log event; \ +could be out of memory"); + DBUG_RETURN(1); + } if (!position) - position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam + position = BIN_LOG_HEADER_SIZE; if (position < BIN_LOG_HEADER_SIZE) { position = BIN_LOG_HEADER_SIZE; - // warn the user sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE); } int4store(buf, position); @@ -707,7 +775,8 @@ static int dump_remote_log_entries(const char* logname) DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", len, net->read_pos[5])); Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 , - len - 1, &error, old_format); + len - 1, &error, + description_event); if (!ev) { fprintf(stderr, "Could not construct log event object\n"); @@ -715,25 +784,26 @@ static int dump_remote_log_entries(const char* logname) } Log_event_type type= ev->get_type_code(); - if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT)) + if (description_event->binlog_version >=3 || (type != LOAD_EVENT && type != CREATE_FILE_EVENT)) { - if (ev->get_type_code() == ROTATE_EVENT) + /* + If this is a Rotate event, maybe it's the end of the requested binlog; + in this case we are done (stop transfer). + This is suitable for binlogs, not relay logs (but for now we don't read + relay logs remotely because the server is not able to do that). If one + day we read relay logs remotely, then we will have a problem with the + detection below: relay logs contain Rotate events which are about the + binlogs, so which would trigger the end-detection below. + */ + if (type == ROTATE_EVENT) { Rotate_log_event *rev= (Rotate_log_event *)ev; /* - mysqld is sending us all its binlogs after the requested one, but we - don't want them. If this is a fake Rotate event, and not about our log, we can stop transfer. If this a real Rotate event (so it's not about our log, it's in our log describing the next log), we print it (because it's part of our log) and then we will stop when we receive the fake one soon. - This is suitable for binlogs, not relay logs (but for now we don't - read relay logs remotely because the server is not able to do - that). If one day we read relay logs remotely, then we will have a - problem with the detection below: relay logs contain Rotate events - which are about the binlogs, so which would trigger the end-detection - below. */ if ((rev->when == 0) && !to_last_remote_log) { @@ -742,15 +812,13 @@ static int dump_remote_log_entries(const char* logname) DBUG_RETURN(0); /* Otherwise, this is a fake Rotate for our log, at the very beginning - for sure. Skip it, because it was not in the original log. If we - are running with to_last_remote_log, we print it, because it serves - as a useful marker between binlogs then. + for sure. Skip it. */ continue; } } - if (process_event(&rec_count,last_db,ev,old_off,old_format)) - DBUG_RETURN(1); + if (process_event(&rec_count,&last_event_info,ev,old_off)) + DBUG_RETURN(1); } else { @@ -758,23 +826,22 @@ static int dump_remote_log_entries(const char* logname) const char *old_fname= le->fname; uint old_len= le->fname_len; File file; - + if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) - DBUG_RETURN(1); - - if (process_event(&rec_count,last_db,ev,old_off,old_format)) + DBUG_RETURN(1); + + if (process_event(&rec_count,&last_event_info,ev,old_off)) { - my_close(file,MYF(MY_WME)); - DBUG_RETURN(1); + my_close(file,MYF(MY_WME)); + DBUG_RETURN(1); } if (load_processor.load_old_format_file(net,old_fname,old_len,file)) { - my_close(file,MYF(MY_WME)); - DBUG_RETURN(1); + my_close(file,MYF(MY_WME)); + DBUG_RETURN(1); } my_close(file,MYF(MY_WME)); } - /* Let's adjust offset for remote log as for local log to produce similar text. As we don't print the fake Rotate event, all events are @@ -786,30 +853,98 @@ static int dump_remote_log_entries(const char* logname) } -static int check_header(IO_CACHE* file) +static void check_header(IO_CACHE* file, + Format_description_log_event **description_event) { byte header[BIN_LOG_HEADER_SIZE]; byte buf[PROBE_HEADER_LEN]; - int old_format=0; - DBUG_ENTER("check_header"); + *description_event= new Format_description_log_event(3); + my_off_t tmp_pos; my_off_t pos = my_b_tell(file); my_b_seek(file, (my_off_t)0); if (my_b_read(file, header, sizeof(header))) die("Failed reading header; Probably an empty file"); if (memcmp(header, BINLOG_MAGIC, sizeof(header))) die("File is not a binary log file"); - if (!my_b_read(file, buf, sizeof(buf))) + + /* + Imagine we are running with --position=1000. We still need to know the + binlog format's. So we still need to find, if there is one, the Format_desc + event, or to know if this is a 3.23 binlog. So we need to first read the + first events of the log, those around offset 4. + Even if we are reading a 3.23 binlog from the start (no --position): we need + to know the header length (which is 13 in 3.23, 19 in 4.x) to be able to + successfully print the first event (Start_log_event_v3). So even in this + case, we need to "probe" the first bytes of the log *before* we do a real + read_log_event(). Because read_log_event() needs to know the header's length + to work fine. + */ + for(;;) { - if (buf[4] == START_EVENT) + tmp_pos= my_b_tell(file); /* should be 4 the first time */ + if (my_b_read(file, buf, sizeof(buf))) { - uint event_len; - event_len = uint4korr(buf + EVENT_LEN_OFFSET); - old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN)); + if (file->error) + die("\ +Could not read entry at offset %lu : Error in log format or read error", + tmp_pos); + /* + Otherwise this is just EOF : this log currently contains 0-2 events. + Maybe it's going to be filled in the next milliseconds; then we are + going to have a problem if this a 3.23 log (imagine we are locally + reading a 3.23 binlog which is being written presently): we won't know + it in read_log_event() and will fail(). + Similar problems could happen with hot relay logs if --position is used + (but a --position which is posterior to the current size of the log). + These are rare problems anyway (reading a hot log + when we read the + first events there are not all there yet + when we read a bit later + there are more events + using a strange --position). + */ + break; + } + else + { + DBUG_PRINT("info",("buf[4]=%d", buf[4])); + /* always test for a Start_v3, even if no --position */ + if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */ + { + if (uint4korr(buf + EVENT_LEN_OFFSET) < + (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); + } + break; + } + else if (tmp_pos>=position) + break; + else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */ + { + my_b_seek(file, tmp_pos); /* seek back to event's start */ + if (!(*description_event= (Format_description_log_event*) + Log_event::read_log_event(file, *description_event))) + /* EOF can't be hit here normally, so it's a real error */ + 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); + DBUG_PRINT("info",("Setting description_event")); + } + else if (buf[4] == ROTATE_EVENT) + { + my_b_seek(file, tmp_pos); /* seek back to event's start */ + if (!Log_event::read_log_event(file, *description_event)) + /* EOF can't be hit here normally, so it's a real error */ + die("Could not read a Rotate_log_event event \ +at offset %lu ; this could be a log format error or read error", + tmp_pos); + } + else + break; } } my_b_seek(file, pos); - DBUG_RETURN(old_format); } @@ -818,13 +953,10 @@ static int dump_local_log_entries(const char* logname) File fd = -1; IO_CACHE cache,*file= &cache; ulonglong rec_count = 0; - char last_db[FN_REFLEN+1]; + LAST_EVENT_INFO last_event_info; byte tmp_buff[BIN_LOG_HEADER_SIZE]; - bool old_format = 0; int error= 0; - last_db[0]= 0; - if (logname && logname[0] != '-') { if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) @@ -833,16 +965,16 @@ static int dump_local_log_entries(const char* logname) MYF(MY_WME | MY_NABP))) { my_close(fd, MYF(MY_WME)); - exit(1); + return 1; } - old_format = check_header(file); + check_header(file, &description_event); } - else + else // reading from stdin; TODO: check that it works { if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0, 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) return 1; - old_format = check_header(file); + check_header(file, &description_event); if (position) { /* skip 'position' characters from stdout */ @@ -852,56 +984,53 @@ static int dump_local_log_entries(const char* logname) { tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) - { - error= 1; - goto end; - } + { + error= 1; + goto end; + } } } file->pos_in_file=position; file->seek_not_done=0; } - if (!position) + if (!description_event || !description_event->is_valid()) + die("Invalid Format_description log event; could be out of memory"); + + if (!position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) { - // Skip header - if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) - { - error= 1; - goto end; - } + error= 1; + goto end; } - for (;;) { char llbuff[21]; my_off_t old_off = my_b_tell(file); - Log_event* ev = Log_event::read_log_event(file, old_format); + Log_event* ev = Log_event::read_log_event(file, description_event); if (!ev) { 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; - } - // file->error == 0 means EOF, that's OK, we break in this case - break; - } - if (process_event(&rec_count,last_db,ev,old_off,false)) - { + "Could not read entry at offset %s:" + "Error in log format or read error\n", + llstr(old_off,llbuff)); error= 1; + // file->error == 0 means EOF, that's OK, we break in this case break; } + if (process_event(&rec_count,&last_event_info,ev,old_off)) + { + error= 1; + break; + } } end: if (fd >= 0) my_close(fd, MYF(MY_WME)); end_io_cache(file); + delete description_event; return error; } |