Skip to content

Commit f43ca02

Browse files
author
Mats Kindahl
committed
BUG#49618: Field length stored incorrectly in binary log
for InnoDB The class Field_bit_as_char stores the metadata for the field incorrecly because bytes_in_rec and bit_len are set to (field_length + 7 ) / 8 and 0 respectively, while Field_bit has the correct values field_length / 8 and field_length % 8. Solved the problem by re-computing the values for the metadata based on the field_length instead of using the bytes_in_rec and bit_len variables. To handle compatibility with old server, a table map flag was added to indicate that the bit computation is exact. If the flag is clear, the slave computes the number of bytes required to store the bit field and compares that instead, effectively allowing replication *without conversion* from any field length that require the same number of bytes to store.
1 parent c701fe6 commit f43ca02

File tree

10 files changed

+104
-35
lines changed

10 files changed

+104
-35
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
stop slave;
2+
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
3+
reset master;
4+
reset slave;
5+
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
6+
start slave;
7+
**** Resetting master and slave ****
8+
include/stop_slave.inc
9+
RESET SLAVE;
10+
RESET MASTER;
11+
include/start_slave.inc
12+
SET @saved_slave_type_conversions = @@GLOBAL.SLAVE_TYPE_CONVERSIONS;
13+
SET GLOBAL SLAVE_TYPE_CONVERSIONS = '';
14+
CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB;
15+
INSERT INTO t1 VALUES (b'0', b'01', b'101');
16+
Comparing tables master:test.t1 and slave:test.t1
17+
DROP TABLE t1;
18+
SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--innodb
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--source include/have_binlog_format_row.inc
2+
--source include/have_innodb.inc
3+
--source include/master-slave.inc
4+
5+
#
6+
# BUG#49618: Field length stored incorrectly in binary log for InnoDB
7+
#
8+
9+
source include/reset_master_and_slave.inc;
10+
11+
connection slave;
12+
SET @saved_slave_type_conversions = @@GLOBAL.SLAVE_TYPE_CONVERSIONS;
13+
SET GLOBAL SLAVE_TYPE_CONVERSIONS = '';
14+
15+
connection master;
16+
CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB;
17+
INSERT INTO t1 VALUES (b'0', b'01', b'101');
18+
sync_slave_with_master;
19+
20+
let $diff_table_1=master:test.t1;
21+
let $diff_table_2=slave:test.t1;
22+
source include/diff_tables.inc;
23+
24+
connection master;
25+
DROP TABLE t1;
26+
sync_slave_with_master;
27+
28+
connection slave;
29+
SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions;

sql/field.cc

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,7 @@ bool Field::send_binary(Protocol *protocol)
14071407
type, 0 is returned in <code>*order_var</code>.
14081408
14091409
@param field_metadata Encoded size in field metadata
1410+
@param mflags Flags from the table map event for the table.
14101411
@param order_var Pointer to variable where the order
14111412
between the source field and this field
14121413
will be returned.
@@ -1416,6 +1417,7 @@ bool Field::send_binary(Protocol *protocol)
14161417
*/
14171418
bool Field::compatible_field_size(uint field_metadata,
14181419
Relay_log_info *rli_arg __attribute__((unused)),
1420+
uint16 mflags __attribute__((unused)),
14191421
int *order_var)
14201422
{
14211423
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)
29502952

29512953
bool Field_new_decimal::compatible_field_size(uint field_metadata,
29522954
Relay_log_info * __attribute__((unused)),
2955+
uint16 mflags __attribute__((unused)),
29532956
int *order_var)
29542957
{
29552958
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
@@ -6733,6 +6736,7 @@ check_field_for_37426(const void *param_arg)
67336736
bool
67346737
Field_string::compatible_field_size(uint field_metadata,
67356738
Relay_log_info *rli_arg,
6739+
uint16 mflags __attribute__((unused)),
67366740
int *order_var)
67376741
{
67386742
#ifdef HAVE_REPLICATION
@@ -6741,7 +6745,7 @@ Field_string::compatible_field_size(uint field_metadata,
67416745
check_field_for_37426, &check_param))
67426746
return FALSE; // Not compatible field sizes
67436747
#endif
6744-
return Field::compatible_field_size(field_metadata, rli_arg, order_var);
6748+
return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
67456749
}
67466750

67476751

@@ -9305,8 +9309,13 @@ int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
93059309
DBUG_ENTER("Field_bit::do_save_field_metadata");
93069310
DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
93079311
bit_len, bytes_in_rec));
9308-
*metadata_ptr= bit_len;
9309-
*(metadata_ptr + 1)= bytes_in_rec;
9312+
/*
9313+
Since this class and Field_bit_as_char have different ideas of
9314+
what should be stored here, we compute the values of the metadata
9315+
explicitly using the field_length.
9316+
*/
9317+
metadata_ptr[0]= field_length % 8;
9318+
metadata_ptr[1]= field_length / 8;
93109319
DBUG_RETURN(2);
93119320
}
93129321

