diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.am | 2 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 162 |
2 files changed, 134 insertions, 30 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 2c54ec45989..612a5f01f8d 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -39,7 +39,7 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) mysqlmanagerc_SOURCES = mysqlmanagerc.c mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -sql_src=log_event.h log_event.cc +sql_src=log_event.h mysql_priv.h log_event.cc # Fix for mit-threads DEFS = -DUNDEF_THREADS_HACK diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 35f0db76ad6..90a1526c2c7 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -14,12 +14,28 @@ 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 <assert.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) @@ -481,21 +497,26 @@ static int check_master_version(MYSQL* mysql) } +/* + TODO fix this for new format (like local log); this will be done when 4.0 is + merged here (Victor's fixes are needed to make dump_remote_log_entries() + work). +*/ + static void dump_remote_log_entries(const char* logname) { char buf[128]; - char last_db[FN_REFLEN+1] = ""; + LAST_EVENT_INFO last_event_info; uint len; NET* net = &mysql->net; int old_format; old_format = check_master_version(mysql); 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 guity 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); @@ -517,10 +538,11 @@ static void 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, 0); + //TODO this ,0) : we need to store the description_event like for local_log if (ev) { - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, &last_event_info); if (ev->get_type_code() == LOAD_EVENT) dump_remote_file(net, ((Load_log_event*)ev)->fname); delete ev; @@ -531,29 +553,98 @@ static void 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; + *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); - return old_format; } @@ -562,11 +653,15 @@ static void 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; - - last_db[0]=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; if (logname && logname[0] != '-') { @@ -575,14 +670,14 @@ static void dump_local_log_entries(const char* logname) if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, MYF(MY_WME | MY_NABP))) exit(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))) exit(1); - old_format = check_header(file); + check_header(file, &description_event); if (position) { /* skip 'position' characters from stdout */ @@ -599,6 +694,9 @@ static void dump_local_log_entries(const char* logname) file->seek_not_done=0; } + if (!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 for (;;) @@ -606,7 +704,7 @@ static void dump_local_log_entries(const char* logname) 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) @@ -633,7 +731,7 @@ Could not read entry at offset %s : Error in log format or read error", continue; // next } } - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, &last_event_info); break; case CREATE_FILE_EVENT: { @@ -661,18 +759,18 @@ Could not read entry at offset %s : Error in log format or read error", filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' below. */ - ce->print(result_file, short_form, last_db, true); + ce->print(result_file, short_form, &last_event_info, true); load_processor.process(ce); ev= 0; break; } case APPEND_BLOCK_EVENT: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, &last_event_info); load_processor.process((Append_block_log_event*)ev); 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); /* @@ -682,7 +780,7 @@ Could not read entry at offset %s : Error in log format or read error", */ 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; } @@ -691,17 +789,23 @@ Could not read entry at offset %s : Error in log format or read error", 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); + break; default: - ev->print(result_file, short_form, last_db); + ev->print(result_file, short_form, &last_event_info); } } rec_count++; - if (ev) - delete ev; + if (ev && ev->get_type_code()!=FORMAT_DESCRIPTION_EVENT) + delete ev; /* otherwise, deleted in the end */ } if (fd >= 0) my_close(fd, MYF(MY_WME)); end_io_cache(file); + delete description_event; } |