mirror of
https://github.com/MariaDB/server.git
synced 2025-10-31 15:50:51 +03:00
This will be pushed only after I fix the testsuite.
This is the main commit for Worklog tasks: * A more dynamic binlog format which allows small changes (1064) * Log session variables in Query_log_event (1063) Below 5.0 means 5.0.0. MySQL 5.0 is able to replicate FOREIGN_KEY_CHECKS, UNIQUE_KEY_CHECKS (for speed), SQL_AUTO_IS_NULL, SQL_MODE. Not charsets (WL#1062), not some vars (I can only think of SQL_SELECT_LIMIT, which deserves a special treatment). Note that this works for queries, except LOAD DATA INFILE (for this it would have to wait for Dmitri's push of WL#874, which in turns waits for the present push, so... the deadlock must be broken!). Note that when Dmitri pushes WL#874 in 5.0.1, 5.0.0 won't be able to replicate a LOAD DATA INFILE from 5.0.1. Apart from that, the new binlog format is designed so that it can tolerate a little variation in the events (so that a 5.0.0 slave could replicate a 5.0.1 master, except for LOAD DATA INFILE unfortunately); that is, when I later add replication of charsets it should break nothing. And when I later add a UID to every event, it should break nothing. The main change brought by this patch is a new type of event, Format_description_log_event, which describes some lengthes in other event types. This event is needed for the master/slave/mysqlbinlog to understand a 5.0 log. Thanks to this event, we can later add more bytes to the header of every event without breaking compatibility. Inside Query_log_event, we have some additional dynamic format, as every Query_log_event can have a different number of status variables, stored as pairs (code, value); that's how SQL_MODE and session variables and catalog are stored. Like this, we can later add count of affected rows, charsets... and we can have options --don't-log-count-affected-rows if we want. MySQL 5.0 is able to run on 4.x relay logs, 4.x binlogs. Upgrading a 4.x master to 5.0 is ok (no need to delete binlogs), upgrading a 4.x slave to 5.0 is ok (no need to delete relay logs); so both can be "hot" upgrades. Upgrading a 3.23 master to 5.0 requires as much as upgrading it to 4.0. 3.23 and 4.x can't be slaves of 5.0. So downgrading from 5.0 to 4.x may be complicated. Log_event::log_pos is now the position of the end of the event, which is more useful than the position of the beginning. We take care about compatibility with <5.0 (in which log_pos is the beginning). I added a short test for replication of SQL_MODE and some other variables. TODO: - after committing this, merge the latest 5.0 into it - fix all tests - update the manual with upgrade notes.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user