Commit fa6ae59d authored by unknown's avatar unknown

Bug#22540: Incorrect value in column End_log_pos of SHOW BINLOG EVENTS using InnoDB

end_log_pos data within a transaction are relative to
the start of the transaction rather than absolute.
we fix those groups in situ before writing the log out.

additional comments and handling for groups with very
large single events, as suggested by Guilhem.

parent 279529c1
...@@ -1835,8 +1835,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) ...@@ -1835,8 +1835,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
if (likely(is_open())) // Should always be true if (likely(is_open())) // Should always be true
{ {
uint length, group, carry, hdr_offs; uint length, group, carry, hdr_offs, val;
long val;
byte header[LOG_EVENT_HEADER_LEN]; byte header[LOG_EVENT_HEADER_LEN];
/* /*
...@@ -1873,57 +1872,104 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) ...@@ -1873,57 +1872,104 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
length= my_b_bytes_in_cache(cache); length= my_b_bytes_in_cache(cache);
DBUG_EXECUTE_IF("half_binlogged_transaction", length-=100;); DBUG_EXECUTE_IF("half_binlogged_transaction", length-=100;);
/*
The events in the buffer have incorrect end_log_pos data
(relative to beginning of group rather than absolute),
so we'll recalculate them in situ so the binlog is always
correct, even in the middle of a group. This is possible
because we now know the start position of the group (the
offset of this cache in the log, if you will); all we need
to do is to find all event-headers, and add the position of
the group to the end_log_pos of each event. This is pretty
straight forward, except that we read the cache in segments,
so an event-header might end up on the cache-border and get
split.
*/
group= my_b_tell(&log_file); group= my_b_tell(&log_file);
hdr_offs= carry= 0; hdr_offs= carry= 0;
do do
{ {
if (likely(carry > 0))
/*
if we only got a partial header in the last iteration,
get the other half now and process a full header.
*/
if (unlikely(carry > 0))
{ {
DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN); DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
/* assemble both halves */
memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry); memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
/* fix end_log_pos */
val= uint4korr(&header[LOG_POS_OFFSET]) + group; val= uint4korr(&header[LOG_POS_OFFSET]) + group;
int4store(&header[LOG_POS_OFFSET], val); int4store(&header[LOG_POS_OFFSET], val);
/* write the first half of the split header */
if (my_b_write(&log_file, header, carry)) if (my_b_write(&log_file, header, carry))
goto err; goto err;
/*
copy fixed second half of header to cache so the correct
version will be written later.
*/
memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry); memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN - carry);
/* next event header at ... */
hdr_offs = LOG_EVENT_HEADER_LEN - carry + hdr_offs = LOG_EVENT_HEADER_LEN - carry +
uint4korr(&header[EVENT_LEN_OFFSET]); uint4korr(&header[EVENT_LEN_OFFSET]);
carry= 0; carry= 0;
} }
/* if there is anything to write, process it. */
if(likely(length > 0)) if(likely(length > 0))
{ {
do { /*
DBUG_ASSERT((hdr_offs + max(EVENT_LEN_OFFSET, LOG_POS_OFFSET) + 4) <= length); next header beyond current read-buffer? we'll get it later
(though not necessarily in the very next iteration).
*/
if (hdr_offs >= length)
hdr_offs -= length;
else
{
val= uint4korr((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET) + group; /* process all event-headers in this (partial) cache. */
int4store((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET, val);
hdr_offs += uint4korr((char *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET);
/* header beyond current read-buffer? */ do {
if (hdr_offs >= length)
{
hdr_offs -= length;
break;
}
/* split header? */ /*
if (hdr_offs + LOG_EVENT_HEADER_LEN > length) partial header only? save what we can get, process once
{ we get the rest.
carry= length - hdr_offs; */
memcpy(header, (char *)cache->read_pos + hdr_offs, carry); if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
length -= carry; {
} carry= length - hdr_offs;
memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
length= hdr_offs;
}
else
{
/* we've got a full event-header, and it came in one piece */
} while (hdr_offs < length); char *log_pos= cache->read_pos + hdr_offs + LOG_POS_OFFSET;
/* fix end_log_pos */
val= uint4korr(log_pos) + group;
int4store(log_pos, val);
/* next event header at ... */
log_pos= (char *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
hdr_offs += uint4korr(log_pos);
}
} while (hdr_offs < length);
}
} }
/* Write data to the binary log file */ /* Write data to the binary log file */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment