Merge gbichot@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/mysql_src/mysql-5.0-new-binlog-format
This commit is contained in:
commit
540e94f9dd
@ -39,7 +39,7 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c
|
|||||||
mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
|
mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
|
||||||
mysqlmanagerc_SOURCES = mysqlmanagerc.c
|
mysqlmanagerc_SOURCES = mysqlmanagerc.c
|
||||||
mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
|
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
|
# Fix for mit-threads
|
||||||
DEFS = -DUNDEF_THREADS_HACK
|
DEFS = -DUNDEF_THREADS_HACK
|
||||||
|
@ -14,12 +14,28 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
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
|
#define MYSQL_CLIENT
|
||||||
#undef MYSQL_SERVER
|
#undef MYSQL_SERVER
|
||||||
#include "client_priv.h"
|
#include "client_priv.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "log_event.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 BIN_LOG_HEADER_SIZE 4
|
||||||
#define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+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)
|
static void dump_remote_log_entries(const char* logname)
|
||||||
{
|
{
|
||||||
char buf[128];
|
char buf[128];
|
||||||
char last_db[FN_REFLEN+1] = "";
|
LAST_EVENT_INFO last_event_info;
|
||||||
uint len;
|
uint len;
|
||||||
NET* net = &mysql->net;
|
NET* net = &mysql->net;
|
||||||
int old_format;
|
int old_format;
|
||||||
old_format = check_master_version(mysql);
|
old_format = check_master_version(mysql);
|
||||||
|
|
||||||
if (!position)
|
if (!position)
|
||||||
position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam
|
position = BIN_LOG_HEADER_SIZE;
|
||||||
if (position < BIN_LOG_HEADER_SIZE)
|
if (position < BIN_LOG_HEADER_SIZE)
|
||||||
{
|
{
|
||||||
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);
|
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);
|
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",
|
DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
|
||||||
len, net->read_pos[5]));
|
len, net->read_pos[5]));
|
||||||
Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
|
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)
|
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)
|
if (ev->get_type_code() == LOAD_EVENT)
|
||||||
dump_remote_file(net, ((Load_log_event*)ev)->fname);
|
dump_remote_file(net, ((Load_log_event*)ev)->fname);
|
||||||
delete ev;
|
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 header[BIN_LOG_HEADER_SIZE];
|
||||||
byte buf[PROBE_HEADER_LEN];
|
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_off_t pos = my_b_tell(file);
|
||||||
my_b_seek(file, (my_off_t)0);
|
my_b_seek(file, (my_off_t)0);
|
||||||
if (my_b_read(file, header, sizeof(header)))
|
if (my_b_read(file, header, sizeof(header)))
|
||||||
die("Failed reading header; Probably an empty file");
|
die("Failed reading header; Probably an empty file");
|
||||||
if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
|
if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
|
||||||
die("File is not a binary log file");
|
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;
|
if (file->error)
|
||||||
event_len = uint4korr(buf + EVENT_LEN_OFFSET);
|
die("\
|
||||||
old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
|
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);
|
my_b_seek(file, pos);
|
||||||
return old_format;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -562,11 +653,15 @@ static void dump_local_log_entries(const char* logname)
|
|||||||
File fd = -1;
|
File fd = -1;
|
||||||
IO_CACHE cache,*file= &cache;
|
IO_CACHE cache,*file= &cache;
|
||||||
ulonglong rec_count = 0;
|
ulonglong rec_count = 0;
|
||||||
char last_db[FN_REFLEN+1];
|
LAST_EVENT_INFO last_event_info;
|
||||||
byte tmp_buff[BIN_LOG_HEADER_SIZE];
|
byte tmp_buff[BIN_LOG_HEADER_SIZE];
|
||||||
bool old_format = 0;
|
/*
|
||||||
|
check_header() will set the pointer below.
|
||||||
last_db[0]=0;
|
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] != '-')
|
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,
|
if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
|
||||||
MYF(MY_WME | MY_NABP)))
|
MYF(MY_WME | MY_NABP)))
|
||||||
exit(1);
|
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,
|
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)))
|
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
|
||||||
exit(1);
|
exit(1);
|
||||||
old_format = check_header(file);
|
check_header(file, &description_event);
|
||||||
if (position)
|
if (position)
|
||||||
{
|
{
|
||||||
/* skip 'position' characters from stdout */
|
/* skip 'position' characters from stdout */
|
||||||
@ -599,6 +694,9 @@ static void dump_local_log_entries(const char* logname)
|
|||||||
file->seek_not_done=0;
|
file->seek_not_done=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!description_event->is_valid())
|
||||||
|
die("Invalid Format_description log event; could be out of memory");
|
||||||
|
|
||||||
if (!position)
|
if (!position)
|
||||||
my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE); // Skip header
|
my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE); // Skip header
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -606,7 +704,7 @@ static void dump_local_log_entries(const char* logname)
|
|||||||
char llbuff[21];
|
char llbuff[21];
|
||||||
my_off_t old_off = my_b_tell(file);
|
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 (!ev)
|
||||||
{
|
{
|
||||||
if (file->error)
|
if (file->error)
|
||||||
@ -633,7 +731,7 @@ Could not read entry at offset %s : Error in log format or read error",
|
|||||||
continue; // next
|
continue; // next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ev->print(result_file, short_form, last_db);
|
ev->print(result_file, short_form, &last_event_info);
|
||||||
break;
|
break;
|
||||||
case CREATE_FILE_EVENT:
|
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'
|
filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT'
|
||||||
below.
|
below.
|
||||||
*/
|
*/
|
||||||
ce->print(result_file, short_form, last_db, true);
|
ce->print(result_file, short_form, &last_event_info, true);
|
||||||
load_processor.process(ce);
|
load_processor.process(ce);
|
||||||
ev= 0;
|
ev= 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case APPEND_BLOCK_EVENT:
|
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);
|
load_processor.process((Append_block_log_event*)ev);
|
||||||
break;
|
break;
|
||||||
case EXEC_LOAD_EVENT:
|
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;
|
Execute_load_log_event *exv= (Execute_load_log_event*)ev;
|
||||||
Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
|
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)
|
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));
|
my_free((char*)ce->fname,MYF(MY_WME));
|
||||||
delete ce;
|
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);
|
Create_file event for file_id: %u\n",exv->file_id);
|
||||||
break;
|
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:
|
default:
|
||||||
ev->print(result_file, short_form, last_db);
|
ev->print(result_file, short_form, &last_event_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rec_count++;
|
rec_count++;
|
||||||
if (ev)
|
if (ev && ev->get_type_code()!=FORMAT_DESCRIPTION_EVENT)
|
||||||
delete ev;
|
delete ev; /* otherwise, deleted in the end */
|
||||||
}
|
}
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
my_close(fd, MYF(MY_WME));
|
my_close(fd, MYF(MY_WME));
|
||||||
end_io_cache(file);
|
end_io_cache(file);
|
||||||
|
delete description_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,6 +498,7 @@ typedef int (*qsort2_cmp)(const void *, const void *, const void *);
|
|||||||
|
|
||||||
/* tell write offset in the SEQ_APPEND cache */
|
/* tell write offset in the SEQ_APPEND cache */
|
||||||
my_off_t my_b_append_tell(IO_CACHE* info);
|
my_off_t my_b_append_tell(IO_CACHE* info);
|
||||||
|
my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
|
||||||
|
|
||||||
#define my_b_bytes_in_cache(info) (uint) (*(info)->current_end - \
|
#define my_b_bytes_in_cache(info) (uint) (*(info)->current_end - \
|
||||||
*(info)->current_pos)
|
*(info)->current_pos)
|
||||||
|
43
mysql-test/r/rpl_session_var.result
Normal file
43
mysql-test/r/rpl_session_var.result
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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;
|
||||||
|
drop table if exists t1;
|
||||||
|
Warnings:
|
||||||
|
Note 1051 Unknown table 't1'
|
||||||
|
create table t1(a varchar(10),b int);
|
||||||
|
set @@session.sql_mode=pipes_as_concat;
|
||||||
|
insert into t1 values('My'||'SQL', 1);
|
||||||
|
set @@session.sql_mode=default;
|
||||||
|
insert into t1 values('My'||'SQL', 2);
|
||||||
|
select * from t1 where b<3 order by a;
|
||||||
|
a b
|
||||||
|
0 2
|
||||||
|
MySQL 1
|
||||||
|
select * from t1 where b<3 order by a;
|
||||||
|
a b
|
||||||
|
0 2
|
||||||
|
MySQL 1
|
||||||
|
set @@session.sql_mode=ignore_space;
|
||||||
|
insert into t1 values(password ('MySQL'), 3);
|
||||||
|
set @@session.sql_mode=ansi_quotes;
|
||||||
|
create table "t2" ("a" int);
|
||||||
|
drop table t1, t2;
|
||||||
|
set @@session.sql_mode=default;
|
||||||
|
create table t1(a int auto_increment primary key);
|
||||||
|
create table t2(b int, a int);
|
||||||
|
set @@session.sql_auto_is_null=1;
|
||||||
|
insert into t1 values(null);
|
||||||
|
insert into t2 select 1,a from t1 where a is null;
|
||||||
|
set @@session.sql_auto_is_null=0;
|
||||||
|
insert into t1 values(null);
|
||||||
|
insert into t2 select 2,a from t1 where a is null;
|
||||||
|
select * from t2 order by b;
|
||||||
|
b a
|
||||||
|
1 1
|
||||||
|
select * from t2 order by b;
|
||||||
|
b a
|
||||||
|
1 1
|
||||||
|
drop table t1,t2;
|
42
mysql-test/t/rpl_session_var.test
Normal file
42
mysql-test/t/rpl_session_var.test
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Replication of session variables.
|
||||||
|
# FOREIGN_KEY_CHECKS is tested in rpl_insert_id.test
|
||||||
|
|
||||||
|
source include/master-slave.inc;
|
||||||
|
drop table if exists t1;
|
||||||
|
create table t1(a varchar(10),b int);
|
||||||
|
set @@session.sql_mode=pipes_as_concat;
|
||||||
|
insert into t1 values('My'||'SQL', 1);
|
||||||
|
set @@session.sql_mode=default;
|
||||||
|
insert into t1 values('My'||'SQL', 2);
|
||||||
|
select * from t1 where b<3 order by a;
|
||||||
|
save_master_pos;
|
||||||
|
connection slave;
|
||||||
|
sync_with_master;
|
||||||
|
select * from t1 where b<3 order by a;
|
||||||
|
connection master;
|
||||||
|
# if the slave does the next sync_with_master fine, then it means it accepts the
|
||||||
|
# two lines of ANSI syntax below, which is what we want to check.
|
||||||
|
set @@session.sql_mode=ignore_space;
|
||||||
|
insert into t1 values(password ('MySQL'), 3);
|
||||||
|
set @@session.sql_mode=ansi_quotes;
|
||||||
|
create table "t2" ("a" int);
|
||||||
|
drop table t1, t2;
|
||||||
|
set @@session.sql_mode=default;
|
||||||
|
create table t1(a int auto_increment primary key);
|
||||||
|
create table t2(b int, a int);
|
||||||
|
set @@session.sql_auto_is_null=1;
|
||||||
|
insert into t1 values(null);
|
||||||
|
insert into t2 select 1,a from t1 where a is null;
|
||||||
|
set @@session.sql_auto_is_null=0;
|
||||||
|
insert into t1 values(null);
|
||||||
|
insert into t2 select 2,a from t1 where a is null;
|
||||||
|
select * from t2 order by b;
|
||||||
|
save_master_pos;
|
||||||
|
connection slave;
|
||||||
|
sync_with_master;
|
||||||
|
select * from t2 order by b;
|
||||||
|
connection master;
|
||||||
|
drop table t1,t2;
|
||||||
|
save_master_pos;
|
||||||
|
connection slave;
|
||||||
|
sync_with_master;
|
@ -66,6 +66,13 @@ my_off_t my_b_append_tell(IO_CACHE* info)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my_off_t my_b_safe_tell(IO_CACHE *info)
|
||||||
|
{
|
||||||
|
if (unlikely(info->type == SEQ_READ_APPEND))
|
||||||
|
return my_b_append_tell(info);
|
||||||
|
return my_b_tell(info);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Make next read happen at the given position
|
Make next read happen at the given position
|
||||||
For write cache, make next write happen at the given position
|
For write cache, make next write happen at the given position
|
||||||
|
@ -987,10 +987,19 @@ innobase_commit_low(
|
|||||||
|
|
||||||
trx->mysql_master_log_file_name
|
trx->mysql_master_log_file_name
|
||||||
= active_mi->rli.group_master_log_name;
|
= active_mi->rli.group_master_log_name;
|
||||||
|
/*
|
||||||
|
Guilhem to Heikki: in 5.0 we don't need to do a computation
|
||||||
|
(old_pos+len) to get the end_pos, because we already have the
|
||||||
|
end_pos under hand in the replication code
|
||||||
|
(Query_log_event::exec_event()).
|
||||||
|
I tested the code change below (simulated a crash with kill
|
||||||
|
-9) and got the good (binlog, position) displayed by InnoDB at
|
||||||
|
crash recovery, so this code change is ok.
|
||||||
|
*/
|
||||||
trx->mysql_master_log_pos = ((ib_longlong)
|
trx->mysql_master_log_pos = ((ib_longlong)
|
||||||
(active_mi->rli.group_master_log_pos +
|
(active_mi->rli.future_group_master_log_pos
|
||||||
active_mi->rli.event_len
|
));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
|
|
||||||
|
73
sql/log.cc
73
sql/log.cc
@ -84,7 +84,8 @@ static int find_uniq_filename(char *name)
|
|||||||
MYSQL_LOG::MYSQL_LOG()
|
MYSQL_LOG::MYSQL_LOG()
|
||||||
:bytes_written(0), last_time(0), query_start(0), name(0),
|
:bytes_written(0), last_time(0), query_start(0), name(0),
|
||||||
file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0),
|
file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0),
|
||||||
need_start_event(1)
|
need_start_event(1), description_event_for_exec(0),
|
||||||
|
description_event_for_queue(0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We don't want to initialize LOCK_Log here as such initialization depends on
|
We don't want to initialize LOCK_Log here as such initialization depends on
|
||||||
@ -111,6 +112,8 @@ void MYSQL_LOG::cleanup()
|
|||||||
{
|
{
|
||||||
inited= 0;
|
inited= 0;
|
||||||
close(LOG_CLOSE_INDEX);
|
close(LOG_CLOSE_INDEX);
|
||||||
|
delete description_event_for_queue;
|
||||||
|
delete description_event_for_exec;
|
||||||
(void) pthread_mutex_destroy(&LOCK_log);
|
(void) pthread_mutex_destroy(&LOCK_log);
|
||||||
(void) pthread_mutex_destroy(&LOCK_index);
|
(void) pthread_mutex_destroy(&LOCK_index);
|
||||||
(void) pthread_cond_destroy(&update_cond);
|
(void) pthread_cond_destroy(&update_cond);
|
||||||
@ -179,7 +182,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
|
|||||||
const char *new_name, const char *index_file_name_arg,
|
const char *new_name, const char *index_file_name_arg,
|
||||||
enum cache_type io_cache_type_arg,
|
enum cache_type io_cache_type_arg,
|
||||||
bool no_auto_events_arg,
|
bool no_auto_events_arg,
|
||||||
ulong max_size_arg)
|
ulong max_size_arg,
|
||||||
|
bool null_created_arg)
|
||||||
{
|
{
|
||||||
char buff[512];
|
char buff[512];
|
||||||
File file= -1, index_file_nr= -1;
|
File file= -1, index_file_nr= -1;
|
||||||
@ -272,8 +276,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
|
|||||||
if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC,
|
if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC,
|
||||||
BIN_LOG_HEADER_SIZE))
|
BIN_LOG_HEADER_SIZE))
|
||||||
goto err;
|
goto err;
|
||||||
bytes_written += BIN_LOG_HEADER_SIZE;
|
bytes_written+= BIN_LOG_HEADER_SIZE;
|
||||||
write_file_name_to_index_file=1;
|
write_file_name_to_index_file= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!my_b_inited(&index_file))
|
if (!my_b_inited(&index_file))
|
||||||
@ -302,10 +306,42 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
|
|||||||
}
|
}
|
||||||
if (need_start_event && !no_auto_events)
|
if (need_start_event && !no_auto_events)
|
||||||
{
|
{
|
||||||
need_start_event=0;
|
/*
|
||||||
Start_log_event s;
|
In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
|
||||||
|
even if this is not the very first binlog.
|
||||||
|
*/
|
||||||
|
Format_description_log_event s(BINLOG_VERSION);
|
||||||
|
if (!s.is_valid())
|
||||||
|
goto err;
|
||||||
s.set_log_pos(this);
|
s.set_log_pos(this);
|
||||||
s.write(&log_file);
|
if (null_created_arg)
|
||||||
|
s.created= 0;
|
||||||
|
if (s.write(&log_file))
|
||||||
|
goto err;
|
||||||
|
bytes_written+= s.get_event_len();
|
||||||
|
}
|
||||||
|
if (description_event_for_queue &&
|
||||||
|
description_event_for_queue->binlog_version>=4)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
This is a relay log written to by the I/O slave thread.
|
||||||
|
Write the event so that others can later know the format of this relay
|
||||||
|
log.
|
||||||
|
Note that this event is very close to the original event from the
|
||||||
|
master (it has binlog version of the master, event types of the
|
||||||
|
master), so this is suitable to parse the next relay log's event. It
|
||||||
|
has been produced by
|
||||||
|
Format_description_log_event::Format_description_log_event(char*
|
||||||
|
buf,).
|
||||||
|
Why don't we want to write the description_event_for_queue if this event
|
||||||
|
is for format<4 (3.23 or 4.x): this is because in that case, the
|
||||||
|
description_event_for_queue describes the data received from the master,
|
||||||
|
but not the data written to the relay log (*conversion*), which is in
|
||||||
|
format 4 (slave's).
|
||||||
|
*/
|
||||||
|
if (description_event_for_queue->write(&log_file))
|
||||||
|
goto err;
|
||||||
|
bytes_written+= description_event_for_queue->get_event_len();
|
||||||
}
|
}
|
||||||
if (flush_io_cache(&log_file))
|
if (flush_io_cache(&log_file))
|
||||||
goto err;
|
goto err;
|
||||||
@ -596,7 +632,7 @@ bool MYSQL_LOG::reset_logs(THD* thd)
|
|||||||
if (!thd->slave_thread)
|
if (!thd->slave_thread)
|
||||||
need_start_event=1;
|
need_start_event=1;
|
||||||
open(save_name, save_log_type, 0, index_file_name,
|
open(save_name, save_log_type, 0, index_file_name,
|
||||||
io_cache_type, no_auto_events, max_size);
|
io_cache_type, no_auto_events, max_size, 0);
|
||||||
my_free((gptr) save_name, MYF(0));
|
my_free((gptr) save_name, MYF(0));
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -986,8 +1022,17 @@ void MYSQL_LOG::new_file(bool need_lock)
|
|||||||
Note that at this point, log_type != LOG_CLOSED (important for is_open()).
|
Note that at this point, log_type != LOG_CLOSED (important for is_open()).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
new_file() is only used for rotation (in FLUSH LOGS or because size >
|
||||||
|
max_binlog_size or max_relay_log_size).
|
||||||
|
If this is a binary log, the Format_description_log_event at the beginning of
|
||||||
|
the new file should have created=0 (to distinguish with the
|
||||||
|
Format_description_log_event written at server startup, which should
|
||||||
|
trigger temp tables deletion on slaves.
|
||||||
|
*/
|
||||||
|
|
||||||
open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type,
|
open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type,
|
||||||
no_auto_events, max_size);
|
no_auto_events, max_size, 1);
|
||||||
my_free(old_name,MYF(0));
|
my_free(old_name,MYF(0));
|
||||||
|
|
||||||
end:
|
end:
|
||||||
@ -1282,6 +1327,12 @@ bool MYSQL_LOG::write(Log_event* event_info)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MYSQL_VERSION_ID < 50000
|
||||||
|
/*
|
||||||
|
In 5.0 this is not needed anymore as we store the value of
|
||||||
|
FOREIGN_KEY_CHECKS in a binary way in the Query event's header.
|
||||||
|
The code below was enabled in 4.0 and 4.1.
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
If the user has set FOREIGN_KEY_CHECKS=0 we wrap every SQL
|
If the user has set FOREIGN_KEY_CHECKS=0 we wrap every SQL
|
||||||
command in the binlog inside:
|
command in the binlog inside:
|
||||||
@ -1297,6 +1348,7 @@ bool MYSQL_LOG::write(Log_event* event_info)
|
|||||||
if (e.write(file))
|
if (e.write(file))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the SQL command */
|
/* Write the SQL command */
|
||||||
@ -1307,6 +1359,7 @@ bool MYSQL_LOG::write(Log_event* event_info)
|
|||||||
|
|
||||||
/* Write log events to reset the 'run environment' of the SQL command */
|
/* Write log events to reset the 'run environment' of the SQL command */
|
||||||
|
|
||||||
|
#if MYSQL_VERSION_ID < 50000
|
||||||
if (thd && thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
|
if (thd && thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
|
||||||
{
|
{
|
||||||
Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0);
|
Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0);
|
||||||
@ -1314,6 +1367,7 @@ bool MYSQL_LOG::write(Log_event* event_info)
|
|||||||
if (e.write(file))
|
if (e.write(file))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Tell for transactional table handlers up to which position in the
|
Tell for transactional table handlers up to which position in the
|
||||||
@ -1720,6 +1774,7 @@ void MYSQL_LOG::close(uint exiting)
|
|||||||
Stop_log_event s;
|
Stop_log_event s;
|
||||||
s.set_log_pos(this);
|
s.set_log_pos(this);
|
||||||
s.write(&log_file);
|
s.write(&log_file);
|
||||||
|
bytes_written+= s.get_event_len();
|
||||||
signal_update();
|
signal_update();
|
||||||
}
|
}
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
|
1230
sql/log_event.cc
1230
sql/log_event.cc
File diff suppressed because it is too large
Load Diff
483
sql/log_event.h
483
sql/log_event.h
@ -35,12 +35,42 @@
|
|||||||
|
|
||||||
#define LOG_EVENT_OFFSET 4
|
#define LOG_EVENT_OFFSET 4
|
||||||
|
|
||||||
#define BINLOG_VERSION 3
|
/*
|
||||||
|
3 is MySQL 4.x; 4 is MySQL 5.0.0.
|
||||||
|
Compared to version 3, version 4 has:
|
||||||
|
- a different Start_log_event, which includes info about the binary log
|
||||||
|
(sizes of headers); this info is included for better compatibility if the
|
||||||
|
master's MySQL version is different from the slave's.
|
||||||
|
- all events have a unique ID (the triplet (server_id, timestamp at server
|
||||||
|
start, other) to be sure an event is not executed more than once in a
|
||||||
|
multimaster setup, example:
|
||||||
|
M1
|
||||||
|
/ \
|
||||||
|
v v
|
||||||
|
M2 M3
|
||||||
|
\ /
|
||||||
|
v v
|
||||||
|
S
|
||||||
|
if a query is run on M1, it will arrive twice on S, so we need that S
|
||||||
|
remembers the last unique ID it has processed, to compare and know if the
|
||||||
|
event should be skipped or not. Example of ID: we already have the server id
|
||||||
|
(4 bytes), plus:
|
||||||
|
timestamp_when_the_master_started (4 bytes), a counter (a sequence number
|
||||||
|
which increments every time we write an event to the binlog) (3 bytes).
|
||||||
|
Q: how do we handle when the counter is overflowed and restarts from 0 ?
|
||||||
|
|
||||||
|
- Query and Load (Create or Execute) events may have a more precise timestamp
|
||||||
|
(with microseconds), number of matched/affected/warnings rows
|
||||||
|
and fields of session variables: SQL_MODE,
|
||||||
|
FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and
|
||||||
|
charsets, the PASSWORD() version (old/new/...).
|
||||||
|
*/
|
||||||
|
#define BINLOG_VERSION 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We could have used SERVER_VERSION_LENGTH, but this introduces an
|
We could have used SERVER_VERSION_LENGTH, but this introduces an
|
||||||
obscure dependency - if somebody decided to change SERVER_VERSION_LENGTH
|
obscure dependency - if somebody decided to change SERVER_VERSION_LENGTH
|
||||||
this would have broken the replication protocol
|
this would break the replication protocol
|
||||||
*/
|
*/
|
||||||
#define ST_SERVER_VER_LEN 50
|
#define ST_SERVER_VER_LEN 50
|
||||||
|
|
||||||
@ -49,6 +79,12 @@
|
|||||||
TERMINATED etc).
|
TERMINATED etc).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
These are flags and structs to handle all the LOAD DATA INFILE options (LINES
|
||||||
|
TERMINATED etc).
|
||||||
|
DUMPFILE_FLAG is probably useless (DUMPFILE is a clause of SELECT, not of LOAD
|
||||||
|
DATA).
|
||||||
|
*/
|
||||||
#define DUMPFILE_FLAG 0x1
|
#define DUMPFILE_FLAG 0x1
|
||||||
#define OPT_ENCLOSED_FLAG 0x2
|
#define OPT_ENCLOSED_FLAG 0x2
|
||||||
#define REPLACE_FLAG 0x4
|
#define REPLACE_FLAG 0x4
|
||||||
@ -136,16 +172,28 @@ struct sql_ex_info
|
|||||||
|
|
||||||
#define LOG_EVENT_HEADER_LEN 19 /* the fixed header length */
|
#define LOG_EVENT_HEADER_LEN 19 /* the fixed header length */
|
||||||
#define OLD_HEADER_LEN 13 /* the fixed header length in 3.23 */
|
#define OLD_HEADER_LEN 13 /* the fixed header length in 3.23 */
|
||||||
|
/*
|
||||||
|
Fixed header length, where 4.x and 5.0 agree. That is, 5.0 may have a longer
|
||||||
|
header (it will for sure when we have the unique event's ID), but at least
|
||||||
|
the first 19 bytes are the same in 4.x and 5.0. So when we have the unique
|
||||||
|
event's ID, LOG_EVENT_HEADER_LEN will be something like 26, but
|
||||||
|
LOG_EVENT_MINIMAL_HEADER_LEN will remain 19.
|
||||||
|
*/
|
||||||
|
#define LOG_EVENT_MINIMAL_HEADER_LEN 19
|
||||||
|
|
||||||
/* event-specific post-header sizes */
|
/* event-specific post-header sizes */
|
||||||
#define QUERY_HEADER_LEN (4 + 4 + 1 + 2)
|
// where 3.23, 4.x and 5.0 agree
|
||||||
|
#define QUERY_HEADER_MINIMAL_LEN (4 + 4 + 1 + 2)
|
||||||
|
// where 5.0 differs: 2 for len of N-bytes vars.
|
||||||
|
#define QUERY_HEADER_LEN (QUERY_HEADER_MINIMAL_LEN + 2)
|
||||||
#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4)
|
#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4)
|
||||||
#define START_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
|
#define START_V3_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
|
||||||
#define ROTATE_HEADER_LEN 8
|
#define ROTATE_HEADER_LEN 8 // this is FROZEN (the Rotate post-header is frozen)
|
||||||
#define CREATE_FILE_HEADER_LEN 4
|
#define CREATE_FILE_HEADER_LEN 4
|
||||||
#define APPEND_BLOCK_HEADER_LEN 4
|
#define APPEND_BLOCK_HEADER_LEN 4
|
||||||
#define EXEC_LOAD_HEADER_LEN 4
|
#define EXEC_LOAD_HEADER_LEN 4
|
||||||
#define DELETE_FILE_HEADER_LEN 4
|
#define DELETE_FILE_HEADER_LEN 4
|
||||||
|
#define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Event header offsets;
|
Event header offsets;
|
||||||
@ -158,11 +206,12 @@ struct sql_ex_info
|
|||||||
#define LOG_POS_OFFSET 13
|
#define LOG_POS_OFFSET 13
|
||||||
#define FLAGS_OFFSET 17
|
#define FLAGS_OFFSET 17
|
||||||
|
|
||||||
/* start event post-header */
|
/* start event post-header (for v3 and v4) */
|
||||||
|
|
||||||
#define ST_BINLOG_VER_OFFSET 0
|
#define ST_BINLOG_VER_OFFSET 0
|
||||||
#define ST_SERVER_VER_OFFSET 2
|
#define ST_SERVER_VER_OFFSET 2
|
||||||
#define ST_CREATED_OFFSET (ST_SERVER_VER_OFFSET + ST_SERVER_VER_LEN)
|
#define ST_CREATED_OFFSET (ST_SERVER_VER_OFFSET + ST_SERVER_VER_LEN)
|
||||||
|
#define ST_COMMON_HEADER_LEN_OFFSET (ST_CREATED_OFFSET + 4)
|
||||||
|
|
||||||
/* slave event post-header (this event is never written) */
|
/* slave event post-header (this event is never written) */
|
||||||
|
|
||||||
@ -176,7 +225,13 @@ struct sql_ex_info
|
|||||||
#define Q_EXEC_TIME_OFFSET 4
|
#define Q_EXEC_TIME_OFFSET 4
|
||||||
#define Q_DB_LEN_OFFSET 8
|
#define Q_DB_LEN_OFFSET 8
|
||||||
#define Q_ERR_CODE_OFFSET 9
|
#define Q_ERR_CODE_OFFSET 9
|
||||||
|
#define Q_STATUS_VARS_LEN_OFFSET 11
|
||||||
#define Q_DATA_OFFSET QUERY_HEADER_LEN
|
#define Q_DATA_OFFSET QUERY_HEADER_LEN
|
||||||
|
/* these are codes, not offsets; not more than 256 values (1 byte). */
|
||||||
|
#define Q_FLAGS2_CODE 0
|
||||||
|
#define Q_SQL_MODE_CODE 1
|
||||||
|
#define Q_CATALOG_CODE 2
|
||||||
|
|
||||||
|
|
||||||
/* Intvar event post-header */
|
/* Intvar event post-header */
|
||||||
|
|
||||||
@ -228,16 +283,6 @@ struct sql_ex_info
|
|||||||
/* DF = "Delete File" */
|
/* DF = "Delete File" */
|
||||||
#define DF_FILE_ID_OFFSET 0
|
#define DF_FILE_ID_OFFSET 0
|
||||||
|
|
||||||
#define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
|
|
||||||
#define QUERY_DATA_OFFSET (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
|
|
||||||
#define ROTATE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+ROTATE_HEADER_LEN)
|
|
||||||
#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN)
|
|
||||||
#define CREATE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+\
|
|
||||||
+LOAD_HEADER_LEN+CREATE_FILE_HEADER_LEN)
|
|
||||||
#define DELETE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+DELETE_FILE_HEADER_LEN)
|
|
||||||
#define EXEC_LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+EXEC_LOAD_HEADER_LEN)
|
|
||||||
#define APPEND_BLOCK_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+APPEND_BLOCK_HEADER_LEN)
|
|
||||||
|
|
||||||
/* 4 bytes which all binlogs should begin with */
|
/* 4 bytes which all binlogs should begin with */
|
||||||
#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
|
#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
|
||||||
|
|
||||||
@ -264,15 +309,54 @@ struct sql_ex_info
|
|||||||
*/
|
*/
|
||||||
#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
|
#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
|
||||||
|
|
||||||
|
/*
|
||||||
|
OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must be written
|
||||||
|
to the binlog. OPTIONS_WRITTEN_TO_BINLOG could be written into the
|
||||||
|
Format_description_log_event, so that if later we don't want to replicate a
|
||||||
|
variable we did replicate, or the contrary, it's doable. But it should not be
|
||||||
|
too hard to decide once for all of what we replicate and what we don't, among
|
||||||
|
the fixed 32 bits of thd->options.
|
||||||
|
I (Guilhem) have read through every option's usage, and it looks like
|
||||||
|
OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones which alter
|
||||||
|
how the query modifies the table. It's good to replicate
|
||||||
|
OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the slave may insert data
|
||||||
|
slower than the master, in InnoDB.
|
||||||
|
OPTION_BIG_SELECTS is not needed (the slave thread runs with
|
||||||
|
max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed either, as
|
||||||
|
the manual says (because a too big in-memory temp table is automatically
|
||||||
|
written to disk).
|
||||||
|
*/
|
||||||
|
#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \
|
||||||
|
OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS)
|
||||||
|
|
||||||
enum Log_event_type
|
enum Log_event_type
|
||||||
{
|
{
|
||||||
UNKNOWN_EVENT= 0, START_EVENT= 1, QUERY_EVENT= 2, STOP_EVENT= 3,
|
/*
|
||||||
ROTATE_EVENT= 4, INTVAR_EVENT= 5, LOAD_EVENT=6, SLAVE_EVENT= 7,
|
Every time you update this enum (when you add a type), you have to
|
||||||
CREATE_FILE_EVENT= 8, APPEND_BLOCK_EVENT= 9, EXEC_LOAD_EVENT= 10,
|
update the code of Format_description_log_event::Format_description_log_event().
|
||||||
DELETE_FILE_EVENT= 11, NEW_LOAD_EVENT= 12, RAND_EVENT= 13,
|
Make sure you always insert new types ***BEFORE*** ENUM_END_EVENT.
|
||||||
USER_VAR_EVENT= 14
|
*/
|
||||||
|
UNKNOWN_EVENT= 0, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT,
|
||||||
|
INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT,
|
||||||
|
APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT,
|
||||||
|
/*
|
||||||
|
NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer sql_ex,
|
||||||
|
allowing multibyte TERMINATED BY etc; both types share the same class
|
||||||
|
(Load_log_event)
|
||||||
|
*/
|
||||||
|
NEW_LOAD_EVENT,
|
||||||
|
RAND_EVENT, USER_VAR_EVENT,
|
||||||
|
FORMAT_DESCRIPTION_EVENT,
|
||||||
|
ENUM_END_EVENT /* end marker */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
The number of types we handle in Format_description_log_event (UNKNOWN_EVENT
|
||||||
|
is not to be handled, it does not exist in binlogs, it does not have a
|
||||||
|
format).
|
||||||
|
*/
|
||||||
|
#define LOG_EVENT_TYPES (ENUM_END_EVENT-1)
|
||||||
|
|
||||||
enum Int_event_type
|
enum Int_event_type
|
||||||
{
|
{
|
||||||
INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
|
INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
|
||||||
@ -285,8 +369,33 @@ class MYSQL_LOG;
|
|||||||
class THD;
|
class THD;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Format_description_log_event;
|
||||||
|
|
||||||
struct st_relay_log_info;
|
struct st_relay_log_info;
|
||||||
|
|
||||||
|
#ifdef MYSQL_CLIENT
|
||||||
|
/*
|
||||||
|
A structure for mysqlbinlog to remember the last db, flags2, sql_mode etc; it
|
||||||
|
is passed to events' print() methods, so that they print only the necessary
|
||||||
|
USE and SET commands.
|
||||||
|
*/
|
||||||
|
typedef struct st_last_event_info
|
||||||
|
{
|
||||||
|
// TODO: have the last catalog here ??
|
||||||
|
char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is
|
||||||
|
bool flags2_inited;
|
||||||
|
uint32 flags2;
|
||||||
|
bool sql_mode_inited;
|
||||||
|
ulonglong sql_mode;
|
||||||
|
st_last_event_info()
|
||||||
|
: flags2_inited(0), flags2(0), sql_mode_inited(0), sql_mode(0)
|
||||||
|
{
|
||||||
|
db[0]= 0; /* initially, the db is unknown */
|
||||||
|
}
|
||||||
|
} LAST_EVENT_INFO;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
||||||
Log_event class
|
Log_event class
|
||||||
@ -337,21 +446,26 @@ public:
|
|||||||
uint16 flags;
|
uint16 flags;
|
||||||
|
|
||||||
bool cache_stmt;
|
bool cache_stmt;
|
||||||
|
|
||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
THD* thd;
|
THD* thd;
|
||||||
|
|
||||||
Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt);
|
|
||||||
Log_event();
|
Log_event();
|
||||||
|
Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt);
|
||||||
/*
|
/*
|
||||||
read_log_event() functions read an event from a binlog or relay log; used by
|
read_log_event() functions read an event from a binlog or relay log; used by
|
||||||
SHOW BINLOG EVENTS, the binlog_dump thread on the master (reads master's
|
SHOW BINLOG EVENTS, the binlog_dump thread on the master (reads master's
|
||||||
binlog), the slave IO thread (reads the event sent by binlog_dump), the
|
binlog), the slave IO thread (reads the event sent by binlog_dump), the
|
||||||
slave SQL thread (reads the event from the relay log).
|
slave SQL thread (reads the event from the relay log).
|
||||||
|
If mutex is 0, the read will proceed without mutex.
|
||||||
|
We need the description_event to be able to parse the event (to know the
|
||||||
|
post-header's size); in fact in read_log_event we detect the event's type,
|
||||||
|
then call the specific event's constructor and pass description_event as an
|
||||||
|
argument.
|
||||||
*/
|
*/
|
||||||
// if mutex is 0, the read will proceed without mutex
|
|
||||||
static Log_event* read_log_event(IO_CACHE* file,
|
static Log_event* read_log_event(IO_CACHE* file,
|
||||||
pthread_mutex_t* log_lock,
|
pthread_mutex_t* log_lock,
|
||||||
bool old_format);
|
const Format_description_log_event *description_event);
|
||||||
static int read_log_event(IO_CACHE* file, String* packet,
|
static int read_log_event(IO_CACHE* file, String* packet,
|
||||||
pthread_mutex_t* log_lock);
|
pthread_mutex_t* log_lock);
|
||||||
/* set_log_pos() is used to fill log_pos with tell(log). */
|
/* set_log_pos() is used to fill log_pos with tell(log). */
|
||||||
@ -379,10 +493,12 @@ public:
|
|||||||
return thd ? thd->db : 0;
|
return thd ? thd->db : 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
Log_event() : temp_buf(0) {}
|
||||||
// avoid having to link mysqlbinlog against libpthread
|
// avoid having to link mysqlbinlog against libpthread
|
||||||
static Log_event* read_log_event(IO_CACHE* file, bool old_format);
|
static Log_event* read_log_event(IO_CACHE* file,
|
||||||
|
const Format_description_log_event *description_event);
|
||||||
/* print*() functions are used by mysqlbinlog */
|
/* print*() functions are used by mysqlbinlog */
|
||||||
virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0;
|
virtual void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0) = 0;
|
||||||
void print_timestamp(FILE* file, time_t *ts = 0);
|
void print_timestamp(FILE* file, time_t *ts = 0);
|
||||||
void print_header(FILE* file);
|
void print_header(FILE* file);
|
||||||
#endif
|
#endif
|
||||||
@ -405,9 +521,9 @@ public:
|
|||||||
virtual int write_data_body(IO_CACHE* file __attribute__((unused)))
|
virtual int write_data_body(IO_CACHE* file __attribute__((unused)))
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
virtual Log_event_type get_type_code() = 0;
|
virtual Log_event_type get_type_code() = 0;
|
||||||
virtual bool is_valid() = 0;
|
virtual const bool is_valid() = 0;
|
||||||
inline bool get_cache_stmt() { return cache_stmt; }
|
inline bool get_cache_stmt() { return cache_stmt; }
|
||||||
Log_event(const char* buf, bool old_format);
|
Log_event(const char* buf, const Format_description_log_event* description_event);
|
||||||
virtual ~Log_event() { free_temp_buf();}
|
virtual ~Log_event() { free_temp_buf();}
|
||||||
void register_temp_buf(char* buf) { temp_buf = buf; }
|
void register_temp_buf(char* buf) { temp_buf = buf; }
|
||||||
void free_temp_buf()
|
void free_temp_buf()
|
||||||
@ -419,18 +535,37 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual int get_data_size() { return 0;}
|
virtual int get_data_size() { return 0;}
|
||||||
virtual int get_data_body_offset() { return 0; }
|
|
||||||
int get_event_len()
|
int get_event_len()
|
||||||
{
|
{
|
||||||
return (cached_event_len ? cached_event_len :
|
/*
|
||||||
(cached_event_len = LOG_EVENT_HEADER_LEN + get_data_size()));
|
We don't re-use the cached event's length anymore (we did in 4.x) because
|
||||||
|
this leads to nasty problems: when the 5.0 slave reads an event from a 4.0
|
||||||
|
master, it caches the event's length, then this event is converted before
|
||||||
|
it goes into the relay log, so it would be written to the relay log with
|
||||||
|
its old length, which is garbage.
|
||||||
|
*/
|
||||||
|
return (cached_event_len=(LOG_EVENT_HEADER_LEN + get_data_size()));
|
||||||
}
|
}
|
||||||
static Log_event* read_log_event(const char* buf, int event_len,
|
static Log_event* read_log_event(const char* buf, uint event_len,
|
||||||
const char **error, bool old_format);
|
const char **error,
|
||||||
|
const Format_description_log_event
|
||||||
|
*description_event);
|
||||||
/* returns the human readable name of the event's type */
|
/* returns the human readable name of the event's type */
|
||||||
const char* get_type_str();
|
const char* get_type_str();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
One class for each type of event.
|
||||||
|
Two constructors for each class:
|
||||||
|
- one to create the event for logging (when the server acts as a master),
|
||||||
|
called after an update to the database is done,
|
||||||
|
which accepts parameters like the query, the database, the options for LOAD
|
||||||
|
DATA INFILE...
|
||||||
|
- one to create the event from a packet (when the server acts as a slave),
|
||||||
|
called before reproducing the update, which accepts parameters (like a
|
||||||
|
buffer). Used to read from the master, from the relay log, and in
|
||||||
|
mysqlbinlog. This constructor must be format-tolerant.
|
||||||
|
*/
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
||||||
@ -445,6 +580,7 @@ protected:
|
|||||||
char* data_buf;
|
char* data_buf;
|
||||||
public:
|
public:
|
||||||
const char* query;
|
const char* query;
|
||||||
|
const char* catalog;
|
||||||
const char* db;
|
const char* db;
|
||||||
/*
|
/*
|
||||||
If we already know the length of the query string
|
If we already know the length of the query string
|
||||||
@ -462,6 +598,52 @@ public:
|
|||||||
BUG#1686).
|
BUG#1686).
|
||||||
*/
|
*/
|
||||||
ulong slave_proxy_id;
|
ulong slave_proxy_id;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Binlog format 3 and 4 start to differ (as far as class members are
|
||||||
|
concerned) from here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32 catalog_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We want to be able to store a variable number of N-bit status vars:
|
||||||
|
(generally N=32; but N=64 for SQL_MODE) a user may want to log the number of
|
||||||
|
affected rows (for debugging) while another does not want to lose 4 bytes in
|
||||||
|
this.
|
||||||
|
The storage on disk is the following:
|
||||||
|
status_vars_len is part of the post-header,
|
||||||
|
status_vars are in the variable-length part, after the post-header, before
|
||||||
|
the db & query.
|
||||||
|
status_vars on disk is a sequence of pairs (code, value) where 'code' means
|
||||||
|
'sql_mode', 'affected' etc. Sometimes 'value' must be a short string, so its
|
||||||
|
first byte is its length. For now the order of status vars is:
|
||||||
|
flags2 - sql_mode - catalog.
|
||||||
|
We should add the same thing to Load_log_event, but in fact
|
||||||
|
LOAD DATA INFILE is going to be logged with a new type of event (logging of
|
||||||
|
the plain text query), so Load_log_event would be frozen, so no need. The
|
||||||
|
new way of logging LOAD DATA INFILE would use a derived class of
|
||||||
|
Query_log_event, so automatically benefit from the work already done for
|
||||||
|
status variables in Query_log_event.
|
||||||
|
*/
|
||||||
|
uint16 status_vars_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
'flags2' is a second set of flags (on top of those in Log_event), for
|
||||||
|
session variables. These are thd->options which is & against a mask
|
||||||
|
(OPTIONS_WRITTEN_TO_BINLOG).
|
||||||
|
flags2_inited helps make a difference between flags2==0 (3.23 or 4.x
|
||||||
|
master, we don't know flags2, so use the slave server's global options) and
|
||||||
|
flags2==0 (5.0 master, we know this has a meaning of flags all down which
|
||||||
|
must influence the query).
|
||||||
|
*/
|
||||||
|
bool flags2_inited;
|
||||||
|
bool sql_mode_inited;
|
||||||
|
|
||||||
|
uint32 flags2;
|
||||||
|
/* In connections sql_mode is 32 bits now but will be 64 bits soon */
|
||||||
|
ulonglong sql_mode;
|
||||||
|
|
||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
|
|
||||||
Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
|
Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
|
||||||
@ -472,10 +654,11 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Query_log_event(const char* buf, int event_len, bool old_format);
|
Query_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event *description_event);
|
||||||
~Query_log_event()
|
~Query_log_event()
|
||||||
{
|
{
|
||||||
if (data_buf)
|
if (data_buf)
|
||||||
@ -486,14 +669,11 @@ public:
|
|||||||
Log_event_type get_type_code() { return QUERY_EVENT; }
|
Log_event_type get_type_code() { return QUERY_EVENT; }
|
||||||
int write(IO_CACHE* file);
|
int write(IO_CACHE* file);
|
||||||
int write_data(IO_CACHE* file); // returns 0 on success, -1 on error
|
int write_data(IO_CACHE* file); // returns 0 on success, -1 on error
|
||||||
bool is_valid() { return query != 0; }
|
const bool is_valid() { return query != 0; }
|
||||||
int get_data_size()
|
int get_data_size()
|
||||||
{
|
{
|
||||||
return (q_len + db_len + 2
|
/* Note that the "1" below is the db's length. */
|
||||||
+ 4 // thread_id
|
return (q_len + db_len + 1 + status_vars_len + QUERY_HEADER_LEN);
|
||||||
+ 4 // exec_time
|
|
||||||
+ 2 // error_code
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -504,6 +684,7 @@ public:
|
|||||||
Slave Log Event class
|
Slave Log Event class
|
||||||
Note that this class is currently not used at all; no code writes a
|
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 Slave_log_event).
|
Slave_log_event (though some code in repl_failsafe.cc reads Slave_log_event).
|
||||||
|
So it's not a problem if this code is not maintained.
|
||||||
|
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
class Slave_log_event: public Log_event
|
class Slave_log_event: public Log_event
|
||||||
@ -524,13 +705,13 @@ public:
|
|||||||
void pack_info(Protocol* protocol);
|
void pack_info(Protocol* protocol);
|
||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Slave_log_event(const char* buf, int event_len);
|
Slave_log_event(const char* buf, uint event_len);
|
||||||
~Slave_log_event();
|
~Slave_log_event();
|
||||||
int get_data_size();
|
int get_data_size();
|
||||||
bool is_valid() { return master_host != 0; }
|
const bool is_valid() { return master_host != 0; }
|
||||||
Log_event_type get_type_code() { return SLAVE_EVENT; }
|
Log_event_type get_type_code() { return SLAVE_EVENT; }
|
||||||
int write_data(IO_CACHE* file );
|
int write_data(IO_CACHE* file );
|
||||||
};
|
};
|
||||||
@ -546,12 +727,18 @@ public:
|
|||||||
class Load_log_event: public Log_event
|
class Load_log_event: public Log_event
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int copy_log_event(const char *buf, ulong event_len, bool old_format);
|
int copy_log_event(const char *buf, ulong event_len,
|
||||||
|
int body_offset, const Format_description_log_event* description_event);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ulong thread_id;
|
ulong thread_id;
|
||||||
ulong slave_proxy_id;
|
ulong slave_proxy_id;
|
||||||
uint32 table_name_len;
|
uint32 table_name_len;
|
||||||
|
/*
|
||||||
|
No need to have a catalog, as these events can only come from 4.x.
|
||||||
|
TODO: this may become false if Dmitri pushes his new LOAD DATA INFILE in
|
||||||
|
5.0 only (not in 4.x).
|
||||||
|
*/
|
||||||
uint32 db_len;
|
uint32 db_len;
|
||||||
uint32 fname_len;
|
uint32 fname_len;
|
||||||
uint32 num_fields;
|
uint32 num_fields;
|
||||||
@ -597,11 +784,18 @@ public:
|
|||||||
bool use_rli_only_for_errors);
|
bool use_rli_only_for_errors);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info = 0);
|
||||||
void print(FILE* file, bool short_form, char* last_db, bool commented);
|
void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool commented);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Load_log_event(const char* buf, int event_len, bool old_format);
|
/*
|
||||||
|
Note that for all the events related to LOAD DATA (Load_log_event,
|
||||||
|
Create_file/Append/Exec/Delete, we pass description_event; however as
|
||||||
|
logging of LOAD DATA is going to be changed in 4.1 or 5.0, this is only used
|
||||||
|
for the common_header_len (post_header_len will not be changed).
|
||||||
|
*/
|
||||||
|
Load_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Load_log_event()
|
~Load_log_event()
|
||||||
{}
|
{}
|
||||||
Log_event_type get_type_code()
|
Log_event_type get_type_code()
|
||||||
@ -610,27 +804,31 @@ public:
|
|||||||
}
|
}
|
||||||
int write_data_header(IO_CACHE* file);
|
int write_data_header(IO_CACHE* file);
|
||||||
int write_data_body(IO_CACHE* file);
|
int write_data_body(IO_CACHE* file);
|
||||||
bool is_valid() { return table_name != 0; }
|
const bool is_valid() { return table_name != 0; }
|
||||||
int get_data_size()
|
int get_data_size()
|
||||||
{
|
{
|
||||||
return (table_name_len + 2 + db_len + 2 + fname_len
|
return (table_name_len + db_len + 2 + fname_len
|
||||||
+ 4 // thread_id
|
+ LOAD_HEADER_LEN
|
||||||
+ 4 // exec_time
|
|
||||||
+ 4 // skip_lines
|
|
||||||
+ 4 // field block len
|
|
||||||
+ sql_ex.data_size() + field_block_len + num_fields);
|
+ sql_ex.data_size() + field_block_len + num_fields);
|
||||||
}
|
}
|
||||||
int get_data_body_offset() { return LOAD_EVENT_OVERHEAD; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern char server_version[SERVER_VERSION_LENGTH];
|
extern char server_version[SERVER_VERSION_LENGTH];
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
||||||
Start Log Event class
|
Start Log Event_v3 class
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
class Start_log_event: public Log_event
|
class Start_log_event_v3: public Log_event
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
@ -658,27 +856,81 @@ public:
|
|||||||
char server_version[ST_SERVER_VER_LEN];
|
char server_version[ST_SERVER_VER_LEN];
|
||||||
|
|
||||||
#ifndef MYSQL_CLIENT
|
#ifndef MYSQL_CLIENT
|
||||||
Start_log_event() :Log_event(), binlog_version(BINLOG_VERSION)
|
Start_log_event_v3();
|
||||||
{
|
|
||||||
created = (time_t) when;
|
|
||||||
memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
|
|
||||||
}
|
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
void pack_info(Protocol* protocol);
|
void pack_info(Protocol* protocol);
|
||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
Start_log_event_v3() {}
|
||||||
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Start_log_event(const char* buf, bool old_format);
|
Start_log_event_v3(const char* buf,
|
||||||
~Start_log_event() {}
|
const Format_description_log_event* description_event);
|
||||||
Log_event_type get_type_code() { return START_EVENT;}
|
~Start_log_event_v3() {}
|
||||||
|
Log_event_type get_type_code() { return START_EVENT_V3;}
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
int get_data_size()
|
int get_data_size()
|
||||||
{
|
{
|
||||||
return START_HEADER_LEN;
|
return START_V3_HEADER_LEN; //no variable-sized part
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
For binlog version 4.
|
||||||
|
This event is saved by threads which read it, as they need it for future
|
||||||
|
use (to decode the ordinary events).
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Format_description_log_event: public Start_log_event_v3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
The size of the fixed header which _all_ events have
|
||||||
|
(for binlogs written by this version, this is equal to
|
||||||
|
LOG_EVENT_HEADER_LEN), except FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT
|
||||||
|
(those have a header of size LOG_EVENT_MINIMAL_HEADER_LEN).
|
||||||
|
*/
|
||||||
|
uint8 common_header_len;
|
||||||
|
uint8 number_of_event_types;
|
||||||
|
/* The list of post-headers' lengthes */
|
||||||
|
uint8 *post_header_len;
|
||||||
|
|
||||||
|
Format_description_log_event(uint8 binlog_ver, const char* server_ver=0);
|
||||||
|
|
||||||
|
#ifndef MYSQL_CLIENT
|
||||||
|
#ifdef HAVE_REPLICATION
|
||||||
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
|
#endif /* HAVE_REPLICATION */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Format_description_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
|
~Format_description_log_event() { my_free((gptr)post_header_len, MYF(0)); }
|
||||||
|
Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;}
|
||||||
|
int write_data(IO_CACHE* file);
|
||||||
|
const bool is_valid()
|
||||||
|
{
|
||||||
|
return ((common_header_len >= ((binlog_version==1) ? OLD_HEADER_LEN :
|
||||||
|
LOG_EVENT_MINIMAL_HEADER_LEN)) &&
|
||||||
|
(post_header_len != NULL));
|
||||||
|
}
|
||||||
|
int get_event_len()
|
||||||
|
{
|
||||||
|
int i= LOG_EVENT_MINIMAL_HEADER_LEN + get_data_size();
|
||||||
|
DBUG_PRINT("info",("event_len=%d",i));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int get_data_size()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The vector of post-header lengths is considered as part of the
|
||||||
|
post-header, because in a given version it never changes (contrary to the
|
||||||
|
query in a Query_log_event).
|
||||||
|
*/
|
||||||
|
return FORMAT_DESCRIPTION_HEADER_LEN;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -705,23 +957,26 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Intvar_log_event(const char* buf, bool old_format);
|
Intvar_log_event(const char* buf, const Format_description_log_event* description_event);
|
||||||
~Intvar_log_event() {}
|
~Intvar_log_event() {}
|
||||||
Log_event_type get_type_code() { return INTVAR_EVENT;}
|
Log_event_type get_type_code() { return INTVAR_EVENT;}
|
||||||
const char* get_var_type_name();
|
const char* get_var_type_name();
|
||||||
int get_data_size() { return 9; /* sizeof(type) + sizeof(val) */;}
|
int get_data_size() { return 9; /* sizeof(type) + sizeof(val) */;}
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
|
||||||
Rand Log Event class
|
Rand Log Event class
|
||||||
|
|
||||||
Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.
|
Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0.
|
||||||
|
4.1.1 does not need it (it's repeatable again) so this event needn't be
|
||||||
|
written in 4.1.1 for PASSWORD() (but the fact that it is written is just a
|
||||||
|
waste, it does not cause bugs).
|
||||||
|
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
class Rand_log_event: public Log_event
|
class Rand_log_event: public Log_event
|
||||||
@ -739,15 +994,15 @@ class Rand_log_event: public Log_event
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Rand_log_event(const char* buf, bool old_format);
|
Rand_log_event(const char* buf, const Format_description_log_event* description_event);
|
||||||
~Rand_log_event() {}
|
~Rand_log_event() {}
|
||||||
Log_event_type get_type_code() { return RAND_EVENT;}
|
Log_event_type get_type_code() { return RAND_EVENT;}
|
||||||
int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ }
|
int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ }
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -757,6 +1012,9 @@ class Rand_log_event: public Log_event
|
|||||||
Every time a query uses the value of a user variable, a User_var_log_event is
|
Every time a query uses the value of a user variable, a User_var_log_event is
|
||||||
written before the Query_log_event, to set the user variable.
|
written before the Query_log_event, to set the user variable.
|
||||||
|
|
||||||
|
Every time a query uses the value of a user variable, a User_var_log_event is
|
||||||
|
written before the Query_log_event, to set the user variable.
|
||||||
|
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
class User_var_log_event: public Log_event
|
class User_var_log_event: public Log_event
|
||||||
{
|
{
|
||||||
@ -778,10 +1036,10 @@ public:
|
|||||||
void pack_info(Protocol* protocol);
|
void pack_info(Protocol* protocol);
|
||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
User_var_log_event(const char* buf, bool old_format);
|
User_var_log_event(const char* buf, const Format_description_log_event* description_event);
|
||||||
~User_var_log_event() {}
|
~User_var_log_event() {}
|
||||||
Log_event_type get_type_code() { return USER_VAR_EVENT;}
|
Log_event_type get_type_code() { return USER_VAR_EVENT;}
|
||||||
int get_data_size()
|
int get_data_size()
|
||||||
@ -791,7 +1049,7 @@ public:
|
|||||||
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE + val_len);
|
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE + val_len);
|
||||||
}
|
}
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -809,15 +1067,15 @@ public:
|
|||||||
{}
|
{}
|
||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Stop_log_event(const char* buf, bool old_format):
|
Stop_log_event(const char* buf, const Format_description_log_event* description_event):
|
||||||
Log_event(buf, old_format)
|
Log_event(buf, description_event)
|
||||||
{}
|
{}
|
||||||
~Stop_log_event() {}
|
~Stop_log_event() {}
|
||||||
Log_event_type get_type_code() { return STOP_EVENT;}
|
Log_event_type get_type_code() { return STOP_EVENT;}
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
@ -850,18 +1108,23 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Rotate_log_event(const char* buf, int event_len, bool old_format);
|
Rotate_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Rotate_log_event()
|
~Rotate_log_event()
|
||||||
{
|
{
|
||||||
if (alloced)
|
if (alloced)
|
||||||
my_free((gptr) new_log_ident, MYF(0));
|
my_free((gptr) new_log_ident, MYF(0));
|
||||||
}
|
}
|
||||||
Log_event_type get_type_code() { return ROTATE_EVENT;}
|
Log_event_type get_type_code() { return ROTATE_EVENT;}
|
||||||
|
int get_event_len()
|
||||||
|
{
|
||||||
|
return (LOG_EVENT_MINIMAL_HEADER_LEN + get_data_size());
|
||||||
|
}
|
||||||
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
|
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
|
||||||
bool is_valid() { return new_log_ident != 0; }
|
const bool is_valid() { return new_log_ident != 0; }
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -899,11 +1162,12 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
void print(FILE* file, bool short_form, char* last_db, bool enable_local);
|
void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Create_file_log_event(const char* buf, int event_len, bool old_format);
|
Create_file_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Create_file_log_event() {}
|
~Create_file_log_event() {}
|
||||||
|
|
||||||
Log_event_type get_type_code()
|
Log_event_type get_type_code()
|
||||||
@ -916,12 +1180,7 @@ public:
|
|||||||
Load_log_event::get_data_size() +
|
Load_log_event::get_data_size() +
|
||||||
4 + 1 + block_len);
|
4 + 1 + block_len);
|
||||||
}
|
}
|
||||||
int get_data_body_offset()
|
const bool is_valid() { return inited_from_old || block != 0; }
|
||||||
{
|
|
||||||
return (fake_base ? LOAD_EVENT_OVERHEAD:
|
|
||||||
LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN);
|
|
||||||
}
|
|
||||||
bool is_valid() { return inited_from_old || block != 0; }
|
|
||||||
int write_data_header(IO_CACHE* file);
|
int write_data_header(IO_CACHE* file);
|
||||||
int write_data_body(IO_CACHE* file);
|
int write_data_body(IO_CACHE* file);
|
||||||
/*
|
/*
|
||||||
@ -963,14 +1222,15 @@ public:
|
|||||||
void pack_info(Protocol* protocol);
|
void pack_info(Protocol* protocol);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Append_block_log_event(const char* buf, int event_len);
|
Append_block_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Append_block_log_event() {}
|
~Append_block_log_event() {}
|
||||||
Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;}
|
Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;}
|
||||||
int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;}
|
int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;}
|
||||||
bool is_valid() { return block != 0; }
|
const bool is_valid() { return block != 0; }
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
const char* get_db() { return db; }
|
const char* get_db() { return db; }
|
||||||
};
|
};
|
||||||
@ -993,15 +1253,16 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
void print(FILE* file, bool short_form, char* last_db, bool enable_local);
|
void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Delete_file_log_event(const char* buf, int event_len);
|
Delete_file_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Delete_file_log_event() {}
|
~Delete_file_log_event() {}
|
||||||
Log_event_type get_type_code() { return DELETE_FILE_EVENT;}
|
Log_event_type get_type_code() { return DELETE_FILE_EVENT;}
|
||||||
int get_data_size() { return DELETE_FILE_HEADER_LEN ;}
|
int get_data_size() { return DELETE_FILE_HEADER_LEN ;}
|
||||||
bool is_valid() { return file_id != 0; }
|
const bool is_valid() { return file_id != 0; }
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
const char* get_db() { return db; }
|
const char* get_db() { return db; }
|
||||||
};
|
};
|
||||||
@ -1024,14 +1285,15 @@ public:
|
|||||||
int exec_event(struct st_relay_log_info* rli);
|
int exec_event(struct st_relay_log_info* rli);
|
||||||
#endif /* HAVE_REPLICATION */
|
#endif /* HAVE_REPLICATION */
|
||||||
#else
|
#else
|
||||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Execute_load_log_event(const char* buf, int event_len);
|
Execute_load_log_event(const char* buf, uint event_len,
|
||||||
|
const Format_description_log_event* description_event);
|
||||||
~Execute_load_log_event() {}
|
~Execute_load_log_event() {}
|
||||||
Log_event_type get_type_code() { return EXEC_LOAD_EVENT;}
|
Log_event_type get_type_code() { return EXEC_LOAD_EVENT;}
|
||||||
int get_data_size() { return EXEC_LOAD_HEADER_LEN ;}
|
int get_data_size() { return EXEC_LOAD_HEADER_LEN ;}
|
||||||
bool is_valid() { return file_id != 0; }
|
const bool is_valid() { return file_id != 0; }
|
||||||
int write_data(IO_CACHE* file);
|
int write_data(IO_CACHE* file);
|
||||||
const char* get_db() { return db; }
|
const char* get_db() { return db; }
|
||||||
};
|
};
|
||||||
@ -1040,13 +1302,18 @@ public:
|
|||||||
class Unknown_log_event: public Log_event
|
class Unknown_log_event: public Log_event
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Unknown_log_event(const char* buf, bool old_format):
|
/*
|
||||||
Log_event(buf, old_format)
|
Even if this is an unknown event, we still pass description_event to
|
||||||
|
Log_event's ctor, this way we can extract maximum information from the
|
||||||
|
event's header (the unique ID for example).
|
||||||
|
*/
|
||||||
|
Unknown_log_event(const char* buf, const Format_description_log_event* description_event):
|
||||||
|
Log_event(buf, description_event)
|
||||||
{}
|
{}
|
||||||
~Unknown_log_event() {}
|
~Unknown_log_event() {}
|
||||||
void print(FILE* file, bool short_form= 0, char* last_db= 0);
|
void print(FILE* file, bool short_form= 0, LAST_EVENT_INFO* last_event_info= 0);
|
||||||
Log_event_type get_type_code() { return UNKNOWN_EVENT;}
|
Log_event_type get_type_code() { return UNKNOWN_EVENT;}
|
||||||
bool is_valid() { return 1; }
|
const bool is_valid() { return 1; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -14,6 +14,15 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mostly this file is used in the server. But a little part of it is used in
|
||||||
|
mysqlbinlog too (definition of SELECT_DISTINCT and others).
|
||||||
|
The consequence is that 90% of the file is wrapped in #ifndef MYSQL_CLIENT,
|
||||||
|
except the part which must be in the server and in the client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MYSQL_CLIENT
|
||||||
|
|
||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <mysql_version.h>
|
#include <mysql_version.h>
|
||||||
@ -176,7 +185,15 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
|
|||||||
#define TEST_NO_STACKTRACE 512
|
#define TEST_NO_STACKTRACE 512
|
||||||
#define TEST_SIGINT 1024 /* Allow sigint on threads */
|
#define TEST_SIGINT 1024 /* Allow sigint on threads */
|
||||||
|
|
||||||
/* options for select set by the yacc parser (stored in lex->options) */
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is included in the server and in the client.
|
||||||
|
Options for select set by the yacc parser (stored in lex->options).
|
||||||
|
None of the 32 defines below should have its value changed, or this will
|
||||||
|
break replication.
|
||||||
|
*/
|
||||||
|
|
||||||
#define SELECT_DISTINCT (1L << 0)
|
#define SELECT_DISTINCT (1L << 0)
|
||||||
#define SELECT_STRAIGHT_JOIN (1L << 1)
|
#define SELECT_STRAIGHT_JOIN (1L << 1)
|
||||||
#define SELECT_DESCRIBE (1L << 2)
|
#define SELECT_DESCRIBE (1L << 2)
|
||||||
@ -214,6 +231,9 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
|
|||||||
#define OPTION_RELAXED_UNIQUE_CHECKS (1L << 27)
|
#define OPTION_RELAXED_UNIQUE_CHECKS (1L << 27)
|
||||||
#define SELECT_NO_UNLOCK (1L << 28)
|
#define SELECT_NO_UNLOCK (1L << 28)
|
||||||
|
|
||||||
|
/* The rest of the file is included in the server only */
|
||||||
|
#ifndef MYSQL_CLIENT
|
||||||
|
|
||||||
/* options for UNION set by the yacc parser (stored in unit->union_option) */
|
/* options for UNION set by the yacc parser (stored in unit->union_option) */
|
||||||
#define UNION_ALL 1
|
#define UNION_ALL 1
|
||||||
|
|
||||||
@ -1102,3 +1122,5 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
|
|||||||
table->map= (table_map) 1 << tablenr;
|
table->map= (table_map) 1 << tablenr;
|
||||||
table->force_index= table_list->force_index;
|
table->force_index= table_list->force_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* MYSQL_CLIENT */
|
||||||
|
@ -2023,7 +2023,7 @@ bool open_log(MYSQL_LOG *log, const char *hostname,
|
|||||||
}
|
}
|
||||||
return log->open(opt_name, type, 0, index_file_name,
|
return log->open(opt_name, type, 0, index_file_name,
|
||||||
(read_append) ? SEQ_READ_APPEND : WRITE_CACHE,
|
(read_append) ? SEQ_READ_APPEND : WRITE_CACHE,
|
||||||
no_auto_events, max_size);
|
no_auto_events, max_size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
602
sql/slave.cc
602
sql/slave.cc
@ -73,8 +73,6 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
|
|||||||
static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
|
static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
|
||||||
static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
|
static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
|
||||||
const char* table_name, bool overwrite);
|
const char* table_name, bool overwrite);
|
||||||
static int check_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Find out which replications threads are running
|
Find out which replications threads are running
|
||||||
@ -215,6 +213,12 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
|
|||||||
pos Position in relay log file
|
pos Position in relay log file
|
||||||
need_data_lock Set to 1 if this functions should do mutex locks
|
need_data_lock Set to 1 if this functions should do mutex locks
|
||||||
errmsg Store pointer to error message here
|
errmsg Store pointer to error message here
|
||||||
|
look_for_description_event
|
||||||
|
1 if we should look for such an event. We only need
|
||||||
|
this when the SQL thread starts and opens an existing
|
||||||
|
relay log and has to execute it (possibly from an offset
|
||||||
|
>4); then we need to read the first event of the relay
|
||||||
|
log to be able to parse the events we have to execute.
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
- Close old open relay log files.
|
- Close old open relay log files.
|
||||||
@ -232,9 +236,11 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
|
|||||||
|
|
||||||
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
|
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
|
||||||
ulonglong pos, bool need_data_lock,
|
ulonglong pos, bool need_data_lock,
|
||||||
const char** errmsg)
|
const char** errmsg,
|
||||||
|
bool look_for_description_event)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("init_relay_log_pos");
|
DBUG_ENTER("init_relay_log_pos");
|
||||||
|
DBUG_PRINT("info", ("pos=%lu", pos));
|
||||||
|
|
||||||
*errmsg=0;
|
*errmsg=0;
|
||||||
pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
|
pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
|
||||||
@ -242,6 +248,24 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
|
|||||||
if (need_data_lock)
|
if (need_data_lock)
|
||||||
pthread_mutex_lock(&rli->data_lock);
|
pthread_mutex_lock(&rli->data_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER
|
||||||
|
is, too, and init_slave() too; these 2 functions allocate a description
|
||||||
|
event in init_relay_log_pos, which is not freed by the terminating SQL slave
|
||||||
|
thread as that thread is not started by these functions. So we have to free
|
||||||
|
the description_event here, in case, so that there is no memory leak in
|
||||||
|
running, say, CHANGE MASTER.
|
||||||
|
*/
|
||||||
|
delete rli->relay_log.description_event_for_exec;
|
||||||
|
/*
|
||||||
|
By default the relay log is in binlog format 3 (4.0).
|
||||||
|
Even if format is 4, this will work enough to read the first event
|
||||||
|
(Format_desc) (remember that format 4 is just lenghtened compared to format
|
||||||
|
3; format 3 is a prefix of format 4).
|
||||||
|
*/
|
||||||
|
rli->relay_log.description_event_for_exec= new
|
||||||
|
Format_description_log_event(3);
|
||||||
|
|
||||||
pthread_mutex_lock(log_lock);
|
pthread_mutex_lock(log_lock);
|
||||||
|
|
||||||
/* Close log file and free buffers if it's already open */
|
/* Close log file and free buffers if it's already open */
|
||||||
@ -280,8 +304,8 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
|
|||||||
In this case, we will use the same IO_CACHE pointer to
|
In this case, we will use the same IO_CACHE pointer to
|
||||||
read data as the IO thread is using to write data.
|
read data as the IO thread is using to write data.
|
||||||
*/
|
*/
|
||||||
if (my_b_tell((rli->cur_log=rli->relay_log.get_log_file())) == 0 &&
|
my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0);
|
||||||
check_binlog_magic(rli->cur_log,errmsg))
|
if (check_binlog_magic(rli->cur_log,errmsg))
|
||||||
goto err;
|
goto err;
|
||||||
rli->cur_log_old_open_count=rli->relay_log.get_open_count();
|
rli->cur_log_old_open_count=rli->relay_log.get_open_count();
|
||||||
}
|
}
|
||||||
@ -295,8 +319,85 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
|
|||||||
goto err;
|
goto err;
|
||||||
rli->cur_log = &rli->cache_buf;
|
rli->cur_log = &rli->cache_buf;
|
||||||
}
|
}
|
||||||
if (pos >= BIN_LOG_HEADER_SIZE)
|
/*
|
||||||
|
In all cases, check_binlog_magic() has been called so we're at offset 4 for
|
||||||
|
sure.
|
||||||
|
*/
|
||||||
|
if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */
|
||||||
|
{
|
||||||
|
Log_event* ev;
|
||||||
|
while (look_for_description_event)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Read the possible Format_description_log_event; if position was 4, no need, it will
|
||||||
|
be read naturally.
|
||||||
|
*/
|
||||||
|
DBUG_PRINT("info",("looking for a Format_description_log_event"));
|
||||||
|
|
||||||
|
if (my_b_tell(rli->cur_log) >= pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Because of we have rli->data_lock and log_lock, we can safely read an
|
||||||
|
event
|
||||||
|
*/
|
||||||
|
if (!(ev=Log_event::read_log_event(rli->cur_log,0,
|
||||||
|
rli->relay_log.description_event_for_exec)))
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d",
|
||||||
|
rli->cur_log->error));
|
||||||
|
if (rli->cur_log->error) /* not EOF */
|
||||||
|
{
|
||||||
|
*errmsg= "I/O error reading event at position 4";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("found Format_description_log_event"));
|
||||||
|
delete rli->relay_log.description_event_for_exec;
|
||||||
|
rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev;
|
||||||
|
/*
|
||||||
|
As ev was returned by read_log_event, it has passed is_valid(), so
|
||||||
|
my_malloc() in ctor worked, no need to check again.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Ok, we found a Format_description event. But it is not sure that this
|
||||||
|
describes the whole relay log; indeed, one can have this sequence
|
||||||
|
(starting from position 4):
|
||||||
|
Format_desc (of slave)
|
||||||
|
Rotate (of master)
|
||||||
|
Format_desc (of slave)
|
||||||
|
So the Format_desc which really describes the rest of the relay log is
|
||||||
|
the 3rd event (it can't be further than that, because we rotate the
|
||||||
|
relay log when we queue a Rotate event from the master).
|
||||||
|
But what describes the Rotate is the first Format_desc.
|
||||||
|
So what we do is:
|
||||||
|
go on searching for Format_description events, until you exceed the
|
||||||
|
position (argument 'pos') or until you find another event than Rotate
|
||||||
|
or Format_desc.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("found event of another type=%d",
|
||||||
|
ev->get_type_code()));
|
||||||
|
look_for_description_event= (ev->get_type_code() == ROTATE_EVENT);
|
||||||
|
delete ev;
|
||||||
|
}
|
||||||
|
}
|
||||||
my_b_seek(rli->cur_log,(off_t)pos);
|
my_b_seek(rli->cur_log,(off_t)pos);
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
{
|
||||||
|
char llbuf1[22], llbuf2[22];
|
||||||
|
DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
|
||||||
|
llstr(my_b_tell(rli->cur_log),llbuf1),
|
||||||
|
llstr(rli->event_relay_log_pos,llbuf2)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
/*
|
/*
|
||||||
@ -311,6 +412,8 @@ err:
|
|||||||
|
|
||||||
if (need_data_lock)
|
if (need_data_lock)
|
||||||
pthread_mutex_unlock(&rli->data_lock);
|
pthread_mutex_unlock(&rli->data_lock);
|
||||||
|
if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg)
|
||||||
|
*errmsg= "Invalid Format_description log event; could be out of memory";
|
||||||
|
|
||||||
DBUG_RETURN ((*errmsg) ? 1 : 0);
|
DBUG_RETURN ((*errmsg) ? 1 : 0);
|
||||||
}
|
}
|
||||||
@ -428,13 +531,15 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
|
|||||||
sizeof(rli->group_relay_log_name)-1);
|
sizeof(rli->group_relay_log_name)-1);
|
||||||
strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
|
strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
|
||||||
sizeof(rli->event_relay_log_name)-1);
|
sizeof(rli->event_relay_log_name)-1);
|
||||||
// Just first log with magic number and nothing else
|
|
||||||
rli->log_space_total= BIN_LOG_HEADER_SIZE;
|
|
||||||
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
|
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
|
||||||
rli->relay_log.reset_bytes_written();
|
if (count_relay_log_space(rli))
|
||||||
|
{
|
||||||
|
*errmsg= "Error counting relay log space";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
if (!just_reset)
|
if (!just_reset)
|
||||||
error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos,
|
error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos,
|
||||||
0 /* do not need data lock */, errmsg);
|
0 /* do not need data lock */, errmsg, 0);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
@ -693,6 +798,10 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
|
|||||||
different results. Note also the order of precedence of the do/ignore
|
different results. Note also the order of precedence of the do/ignore
|
||||||
rules (see code below). For that reason, users should not set conflicting
|
rules (see code below). For that reason, users should not set conflicting
|
||||||
rules because they may get unpredicted results.
|
rules because they may get unpredicted results.
|
||||||
|
Thought which arose from a question of a big customer "I want to include all
|
||||||
|
tables like "abc.%" except the "%.EFG"". This can't be done now. If we
|
||||||
|
supported Perl regexps we could do it with this pattern: /^abc\.(?!EFG)/
|
||||||
|
(I could not find an equivalent in the regex library MySQL uses).
|
||||||
|
|
||||||
RETURN VALUES
|
RETURN VALUES
|
||||||
0 should not be logged/replicated
|
0 should not be logged/replicated
|
||||||
@ -1087,28 +1196,72 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note that we rely on the master's version (3.23, 4.0.14 etc) instead of
|
||||||
|
relying on the binlog's version. This is not perfect: imagine an upgrade
|
||||||
|
of the master without waiting that all slaves are in sync with the master;
|
||||||
|
then a slave could be fooled about the binlog's format. This is what happens
|
||||||
|
when people upgrade a 3.23 master to 4.0 without doing RESET MASTER: 4.0
|
||||||
|
slaves are fooled. So we do this only to distinguish between 3.23 and more
|
||||||
|
recent masters (it's too late to change things for 3.23).
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
0 ok
|
||||||
|
1 error
|
||||||
|
*/
|
||||||
|
|
||||||
static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
|
static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
|
||||||
{
|
{
|
||||||
const char* errmsg= 0;
|
const char* errmsg= 0;
|
||||||
|
|
||||||
/*
|
if (!my_isdigit(&my_charset_bin,*mysql->server_version))
|
||||||
Note the following switch will bug when we have MySQL branch 30 ;)
|
|
||||||
*/
|
|
||||||
switch (*mysql->server_version) {
|
|
||||||
case '3':
|
|
||||||
mi->old_format =
|
|
||||||
(strncmp(mysql->server_version, "3.23.57", 7) < 0) /* < .57 */ ?
|
|
||||||
BINLOG_FORMAT_323_LESS_57 :
|
|
||||||
BINLOG_FORMAT_323_GEQ_57 ;
|
|
||||||
break;
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
mi->old_format = BINLOG_FORMAT_CURRENT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errmsg = "Master reported unrecognized MySQL version";
|
errmsg = "Master reported unrecognized MySQL version";
|
||||||
break;
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Note the following switch will bug when we have MySQL branch 30 ;)
|
||||||
|
*/
|
||||||
|
switch (*mysql->server_version)
|
||||||
|
{
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
errmsg = "Master reported unrecognized MySQL version";
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
mi->rli.relay_log.description_event_for_queue= new
|
||||||
|
Format_description_log_event(1, mysql->server_version);
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
mi->rli.relay_log.description_event_for_queue= new
|
||||||
|
Format_description_log_event(3, mysql->server_version);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
Master is MySQL >=5.0. Give a default Format_desc event, so that we can
|
||||||
|
take the early steps (like tests for "is this a 3.23 master") which we
|
||||||
|
have to take before we receive the real master's Format_desc which will
|
||||||
|
override this one. Note that the Format_desc we create below is garbage
|
||||||
|
(it has the format of the *slave*); it's only good to help know if the
|
||||||
|
master is 3.23, 4.0, etc.
|
||||||
|
*/
|
||||||
|
mi->rli.relay_log.description_event_for_queue= new
|
||||||
|
Format_description_log_event(4, mysql->server_version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This does not mean that a 5.0 slave will be able to read a 6.0 master; but
|
||||||
|
as we don't know yet, we don't want to forbid this for now. If a 5.0 slave
|
||||||
|
can't read a 6.0 master, this will show up when the slave can't read some
|
||||||
|
events sent by the master, and there will be error messages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (errmsg)
|
||||||
|
{
|
||||||
|
sql_print_error(errmsg);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *master_clock_res;
|
MYSQL_RES *master_clock_res;
|
||||||
@ -1385,7 +1538,7 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname)
|
|||||||
if (open_log(&rli->relay_log, glob_hostname, opt_relay_logname,
|
if (open_log(&rli->relay_log, glob_hostname, opt_relay_logname,
|
||||||
"-relay-bin", opt_relaylog_index_name,
|
"-relay-bin", opt_relaylog_index_name,
|
||||||
LOG_BIN, 1 /* read_append cache */,
|
LOG_BIN, 1 /* read_append cache */,
|
||||||
1 /* no auto events */,
|
0 /* starting from 5.0 we want relay logs to have auto events */,
|
||||||
max_relay_log_size ? max_relay_log_size : max_binlog_size))
|
max_relay_log_size ? max_relay_log_size : max_binlog_size))
|
||||||
{
|
{
|
||||||
sql_print_error("Failed in open_log() called from init_relay_log_info()");
|
sql_print_error("Failed in open_log() called from init_relay_log_info()");
|
||||||
@ -1419,7 +1572,7 @@ file '%s', errno %d)", fname, my_errno);
|
|||||||
|
|
||||||
/* Init relay log with first entry in the relay index file */
|
/* Init relay log with first entry in the relay index file */
|
||||||
if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */,
|
if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */,
|
||||||
&msg))
|
&msg, 0))
|
||||||
{
|
{
|
||||||
sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)");
|
sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)");
|
||||||
goto err;
|
goto err;
|
||||||
@ -1484,7 +1637,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
|
|||||||
rli->group_relay_log_name,
|
rli->group_relay_log_name,
|
||||||
rli->group_relay_log_pos,
|
rli->group_relay_log_pos,
|
||||||
0 /* no data lock*/,
|
0 /* no data lock*/,
|
||||||
&msg))
|
&msg, 0))
|
||||||
{
|
{
|
||||||
char llbuf[22];
|
char llbuf[22];
|
||||||
sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)",
|
sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)",
|
||||||
@ -1493,8 +1646,18 @@ Failed to open the existing relay log info file '%s' (errno %d)",
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
|
|
||||||
DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
|
#ifndef DBUG_OFF
|
||||||
|
{
|
||||||
|
char llbuf1[22], llbuf2[22];
|
||||||
|
DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
|
||||||
|
llstr(my_b_tell(rli->cur_log),llbuf1),
|
||||||
|
llstr(rli->event_relay_log_pos,llbuf2)));
|
||||||
|
DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
|
||||||
|
DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Now change the cache from READ to WRITE - must do this
|
Now change the cache from READ to WRITE - must do this
|
||||||
before flush_relay_log_info
|
before flush_relay_log_info
|
||||||
@ -2251,14 +2414,18 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cmp_result;
|
||||||
|
|
||||||
/* The "compare and wait" main loop */
|
/* The "compare and wait" main loop */
|
||||||
while (!thd->killed &&
|
while (!thd->killed &&
|
||||||
init_abort_pos_wait == abort_pos_wait &&
|
init_abort_pos_wait == abort_pos_wait &&
|
||||||
slave_running)
|
slave_running)
|
||||||
{
|
{
|
||||||
bool pos_reached;
|
/*
|
||||||
int cmp_result= 0;
|
If we are after RESET SLAVE, and the SQL slave thread has not processed
|
||||||
DBUG_ASSERT(*group_master_log_name || group_master_log_pos == 0);
|
any event yet, it could be that group_master_log_name is "". In that case,
|
||||||
|
just wait for more events (as there is no sensible comparison to do).
|
||||||
|
*/
|
||||||
if (*group_master_log_name)
|
if (*group_master_log_name)
|
||||||
{
|
{
|
||||||
char *basename= group_master_log_name + dirname_length(group_master_log_name);
|
char *basename= group_master_log_name + dirname_length(group_master_log_name);
|
||||||
@ -2280,13 +2447,12 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name,
|
|||||||
if (group_master_log_name_extension < log_name_extension)
|
if (group_master_log_name_extension < log_name_extension)
|
||||||
cmp_result = -1 ;
|
cmp_result = -1 ;
|
||||||
else
|
else
|
||||||
cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ;
|
cmp_result= (group_master_log_name_extension > log_name_extension) ?
|
||||||
|
1 : 0 ;
|
||||||
|
if (((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) ||
|
||||||
|
cmp_result > 0) || thd->killed)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
pos_reached = ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) ||
|
|
||||||
cmp_result > 0);
|
|
||||||
if (pos_reached || thd->killed)
|
|
||||||
break;
|
|
||||||
|
|
||||||
//wait for master update, with optional timeout.
|
//wait for master update, with optional timeout.
|
||||||
|
|
||||||
DBUG_PRINT("info",("Waiting for master update"));
|
DBUG_PRINT("info",("Waiting for master update"));
|
||||||
@ -2343,6 +2509,11 @@ improper_arguments: %d timed_out: %d",
|
|||||||
DBUG_RETURN( error ? error : event_count );
|
DBUG_RETURN( error ? error : event_count );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_slave_thread_options(THD* thd)
|
||||||
|
{
|
||||||
|
thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) |
|
||||||
|
OPTION_AUTO_IS_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
init_slave_thread()
|
init_slave_thread()
|
||||||
@ -2359,8 +2530,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
|
|||||||
thd->master_access= ~0;
|
thd->master_access= ~0;
|
||||||
thd->priv_user = 0;
|
thd->priv_user = 0;
|
||||||
thd->slave_thread = 1;
|
thd->slave_thread = 1;
|
||||||
thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) |
|
set_slave_thread_options(thd);
|
||||||
OPTION_AUTO_IS_NULL;
|
|
||||||
/*
|
/*
|
||||||
It's nonsense to constrain the slave threads with max_join_size; if a
|
It's nonsense to constrain the slave threads with max_join_size; if a
|
||||||
query succeeded on master, we HAVE to execute it.
|
query succeeded on master, we HAVE to execute it.
|
||||||
@ -2624,12 +2794,13 @@ bool st_relay_log_info::is_until_satisfied()
|
|||||||
if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
|
if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We have no cached comaprison results so we should compare log names
|
We have no cached comparison results so we should compare log names
|
||||||
and cache result
|
and cache result.
|
||||||
|
If we are after RESET SLAVE, and the SQL slave thread has not processed
|
||||||
|
any event yet, it could be that group_master_log_name is "". In that case,
|
||||||
|
just wait for more events (as there is no sensible comparison to do).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DBUG_ASSERT(*log_name || log_pos == 0);
|
|
||||||
|
|
||||||
if (*log_name)
|
if (*log_name)
|
||||||
{
|
{
|
||||||
const char *basename= log_name + dirname_length(log_name);
|
const char *basename= log_name + dirname_length(log_name);
|
||||||
@ -2704,28 +2875,45 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
|
|||||||
int exec_res;
|
int exec_res;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Skip queries originating from this server or number of
|
Queries originating from this server must be skipped.
|
||||||
queries specified by the user in slave_skip_counter
|
Low-level events (Format_desc, Rotate, Stop) from this server
|
||||||
We can't however skip event's that has something to do with the
|
must also be skipped. But for those we don't want to modify
|
||||||
|
group_master_log_pos, because these events did not exist on the master.
|
||||||
|
Format_desc is not completely skipped.
|
||||||
|
Skip queries specified by the user in slave_skip_counter.
|
||||||
|
We can't however skip events that has something to do with the
|
||||||
log files themselves.
|
log files themselves.
|
||||||
|
Filtering on own server id is extremely important, to ignore execution of
|
||||||
|
events created by the creation/rotation of the relay log (remember that
|
||||||
|
now the relay log starts with its Format_desc, has a Rotate etc).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (ev->server_id == (uint32) ::server_id ||
|
DBUG_PRINT("info",("type_code=%d, server_id=%d",type_code,ev->server_id));
|
||||||
(rli->slave_skip_counter && type_code != ROTATE_EVENT))
|
|
||||||
|
if ((ev->server_id == (uint32) ::server_id &&
|
||||||
|
type_code!= FORMAT_DESCRIPTION_EVENT) ||
|
||||||
|
(rli->slave_skip_counter &&
|
||||||
|
type_code != ROTATE_EVENT && type_code != STOP_EVENT &&
|
||||||
|
type_code != START_EVENT_V3 && type_code!= FORMAT_DESCRIPTION_EVENT))
|
||||||
{
|
{
|
||||||
/* TODO: I/O thread should not even log events with the same server id */
|
DBUG_PRINT("info", ("event skipped"));
|
||||||
rli->inc_group_relay_log_pos(ev->get_event_len(),
|
rli->inc_group_relay_log_pos((type_code == ROTATE_EVENT ||
|
||||||
type_code != STOP_EVENT ? ev->log_pos : LL(0),
|
type_code == STOP_EVENT ||
|
||||||
1/* skip lock*/);
|
type_code == FORMAT_DESCRIPTION_EVENT) ?
|
||||||
|
LL(0) : ev->log_pos,
|
||||||
|
1/* skip lock*/);
|
||||||
flush_relay_log_info(rli);
|
flush_relay_log_info(rli);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Protect against common user error of setting the counter to 1
|
Protect against common user error of setting the counter to 1
|
||||||
instead of 2 while recovering from an failed auto-increment insert
|
instead of 2 while recovering from an insert which used auto_increment,
|
||||||
|
rand or user var.
|
||||||
*/
|
*/
|
||||||
if (rli->slave_skip_counter &&
|
if (rli->slave_skip_counter &&
|
||||||
!((type_code == INTVAR_EVENT || type_code == STOP_EVENT) &&
|
!((type_code == INTVAR_EVENT ||
|
||||||
rli->slave_skip_counter == 1))
|
type_code == RAND_EVENT ||
|
||||||
|
type_code == USER_VAR_EVENT) &&
|
||||||
|
rli->slave_skip_counter == 1))
|
||||||
--rli->slave_skip_counter;
|
--rli->slave_skip_counter;
|
||||||
pthread_mutex_unlock(&rli->data_lock);
|
pthread_mutex_unlock(&rli->data_lock);
|
||||||
delete ev;
|
delete ev;
|
||||||
@ -2741,7 +2929,16 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
|
|||||||
ev->thd = thd;
|
ev->thd = thd;
|
||||||
exec_res = ev->exec_event(rli);
|
exec_res = ev->exec_event(rli);
|
||||||
DBUG_ASSERT(rli->sql_thd==thd);
|
DBUG_ASSERT(rli->sql_thd==thd);
|
||||||
delete ev;
|
/*
|
||||||
|
Format_description_log_event should not be deleted because it will be
|
||||||
|
used to read info about the relay log's format; it will be deleted when
|
||||||
|
the SQL thread does not need it, i.e. when this thread terminates.
|
||||||
|
*/
|
||||||
|
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info", ("Deleting the event after it has been executed"));
|
||||||
|
delete ev;
|
||||||
|
}
|
||||||
return exec_res;
|
return exec_res;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2842,7 +3039,8 @@ connected:
|
|||||||
thd->proc_info = "Checking master version";
|
thd->proc_info = "Checking master version";
|
||||||
if (get_master_version_and_clock(mysql, mi))
|
if (get_master_version_and_clock(mysql, mi))
|
||||||
goto err;
|
goto err;
|
||||||
if (!mi->old_format)
|
|
||||||
|
if (mi->rli.relay_log.description_event_for_queue->binlog_version > 1)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Register ourselves with the master.
|
Register ourselves with the master.
|
||||||
@ -3043,6 +3241,9 @@ err:
|
|||||||
pthread_mutex_lock(&mi->run_lock);
|
pthread_mutex_lock(&mi->run_lock);
|
||||||
mi->slave_running = 0;
|
mi->slave_running = 0;
|
||||||
mi->io_thd = 0;
|
mi->io_thd = 0;
|
||||||
|
/* Forget the relay log's format */
|
||||||
|
delete mi->rli.relay_log.description_event_for_queue;
|
||||||
|
mi->rli.relay_log.description_event_for_queue= 0;
|
||||||
// TODO: make rpl_status part of MASTER_INFO
|
// TODO: make rpl_status part of MASTER_INFO
|
||||||
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
|
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
|
||||||
mi->abort_slave = 0; // TODO: check if this is needed
|
mi->abort_slave = 0; // TODO: check if this is needed
|
||||||
@ -3137,15 +3338,38 @@ slave_begin:
|
|||||||
if (init_relay_log_pos(rli,
|
if (init_relay_log_pos(rli,
|
||||||
rli->group_relay_log_name,
|
rli->group_relay_log_name,
|
||||||
rli->group_relay_log_pos,
|
rli->group_relay_log_pos,
|
||||||
1 /*need data lock*/, &errmsg))
|
1 /*need data lock*/, &errmsg,
|
||||||
|
1 /*look for a description_event*/))
|
||||||
{
|
{
|
||||||
sql_print_error("Error initializing relay log position: %s",
|
sql_print_error("Error initializing relay log position: %s",
|
||||||
errmsg);
|
errmsg);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
THD_CHECK_SENTRY(thd);
|
THD_CHECK_SENTRY(thd);
|
||||||
DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
|
#ifndef DBUG_OFF
|
||||||
DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
|
{
|
||||||
|
char llbuf1[22], llbuf2[22];
|
||||||
|
DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
|
||||||
|
llstr(my_b_tell(rli->cur_log),llbuf1),
|
||||||
|
llstr(rli->event_relay_log_pos,llbuf2)));
|
||||||
|
DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
|
||||||
|
/*
|
||||||
|
Wonder if this is correct. I (Guilhem) wonder if my_b_tell() returns the
|
||||||
|
correct position when it's called just after my_b_seek() (the questionable
|
||||||
|
stuff is those "seek is done on next read" comments in the my_b_seek()
|
||||||
|
source code).
|
||||||
|
The crude reality is that this assertion randomly fails whereas
|
||||||
|
replication seems to work fine. And there is no easy explanation why it
|
||||||
|
fails (as we my_b_seek(rli->event_relay_log_pos) at the very end of
|
||||||
|
init_relay_log_pos() called above). Maybe the assertion would be
|
||||||
|
meaningful if we held rli->data_lock between the my_b_seek() and the
|
||||||
|
DBUG_ASSERT().
|
||||||
|
*/
|
||||||
|
#ifdef SHOULD_BE_CHECKED
|
||||||
|
DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
DBUG_ASSERT(rli->sql_thd == thd);
|
DBUG_ASSERT(rli->sql_thd == thd);
|
||||||
|
|
||||||
DBUG_PRINT("master_info",("log_file_name: %s position: %s",
|
DBUG_PRINT("master_info",("log_file_name: %s position: %s",
|
||||||
@ -3205,11 +3429,9 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
|
|||||||
DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
|
DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
|
||||||
/* When master_pos_wait() wakes up it will check this and terminate */
|
/* When master_pos_wait() wakes up it will check this and terminate */
|
||||||
rli->slave_running= 0;
|
rli->slave_running= 0;
|
||||||
/*
|
/* Forget the relay log's format */
|
||||||
Going out of the transaction. Necessary to mark it, in case the user
|
delete rli->relay_log.description_event_for_exec;
|
||||||
restarts replication from a non-transactional statement (with CHANGE
|
rli->relay_log.description_event_for_exec= 0;
|
||||||
MASTER).
|
|
||||||
*/
|
|
||||||
/* Wake up master_pos_wait() */
|
/* Wake up master_pos_wait() */
|
||||||
pthread_mutex_unlock(&rli->data_lock);
|
pthread_mutex_unlock(&rli->data_lock);
|
||||||
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
|
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
|
||||||
@ -3299,7 +3521,7 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
|
|||||||
{
|
{
|
||||||
net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */
|
net_write_command(net, 0, "", 0, "", 0);/* 3.23 master wants it */
|
||||||
Execute_load_log_event xev(thd,0,0);
|
Execute_load_log_event xev(thd,0,0);
|
||||||
xev.log_pos = mi->master_log_pos;
|
xev.log_pos = cev->log_pos;
|
||||||
if (unlikely(mi->rli.relay_log.append(&xev)))
|
if (unlikely(mi->rli.relay_log.append(&xev)))
|
||||||
{
|
{
|
||||||
sql_print_error("Slave I/O: error writing Exec_load event to \
|
sql_print_error("Slave I/O: error writing Exec_load event to \
|
||||||
@ -3313,7 +3535,6 @@ relay log");
|
|||||||
{
|
{
|
||||||
cev->block = (char*)net->read_pos;
|
cev->block = (char*)net->read_pos;
|
||||||
cev->block_len = num_bytes;
|
cev->block_len = num_bytes;
|
||||||
cev->log_pos = mi->master_log_pos;
|
|
||||||
if (unlikely(mi->rli.relay_log.append(cev)))
|
if (unlikely(mi->rli.relay_log.append(cev)))
|
||||||
{
|
{
|
||||||
sql_print_error("Slave I/O: error writing Create_file event to \
|
sql_print_error("Slave I/O: error writing Create_file event to \
|
||||||
@ -3327,7 +3548,7 @@ relay log");
|
|||||||
{
|
{
|
||||||
aev.block = (char*)net->read_pos;
|
aev.block = (char*)net->read_pos;
|
||||||
aev.block_len = num_bytes;
|
aev.block_len = num_bytes;
|
||||||
aev.log_pos = mi->master_log_pos;
|
aev.log_pos = cev->log_pos;
|
||||||
if (unlikely(mi->rli.relay_log.append(&aev)))
|
if (unlikely(mi->rli.relay_log.append(&aev)))
|
||||||
{
|
{
|
||||||
sql_print_error("Slave I/O: error writing Append_block event to \
|
sql_print_error("Slave I/O: error writing Append_block event to \
|
||||||
@ -3355,6 +3576,7 @@ err:
|
|||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
Updates the master info with the place in the next binary
|
Updates the master info with the place in the next binary
|
||||||
log where we should start reading.
|
log where we should start reading.
|
||||||
|
Rotate the relay log to avoid mixed-format relay logs.
|
||||||
|
|
||||||
NOTES
|
NOTES
|
||||||
We assume we already locked mi->data_lock
|
We assume we already locked mi->data_lock
|
||||||
@ -3386,21 +3608,30 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
|
|||||||
if (disconnect_slave_event_count)
|
if (disconnect_slave_event_count)
|
||||||
events_till_disconnect++;
|
events_till_disconnect++;
|
||||||
#endif
|
#endif
|
||||||
|
/*
|
||||||
|
If description_event_for_queue is format <4, there is conversion in the
|
||||||
|
relay log to the slave's format (4). And Rotate can mean upgrade or
|
||||||
|
nothing. If upgrade, it's to 5.0 or newer, so we will get a Format_desc, so
|
||||||
|
no need to reset description_event_for_queue now. And if it's nothing (same
|
||||||
|
master version as before), no need (still using the slave's format).
|
||||||
|
*/
|
||||||
|
if (mi->rli.relay_log.description_event_for_queue->binlog_version >= 4)
|
||||||
|
{
|
||||||
|
delete mi->rli.relay_log.description_event_for_queue;
|
||||||
|
/* start from format 3 (MySQL 4.0) again */
|
||||||
|
mi->rli.relay_log.description_event_for_queue= new
|
||||||
|
Format_description_log_event(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate_relay_log(mi); /* will take the right mutexes */
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
queue_old_event()
|
Reads a 3.23 event and converts it to the slave's format. This code was copied
|
||||||
|
from MySQL 4.0.
|
||||||
Writes a 3.23 event to the relay log.
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
Test this code before release - it has to be tested on a separate
|
|
||||||
setup with 3.23 master
|
|
||||||
*/
|
*/
|
||||||
|
static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
|
||||||
static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|
||||||
ulong event_len)
|
ulong event_len)
|
||||||
{
|
{
|
||||||
const char *errmsg = 0;
|
const char *errmsg = 0;
|
||||||
@ -3408,7 +3639,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
bool ignore_event= 0;
|
bool ignore_event= 0;
|
||||||
char *tmp_buf = 0;
|
char *tmp_buf = 0;
|
||||||
RELAY_LOG_INFO *rli= &mi->rli;
|
RELAY_LOG_INFO *rli= &mi->rli;
|
||||||
DBUG_ENTER("queue_old_event");
|
DBUG_ENTER("queue_binlog_ver_1_event");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If we get Load event, we need to pass a non-reusable buffer
|
If we get Load event, we need to pass a non-reusable buffer
|
||||||
@ -3432,7 +3663,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
connected to the master).
|
connected to the master).
|
||||||
*/
|
*/
|
||||||
Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg,
|
Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg,
|
||||||
1 /*old format*/ );
|
mi->rli.relay_log.description_event_for_queue);
|
||||||
if (unlikely(!ev))
|
if (unlikely(!ev))
|
||||||
{
|
{
|
||||||
sql_print_error("Read invalid event from master: '%s',\
|
sql_print_error("Read invalid event from master: '%s',\
|
||||||
@ -3442,7 +3673,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&mi->data_lock);
|
pthread_mutex_lock(&mi->data_lock);
|
||||||
ev->log_pos = mi->master_log_pos;
|
ev->log_pos= mi->master_log_pos; /* 3.23 events don't contain log_pos */
|
||||||
switch (ev->get_type_code()) {
|
switch (ev->get_type_code()) {
|
||||||
case STOP_EVENT:
|
case STOP_EVENT:
|
||||||
ignore_event= 1;
|
ignore_event= 1;
|
||||||
@ -3467,9 +3698,11 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
{
|
{
|
||||||
/* We come here when and only when tmp_buf != 0 */
|
/* We come here when and only when tmp_buf != 0 */
|
||||||
DBUG_ASSERT(tmp_buf);
|
DBUG_ASSERT(tmp_buf);
|
||||||
|
inc_pos=event_len;
|
||||||
|
ev->log_pos+= inc_pos;
|
||||||
int error = process_io_create_file(mi,(Create_file_log_event*)ev);
|
int error = process_io_create_file(mi,(Create_file_log_event*)ev);
|
||||||
delete ev;
|
delete ev;
|
||||||
mi->master_log_pos += event_len;
|
mi->master_log_pos += inc_pos;
|
||||||
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
|
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
|
||||||
pthread_mutex_unlock(&mi->data_lock);
|
pthread_mutex_unlock(&mi->data_lock);
|
||||||
my_free((char*)tmp_buf, MYF(0));
|
my_free((char*)tmp_buf, MYF(0));
|
||||||
@ -3481,6 +3714,12 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
}
|
}
|
||||||
if (likely(!ignore_event))
|
if (likely(!ignore_event))
|
||||||
{
|
{
|
||||||
|
if (ev->log_pos)
|
||||||
|
/*
|
||||||
|
Don't do it for fake Rotate events (see comment in
|
||||||
|
Log_event::Log_event(const char* buf...) in log_event.cc).
|
||||||
|
*/
|
||||||
|
ev->log_pos+= event_len; /* make log_pos be the pos of the end of the event */
|
||||||
if (unlikely(rli->relay_log.append(ev)))
|
if (unlikely(rli->relay_log.append(ev)))
|
||||||
{
|
{
|
||||||
delete ev;
|
delete ev;
|
||||||
@ -3496,10 +3735,98 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads a 4.0 event and converts it to the slave's format. This code was copied
|
||||||
|
from queue_binlog_ver_1_event(), with some affordable simplifications.
|
||||||
|
*/
|
||||||
|
static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf,
|
||||||
|
ulong event_len)
|
||||||
|
{
|
||||||
|
const char *errmsg = 0;
|
||||||
|
ulong inc_pos;
|
||||||
|
char *tmp_buf = 0;
|
||||||
|
RELAY_LOG_INFO *rli= &mi->rli;
|
||||||
|
DBUG_ENTER("queue_binlog_ver_3_event");
|
||||||
|
|
||||||
|
/* read_log_event() will adjust log_pos to be end_log_pos */
|
||||||
|
Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg,
|
||||||
|
mi->rli.relay_log.description_event_for_queue);
|
||||||
|
if (unlikely(!ev))
|
||||||
|
{
|
||||||
|
sql_print_error("Read invalid event from master: '%s',\
|
||||||
|
master could be corrupt but a more likely cause of this is a bug",
|
||||||
|
errmsg);
|
||||||
|
my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&mi->data_lock);
|
||||||
|
switch (ev->get_type_code()) {
|
||||||
|
case STOP_EVENT:
|
||||||
|
goto err;
|
||||||
|
case ROTATE_EVENT:
|
||||||
|
if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev)))
|
||||||
|
{
|
||||||
|
delete ev;
|
||||||
|
pthread_mutex_unlock(&mi->data_lock);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
inc_pos= 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
inc_pos= event_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (unlikely(rli->relay_log.append(ev)))
|
||||||
|
{
|
||||||
|
delete ev;
|
||||||
|
pthread_mutex_unlock(&mi->data_lock);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
||||||
|
delete ev;
|
||||||
|
mi->master_log_pos+= inc_pos;
|
||||||
|
err:
|
||||||
|
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
|
||||||
|
pthread_mutex_unlock(&mi->data_lock);
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
queue_old_event()
|
||||||
|
|
||||||
|
Writes a 3.23 or 4.0 event to the relay log, after converting it to the 5.0
|
||||||
|
(exactly, slave's) format. To do the conversion, we create a 5.0 event from
|
||||||
|
the 3.23/4.0 bytes, then write this event to the relay log.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
Test this code before release - it has to be tested on a separate
|
||||||
|
setup with 3.23 master or 4.0 master
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int queue_old_event(MASTER_INFO *mi, const char *buf,
|
||||||
|
ulong event_len)
|
||||||
|
{
|
||||||
|
switch (mi->rli.relay_log.description_event_for_queue->binlog_version)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return queue_binlog_ver_1_event(mi,buf,event_len);
|
||||||
|
case 3:
|
||||||
|
return queue_binlog_ver_3_event(mi,buf,event_len);
|
||||||
|
default: /* unsupported format; eg version 2 */
|
||||||
|
DBUG_PRINT("info",("unsupported binlog format %d in queue_old_event()",
|
||||||
|
mi->rli.relay_log.description_event_for_queue->binlog_version));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
queue_event()
|
queue_event()
|
||||||
|
|
||||||
|
If the event is 3.23/4.0, passes it to queue_old_event() which will convert
|
||||||
|
it. Otherwise, writes a 5.0 (or newer) event to the relay log. Then there is
|
||||||
|
no format conversion, it's pure read/write of bytes.
|
||||||
|
So a 5.0.0 slave's relay log can contain events in the slave's format or in
|
||||||
|
any >=5.0.0 format.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
||||||
@ -3509,7 +3836,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
|||||||
RELAY_LOG_INFO *rli= &mi->rli;
|
RELAY_LOG_INFO *rli= &mi->rli;
|
||||||
DBUG_ENTER("queue_event");
|
DBUG_ENTER("queue_event");
|
||||||
|
|
||||||
if (mi->old_format)
|
if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 &&
|
||||||
|
buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */)
|
||||||
DBUG_RETURN(queue_old_event(mi,buf,event_len));
|
DBUG_RETURN(queue_old_event(mi,buf,event_len));
|
||||||
|
|
||||||
pthread_mutex_lock(&mi->data_lock);
|
pthread_mutex_lock(&mi->data_lock);
|
||||||
@ -3536,7 +3864,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
|||||||
goto err;
|
goto err;
|
||||||
case ROTATE_EVENT:
|
case ROTATE_EVENT:
|
||||||
{
|
{
|
||||||
Rotate_log_event rev(buf,event_len,0);
|
Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue);
|
||||||
if (unlikely(process_io_rotate(mi,&rev)))
|
if (unlikely(process_io_rotate(mi,&rev)))
|
||||||
{
|
{
|
||||||
error= 1;
|
error= 1;
|
||||||
@ -3549,6 +3877,47 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
|||||||
inc_pos= 0;
|
inc_pos= 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FORMAT_DESCRIPTION_EVENT:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Create an event, and save it (when we rotate the relay log, we will have
|
||||||
|
to write this event again).
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
We are the only thread which reads/writes description_event_for_queue. The
|
||||||
|
relay_log struct does not move (though some members of it can change), so
|
||||||
|
we needn't any lock (no rli->data_lock, no log lock).
|
||||||
|
*/
|
||||||
|
Format_description_log_event* tmp= mi->rli.relay_log.description_event_for_queue;
|
||||||
|
const char* errmsg;
|
||||||
|
if (!(mi->rli.relay_log.description_event_for_queue= (Format_description_log_event*)
|
||||||
|
Log_event::read_log_event(buf, event_len, &errmsg,
|
||||||
|
mi->rli.relay_log.description_event_for_queue)))
|
||||||
|
{
|
||||||
|
delete tmp;
|
||||||
|
error= 2;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
delete tmp;
|
||||||
|
/*
|
||||||
|
Set 'created' to 0, so that in next relay logs this event does not trigger
|
||||||
|
cleaning actions on the slave in Format_description_log_event::exec_event().
|
||||||
|
*/
|
||||||
|
mi->rli.relay_log.description_event_for_queue->created= 0;
|
||||||
|
/*
|
||||||
|
Though this does some conversion to the slave's format, this will
|
||||||
|
preserve the master's binlog format version, and number of event types.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
If the event was not requested by the slave (the slave did not ask for
|
||||||
|
it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos
|
||||||
|
*/
|
||||||
|
inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
|
||||||
|
DBUG_PRINT("info",("binlog format is now %d",
|
||||||
|
mi->rli.relay_log.description_event_for_queue->binlog_version));
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
inc_pos= event_len;
|
inc_pos= event_len;
|
||||||
break;
|
break;
|
||||||
@ -3574,20 +3943,29 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
|
|||||||
We still want to increment, so that we won't re-read this event from the
|
We still want to increment, so that we won't re-read this event from the
|
||||||
master if the slave IO thread is now stopped/restarted (more efficient if
|
master if the slave IO thread is now stopped/restarted (more efficient if
|
||||||
the events we are ignoring are big LOAD DATA INFILE).
|
the events we are ignoring are big LOAD DATA INFILE).
|
||||||
|
But events which were generated by this slave and which do not exist in
|
||||||
|
the master's binlog (i.e. Format_desc, Rotate & Stop) should not increment
|
||||||
|
mi->master_log_pos.
|
||||||
*/
|
*/
|
||||||
mi->master_log_pos+= inc_pos;
|
if (buf[EVENT_TYPE_OFFSET]!=FORMAT_DESCRIPTION_EVENT &&
|
||||||
|
buf[EVENT_TYPE_OFFSET]!=ROTATE_EVENT &&
|
||||||
|
buf[EVENT_TYPE_OFFSET]!=STOP_EVENT)
|
||||||
|
mi->master_log_pos+= inc_pos;
|
||||||
DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos));
|
DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos));
|
||||||
}
|
}
|
||||||
else /* write the event to the relay log */
|
else /* write the event to the relay log */
|
||||||
if (likely(!(error= rli->relay_log.appendv(buf,event_len,0))))
|
if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
|
||||||
{
|
{
|
||||||
mi->master_log_pos+= inc_pos;
|
mi->master_log_pos+= inc_pos;
|
||||||
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
|
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
|
||||||
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
error=3;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
pthread_mutex_unlock(&mi->data_lock);
|
pthread_mutex_unlock(&mi->data_lock);
|
||||||
|
DBUG_PRINT("info", ("error=%d", error));
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3612,6 +3990,7 @@ void end_relay_log_info(RELAY_LOG_INFO* rli)
|
|||||||
}
|
}
|
||||||
rli->inited = 0;
|
rli->inited = 0;
|
||||||
rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT);
|
rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT);
|
||||||
|
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
||||||
/*
|
/*
|
||||||
Delete the slave's temporary tables from memory.
|
Delete the slave's temporary tables from memory.
|
||||||
In the future there will be other actions than this, to ensure persistance
|
In the future there will be other actions than this, to ensure persistance
|
||||||
@ -3832,6 +4211,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg)
|
|||||||
relay_log_pos Current log pos
|
relay_log_pos Current log pos
|
||||||
pending Number of bytes already processed from the event
|
pending Number of bytes already processed from the event
|
||||||
*/
|
*/
|
||||||
|
rli->event_relay_log_pos= max(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE);
|
||||||
my_b_seek(cur_log,rli->event_relay_log_pos);
|
my_b_seek(cur_log,rli->event_relay_log_pos);
|
||||||
DBUG_RETURN(cur_log);
|
DBUG_RETURN(cur_log);
|
||||||
}
|
}
|
||||||
@ -3890,28 +4270,40 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
|
|||||||
hot_log=0; // Using old binary log
|
hot_log=0; // Using old binary log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
{
|
{
|
||||||
|
/* This is an assertion which sometimes fails, let's try to track it */
|
||||||
char llbuf1[22], llbuf2[22];
|
char llbuf1[22], llbuf2[22];
|
||||||
DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE);
|
DBUG_PRINT("info", ("my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
|
||||||
/*
|
|
||||||
The next assertion sometimes (very rarely) fails, let's try to track
|
|
||||||
it
|
|
||||||
*/
|
|
||||||
DBUG_PRINT("info", ("\
|
|
||||||
Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
|
|
||||||
llstr(my_b_tell(cur_log),llbuf1),
|
llstr(my_b_tell(cur_log),llbuf1),
|
||||||
llstr(rli->group_relay_log_pos,llbuf2)));
|
llstr(rli->event_relay_log_pos,llbuf2)));
|
||||||
DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos);
|
DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE);
|
||||||
|
DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
Relay log is always in new format - if the master is 3.23, the
|
Relay log is always in new format - if the master is 3.23, the
|
||||||
I/O thread will convert the format for us
|
I/O thread will convert the format for us.
|
||||||
|
A problem: the description event may be in a previous relay log. So if the
|
||||||
|
slave has been shutdown meanwhile, we would have to look in old relay
|
||||||
|
logs, which may even have been deleted. So we need to write this
|
||||||
|
description event at the beginning of the relay log.
|
||||||
|
When the relay log is created when the I/O thread starts, easy: the master
|
||||||
|
will send the description event and we will queue it.
|
||||||
|
But if the relay log is created by new_file(): then the solution is:
|
||||||
|
MYSQL_LOG::open() will write the buffered description event.
|
||||||
*/
|
*/
|
||||||
if ((ev=Log_event::read_log_event(cur_log,0,(bool)0 /* new format */)))
|
if ((ev=Log_event::read_log_event(cur_log,0,
|
||||||
|
rli->relay_log.description_event_for_exec)))
|
||||||
|
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(thd==rli->sql_thd);
|
DBUG_ASSERT(thd==rli->sql_thd);
|
||||||
|
/*
|
||||||
|
read it while we have a lock, to avoid a mutex lock in
|
||||||
|
inc_event_relay_log_pos()
|
||||||
|
*/
|
||||||
|
rli->future_event_relay_log_pos= my_b_tell(cur_log);
|
||||||
if (hot_log)
|
if (hot_log)
|
||||||
pthread_mutex_unlock(log_lock);
|
pthread_mutex_unlock(log_lock);
|
||||||
DBUG_RETURN(ev);
|
DBUG_RETURN(ev);
|
||||||
@ -4106,8 +4498,9 @@ void rotate_relay_log(MASTER_INFO* mi)
|
|||||||
DBUG_ENTER("rotate_relay_log");
|
DBUG_ENTER("rotate_relay_log");
|
||||||
RELAY_LOG_INFO* rli= &mi->rli;
|
RELAY_LOG_INFO* rli= &mi->rli;
|
||||||
|
|
||||||
lock_slave_threads(mi);
|
/* We don't lock rli->run_lock. This would lead to deadlocks. */
|
||||||
pthread_mutex_lock(&rli->data_lock);
|
pthread_mutex_lock(&mi->run_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We need to test inited because otherwise, new_file() will attempt to lock
|
We need to test inited because otherwise, new_file() will attempt to lock
|
||||||
LOCK_log, which may not be inited (if we're not a slave).
|
LOCK_log, which may not be inited (if we're not a slave).
|
||||||
@ -4136,8 +4529,7 @@ void rotate_relay_log(MASTER_INFO* mi)
|
|||||||
*/
|
*/
|
||||||
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
|
||||||
end:
|
end:
|
||||||
pthread_mutex_unlock(&rli->data_lock);
|
pthread_mutex_unlock(&mi->run_lock);
|
||||||
unlock_slave_threads(mi);
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
sql/slave.h
60
sql/slave.h
@ -67,11 +67,6 @@ extern my_bool opt_log_slave_updates;
|
|||||||
extern ulonglong relay_log_space_limit;
|
extern ulonglong relay_log_space_limit;
|
||||||
struct st_master_info;
|
struct st_master_info;
|
||||||
|
|
||||||
enum enum_binlog_formats {
|
|
||||||
BINLOG_FORMAT_CURRENT=0, /* 0 is important for easy 'if (mi->old_format)' */
|
|
||||||
BINLOG_FORMAT_323_LESS_57,
|
|
||||||
BINLOG_FORMAT_323_GEQ_57 };
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: this needs to be redone, but for now it does not matter since
|
TODO: this needs to be redone, but for now it does not matter since
|
||||||
we do not have multi-master yet.
|
we do not have multi-master yet.
|
||||||
@ -186,6 +181,8 @@ typedef struct st_relay_log_info
|
|||||||
ulonglong group_relay_log_pos;
|
ulonglong group_relay_log_pos;
|
||||||
char event_relay_log_name[FN_REFLEN];
|
char event_relay_log_name[FN_REFLEN];
|
||||||
ulonglong event_relay_log_pos;
|
ulonglong event_relay_log_pos;
|
||||||
|
ulonglong future_event_relay_log_pos;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Original log name and position of the group we're currently executing
|
Original log name and position of the group we're currently executing
|
||||||
(whose coordinates are group_relay_log_name/pos in the relay log)
|
(whose coordinates are group_relay_log_name/pos in the relay log)
|
||||||
@ -207,11 +204,13 @@ typedef struct st_relay_log_info
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
InnoDB internally stores the master log position it has processed
|
InnoDB internally stores the master log position it has processed
|
||||||
so far; the position to store is really the sum of
|
so far; when the InnoDB code to store this position is called, we have not
|
||||||
pos + pending + event_len here since we must store the pos of the
|
updated rli->group_master_log_pos yet. So the position is the event's
|
||||||
END of the current log event
|
log_pos (the position of the end of the event); we save it in the variable
|
||||||
|
below. It's the *coming* group_master_log_pos (the one which will be
|
||||||
|
group_master_log_pos in the coming milliseconds).
|
||||||
*/
|
*/
|
||||||
int event_len;
|
ulonglong future_group_master_log_pos;
|
||||||
|
|
||||||
time_t last_master_timestamp;
|
time_t last_master_timestamp;
|
||||||
|
|
||||||
@ -285,16 +284,17 @@ typedef struct st_relay_log_info
|
|||||||
until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
|
until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void inc_event_relay_log_pos(ulonglong val)
|
inline void inc_event_relay_log_pos()
|
||||||
{
|
{
|
||||||
event_relay_log_pos+= val;
|
event_relay_log_pos= future_event_relay_log_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void inc_group_relay_log_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0)
|
void inc_group_relay_log_pos(ulonglong log_pos,
|
||||||
|
bool skip_lock=0)
|
||||||
{
|
{
|
||||||
if (!skip_lock)
|
if (!skip_lock)
|
||||||
pthread_mutex_lock(&data_lock);
|
pthread_mutex_lock(&data_lock);
|
||||||
inc_event_relay_log_pos(val);
|
inc_event_relay_log_pos();
|
||||||
group_relay_log_pos= event_relay_log_pos;
|
group_relay_log_pos= event_relay_log_pos;
|
||||||
strmake(group_relay_log_name,event_relay_log_name,
|
strmake(group_relay_log_name,event_relay_log_name,
|
||||||
sizeof(group_relay_log_name)-1);
|
sizeof(group_relay_log_name)-1);
|
||||||
@ -311,8 +311,31 @@ typedef struct st_relay_log_info
|
|||||||
not advance as it should on the non-transactional slave (it advances by
|
not advance as it should on the non-transactional slave (it advances by
|
||||||
big leaps, whereas it should advance by small leaps).
|
big leaps, whereas it should advance by small leaps).
|
||||||
*/
|
*/
|
||||||
if (log_pos) // 3.23 binlogs don't have log_posx
|
/*
|
||||||
group_master_log_pos= log_pos+ val;
|
In 4.x we used the event's len to compute the positions here. This is
|
||||||
|
wrong if the event was 3.23/4.0 and has been converted to 5.0, because
|
||||||
|
then the event's len is not what is was in the master's binlog, so this
|
||||||
|
will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0
|
||||||
|
replication: Exec_master_log_pos is wrong). Only way to solve this is to
|
||||||
|
have the original offset of the end of the event the relay log. This is
|
||||||
|
what we do in 5.0: log_pos has become "end_log_pos" (because the real use
|
||||||
|
of log_pos in 4.0 was to compute the end_log_pos; so better to store
|
||||||
|
end_log_pos instead of begin_log_pos.
|
||||||
|
If we had not done this fix here, the problem would also have appeared
|
||||||
|
when the slave and master are 5.0 but with different event length (for
|
||||||
|
example the slave is more recent than the master and features the event
|
||||||
|
UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in
|
||||||
|
SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this
|
||||||
|
value which would lead to badly broken replication.
|
||||||
|
Even the relay_log_pos will be corrupted in this case, because the len is
|
||||||
|
the relay log is not "val".
|
||||||
|
With the end_log_pos solution, we avoid computations involving lengthes.
|
||||||
|
*/
|
||||||
|
DBUG_PRINT("info", ("log_pos=%lld group_master_log_pos=%lld",
|
||||||
|
log_pos,group_master_log_pos));
|
||||||
|
if (log_pos) // some events (like fake Rotate) don't have log_pos
|
||||||
|
// when we are here, log_pos is the end of the event
|
||||||
|
group_master_log_pos= log_pos;
|
||||||
pthread_cond_broadcast(&data_cond);
|
pthread_cond_broadcast(&data_cond);
|
||||||
if (!skip_lock)
|
if (!skip_lock)
|
||||||
pthread_mutex_unlock(&data_lock);
|
pthread_mutex_unlock(&data_lock);
|
||||||
@ -389,7 +412,6 @@ typedef struct st_master_info
|
|||||||
int events_till_abort;
|
int events_till_abort;
|
||||||
#endif
|
#endif
|
||||||
bool inited;
|
bool inited;
|
||||||
enum enum_binlog_formats old_format;
|
|
||||||
volatile bool abort_slave, slave_running;
|
volatile bool abort_slave, slave_running;
|
||||||
volatile ulong slave_run_id;
|
volatile ulong slave_run_id;
|
||||||
/*
|
/*
|
||||||
@ -404,7 +426,7 @@ typedef struct st_master_info
|
|||||||
long clock_diff_with_master;
|
long clock_diff_with_master;
|
||||||
|
|
||||||
st_master_info()
|
st_master_info()
|
||||||
:ssl(0), fd(-1), io_thd(0), inited(0), old_format(BINLOG_FORMAT_CURRENT),
|
:ssl(0), fd(-1), io_thd(0), inited(0),
|
||||||
abort_slave(0),slave_running(0), slave_run_id(0)
|
abort_slave(0),slave_running(0), slave_run_id(0)
|
||||||
{
|
{
|
||||||
host[0] = 0; user[0] = 0; password[0] = 0;
|
host[0] = 0; user[0] = 0; password[0] = 0;
|
||||||
@ -535,10 +557,12 @@ void lock_slave_threads(MASTER_INFO* mi);
|
|||||||
void unlock_slave_threads(MASTER_INFO* mi);
|
void unlock_slave_threads(MASTER_INFO* mi);
|
||||||
void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse);
|
void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse);
|
||||||
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos,
|
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos,
|
||||||
bool need_data_lock, const char** errmsg);
|
bool need_data_lock, const char** errmsg,
|
||||||
|
bool look_for_description_event);
|
||||||
|
|
||||||
int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
|
int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
|
||||||
const char** errmsg);
|
const char** errmsg);
|
||||||
|
void set_slave_thread_options(THD* thd);
|
||||||
void rotate_relay_log(MASTER_INFO* mi);
|
void rotate_relay_log(MASTER_INFO* mi);
|
||||||
|
|
||||||
extern "C" pthread_handler_decl(handle_slave_io,arg);
|
extern "C" pthread_handler_decl(handle_slave_io,arg);
|
||||||
|
@ -92,6 +92,7 @@ THD::THD():user_time(0), is_fatal_error(0),
|
|||||||
global_read_lock(0), bootstrap(0), spcont(NULL)
|
global_read_lock(0), bootstrap(0), spcont(NULL)
|
||||||
{
|
{
|
||||||
host= user= priv_user= db= ip= 0;
|
host= user= priv_user= db= ip= 0;
|
||||||
|
catalog= (char*)"std"; // the only catalog we have for now
|
||||||
host_or_ip= "connecting host";
|
host_or_ip= "connecting host";
|
||||||
locked=some_tables_deleted=no_errors=password= 0;
|
locked=some_tables_deleted=no_errors=password= 0;
|
||||||
query_start_used= 0;
|
query_start_used= 0;
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
class Query_log_event;
|
class Query_log_event;
|
||||||
class Load_log_event;
|
class Load_log_event;
|
||||||
class Slave_log_event;
|
class Slave_log_event;
|
||||||
|
class Format_description_log_event;
|
||||||
class sp_rcontext;
|
class sp_rcontext;
|
||||||
class sp_cache;
|
class sp_cache;
|
||||||
|
|
||||||
@ -99,7 +100,14 @@ class MYSQL_LOG
|
|||||||
enum cache_type io_cache_type;
|
enum cache_type io_cache_type;
|
||||||
bool write_error, inited;
|
bool write_error, inited;
|
||||||
bool need_start_event;
|
bool need_start_event;
|
||||||
bool no_auto_events; // For relay binlog
|
/*
|
||||||
|
no_auto_events means we don't want any of these automatic events :
|
||||||
|
Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't want
|
||||||
|
a Rotate_log event to be written to the relay log. When we start a relay log
|
||||||
|
etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
|
||||||
|
In 5.0 it's 0 for relay logs too!
|
||||||
|
*/
|
||||||
|
bool no_auto_events;
|
||||||
/*
|
/*
|
||||||
The max size before rotation (usable only if log_type == LOG_BIN: binary
|
The max size before rotation (usable only if log_type == LOG_BIN: binary
|
||||||
logs and relay logs).
|
logs and relay logs).
|
||||||
@ -116,6 +124,18 @@ class MYSQL_LOG
|
|||||||
public:
|
public:
|
||||||
MYSQL_LOG();
|
MYSQL_LOG();
|
||||||
~MYSQL_LOG();
|
~MYSQL_LOG();
|
||||||
|
|
||||||
|
/*
|
||||||
|
These describe the log's format. This is used only for relay logs.
|
||||||
|
_for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
|
||||||
|
necessary to have 2 distinct objects, because the I/O thread may be reading
|
||||||
|
events in a different format from what the SQL thread is reading (consider
|
||||||
|
the case of a master which has been upgraded from 5.0 to 5.1 without doing
|
||||||
|
RESET MASTER, or from 4.x to 5.0).
|
||||||
|
*/
|
||||||
|
Format_description_log_event *description_event_for_exec,
|
||||||
|
*description_event_for_queue;
|
||||||
|
|
||||||
void reset_bytes_written()
|
void reset_bytes_written()
|
||||||
{
|
{
|
||||||
bytes_written = 0;
|
bytes_written = 0;
|
||||||
@ -144,7 +164,8 @@ public:
|
|||||||
bool open(const char *log_name,enum_log_type log_type,
|
bool open(const char *log_name,enum_log_type log_type,
|
||||||
const char *new_name, const char *index_file_name_arg,
|
const char *new_name, const char *index_file_name_arg,
|
||||||
enum cache_type io_cache_type_arg,
|
enum cache_type io_cache_type_arg,
|
||||||
bool no_auto_events_arg, ulong max_size);
|
bool no_auto_events_arg, ulong max_size,
|
||||||
|
bool null_created);
|
||||||
void new_file(bool need_lock= 1);
|
void new_file(bool need_lock= 1);
|
||||||
bool write(THD *thd, enum enum_server_command command,
|
bool write(THD *thd, enum enum_server_command command,
|
||||||
const char *format,...);
|
const char *format,...);
|
||||||
@ -590,9 +611,10 @@ public:
|
|||||||
the connection
|
the connection
|
||||||
priv_user - The user privilege we are using. May be '' for anonymous user.
|
priv_user - The user privilege we are using. May be '' for anonymous user.
|
||||||
db - currently selected database
|
db - currently selected database
|
||||||
|
catalog - currently selected catalog
|
||||||
ip - client IP
|
ip - client IP
|
||||||
*/
|
*/
|
||||||
char *host,*user,*priv_user,*db,*ip;
|
char *host,*user,*priv_user,*db,*catalog,*ip;
|
||||||
char priv_host[MAX_HOSTNAME];
|
char priv_host[MAX_HOSTNAME];
|
||||||
/* remote (peer) port */
|
/* remote (peer) port */
|
||||||
uint16 peer_port;
|
uint16 peer_port;
|
||||||
|
210
sql/sql_repl.cc
210
sql/sql_repl.cc
@ -48,16 +48,34 @@ int check_binlog_magic(IO_CACHE* log, const char** errmsg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fake_rotate_event() builds a fake (=which does not exist physically in any
|
||||||
|
binlog) Rotate event, which contains the name of the binlog we are going to
|
||||||
|
send to the slave (because the slave may not know it if it just asked for
|
||||||
|
MASTER_LOG_FILE='', MASTER_LOG_POS=4).
|
||||||
|
< 4.0.14, fake_rotate_event() was called only if the requested pos was
|
||||||
|
4. After this version we always call it, so that a 3.23.58 slave can rely on
|
||||||
|
it to detect if the master is 4.0 (and stop) (the _fake_ Rotate event has
|
||||||
|
zeros in the good positions which, by chance, make it possible for the 3.23
|
||||||
|
slave to detect that this event is unexpected) (this is luck which happens
|
||||||
|
because the master and slave disagree on the size of the header of
|
||||||
|
Log_event).
|
||||||
|
|
||||||
|
Relying on the event length of the Rotate event instead of these well-placed
|
||||||
|
zeros was not possible as Rotate events have a variable-length part.
|
||||||
|
*/
|
||||||
|
|
||||||
static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
|
static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
|
||||||
ulonglong position, const char**errmsg)
|
ulonglong position, const char** errmsg)
|
||||||
{
|
{
|
||||||
|
DBUG_ENTER("fake_rotate_event");
|
||||||
char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN];
|
char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN];
|
||||||
memset(header, 0, 4); // when does not matter
|
memset(header, 0, 4); // 'when' (the timestamp) does not matter, is set to 0
|
||||||
header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
|
header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
|
||||||
|
|
||||||
char* p = log_file_name+dirname_length(log_file_name);
|
char* p = log_file_name+dirname_length(log_file_name);
|
||||||
uint ident_len = (uint) strlen(p);
|
uint ident_len = (uint) strlen(p);
|
||||||
ulong event_len = ident_len + ROTATE_EVENT_OVERHEAD;
|
ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN;
|
||||||
int4store(header + SERVER_ID_OFFSET, server_id);
|
int4store(header + SERVER_ID_OFFSET, server_id);
|
||||||
int4store(header + EVENT_LEN_OFFSET, event_len);
|
int4store(header + EVENT_LEN_OFFSET, event_len);
|
||||||
int2store(header + FLAGS_OFFSET, 0);
|
int2store(header + FLAGS_OFFSET, 0);
|
||||||
@ -72,9 +90,9 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
|
|||||||
if (my_net_write(net, (char*)packet->ptr(), packet->length()))
|
if (my_net_write(net, (char*)packet->ptr(), packet->length()))
|
||||||
{
|
{
|
||||||
*errmsg = "failed on my_net_write()";
|
*errmsg = "failed on my_net_write()";
|
||||||
return -1;
|
DBUG_RETURN(-1);
|
||||||
}
|
}
|
||||||
return 0;
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_file(THD *thd)
|
static int send_file(THD *thd)
|
||||||
@ -310,6 +328,36 @@ int purge_master_logs_before_date(THD* thd, time_t purge_time)
|
|||||||
return purge_error_message(thd ,res);
|
return purge_error_message(thd ,res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int test_for_non_eof_log_read_errors(int error, const char *errmsg)
|
||||||
|
{
|
||||||
|
if (error == LOG_READ_EOF)
|
||||||
|
return 0;
|
||||||
|
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
|
||||||
|
switch (error) {
|
||||||
|
case LOG_READ_BOGUS:
|
||||||
|
errmsg = "bogus data in log event";
|
||||||
|
break;
|
||||||
|
case LOG_READ_TOO_LARGE:
|
||||||
|
errmsg = "log event entry exceeded max_allowed_packet; \
|
||||||
|
Increase max_allowed_packet on master";
|
||||||
|
break;
|
||||||
|
case LOG_READ_IO:
|
||||||
|
errmsg = "I/O error reading log event";
|
||||||
|
break;
|
||||||
|
case LOG_READ_MEM:
|
||||||
|
errmsg = "memory allocation failed reading log event";
|
||||||
|
break;
|
||||||
|
case LOG_READ_TRUNC:
|
||||||
|
errmsg = "binlog truncated in the middle of event";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errmsg = "unknown error reading log event on the master";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Clean up loop to only have one call to send_file()
|
TODO: Clean up loop to only have one call to send_file()
|
||||||
*/
|
*/
|
||||||
@ -326,6 +374,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
|
|||||||
int error;
|
int error;
|
||||||
const char *errmsg = "Unknown error";
|
const char *errmsg = "Unknown error";
|
||||||
NET* net = &thd->net;
|
NET* net = &thd->net;
|
||||||
|
pthread_mutex_t *log_lock;
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
int left_events = max_binlog_dump_events;
|
int left_events = max_binlog_dump_events;
|
||||||
#endif
|
#endif
|
||||||
@ -385,18 +434,25 @@ impossible position";
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
my_b_seek(&log, pos); // Seek will done on next read
|
|
||||||
/*
|
/*
|
||||||
We need to start a packet with something other than 255
|
We need to start a packet with something other than 255
|
||||||
to distiquish it from error
|
to distinguish it from error
|
||||||
*/
|
*/
|
||||||
packet->set("\0", 1, &my_charset_bin);
|
packet->set("\0", 1, &my_charset_bin); /* This is the start of a new packet */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Tell the client about the log name with a fake Rotate event;
|
||||||
|
this is needed even if we also send a Format_description_log_event just
|
||||||
|
after, because that event does not contain the binlog's name.
|
||||||
|
Note that as this Rotate event is sent before Format_description_log_event,
|
||||||
|
the slave cannot have any info to understand this event's format, so the
|
||||||
|
header len of Rotate_log_event is FROZEN
|
||||||
|
(so in 5.0 it will have a header shorter than other events except
|
||||||
|
FORMAT_DESCRIPTION_EVENT).
|
||||||
Before 4.0.14 we called fake_rotate_event below only if
|
Before 4.0.14 we called fake_rotate_event below only if
|
||||||
(pos == BIN_LOG_HEADER_SIZE), because if this is false then the slave
|
(pos == BIN_LOG_HEADER_SIZE), because if this is false then the slave
|
||||||
already knows the binlog's name.
|
already knows the binlog's name.
|
||||||
Now we always call fake_rotate_event; if the slave already knew the log's
|
Since, we always call fake_rotate_event; if the slave already knew the log's
|
||||||
name (ex: CHANGE MASTER TO MASTER_LOG_FILE=...) this is useless but does
|
name (ex: CHANGE MASTER TO MASTER_LOG_FILE=...) this is useless but does
|
||||||
not harm much. It is nice for 3.23 (>=.58) slaves which test Rotate events
|
not harm much. It is nice for 3.23 (>=.58) slaves which test Rotate events
|
||||||
to see if the master is 4.0 (then they choose to stop because they can't
|
to see if the master is 4.0 (then they choose to stop because they can't
|
||||||
@ -413,15 +469,72 @@ impossible position";
|
|||||||
*/
|
*/
|
||||||
if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg))
|
if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg))
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
This error code is not perfect, as fake_rotate_event() does not read
|
||||||
|
anything from the binlog; if it fails it's because of an error in
|
||||||
|
my_net_write(), fortunately it will say it in errmsg.
|
||||||
|
*/
|
||||||
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
|
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
packet->set("\0", 1, &my_charset_bin);
|
packet->set("\0", 1, &my_charset_bin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
We can set log_lock now, it does not move (it's a member of mysql_bin_log,
|
||||||
|
and it's already inited, and it will be destroyed only at shutdown).
|
||||||
|
*/
|
||||||
|
log_lock = mysql_bin_log.get_log_lock();
|
||||||
|
if (pos > BIN_LOG_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
/* Try to find a Format_description_log_event at the beginning of the binlog */
|
||||||
|
if (!(error = Log_event::read_log_event(&log, packet, log_lock)))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The packet has offsets equal to the normal offsets in a binlog event
|
||||||
|
+1 (the first character is \0).
|
||||||
|
*/
|
||||||
|
DBUG_PRINT("info",
|
||||||
|
("Looked for a Format_description_log_event, found event type %d",
|
||||||
|
(*packet)[EVENT_TYPE_OFFSET+1]));
|
||||||
|
if ((*packet)[EVENT_TYPE_OFFSET+1] == FORMAT_DESCRIPTION_EVENT)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
mark that this event with "log_pos=0", so the slave
|
||||||
|
should not increment master's binlog position
|
||||||
|
(rli->group_master_log_pos)
|
||||||
|
*/
|
||||||
|
int4store(packet->c_ptr() +LOG_POS_OFFSET+1,0);
|
||||||
|
/* send it */
|
||||||
|
if (my_net_write(net, (char*)packet->ptr(), packet->length()))
|
||||||
|
{
|
||||||
|
errmsg = "Failed on my_net_write()";
|
||||||
|
my_errno= ER_UNKNOWN_ERROR;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
No need to save this event. We are only doing simple reads (no real
|
||||||
|
parsing of the events) so we don't need it. And so we don't need the
|
||||||
|
artificial Format_description_log_event of 3.23&4.x.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (test_for_non_eof_log_read_errors(error, errmsg))
|
||||||
|
goto err;
|
||||||
|
/*
|
||||||
|
else: it's EOF, nothing to do, go on reading next events, the
|
||||||
|
Format_description_log_event will be found naturally if it is written.
|
||||||
|
*/
|
||||||
|
/* reset the packet as we wrote to it in any case */
|
||||||
|
packet->set("\0", 1, &my_charset_bin);
|
||||||
|
} /* end of if (pos > BIN_LOG_HEADER_SIZE); if false, the Format_description_log_event
|
||||||
|
event will be found naturally. */
|
||||||
|
|
||||||
|
/* seek to the requested position, to start the requested dump */
|
||||||
|
my_b_seek(&log, pos); // Seek will done on next read
|
||||||
|
|
||||||
while (!net->error && net->vio != 0 && !thd->killed)
|
while (!net->error && net->vio != 0 && !thd->killed)
|
||||||
{
|
{
|
||||||
pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock();
|
|
||||||
|
|
||||||
while (!(error = Log_event::read_log_event(&log, packet, log_lock)))
|
while (!(error = Log_event::read_log_event(&log, packet, log_lock)))
|
||||||
{
|
{
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
@ -433,7 +546,7 @@ impossible position";
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (my_net_write(net, (char*)packet->ptr(), packet->length()) )
|
if (my_net_write(net, (char*)packet->ptr(), packet->length()))
|
||||||
{
|
{
|
||||||
errmsg = "Failed on my_net_write()";
|
errmsg = "Failed on my_net_write()";
|
||||||
my_errno= ER_UNKNOWN_ERROR;
|
my_errno= ER_UNKNOWN_ERROR;
|
||||||
@ -454,34 +567,14 @@ impossible position";
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
TODO: now that we are logging the offset, check to make sure
|
TODO: now that we are logging the offset, check to make sure
|
||||||
the recorded offset and the actual match
|
the recorded offset and the actual match.
|
||||||
|
Guilhem 2003-06: this is not true if this master is a slave <4.0.15
|
||||||
|
running with --log-slave-updates, because then log_pos may be the offset
|
||||||
|
in the-master-of-this-master's binlog.
|
||||||
*/
|
*/
|
||||||
if (error != LOG_READ_EOF)
|
|
||||||
{
|
if (test_for_non_eof_log_read_errors(error, errmsg))
|
||||||
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
|
|
||||||
switch (error) {
|
|
||||||
case LOG_READ_BOGUS:
|
|
||||||
errmsg = "bogus data in log event";
|
|
||||||
break;
|
|
||||||
case LOG_READ_TOO_LARGE:
|
|
||||||
errmsg = "log event entry exceeded max_allowed_packet; \
|
|
||||||
Increase max_allowed_packet on master";
|
|
||||||
break;
|
|
||||||
case LOG_READ_IO:
|
|
||||||
errmsg = "I/O error reading log event";
|
|
||||||
break;
|
|
||||||
case LOG_READ_MEM:
|
|
||||||
errmsg = "memory allocation failed reading log event";
|
|
||||||
break;
|
|
||||||
case LOG_READ_TRUNC:
|
|
||||||
errmsg = "binlog truncated in the middle of event";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errmsg = "unknown error reading log event on the master";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
|
||||||
|
|
||||||
if (!(flags & BINLOG_DUMP_NON_BLOCK) &&
|
if (!(flags & BINLOG_DUMP_NON_BLOCK) &&
|
||||||
mysql_bin_log.is_active(log_file_name))
|
mysql_bin_log.is_active(log_file_name))
|
||||||
@ -615,8 +708,13 @@ Increase max_allowed_packet on master";
|
|||||||
(void) my_close(file, MYF(MY_WME));
|
(void) my_close(file, MYF(MY_WME));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Even if the previous log contained a Rotate_log_event, we still fake
|
Call fake_rotate_event() in case the previous log (the one which we have
|
||||||
one.
|
just finished reading) did not contain a Rotate event (for example (I
|
||||||
|
don't know any other example) the previous log was the last one before
|
||||||
|
the master was shutdown & restarted).
|
||||||
|
This way we tell the slave about the new log's name and position.
|
||||||
|
If the binlog is 5.0, the next event we are going to read and send is
|
||||||
|
Format_description_log_event.
|
||||||
*/
|
*/
|
||||||
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
|
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
|
||||||
fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE, &errmsg))
|
fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE, &errmsg))
|
||||||
@ -1112,7 +1210,7 @@ int change_master(THD* thd, MASTER_INFO* mi)
|
|||||||
mi->rli.group_relay_log_name,
|
mi->rli.group_relay_log_name,
|
||||||
mi->rli.group_relay_log_pos,
|
mi->rli.group_relay_log_pos,
|
||||||
0 /*no data lock*/,
|
0 /*no data lock*/,
|
||||||
&msg))
|
&msg, 0))
|
||||||
{
|
{
|
||||||
net_printf(thd,0,"Failed initializing relay log position: %s",msg);
|
net_printf(thd,0,"Failed initializing relay log position: %s",msg);
|
||||||
unlock_slave_threads(mi);
|
unlock_slave_threads(mi);
|
||||||
@ -1197,6 +1295,8 @@ int show_binlog_events(THD* thd)
|
|||||||
const char *errmsg = 0;
|
const char *errmsg = 0;
|
||||||
IO_CACHE log;
|
IO_CACHE log;
|
||||||
File file = -1;
|
File file = -1;
|
||||||
|
Format_description_log_event *description_event= new
|
||||||
|
Format_description_log_event(3); /* MySQL 4.0 by default */
|
||||||
|
|
||||||
Log_event::init_show_field_list(&field_list);
|
Log_event::init_show_field_list(&field_list);
|
||||||
if (protocol-> send_fields(&field_list, 1))
|
if (protocol-> send_fields(&field_list, 1))
|
||||||
@ -1235,10 +1335,35 @@ int show_binlog_events(THD* thd)
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
pthread_mutex_lock(log_lock);
|
pthread_mutex_lock(log_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
open_binlog() sought to position 4.
|
||||||
|
Read the first event in case it's a Format_description_log_event, to know the
|
||||||
|
format. If there's no such event, we are 3.23 or 4.x. This code, like
|
||||||
|
before, can't read 3.23 binlogs.
|
||||||
|
This code will fail on a mixed relay log (one which has Format_desc then
|
||||||
|
Rotate then Format_desc).
|
||||||
|
*/
|
||||||
|
|
||||||
|
ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event);
|
||||||
|
if (ev)
|
||||||
|
{
|
||||||
|
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
|
||||||
|
description_event= (Format_description_log_event*) ev;
|
||||||
|
else
|
||||||
|
delete ev;
|
||||||
|
}
|
||||||
|
|
||||||
my_b_seek(&log, pos);
|
my_b_seek(&log, pos);
|
||||||
|
|
||||||
|
if (!description_event->is_valid())
|
||||||
|
{
|
||||||
|
errmsg="Invalid Format_description event; could be out of memory";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
for (event_count = 0;
|
for (event_count = 0;
|
||||||
(ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,0)); )
|
(ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event)); )
|
||||||
{
|
{
|
||||||
if (event_count >= limit_start &&
|
if (event_count >= limit_start &&
|
||||||
ev->net_send(protocol, linfo.log_file_name, pos))
|
ev->net_send(protocol, linfo.log_file_name, pos))
|
||||||
@ -1267,6 +1392,7 @@ int show_binlog_events(THD* thd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
delete description_event;
|
||||||
if (file >= 0)
|
if (file >= 0)
|
||||||
{
|
{
|
||||||
end_io_cache(&log);
|
end_io_cache(&log);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user