summaryrefslogtreecommitdiff
path: root/sql/log.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/log.cc')
-rw-r--r--sql/log.cc92
1 files changed, 69 insertions, 23 deletions
diff --git a/sql/log.cc b/sql/log.cc
index de5d94a2e93..831066ab401 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3949,65 +3949,111 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
long val;
uchar header[LOG_EVENT_HEADER_LEN];
+ /*
+ 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);
hdr_offs= carry= 0;
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);
+ /* assemble both halves */
memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
+ /* fix end_log_pos */
val= uint4korr(&header[LOG_POS_OFFSET]) + group;
int4store(&header[LOG_POS_OFFSET], val);
+ /* write the first half of the split header */
if (my_b_write(&log_file, header, carry))
return ER_ERROR_ON_WRITE;
+ /*
+ 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);
+ /* next event header at ... */
hdr_offs = LOG_EVENT_HEADER_LEN - carry +
uint4korr(&header[EVENT_LEN_OFFSET]);
carry= 0;
}
+ /* if there is anything to write, process it. */
+
if(likely(bytes > 0))
{
- do {
- DBUG_ASSERT((hdr_offs + max(EVENT_LEN_OFFSET, LOG_POS_OFFSET) + 4) <= bytes);
+ /*
+ next header beyond current read-buffer? we'll get it later
+ (though not necessarily in the very next iteration).
+ */
- val= uint4korr((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET) + group;
- int4store((char *)cache->read_pos + hdr_offs + LOG_POS_OFFSET, val);
- hdr_offs += uint4korr((char *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET);
+ if (hdr_offs >= bytes)
+ hdr_offs -= bytes;
+ else
+ {
- /* header beyond current read-buffer? */
- if (hdr_offs >= bytes)
- {
- hdr_offs -= bytes;
- break;
- }
+ /* process all event-headers in this (partial) cache. */
- /* split header? */
- if (hdr_offs + LOG_EVENT_HEADER_LEN > bytes)
- {
- carry= bytes - hdr_offs;
+ do {
- memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
- bytes -= carry;
- }
+ /*
+ partial header only? save what we can get, process once
+ we get the rest.
+ */
+
+ if (hdr_offs + LOG_EVENT_HEADER_LEN > bytes)
+ {
+ carry= bytes - hdr_offs;
+ memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
+ bytes= hdr_offs;
+ }
+ else
+ {
+ /* we've got a full event-header, and it came in one piece */
+
+ uchar *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= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
+ hdr_offs += uint4korr(log_pos);
- } while (hdr_offs < bytes);
+ }
+ } while (hdr_offs < bytes);
+ }
}
/* Write data to the binary log file */
-
if (my_b_write(&log_file, cache->read_pos, bytes))
return ER_ERROR_ON_WRITE;
- cache->read_pos= cache->read_end;
- } while ((bytes= my_b_fill(cache)));
+ cache->read_pos=cache->read_end; // Mark buffer used up
+ } while ((bytes=my_b_fill(cache)));
if (sync_log)
flush_and_sync();