summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorunknown <guilhem@gbichot2>2003-12-18 01:09:05 +0100
committerunknown <guilhem@gbichot2>2003-12-18 01:09:05 +0100
commit378894b000f44b925bd8e51d445fe4acb4b786f1 (patch)
treea63a2df9b4b125224ed296006bc1855fe5b37074 /client
parentfd4544879b91fec027909f3607fe9fa68a214241 (diff)
downloadmariadb-git-378894b000f44b925bd8e51d445fe4acb4b786f1.tar.gz
This will be pushed only after I fix the testsuite.
This is the main commit for Worklog tasks: * A more dynamic binlog format which allows small changes (1064) * Log session variables in Query_log_event (1063) Below 5.0 means 5.0.0. MySQL 5.0 is able to replicate FOREIGN_KEY_CHECKS, UNIQUE_KEY_CHECKS (for speed), SQL_AUTO_IS_NULL, SQL_MODE. Not charsets (WL#1062), not some vars (I can only think of SQL_SELECT_LIMIT, which deserves a special treatment). Note that this works for queries, except LOAD DATA INFILE (for this it would have to wait for Dmitri's push of WL#874, which in turns waits for the present push, so... the deadlock must be broken!). Note that when Dmitri pushes WL#874 in 5.0.1, 5.0.0 won't be able to replicate a LOAD DATA INFILE from 5.0.1. Apart from that, the new binlog format is designed so that it can tolerate a little variation in the events (so that a 5.0.0 slave could replicate a 5.0.1 master, except for LOAD DATA INFILE unfortunately); that is, when I later add replication of charsets it should break nothing. And when I later add a UID to every event, it should break nothing. The main change brought by this patch is a new type of event, Format_description_log_event, which describes some lengthes in other event types. This event is needed for the master/slave/mysqlbinlog to understand a 5.0 log. Thanks to this event, we can later add more bytes to the header of every event without breaking compatibility. Inside Query_log_event, we have some additional dynamic format, as every Query_log_event can have a different number of status variables, stored as pairs (code, value); that's how SQL_MODE and session variables and catalog are stored. Like this, we can later add count of affected rows, charsets... and we can have options --don't-log-count-affected-rows if we want. MySQL 5.0 is able to run on 4.x relay logs, 4.x binlogs. Upgrading a 4.x master to 5.0 is ok (no need to delete binlogs), upgrading a 4.x slave to 5.0 is ok (no need to delete relay logs); so both can be "hot" upgrades. Upgrading a 3.23 master to 5.0 requires as much as upgrading it to 4.0. 3.23 and 4.x can't be slaves of 5.0. So downgrading from 5.0 to 4.x may be complicated. Log_event::log_pos is now the position of the end of the event, which is more useful than the position of the beginning. We take care about compatibility with <5.0 (in which log_pos is the beginning). I added a short test for replication of SQL_MODE and some other variables. TODO: - after committing this, merge the latest 5.0 into it - fix all tests - update the manual with upgrade notes. client/Makefile.am: mysqlbinlog.cc depends slightly on sql/mysql_priv.h client/mysqlbinlog.cc: Make mysqlbinlog able to read the new binlog format, by seeking to the start and reading the first few events, to detect the format of the binlog. include/my_sys.h: a correct tell() for SEQ_READ_APPEND caches. mysys/mf_iocache2.c: a correct tell() for SEQ_READ_APPEND caches (my_b_tell() is not working for such caches). sql/ha_innodb.cc: we are getting rid of event lengthes here and there, which is good. sql/log.cc: Start events will have created==0 if generated by rotation (like in 3.23). In 5.0 we always write a Format_description_log_event at the beginning of every master's binary log and of every slave's relay log. We also add Rotate and Stop to relay logs (like there already was in master's binary logs). When we rotate a relay log, we write the previous relay log's Start event (the one which was sent from the master) to the beginning of the new log, so that we don't need the previous relay log to understand the new one; that's the purpose of MYSQL_LOG::description_event_for_queue. Removed logging of SET FOREIGN_KEY_CHECKS, because we handle it as flags in the Query event now. sql/log_event.cc: New event type: Format_description_log_event, to describe the log's format. read_log_event() needs to be passed this event to be able to read 5.0 events. Query_log_event has new members flags2 and sql_mode for replication of session variables (except charsets which are WL#1062) and SQL_MODE. flags2 is in fact a kind of copy of thd->options (&'d with a mask). Now with this replication of FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS and SQL_MODE work; with mysqlbinlog too. sql/log_event.h: Binlog version is changed to 4. New classes (details in sql/log_event.cc). Removing some useless #defines. sql/mysql_priv.h: Definition of SELECT_DISTINCT and others must be visible in client/mysqlbinlog.cc, so adding #ifdefs. sql/mysqld.cc: update for prototype change sql/slave.cc: When the slave opens a relay log, it reads the first few events to know the format. When slave I/O thread receives a Rotate from the master, it rotates its relay log (to avoid mixed format in the relay log). sql/slave.h: in the slave we avoid lengthes and rely on absolute positions instead; hence the introduction of future_group_master_log_pos and future_event_relay_log_pos (explained in code). sql/sql_class.cc: catalog in THD sql/sql_class.h: catalog, and new members in MYSQL_LOG sql/sql_repl.cc: When the master starts sending binlog to slave, it must first read the first few events to detect the binlog's format. Same for SHOW BINLOG EVENTS.
Diffstat (limited to 'client')
-rw-r--r--client/Makefile.am2
-rw-r--r--client/mysqlbinlog.cc162
2 files changed, 134 insertions, 30 deletions
diff --git a/client/Makefile.am b/client/Makefile.am
index 2c54ec45989..612a5f01f8d 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -39,7 +39,7 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c
mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
mysqlmanagerc_SOURCES = mysqlmanagerc.c
mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
-sql_src=log_event.h log_event.cc
+sql_src=log_event.h mysql_priv.h log_event.cc
# Fix for mit-threads
DEFS = -DUNDEF_THREADS_HACK
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index 35f0db76ad6..90a1526c2c7 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -14,12 +14,28 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+
+ TODO: print the catalog (some USE catalog.db ????).
+
+ Standalone program to read a MySQL binary log (or relay log);
+ can read files produced by 3.23, 4.x, 5.0 servers.
+
+ Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0.
+ Should be able to read any file of these categories, even with --position.
+ An important fact: the Format_desc event of the log is at most the 3rd event
+ of the log; if it is the 3rd then there is this combination:
+ Format_desc_of_slave, Rotate_of_master, Format_desc_of_master.
+*/
+
#define MYSQL_CLIENT
#undef MYSQL_SERVER
#include "client_priv.h"
#include <time.h>
#include <assert.h>
#include "log_event.h"
+/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
+#include "mysql_priv.h"
#define BIN_LOG_HEADER_SIZE 4
#define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4)
@@ -481,21 +497,26 @@ static int check_master_version(MYSQL* mysql)
}
+/*
+ TODO fix this for new format (like local log); this will be done when 4.0 is
+ merged here (Victor's fixes are needed to make dump_remote_log_entries()
+ work).
+*/
+
static void dump_remote_log_entries(const char* logname)
{
char buf[128];
- char last_db[FN_REFLEN+1] = "";
+ LAST_EVENT_INFO last_event_info;
uint len;
NET* net = &mysql->net;
int old_format;
old_format = check_master_version(mysql);
if (!position)
- position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam
+ position = BIN_LOG_HEADER_SIZE;
if (position < BIN_LOG_HEADER_SIZE)
{
position = BIN_LOG_HEADER_SIZE;
- // warn the guity
sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE);
}
int4store(buf, position);
@@ -517,10 +538,11 @@ static void dump_remote_log_entries(const char* logname)
DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
len, net->read_pos[5]));
Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
- len - 1, &error, old_format);
+ len - 1, &error, 0);
+ //TODO this ,0) : we need to store the description_event like for local_log
if (ev)
{
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, &last_event_info);
if (ev->get_type_code() == LOAD_EVENT)
dump_remote_file(net, ((Load_log_event*)ev)->fname);
delete ev;
@@ -531,29 +553,98 @@ static void dump_remote_log_entries(const char* logname)
}
-static int check_header(IO_CACHE* file)
+static void check_header(IO_CACHE* file,
+ Format_description_log_event **description_event)
{
byte header[BIN_LOG_HEADER_SIZE];
byte buf[PROBE_HEADER_LEN];
- int old_format=0;
+ *description_event= new Format_description_log_event(3);
+ my_off_t tmp_pos;
my_off_t pos = my_b_tell(file);
my_b_seek(file, (my_off_t)0);
if (my_b_read(file, header, sizeof(header)))
die("Failed reading header; Probably an empty file");
if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
die("File is not a binary log file");
- if (!my_b_read(file, buf, sizeof(buf)))
+
+ /*
+ Imagine we are running with --position=1000. We still need to know the
+ binlog format's. So we still need to find, if there is one, the Format_desc
+ event, or to know if this is a 3.23 binlog. So we need to first read the
+ first events of the log, those around offset 4.
+ Even if we are reading a 3.23 binlog from the start (no --position): we need
+ to know the header length (which is 13 in 3.23, 19 in 4.x) to be able to
+ successfully print the first event (Start_log_event_v3). So even in this
+ case, we need to "probe" the first bytes of the log *before* we do a real
+ read_log_event(). Because read_log_event() needs to know the header's length
+ to work fine.
+ */
+ for(;;)
{
- if (buf[4] == START_EVENT)
+ tmp_pos= my_b_tell(file); /* should be 4 the first time */
+ if (my_b_read(file, buf, sizeof(buf)))
{
- uint event_len;
- event_len = uint4korr(buf + EVENT_LEN_OFFSET);
- old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
+ if (file->error)
+ die("\
+Could not read entry at offset %lu : Error in log format or read error",
+ tmp_pos);
+ /*
+ Otherwise this is just EOF : this log currently contains 0-2 events.
+ Maybe it's going to be filled in the next milliseconds; then we are
+ going to have a problem if this a 3.23 log (imagine we are locally
+ reading a 3.23 binlog which is being written presently): we won't know
+ it in read_log_event() and will fail().
+ Similar problems could happen with hot relay logs if --position is used
+ (but a --position which is posterior to the current size of the log).
+ These are rare problems anyway (reading a hot log + when we read the
+ first events there are not all there yet + when we read a bit later
+ there are more events + using a strange --position).
+ */
+ break;
+ }
+ else
+ {
+ DBUG_PRINT("info",("buf[4]=%d", buf[4]));
+ /* always test for a Start_v3, even if no --position */
+ if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */
+ {
+ if (uint4korr(buf + EVENT_LEN_OFFSET) <
+ (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN))
+ {
+ /* This is 3.23 (format 1) */
+ delete *description_event;
+ *description_event= new Format_description_log_event(1);
+ }
+ break;
+ }
+ else if (tmp_pos>=position)
+ break;
+ else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */
+ {
+ my_b_seek(file, tmp_pos); /* seek back to event's start */
+ if (!(*description_event= (Format_description_log_event*)
+ Log_event::read_log_event(file, *description_event)))
+ /* EOF can't be hit here normally, so it's a real error */
+ die("Could not read a Format_description_log_event event \
+at offset %lu ; this could be a log format error or read error",
+ tmp_pos);
+ DBUG_PRINT("info",("Setting description_event"));
+ }
+ else if (buf[4] == ROTATE_EVENT)
+ {
+ my_b_seek(file, tmp_pos); /* seek back to event's start */
+ if (!Log_event::read_log_event(file, *description_event))
+ /* EOF can't be hit here normally, so it's a real error */
+ die("Could not read a Rotate_log_event event \
+at offset %lu ; this could be a log format error or read error",
+ tmp_pos);
+ }
+ else
+ break;
}
}
my_b_seek(file, pos);
- return old_format;
}
@@ -562,11 +653,15 @@ static void dump_local_log_entries(const char* logname)
File fd = -1;
IO_CACHE cache,*file= &cache;
ulonglong rec_count = 0;
- char last_db[FN_REFLEN+1];
+ LAST_EVENT_INFO last_event_info;
byte tmp_buff[BIN_LOG_HEADER_SIZE];
- bool old_format = 0;
-
- last_db[0]=0;
+ /*
+ check_header() will set the pointer below.
+ Why do we need here a pointer on an event instead of an event ?
+ This is because the event will be created (alloced) in read_log_event()
+ (which returns a pointer) in check_header().
+ */
+ Format_description_log_event* description_event;
if (logname && logname[0] != '-')
{
@@ -575,14 +670,14 @@ static void dump_local_log_entries(const char* logname)
if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0,
MYF(MY_WME | MY_NABP)))
exit(1);
- old_format = check_header(file);
+ check_header(file, &description_event);
}
- else
+ else // reading from stdin; TODO: check that it works
{
if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0,
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
exit(1);
- old_format = check_header(file);
+ check_header(file, &description_event);
if (position)
{
/* skip 'position' characters from stdout */
@@ -599,6 +694,9 @@ static void dump_local_log_entries(const char* logname)
file->seek_not_done=0;
}
+ if (!description_event->is_valid())
+ die("Invalid Format_description log event; could be out of memory");
+
if (!position)
my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE); // Skip header
for (;;)
@@ -606,7 +704,7 @@ static void dump_local_log_entries(const char* logname)
char llbuff[21];
my_off_t old_off = my_b_tell(file);
- Log_event* ev = Log_event::read_log_event(file, old_format);
+ Log_event* ev = Log_event::read_log_event(file, description_event);
if (!ev)
{
if (file->error)
@@ -633,7 +731,7 @@ Could not read entry at offset %s : Error in log format or read error",
continue; // next
}
}
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, &last_event_info);
break;
case CREATE_FILE_EVENT:
{
@@ -661,18 +759,18 @@ Could not read entry at offset %s : Error in log format or read error",
filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT'
below.
*/
- ce->print(result_file, short_form, last_db, true);
+ ce->print(result_file, short_form, &last_event_info, true);
load_processor.process(ce);
ev= 0;
break;
}
case APPEND_BLOCK_EVENT:
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, &last_event_info);
load_processor.process((Append_block_log_event*)ev);
break;
case EXEC_LOAD_EVENT:
{
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, &last_event_info);
Execute_load_log_event *exv= (Execute_load_log_event*)ev;
Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
/*
@@ -682,7 +780,7 @@ Could not read entry at offset %s : Error in log format or read error",
*/
if (ce)
{
- ce->print(result_file, short_form, last_db,true);
+ ce->print(result_file, short_form, &last_event_info,true);
my_free((char*)ce->fname,MYF(MY_WME));
delete ce;
}
@@ -691,17 +789,23 @@ Could not read entry at offset %s : Error in log format or read error",
Create_file event for file_id: %u\n",exv->file_id);
break;
}
+ case FORMAT_DESCRIPTION_EVENT:
+ delete description_event;
+ description_event= (Format_description_log_event*) ev;
+ ev->print(result_file, short_form, &last_event_info);
+ break;
default:
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, &last_event_info);
}
}
rec_count++;
- if (ev)
- delete ev;
+ if (ev && ev->get_type_code()!=FORMAT_DESCRIPTION_EVENT)
+ delete ev; /* otherwise, deleted in the end */
}
if (fd >= 0)
my_close(fd, MYF(MY_WME));
end_io_cache(file);
+ delete description_event;
}