@@ -9335,15 +9344,29 @@ uint Field_bit::pack_length_from_metadata(uint field_metadata)
93359344
bool
93369345
Field_bit::compatible_field_size(uint field_metadata,
93379346
Relay_log_info * __attribute__((unused)),
9347+
uint16 mflags,
93389348
int *order_var)
93399349
{
93409350
DBUG_ENTER("Field_bit::compatible_field_size");
93419351
DBUG_ASSERT((field_metadata >> 16) == 0);
9342-
uint const from_bit_len=
9352+
uint from_bit_len=
93439353
8 * (field_metadata >> 8) + (field_metadata & 0xff);
9344-
uint const to_bit_len= max_display_length();
9354+
uint to_bit_len= max_display_length();
93459355
DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
93469356
from_bit_len, to_bit_len));
9357+
/*
9358+
If the bit length exact flag is clear, we are dealing with an old
9359+
master, so we allow some less strict behaviour if replicating by
9360+
moving both bit lengths to an even multiple of 8.
9361+
9362+
We do this by computing the number of bytes to store the field
9363+
instead, and then compare the result.
9364+
*/
9365+
if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
9366+
from_bit_len= (from_bit_len + 7) / 8;
9367+
to_bit_len= (to_bit_len + 7) / 8;
9368+
}
9369+
93479370
*order_var= compare(from_bit_len, to_bit_len);
93489371
DBUG_RETURN(TRUE);
93499372
}

sql/field.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class Field
165165
*/
166166
virtual uint32 pack_length_in_rec() const { return pack_length(); }
167167
virtual bool compatible_field_size(uint metadata, Relay_log_info *rli,
168-
int *order);
168+
uint16 mflags, int *order);
169169
virtual uint pack_length_from_metadata(uint field_metadata)
170170
{
171171
DBUG_ENTER("Field::pack_length_from_metadata");
@@ -805,7 +805,7 @@ class Field_new_decimal :public Field_num {
805805
uint pack_length_from_metadata(uint field_metadata);
806806
uint row_pack_length() { return pack_length(); }
807807
bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
808-
int *order_var);
808+
uint16 mflags, int *order_var);
809809
uint is_equal(Create_field *new_field);
810810
virtual const uchar *unpack(uchar* to, const uchar *from,
811811
uint param_data, bool low_byte_first);
@@ -1500,7 +1500,7 @@ class Field_string :public Field_longstr {
15001500
return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff);
15011501
}
15021502
bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
1503-
int *order_var);
1503+
uint16 mflags, int *order_var);
15041504
uint row_pack_length() { return field_length; }
15051505
int pack_cmp(const uchar *a,const uchar *b,uint key_length,
15061506
my_bool insert_or_update);
@@ -1964,7 +1964,7 @@ class Field_bit :public Field {
19641964
uint row_pack_length()
19651965
{ return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
19661966
bool compatible_field_size(uint metadata, Relay_log_info *rli,
1967-
int *order_var);
1967+
uint16 mflags, int *order_var);
19681968
void sql_type(String &str) const;
19691969
virtual uchar *pack(uchar *to, const uchar *from,
19701970
uint max_length, bool low_byte_first);

sql/log.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3846,11 +3846,8 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans)
38463846
DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
38473847
DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
38483848

3849-
Table_map_log_event::flag_set const
3850-
flags= Table_map_log_event::TM_NO_FLAGS;
3851-
38523849
Table_map_log_event
3853-
the_event(this, table, table->s->table_map_id, is_trans, flags);
3850+
the_event(this, table, table->s->table_map_id, is_trans);
38543851

38553852
if (is_trans && binlog_table_maps == 0)
38563853
binlog_start_trans_and_stmt();

sql/log_event.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7852,7 +7852,7 @@ int Table_map_log_event::save_field_metadata()
78527852
*/
78537853
#if !defined(MYSQL_CLIENT)
78547854
Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
7855-
bool is_transactional, uint16 flags)
7855+
bool is_transactional)
78567856
: Log_event(thd, 0, true),
78577857
m_table(tbl),
78587858
m_dbnam(tbl->s->db.str),
@@ -7862,7 +7862,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
78627862
m_colcnt(tbl->s->fields),
78637863
m_memory(NULL),
78647864
m_table_id(tid),
7865-
m_flags(flags),
7865+
m_flags(TM_BIT_LEN_EXACT_F),
78667866
m_data_size(0),
78677867
m_field_metadata(0),
78687868
m_field_metadata_size(0),
@@ -8120,8 +8120,10 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
81208120
inside Relay_log_info::clear_tables_to_lock() by calling the
81218121
table_def destructor explicitly.
81228122
*/
8123-
new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt,
8124-
m_field_metadata, m_field_metadata_size, m_null_bits);
8123+
new (&table_list->m_tabledef)
8124+
table_def(m_coltype, m_colcnt,
8125+
m_field_metadata, m_field_metadata_size,
8126+
m_null_bits, m_flags);
81258127
table_list->m_tabledef_valid= TRUE;
81268128

