1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

Mainly resolving Guilhem's 4.1 patch to 5.0

This commit is contained in:
brian@avenger.(none)
2004-11-11 19:01:46 -08:00
parent f05daf02ba
commit 3e4ca194f8
8 changed files with 5390 additions and 1345 deletions

View File

@ -14,11 +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
--start-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 <my_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)
@ -66,6 +83,14 @@ static MYSQL* mysql = NULL;
static const char* dirname_for_local_load= 0;
static bool stop_passed= 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);
@ -305,8 +330,8 @@ int Load_log_processor::process(Append_block_log_event *ae)
/*
There is no Create_file event (a bad binlog or a big
--position). Assuming it's a big --position, we just do nothing and
print a warning.
--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);
@ -316,30 +341,49 @@ Create_file event for file_id: %u\n",ae->file_id);
Load_log_processor load_processor;
/*
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.
*/
int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
int process_event(LAST_EVENT_INFO *last_event_info, Log_event *ev,
my_off_t pos)
{
char ll_buff[21];
Log_event_type ev_type= ev->get_type_code();
DBUG_ENTER("process_event");
/*
Format events are not concerned by --offset and such, we always need to
read them to be able to process the wanted events.
*/
if ((rec_count >= offset) &&
((my_time_t)(ev->when) >= start_datetime))
((my_time_t)(ev->when) >= start_datetime) ||
(ev_type == FORMAT_DESCRIPTION_EVENT))
{
/*
We have found an event after start_datetime, from now on print
everything (in case the binlog has timestamps increasing and decreasing,
we do this to avoid cutting the middle).
*/
start_datetime= 0;
offset= 0; // print everything and protect against cycling rec_count
if (ev_type != FORMAT_DESCRIPTION_EVENT)
{
/*
We have found an event after start_datetime, from now on print
everything (in case the binlog has timestamps increasing and
decreasing, we do this to avoid cutting the middle).
*/
start_datetime= 0;
offset= 0; // print everything and protect against cycling rec_count
}
if (((my_time_t)(ev->when) >= stop_datetime)
|| (pos >= stop_position_mot))
{
@ -349,7 +393,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
if (!short_form)
fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
switch (ev->get_type_code()) {
switch (ev_type) {
case QUERY_EVENT:
if (one_database)
{
@ -357,7 +401,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
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:
{
@ -381,8 +425,9 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
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
@ -391,23 +436,23 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
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);
/*
if ce is 0, it probably means that we have not seen the Create_file
event (a bad binlog, or most probably --position is after the
event (a bad binlog, or most probably --start-position is after the
Create_file event). Print a warning comment.
*/
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;
}
@ -416,8 +461,20 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
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;
default:
ev->print(result_file, short_form, last_db);
ev->print(result_file, short_form, last_event_info);
}
}
@ -558,7 +615,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);
}
@ -701,12 +758,17 @@ static int dump_log_entries(const char* 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)))
@ -731,11 +793,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'",
@ -745,18 +814,17 @@ 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] = "";
ulong len;
uint logname_len;
LAST_EVENT_INFO last_event_info;
uint len, logname_len;
NET* net;
int old_format;
int error= 0;
my_off_t old_off= start_position_mot;
char fname[FN_REFLEN+1];
@ -769,7 +837,18 @@ static int dump_remote_log_entries(const char* logname)
*/
mysql= safe_connect();
net= &mysql->net;
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);
}
/*
COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
@ -798,6 +877,8 @@ static int dump_remote_log_entries(const char* logname)
for (;;)
{
const char *error_msg;
Log_event *ev;
len = net_safe_read(mysql);
if (len == packet_error)
{
@ -810,9 +891,9 @@ static int dump_remote_log_entries(const char* logname)
break; // end of data
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_msg, old_format);
if (!ev)
if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 ,
len - 1, &error_msg,
description_event)))
{
fprintf(stderr, "Could not construct log event object\n");
error= 1;
@ -820,25 +901,27 @@ 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)
{
@ -861,7 +944,7 @@ static int dump_remote_log_entries(const char* logname)
len= 1; // fake Rotate, so don't increment old_off
}
}
if ((error= process_event(last_db,ev,old_off,old_format)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
error= ((error < 0) ? 0 : 1);
goto err;
@ -873,64 +956,135 @@ 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)
{
error= 1;
goto err;
}
if ((error= process_event(last_db,ev,old_off,old_format)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
my_close(file,MYF(MY_WME));
my_close(file,MYF(MY_WME));
error= ((error < 0) ? 0 : 1);
goto err;
}
if (load_processor.load_old_format_file(net,old_fname,old_len,file))
error= load_processor.load_old_format_file(net,old_fname,old_len,file);
my_close(file,MYF(MY_WME));
if (error)
{
my_close(file,MYF(MY_WME));
error= 1;
goto err;
}
my_close(file,MYF(MY_WME));
}
/*
Let's adjust offset for remote log as for local log to produce
similar text.
*/
old_off+= len-1;
}
err:
mysql_close(mysql);
DBUG_RETURN(error);
}
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");
my_off_t tmp_pos, pos;
my_off_t pos = my_b_tell(file);
*description_event= new Format_description_log_event(3);
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 --start-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 --start-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 --start-position is used (but a
--start-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
--start-position).
*/
break;
}
else
{
DBUG_PRINT("info",("buf[4]=%d", buf[4]));
/* always test for a Start_v3, even if no --start-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 >= start_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);
}
@ -938,13 +1092,10 @@ static int dump_local_log_entries(const char* logname)
{
File fd = -1;
IO_CACHE cache,*file= &cache;
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)
@ -953,16 +1104,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 (start_position)
{
/* skip 'start_position' characters from stdout */
@ -972,46 +1123,44 @@ 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= start_position_mot;
file->seek_not_done=0;
}
if (!start_position)
{
// Skip header
if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
{
error= 1;
goto end;
}
}
if (!description_event || !description_event->is_valid())
die("Invalid Format_description log event; could be out of memory");
if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
{
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;
"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 ((error= process_event(last_db,ev,old_off,false)))
if ((error= process_event(&last_event_info,ev,old_off)))
{
if (error < 0)
error= 0;
@ -1023,6 +1172,7 @@ end:
if (fd >= 0)
my_close(fd, MYF(MY_WME));
end_io_cache(file);
delete description_event;
return error;
}