diff options
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_typeconv_innodb.result | 18 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_typeconv-slave.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_typeconv_innodb.test | 29 | ||||
-rw-r--r-- | sql/field.cc | 33 | ||||
-rw-r--r-- | sql/field.h | 8 | ||||
-rw-r--r-- | sql/log.cc | 5 | ||||
-rw-r--r-- | sql/log_event.cc | 10 | ||||
-rw-r--r-- | sql/log_event.h | 10 | ||||
-rw-r--r-- | sql/rpl_utility.cc | 16 | ||||
-rw-r--r-- | sql/rpl_utility.h | 9 |
10 files changed, 104 insertions, 35 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_typeconv_innodb.result b/mysql-test/suite/rpl/r/rpl_typeconv_innodb.result new file mode 100644 index 00000000000..0b4286c54eb --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_typeconv_innodb.result @@ -0,0 +1,18 @@ +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; +**** Resetting master and slave **** +include/stop_slave.inc +RESET SLAVE; +RESET MASTER; +include/start_slave.inc +SET @saved_slave_type_conversions = @@GLOBAL.SLAVE_TYPE_CONVERSIONS; +SET GLOBAL SLAVE_TYPE_CONVERSIONS = ''; +CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (b'0', b'01', b'101'); +Comparing tables master:test.t1 and slave:test.t1 +DROP TABLE t1; +SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions; diff --git a/mysql-test/suite/rpl/t/rpl_typeconv-slave.opt b/mysql-test/suite/rpl/t/rpl_typeconv-slave.opt new file mode 100644 index 00000000000..73ca7001985 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_typeconv-slave.opt @@ -0,0 +1 @@ +--innodb
\ No newline at end of file diff --git a/mysql-test/suite/rpl/t/rpl_typeconv_innodb.test b/mysql-test/suite/rpl/t/rpl_typeconv_innodb.test new file mode 100644 index 00000000000..729a622348f --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_typeconv_innodb.test @@ -0,0 +1,29 @@ +--source include/have_binlog_format_row.inc +--source include/have_innodb.inc +--source include/master-slave.inc + +# +# BUG#49618: Field length stored incorrectly in binary log for InnoDB +# + +source include/reset_master_and_slave.inc; + +connection slave; +SET @saved_slave_type_conversions = @@GLOBAL.SLAVE_TYPE_CONVERSIONS; +SET GLOBAL SLAVE_TYPE_CONVERSIONS = ''; + +connection master; +CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (b'0', b'01', b'101'); +sync_slave_with_master; + +let $diff_table_1=master:test.t1; +let $diff_table_2=slave:test.t1; +source include/diff_tables.inc; + +connection master; +DROP TABLE t1; +sync_slave_with_master; + +connection slave; +SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions; diff --git a/sql/field.cc b/sql/field.cc index b6a795d34aa..477cdc0a993 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1407,6 +1407,7 @@ bool Field::send_binary(Protocol *protocol) type, 0 is returned in <code>*order_var</code>. @param field_metadata Encoded size in field metadata + @param mflags Flags from the table map event for the table. @param order_var Pointer to variable where the order between the source field and this field will be returned. @@ -1416,6 +1417,7 @@ bool Field::send_binary(Protocol *protocol) */ bool Field::compatible_field_size(uint field_metadata, Relay_log_info *rli_arg __attribute__((unused)), + uint16 mflags __attribute__((unused)), int *order_var) { uint const source_size= pack_length_from_metadata(field_metadata); @@ -2950,6 +2952,7 @@ uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) bool Field_new_decimal::compatible_field_size(uint field_metadata, Relay_log_info * __attribute__((unused)), + uint16 mflags __attribute__((unused)), int *order_var) { uint const source_precision= (field_metadata >> 8U) & 0x00ff; @@ -6733,6 +6736,7 @@ check_field_for_37426(const void *param_arg) bool Field_string::compatible_field_size(uint field_metadata, Relay_log_info *rli_arg, + uint16 mflags __attribute__((unused)), int *order_var) { #ifdef HAVE_REPLICATION @@ -6741,7 +6745,7 @@ Field_string::compatible_field_size(uint field_metadata, check_field_for_37426, &check_param)) return FALSE; // Not compatible field sizes #endif - return Field::compatible_field_size(field_metadata, rli_arg, order_var); + return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var); } @@ -9305,8 +9309,13 @@ int Field_bit::do_save_field_metadata(uchar *metadata_ptr) DBUG_ENTER("Field_bit::do_save_field_metadata"); DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d", bit_len, bytes_in_rec)); - *metadata_ptr= bit_len; - *(metadata_ptr + 1)= bytes_in_rec; + /* + Since this class and Field_bit_as_char have different ideas of + what should be stored here, we compute the values of the metadata + explicitly using the field_length. + */ + metadata_ptr[0]= field_length % 8; + metadata_ptr[1]= field_length / 8; DBUG_RETURN(2); } @@ -9335,15 +9344,29 @@ uint Field_bit::pack_length_from_metadata(uint field_metadata) bool Field_bit::compatible_field_size(uint field_metadata, Relay_log_info * __attribute__((unused)), + uint16 mflags, int *order_var) { DBUG_ENTER("Field_bit::compatible_field_size"); DBUG_ASSERT((field_metadata >> 16) == 0); - uint const from_bit_len= + uint from_bit_len= 8 * (field_metadata >> 8) + (field_metadata & 0xff); - uint const to_bit_len= max_display_length(); + uint to_bit_len= max_display_length(); DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u", from_bit_len, to_bit_len)); + /* + If the bit length exact flag is clear, we are dealing with an old + master, so we allow some less strict behaviour if replicating by + moving both bit lengths to an even multiple of 8. + + We do this by computing the number of bytes to store the field + instead, and then compare the result. + */ + if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) { + from_bit_len= (from_bit_len + 7) / 8; + to_bit_len= (to_bit_len + 7) / 8; + } + *order_var= compare(from_bit_len, to_bit_len); DBUG_RETURN(TRUE); } diff --git a/sql/field.h b/sql/field.h index cfa3e57d2a0..12ee9b46d03 100644 --- a/sql/field.h +++ b/sql/field.h @@ -165,7 +165,7 @@ public: */ virtual uint32 pack_length_in_rec() const { return pack_length(); } virtual bool compatible_field_size(uint metadata, Relay_log_info *rli, - int *order); + uint16 mflags, int *order); virtual uint pack_length_from_metadata(uint field_metadata) { DBUG_ENTER("Field::pack_length_from_metadata"); @@ -805,7 +805,7 @@ public: uint pack_length_from_metadata(uint field_metadata); uint row_pack_length() { return pack_length(); } bool compatible_field_size(uint field_metadata, Relay_log_info *rli, - int *order_var); + uint16 mflags, int *order_var); uint is_equal(Create_field *new_field); virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data, bool low_byte_first); @@ -1500,7 +1500,7 @@ public: return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff); } bool compatible_field_size(uint field_metadata, Relay_log_info *rli, - int *order_var); + uint16 mflags, int *order_var); uint row_pack_length() { return field_length; } int pack_cmp(const uchar *a,const uchar *b,uint key_length, my_bool insert_or_update); @@ -1964,7 +1964,7 @@ public: uint row_pack_length() { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } bool compatible_field_size(uint metadata, Relay_log_info *rli, - int *order_var); + uint16 mflags, int *order_var); void sql_type(String &str) const; virtual uchar *pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first); diff --git a/sql/log.cc b/sql/log.cc index e8366c47863..f74b3ef858a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3846,11 +3846,8 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans) DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open()); DBUG_ASSERT(table->s->table_map_id != ULONG_MAX); - Table_map_log_event::flag_set const - flags= Table_map_log_event::TM_NO_FLAGS; - Table_map_log_event - the_event(this, table, table->s->table_map_id, is_trans, flags); + the_event(this, table, table->s->table_map_id, is_trans); if (is_trans && binlog_table_maps == 0) binlog_start_trans_and_stmt(); diff --git a/sql/log_event.cc b/sql/log_event.cc index 00919b65fa0..513fb7fe7e8 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -7852,7 +7852,7 @@ int Table_map_log_event::save_field_metadata() */ #if !defined(MYSQL_CLIENT) Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, - bool is_transactional, uint16 flags) + bool is_transactional) : Log_event(thd, 0, true), m_table(tbl), m_dbnam(tbl->s->db.str), @@ -7862,7 +7862,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, m_colcnt(tbl->s->fields), m_memory(NULL), m_table_id(tid), - m_flags(flags), + m_flags(TM_BIT_LEN_EXACT_F), m_data_size(0), m_field_metadata(0), m_field_metadata_size(0), @@ -8120,8 +8120,10 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli) inside Relay_log_info::clear_tables_to_lock() by calling the table_def destructor explicitly. */ - new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt, - m_field_metadata, m_field_metadata_size, m_null_bits); + new (&table_list->m_tabledef) + table_def(m_coltype, m_colcnt, + m_field_metadata, m_field_metadata_size, + m_null_bits, m_flags); table_list->m_tabledef_valid= TRUE; /* diff --git a/sql/log_event.h b/sql/log_event.h index 31d4a7480c2..673760557ca 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3283,16 +3283,14 @@ public: /* Special constants representing sets of flags */ enum { - TM_NO_FLAGS = 0U + TM_NO_FLAGS = 0U, + TM_BIT_LEN_EXACT_F = (1U << 0) }; - void set_flags(flag_set flag) { m_flags |= flag; } - void clear_flags(flag_set flag) { m_flags &= ~flag; } flag_set get_flags(flag_set flag) const { return m_flags & flag; } #ifndef MYSQL_CLIENT - Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, - bool is_transactional, uint16 flags); + Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional); #endif #ifdef HAVE_REPLICATION Table_map_log_event(const char *buf, uint event_len, @@ -3305,7 +3303,7 @@ public: table_def *create_table_def() { return new table_def(m_coltype, m_colcnt, m_field_metadata, - m_field_metadata_size, m_null_bits); + m_field_metadata_size, m_null_bits, m_flags); } ulong get_table_id() const { return m_table_id; } const char *get_table_name() const { return m_tblnam; } diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 9d26c133279..6d30a8bc99f 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -510,6 +510,9 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str) /** Check the order variable and print errors if the order is not acceptable according to the current settings. + + @param order The computed order of the conversion needed. + @param rli The relay log info data structure: for error reporting. */ bool is_conversion_ok(int order, Relay_log_info *rli) { @@ -562,6 +565,7 @@ bool is_conversion_ok(int order, Relay_log_info *rli) @param[in] type Source field type @param[in] metadata Source field metadata @param[in] rli Relay log info (for error reporting) + @param[in] mflags Flags from the table map event @param[out] order Order between source field and target field @return @c true if conversion is possible according to the current @@ -571,7 +575,8 @@ bool is_conversion_ok(int order, Relay_log_info *rli) static bool can_convert_field_to(Field *field, enum_field_types source_type, uint16 metadata, - Relay_log_info *rli, int *order_var) + Relay_log_info *rli, uint16 mflags, + int *order_var) { DBUG_ENTER("can_convert_field_to"); #ifndef DBUG_OFF @@ -588,7 +593,7 @@ can_convert_field_to(Field *field, if (field->real_type() == source_type) { DBUG_PRINT("debug", ("Base types are identical, doing field size comparison")); - if (field->compatible_field_size(metadata, rli, order_var)) + if (field->compatible_field_size(metadata, rli, mflags, order_var)) DBUG_RETURN(is_conversion_ok(*order_var, rli)); else DBUG_RETURN(false); @@ -765,7 +770,7 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli, { Field *const field= table->field[col]; int order; - if (can_convert_field_to(field, type(col), field_metadata(col), rli, &order)) + if (can_convert_field_to(field, type(col), field_metadata(col), rli, m_flags, &order)) { DBUG_PRINT("debug", ("Checking column %d -" " field '%s' can be converted - order: %d", @@ -937,9 +942,10 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * table_def::table_def(unsigned char *types, ulong size, uchar *field_metadata, int metadata_size, - uchar *null_bitmap) + uchar *null_bitmap, uint16 flags) : m_size(size), m_type(0), m_field_metadata_size(metadata_size), - m_field_metadata(0), m_null_bits(0), m_memory(NULL) + m_field_metadata(0), m_null_bits(0), m_flags(flags), + m_memory(NULL) { m_memory= (uchar *)my_multi_malloc(MYF(MY_WME), &m_type, size, diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 97ee668c82d..4b9bf3be93f 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -33,12 +33,6 @@ class Relay_log_info; - Extract and decode table definition data from the table map event - Check if table definition in table map is compatible with table definition on slave - - Currently, the only field type data available is an array of the - type operators that are present in the table map event. - - @todo Add type operands to this structure to allow detection of - difference between, e.g., BIT(5) and BIT(10). */ class table_def @@ -54,7 +48,7 @@ public: @param null_bitmap The bitmap of fields that can be null */ table_def(unsigned char *types, ulong size, uchar *field_metadata, - int metadata_size, uchar *null_bitmap); + int metadata_size, uchar *null_bitmap, uint16 flags); ~table_def(); @@ -215,6 +209,7 @@ private: uint m_field_metadata_size; uint16 *m_field_metadata; uchar *m_null_bits; + uint16 m_flags; // Table flags uchar *m_memory; }; |