81278129
/*

sql/log_event.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,16 +3283,14 @@ class Table_map_log_event : public Log_event
32833283
/* Special constants representing sets of flags */
32843284
enum
32853285
{
3286-
TM_NO_FLAGS = 0U
3286+
TM_NO_FLAGS = 0U,
3287+
TM_BIT_LEN_EXACT_F = (1U << 0)
32873288
};
32883289

3289-
void set_flags(flag_set flag) { m_flags |= flag; }
3290-
void clear_flags(flag_set flag) { m_flags &= ~flag; }
32913290
flag_set get_flags(flag_set flag) const { return m_flags & flag; }
32923291

32933292
#ifndef MYSQL_CLIENT
3294-
Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
3295-
bool is_transactional, uint16 flags);
3293+
Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional);
32963294
#endif
32973295
#ifdef HAVE_REPLICATION
32983296
Table_map_log_event(const char *buf, uint event_len,
@@ -3305,7 +3303,7 @@ class Table_map_log_event : public Log_event
33053303
table_def *create_table_def()
33063304
{
33073305
return new table_def(m_coltype, m_colcnt, m_field_metadata,
3308-
m_field_metadata_size, m_null_bits);
3306+
m_field_metadata_size, m_null_bits, m_flags);
33093307
}
33103308
ulong get_table_id() const { return m_table_id; }
33113309
const char *get_table_name() const { return m_tblnam; }

sql/rpl_utility.cc

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str)
510510
/**
511511
Check the order variable and print errors if the order is not
512512
acceptable according to the current settings.
513+
514+
@param order The computed order of the conversion needed.
515+
@param rli The relay log info data structure: for error reporting.
513516
*/
514517
bool is_conversion_ok(int order, Relay_log_info *rli)
515518
{
@@ -562,6 +565,7 @@ bool is_conversion_ok(int order, Relay_log_info *rli)
562565
@param[in] type Source field type
563566
@param[in] metadata Source field metadata
564567
@param[in] rli Relay log info (for error reporting)
568+
@param[in] mflags Flags from the table map event
565569
@param[out] order Order between source field and target field
566570
567571
@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)
571575
static bool
572576
can_convert_field_to(Field *field,
573577
enum_field_types source_type, uint16 metadata,
574-
Relay_log_info *rli, int *order_var)
578+
Relay_log_info *rli, uint16 mflags,
579+
int *order_var)
575580
{
576581
DBUG_ENTER("can_convert_field_to");
577582
#ifndef DBUG_OFF
@@ -588,7 +593,7 @@ can_convert_field_to(Field *field,
588593
if (field->real_type() == source_type)
589594
{
590595
DBUG_PRINT("debug", ("Base types are identical, doing field size comparison"));
591-
if (field->compatible_field_size(metadata, rli, order_var))
596+
if (field->compatible_field_size(metadata, rli, mflags, order_var))
592597
DBUG_RETURN(is_conversion_ok(*order_var, rli));
593598
else
594599
DBUG_RETURN(false);
@@ -765,7 +770,7 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
765770
{
766771
Field *const field= table->field[col];
767772
int order;
768-
if (can_convert_field_to(field, type(col), field_metadata(col), rli, &order))
773+
if (can_convert_field_to(field, type(col), field_metadata(col), rli, m_flags, &order))
769774
{
770775
DBUG_PRINT("debug", ("Checking column %d -"
771776
" field '%s' can be converted - order: %d",
@@ -937,9 +942,10 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
937942

938943
table_def::table_def(unsigned char *types, ulong size,
939944
uchar *field_metadata, int metadata_size,
940-
uchar *null_bitmap)
945+
uchar *null_bitmap, uint16 flags)
941946
: m_size(size), m_type(0), m_field_metadata_size(metadata_size),
942-
m_field_metadata(0), m_null_bits(0), m_memory(NULL)
947+
m_field_metadata(0), m_null_bits(0), m_flags(flags),
948+
m_memory(NULL)
943949
{
944950
m_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
945951
&m_type, size,

sql/rpl_utility.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ class Relay_log_info;
3333
- Extract and decode table definition data from the table map event
3434
- Check if table definition in table map is compatible with table
3535
definition on slave
36-
37-
Currently, the only field type data available is an array of the
38-
type operators that are present in the table map event.
39-
40-
@todo Add type operands to this structure to allow detection of
41-
difference between, e.g., BIT(5) and BIT(10).
4236
*/
4337

4438
class table_def
@@ -54,7 +48,7 @@ class table_def
5448
@param null_bitmap The bitmap of fields that can be null
5549
*/
5650
table_def(unsigned char *types, ulong size, uchar *field_metadata,
57-
int metadata_size, uchar *null_bitmap);
51+
int metadata_size, uchar *null_bitmap, uint16 flags);
5852

5953
~table_def();
6054

@@ -215,6 +209,7 @@ class table_def
215209
uint m_field_metadata_size;
216210
uint16 *m_field_metadata;
217211
uchar *m_null_bits;
212+
uint16 m_flags; // Table flags
218213
uchar *m_memory;
219214
};
220215

0 commit comments

Comments
 (0)