Skip to content

Commit 9ac3eea

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. --BZR-- revision-id: [email protected] property-branch-nick: b49618-mysql-5.1 property-file-info: ld7:file_id54:rpl_typeconv_innodb.-20091214224111-2zdokg6suo6r12eb-37:message78:Adding test to check compatibility for bit field property-file-info: replication when using InnoDB4:path47:mysql-test/suite/rpl/t/rpl_typeconv_innodb.tested7:file_id61:sp1f-field.cc-19700101030959-f4imaofclsea3n4fj4ow5m7havmyxa2r7:message97:Extending compatible_field_size() with flags from property-file-info: table map to allow fields to check master info.4:path12:sql/field.cced7:file_id60:sp1f-field.h-19700101030959-3n6smzxcwkjl7bikm3wg4hfkjn66uvvp7:message98:Extending compatible_field_size() with flags from property-file-info: table map to allow fields to check master info. property-file-info: 4:path11:sql/field.hed7:file_id59:sp1f-log.cc-19700101030959-r3hdfovek4kl6nd64ovoaknmirota6bq7:message73:Removing table map flags since they are not used property-file-info: outside table map class.4:path10:sql/log.cced7:file_id65:sp1f-log_event.cc-19700101030959-msmqlflsngxosswid2hpzxly5vfqdddc7:message105:Removing flags parameter from table map constructor property-file-info: since it is not used and does not have to be exposed.4:path16:sql/log_event.cced7:file_id64:sp1f-log_event.h-19700101030959-clq6ett55tcqbpys2i4cpfrdccq7j4om7:message108:Adding flag to denote that bit length for bit field type property-file-info: is exact and not potentially rounded to even bytes.4:path15:sql/log_event.hed7:file_id67:sp1f-rpl_utility.cc-20060503130029-owknu72jo3254qe7gjoeh7oilfrz4atk7:message52:Adding fields to table_def to store table map flags.4:path18:sql/rpl_utility.cced7:file_id66:sp1f-rpl_utility.h-20060503130029-u44nzzcbdenh2gegnnyzro26kbk5quw77:message80:Removing obsolete comment and adding flags to store property-file-info: table map flags from master.4:path17:sql/rpl_utility.hee testament3-sha1: 8a6f2f2cf493b172642e1010e405f82c46590aa1
1 parent 141dcaa commit 9ac3eea

11 files changed

Lines changed: 91 additions & 46 deletions

File tree

.bzrfileids

309 Bytes
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB;
13+
INSERT INTO t1 VALUES (b'0', b'01', b'101');
14+
Comparing tables master:test.t1 and slave:test.t1
15+
DROP TABLE t1;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--innodb
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 master;
12+
CREATE TABLE t1(b1 BIT(1), b2 BIT(2), b3 BIT(3)) ENGINE=InnoDB;
13+
INSERT INTO t1 VALUES (b'0', b'01', b'101');
14+
sync_slave_with_master;
15+
16+
let $diff_table_1=master:test.t1;
17+
let $diff_table_2=slave:test.t1;
18+
source include/diff_tables.inc;
19+
20+
connection master;
21+
DROP TABLE t1;
22+
sync_slave_with_master;

sql/field.cc

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,14 @@ bool Field::send_binary(Protocol *protocol)
13731373
to the size of this field (the slave or destination).
13741374
13751375
@param field_metadata Encoded size in field metadata
1376+
@param mflags Flags from the table map event for the table.
13761377
13771378
@retval 0 if this field's size is < the source field's size
13781379
@retval 1 if this field's size is >= the source field's size
13791380
*/
13801381
int Field::compatible_field_size(uint field_metadata,
1381-
const Relay_log_info *rli_arg __attribute__((unused)))
1382+
const Relay_log_info *rli_arg __attribute__((unused)),
1383+
uint16 mflags __attribute__((unused)))
13821384
{
13831385
uint const source_size= pack_length_from_metadata(field_metadata);
13841386
uint const destination_size= row_pack_length();
@@ -2836,7 +2838,8 @@ uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
28362838
@retval 1 if this field's size is >= the source field's size
28372839
*/
28382840
int Field_new_decimal::compatible_field_size(uint field_metadata,
2839-
const Relay_log_info * __attribute__((unused)))
2841+
const Relay_log_info * __attribute__((unused)),
2842+
uint16 mflags __attribute__((unused)))
28402843
{
28412844
int compatible= 0;
28422845
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
@@ -6612,15 +6615,16 @@ check_field_for_37426(const void *param_arg)
66126615
#endif
66136616

66146617
int Field_string::compatible_field_size(uint field_metadata,
6615-
const Relay_log_info *rli_arg)
6618+
const Relay_log_info *rli_arg,
6619+
uint16 mflags __attribute__((unused)))
66166620
{
66176621
#ifdef HAVE_REPLICATION
66186622
const Check_field_param check_param = { this };
66196623
if (rpl_master_has_bug(rli_arg, 37426, TRUE,
66206624
check_field_for_37426, &check_param))
66216625
return FALSE; // Not compatible field sizes
66226626
#endif
6623-
return Field::compatible_field_size(field_metadata, rli_arg);
6627+
return Field::compatible_field_size(field_metadata, rli_arg, mflags);
66246628
}
66256629

66266630

@@ -9172,8 +9176,13 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
91729176
*/
91739177
int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
91749178
{
9175-
*metadata_ptr= bit_len;
9176-
*(metadata_ptr + 1)= bytes_in_rec;
9179+
/*
9180+
Since this class and Field_bit_as_char have different ideas of
9181+
what should be stored here, we compute the values of the metadata
9182+
explicitly using the field_length.
9183+
*/
9184+
metadata_ptr[0]= field_length % 8;
9185+
metadata_ptr[1]= field_length / 8;
91779186
return 2;
91789187
}
91799188

@@ -9213,20 +9222,26 @@ uint Field_bit::pack_length_from_metadata(uint field_metadata)
92139222
@retval 1 if this field's size is >= the source field's size
92149223
*/
92159224
int Field_bit::compatible_field_size(uint field_metadata,
9216-
const Relay_log_info * __attribute__((unused)))
9225+
const Relay_log_info * __attribute__((unused)),
9226+
uint16 mflags)
92179227
{
9218-
int compatible= 0;
9219-
uint const source_size= pack_length_from_metadata(field_metadata);
9220-
uint const destination_size= row_pack_length();
9221-
uint const from_bit_len= field_metadata & 0x00ff;
9222-
uint const from_len= (field_metadata >> 8U) & 0x00ff;
9223-
if ((bit_len == 0) || (from_bit_len == 0))
9224-
compatible= (source_size <= destination_size);
9225-
else if (from_bit_len > bit_len)
9226-
compatible= (from_len < bytes_in_rec);
9227-
else
9228-
compatible= ((from_bit_len <= bit_len) && (from_len <= bytes_in_rec));
9229-
return (compatible);
9228+
uint from_bit_len= 8 * (field_metadata >> 8) + (field_metadata & 0xff);
9229+
uint to_bit_len= max_display_length();
9230+
9231+
/*
9232+
If the bit length exact flag is clear, we are dealing with an old
9233+
master, so we allow some less strict behaviour if replicating by
9234+
moving both bit lengths to an even multiple of 8.
9235+
9236+
We do this by computing the number of bytes to store the field
9237+
instead, and then compare the result.
9238+
*/
9239+
if (!(mflags & Table_map_log_event::TM_BIT_LEN_EXACT_F)) {
9240+
from_bit_len= (from_bit_len + 7) / 8;
9241+
to_bit_len= (to_bit_len + 7) / 8;
9242+
}
9243+
9244+
return from_bit_len <= to_bit_len;
92309245
}
92319246

92329247

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 int compatible_field_size(uint field_metadata,
168-
const Relay_log_info *);
168+
const Relay_log_info *, uint16 mflags);
169169
virtual uint pack_length_from_metadata(uint field_metadata)
170170
{ return field_metadata; }
171171
/*
@@ -803,7 +803,7 @@ class Field_new_decimal :public Field_num {
803803
uint pack_length_from_metadata(uint field_metadata);
804804
uint row_pack_length() { return pack_length(); }
805805
int compatible_field_size(uint field_metadata,
806-
const Relay_log_info *rli);
806+
const Relay_log_info *rli, uint16 mflags);
807807
uint is_equal(Create_field *new_field);
808808
virtual const uchar *unpack(uchar* to, const uchar *from,
809809
uint param_data, bool low_byte_first);
@@ -1498,7 +1498,7 @@ class Field_string :public Field_longstr {
14981498
return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff);
14991499
}
15001500
int compatible_field_size(uint field_metadata,
1501-
const Relay_log_info *rli);
1501+
const Relay_log_info *rli, uint16 mflags);
15021502
uint row_pack_length() { return (field_length + 1); }
15031503
int pack_cmp(const uchar *a,const uchar *b,uint key_length,
15041504
my_bool insert_or_update);
@@ -1962,7 +1962,7 @@ class Field_bit :public Field {
19621962
uint row_pack_length()
19631963
{ return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
19641964
int compatible_field_size(uint field_metadata,
1965-
const Relay_log_info *rli);
1965+
const Relay_log_info *rli, uint16 mflags);
19661966
void sql_type(String &str) const;
19671967
virtual uchar *pack(uchar *to, const uchar *from,
19681968
uint max_length, bool low_byte_first);

sql/log.cc

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

3850-
Table_map_log_event::flag_set const
3851-
flags= Table_map_log_event::TM_NO_FLAGS;
3852-
38533850
Table_map_log_event
3854-
the_event(this, table, table->s->table_map_id, is_trans, flags);
3851+
the_event(this, table, table->s->table_map_id, is_trans);
38553852

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

sql/log_event.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7837,7 +7837,7 @@ int Table_map_log_event::save_field_metadata()
78377837
*/
78387838
#if !defined(MYSQL_CLIENT)
78397839
Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
7840-
bool is_transactional, uint16 flags)
7840+
bool is_transactional)
78417841
: Log_event(thd, 0, true),
78427842
m_table(tbl),
78437843
m_dbnam(tbl->s->db.str),
@@ -7847,7 +7847,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
78477847
m_colcnt(tbl->s->fields),
78487848
m_memory(NULL),
78497849
m_table_id(tid),
7850-
m_flags(flags),
7850+
m_flags(TM_BIT_LEN_EXACT_F),
78517851
m_data_size(0),
78527852
m_field_metadata(0),
78537853
m_field_metadata_size(0),
@@ -8105,8 +8105,10 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
81058105
inside Relay_log_info::clear_tables_to_lock() by calling the
81068106
table_def destructor explicitly.
81078107
*/
8108-
new (&table_list->m_tabledef) table_def(m_coltype, m_colcnt,
8109-
m_field_metadata, m_field_metadata_size, m_null_bits);
8108+
new (&table_list->m_tabledef)
8109+
table_def(m_coltype, m_colcnt,
8110+
m_field_metadata, m_field_metadata_size,
8111+
m_null_bits, m_flags);
81108112
table_list->m_tabledef_valid= TRUE;
81118113

81128114
/*

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ table_def::compatible_with(Relay_log_info const *rli_arg, TABLE *table)
206206
Check the slave's field size against that of the master.
207207
*/
208208
if (!error &&
209-
!field->compatible_field_size(field_metadata(col), rli_arg))
209+
!field->compatible_field_size(field_metadata(col), rli_arg, m_flags))
210210
{
211211
error= 1;
212212
char buf[256];

0 commit comments

Comments
 (0)