Browse Source
BUG#27779 (Slave cannot read old rows log events):
BUG#27779 (Slave cannot read old rows log events):
Taking code from before BUG#22583 and incorporating as events to be able to read old events. Also incorporating old pack and unpack functions into patch.pull/73/head
11 changed files with 403 additions and 4 deletions
-
4client/Makefile.am
-
2client/mysqlbinlog.cc
-
4libmysqld/Makefile.am
-
1sql/CMakeLists.txt
-
2sql/Makefile.am
-
9sql/log_event.cc
-
6sql/log_event.h
-
98sql/log_event_old.cc
-
91sql/log_event_old.h
-
173sql/rpl_record_old.cc
-
17sql/rpl_record_old.h
@ -0,0 +1,98 @@ |
|||
|
|||
#include "mysql_priv.h"
|
|||
#include "log_event_old.h"
|
|||
#include "rpl_record_old.h"
|
|||
|
|||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
|||
int |
|||
Write_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, |
|||
TABLE *table, |
|||
char const *row_start, |
|||
char const **row_end) |
|||
{ |
|||
DBUG_ASSERT(table != NULL); |
|||
DBUG_ASSERT(row_start && row_end); |
|||
|
|||
int error; |
|||
error= unpack_row_old(rli, table, m_width, table->record[0], |
|||
row_start, &m_cols, row_end, &m_master_reclength, |
|||
table->write_set, PRE_GA_WRITE_ROWS_EVENT); |
|||
bitmap_copy(table->read_set, table->write_set); |
|||
return error; |
|||
} |
|||
|
|||
|
|||
int |
|||
Delete_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, |
|||
TABLE *table, |
|||
char const *row_start, |
|||
char const **row_end) |
|||
{ |
|||
int error; |
|||
DBUG_ASSERT(row_start && row_end); |
|||
/*
|
|||
This assertion actually checks that there is at least as many |
|||
columns on the slave as on the master. |
|||
*/ |
|||
DBUG_ASSERT(table->s->fields >= m_width); |
|||
|
|||
error= unpack_row_old(rli, table, m_width, table->record[0], |
|||
row_start, &m_cols, row_end, &m_master_reclength, |
|||
table->read_set, PRE_GA_DELETE_ROWS_EVENT); |
|||
/*
|
|||
If we will access rows using the random access method, m_key will |
|||
be set to NULL, so we do not need to make a key copy in that case. |
|||
*/ |
|||
if (m_key) |
|||
{ |
|||
KEY *const key_info= table->key_info; |
|||
|
|||
key_copy(m_key, table->record[0], key_info, 0); |
|||
} |
|||
|
|||
return error; |
|||
} |
|||
|
|||
|
|||
int Update_rows_log_event_old::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, |
|||
TABLE *table, |
|||
char const *row_start, |
|||
char const **row_end) |
|||
{ |
|||
int error; |
|||
DBUG_ASSERT(row_start && row_end); |
|||
/*
|
|||
This assertion actually checks that there is at least as many |
|||
columns on the slave as on the master. |
|||
*/ |
|||
DBUG_ASSERT(table->s->fields >= m_width); |
|||
|
|||
/* record[0] is the before image for the update */ |
|||
error= unpack_row_old(rli, table, m_width, table->record[0], |
|||
row_start, &m_cols, row_end, &m_master_reclength, |
|||
table->read_set, PRE_GA_UPDATE_ROWS_EVENT); |
|||
row_start = *row_end; |
|||
/* m_after_image is the after image for the update */ |
|||
error= unpack_row_old(rli, table, m_width, m_after_image, |
|||
row_start, &m_cols, row_end, &m_master_reclength, |
|||
table->write_set, PRE_GA_UPDATE_ROWS_EVENT); |
|||
|
|||
DBUG_DUMP("record[0]", table->record[0], table->s->reclength); |
|||
DBUG_DUMP("m_after_image", m_after_image, table->s->reclength); |
|||
|
|||
|
|||
/*
|
|||
If we will access rows using the random access method, m_key will |
|||
be set to NULL, so we do not need to make a key copy in that case. |
|||
*/ |
|||
if (m_key) |
|||
{ |
|||
KEY *const key_info= table->key_info; |
|||
|
|||
key_copy(m_key, table->record[0], key_info, 0); |
|||
} |
|||
|
|||
return error; |
|||
} |
|||
|
|||
#endif
|
|||
@ -0,0 +1,91 @@ |
|||
#ifndef LOG_EVENT_OLD_H |
|||
#define LOG_EVENT_OLD_H |
|||
|
|||
/* |
|||
Need to include this file at the proper position of log_event.h |
|||
*/ |
|||
|
|||
|
|||
class Write_rows_log_event_old : public Write_rows_log_event |
|||
{ |
|||
public: |
|||
enum |
|||
{ |
|||
/* Support interface to THD::binlog_prepare_pending_rows_event */ |
|||
TYPE_CODE = PRE_GA_WRITE_ROWS_EVENT |
|||
}; |
|||
|
|||
#if defined(HAVE_REPLICATION) |
|||
Write_rows_log_event_old(const char *buf, uint event_len, |
|||
const Format_description_log_event *descr) |
|||
: Write_rows_log_event(buf, event_len, descr) |
|||
{ |
|||
} |
|||
#endif |
|||
|
|||
private: |
|||
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } |
|||
|
|||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) |
|||
virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, |
|||
char const *row_start, char const **row_end); |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
class Update_rows_log_event_old : public Update_rows_log_event |
|||
{ |
|||
public: |
|||
enum |
|||
{ |
|||
/* Support interface to THD::binlog_prepare_pending_rows_event */ |
|||
TYPE_CODE = PRE_GA_UPDATE_ROWS_EVENT |
|||
}; |
|||
|
|||
#if defined(HAVE_REPLICATION) |
|||
Update_rows_log_event_old(const char *buf, uint event_len, |
|||
const Format_description_log_event *descr) |
|||
: Update_rows_log_event(buf, event_len, descr) |
|||
{ |
|||
} |
|||
#endif |
|||
|
|||
private: |
|||
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } |
|||
|
|||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) |
|||
virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, |
|||
char const *row_start, char const **row_end); |
|||
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ |
|||
}; |
|||
|
|||
|
|||
class Delete_rows_log_event_old : public Delete_rows_log_event |
|||
{ |
|||
public: |
|||
enum |
|||
{ |
|||
/* Support interface to THD::binlog_prepare_pending_rows_event */ |
|||
TYPE_CODE = PRE_GA_DELETE_ROWS_EVENT |
|||
}; |
|||
|
|||
#if defined(HAVE_REPLICATION) |
|||
Delete_rows_log_event_old(const char *buf, uint event_len, |
|||
const Format_description_log_event *descr) |
|||
: Delete_rows_log_event(buf, event_len, descr) |
|||
{ |
|||
} |
|||
#endif |
|||
|
|||
private: |
|||
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; } |
|||
|
|||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) |
|||
virtual int do_prepare_row(THD*, RELAY_LOG_INFO*, TABLE*, |
|||
char const *row_start, char const **row_end); |
|||
#endif |
|||
}; |
|||
|
|||
|
|||
#endif |
|||
|
|||
@ -0,0 +1,173 @@ |
|||
|
|||
#include "mysql_priv.h"
|
|||
#include "rpl_record_old.h"
|
|||
|
|||
my_size_t |
|||
pack_row_old(THD *thd, TABLE *table, MY_BITMAP const* cols, |
|||
byte *row_data, const byte *record) |
|||
{ |
|||
Field **p_field= table->field, *field; |
|||
int n_null_bytes= table->s->null_bytes; |
|||
byte *ptr; |
|||
uint i; |
|||
my_ptrdiff_t const rec_offset= record - table->record[0]; |
|||
my_ptrdiff_t const def_offset= table->s->default_values - table->record[0]; |
|||
memcpy(row_data, record, n_null_bytes); |
|||
ptr= row_data+n_null_bytes; |
|||
|
|||
for (i= 0 ; (field= *p_field) ; i++, p_field++) |
|||
{ |
|||
if (bitmap_is_set(cols,i)) |
|||
{ |
|||
my_ptrdiff_t const offset= |
|||
field->is_null(rec_offset) ? def_offset : rec_offset; |
|||
field->move_field_offset(offset); |
|||
ptr= (byte*)field->pack((char *) ptr, field->ptr); |
|||
field->move_field_offset(-offset); |
|||
} |
|||
} |
|||
return (static_cast<my_size_t>(ptr - row_data)); |
|||
} |
|||
|
|||
|
|||
/*
|
|||
Unpack a row into a record. |
|||
|
|||
SYNOPSIS |
|||
unpack_row() |
|||
rli Relay log info |
|||
table Table to unpack into |
|||
colcnt Number of columns to read from record |
|||
record Record where the data should be unpacked |
|||
row Packed row data |
|||
cols Pointer to columns data to fill in |
|||
row_end Pointer to variable that will hold the value of the |
|||
one-after-end position for the row |
|||
master_reclength |
|||
Pointer to variable that will be set to the length of the |
|||
record on the master side |
|||
rw_set Pointer to bitmap that holds either the read_set or the |
|||
write_set of the table |
|||
|
|||
DESCRIPTION |
|||
|
|||
The row is assumed to only consist of the fields for which the |
|||
bitset represented by 'arr' and 'bits'; the other parts of the |
|||
record are left alone. |
|||
|
|||
At most 'colcnt' columns are read: if the table is larger than |
|||
that, the remaining fields are not filled in. |
|||
|
|||
RETURN VALUE |
|||
|
|||
Error code, or zero if no error. The following error codes can |
|||
be returned: |
|||
|
|||
ER_NO_DEFAULT_FOR_FIELD |
|||
Returned if one of the fields existing on the slave but not on |
|||
the master does not have a default value (and isn't nullable) |
|||
*/ |
|||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
|||
int |
|||
unpack_row_old(RELAY_LOG_INFO *rli, |
|||
TABLE *table, uint const colcnt, byte *record, |
|||
char const *row, MY_BITMAP const *cols, |
|||
char const **row_end, ulong *master_reclength, |
|||
MY_BITMAP* const rw_set, Log_event_type const event_type) |
|||
{ |
|||
DBUG_ASSERT(record && row); |
|||
my_ptrdiff_t const offset= record - (byte*) table->record[0]; |
|||
my_size_t master_null_bytes= table->s->null_bytes; |
|||
|
|||
if (colcnt != table->s->fields) |
|||
{ |
|||
Field **fptr= &table->field[colcnt-1]; |
|||
do |
|||
master_null_bytes= (*fptr)->last_null_byte(); |
|||
while (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF && |
|||
fptr-- > table->field); |
|||
|
|||
/*
|
|||
If master_null_bytes is LAST_NULL_BYTE_UNDEF (0) at this time, |
|||
there were no nullable fields nor BIT fields at all in the |
|||
columns that are common to the master and the slave. In that |
|||
case, there is only one null byte holding the X bit. |
|||
|
|||
OBSERVE! There might still be nullable columns following the |
|||
common columns, so table->s->null_bytes might be greater than 1. |
|||
*/ |
|||
if (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF) |
|||
master_null_bytes= 1; |
|||
} |
|||
|
|||
DBUG_ASSERT(master_null_bytes <= table->s->null_bytes); |
|||
memcpy(record, row, master_null_bytes); // [1]
|
|||
int error= 0; |
|||
|
|||
bitmap_set_all(rw_set); |
|||
|
|||
Field **const begin_ptr = table->field; |
|||
Field **field_ptr; |
|||
char const *ptr= row + master_null_bytes; |
|||
Field **const end_ptr= begin_ptr + colcnt; |
|||
for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr) |
|||
{ |
|||
Field *const f= *field_ptr; |
|||
|
|||
if (bitmap_is_set(cols, field_ptr - begin_ptr)) |
|||
{ |
|||
f->move_field_offset(offset); |
|||
ptr= f->unpack(f->ptr, ptr); |
|||
f->move_field_offset(-offset); |
|||
/* Field...::unpack() cannot return 0 */ |
|||
DBUG_ASSERT(ptr != NULL); |
|||
} |
|||
else |
|||
bitmap_clear_bit(rw_set, field_ptr - begin_ptr); |
|||
} |
|||
|
|||
*row_end = ptr; |
|||
if (master_reclength) |
|||
{ |
|||
if (*field_ptr) |
|||
*master_reclength = (*field_ptr)->ptr - (char*) table->record[0]; |
|||
else |
|||
*master_reclength = table->s->reclength; |
|||
} |
|||
|
|||
/*
|
|||
Set properties for remaining columns, if there are any. We let the |
|||
corresponding bit in the write_set be set, to write the value if |
|||
it was not there already. We iterate over all remaining columns, |
|||
even if there were an error, to get as many error messages as |
|||
possible. We are still able to return a pointer to the next row, |
|||
so redo that. |
|||
|
|||
This generation of error messages is only relevant when inserting |
|||
new rows. |
|||
*/ |
|||
for ( ; *field_ptr ; ++field_ptr) |
|||
{ |
|||
uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; |
|||
|
|||
DBUG_PRINT("debug", ("flags = 0x%x, mask = 0x%x, flags & mask = 0x%x", |
|||
(*field_ptr)->flags, mask, |
|||
(*field_ptr)->flags & mask)); |
|||
|
|||
if (event_type == WRITE_ROWS_EVENT && |
|||
((*field_ptr)->flags & mask) == mask) |
|||
{ |
|||
slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, |
|||
"Field `%s` of table `%s`.`%s` " |
|||
"has no default value and cannot be NULL", |
|||
(*field_ptr)->field_name, table->s->db.str, |
|||
table->s->table_name.str); |
|||
error = ER_NO_DEFAULT_FOR_FIELD; |
|||
} |
|||
else |
|||
(*field_ptr)->set_default(); |
|||
} |
|||
|
|||
return error; |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,17 @@ |
|||
#ifndef RPL_RECORD_OLD_H |
|||
#define RPL_RECORD_OLD_H |
|||
|
|||
#ifndef MYSQL_CLIENT |
|||
my_size_t pack_row_old(THD *thd, TABLE *table, MY_BITMAP const* cols, |
|||
byte *row_data, const byte *record); |
|||
|
|||
#ifdef HAVE_REPLICATION |
|||
int unpack_row_old(RELAY_LOG_INFO *rli, |
|||
TABLE *table, uint const colcnt, byte *record, |
|||
char const *row, MY_BITMAP const *cols, |
|||
char const **row_end, ulong *master_reclength, |
|||
MY_BITMAP* const rw_set, |
|||
Log_event_type const event_type); |
|||
#endif |
|||
#endif |
|||
#endif |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue