diff options
author | Mats Kindahl <mats@sun.com> | 2009-12-15 16:11:44 +0100 |
---|---|---|
committer | Mats Kindahl <mats@sun.com> | 2009-12-15 16:11:44 +0100 |
commit | 9e980bf79ef0c727c630e79c1bc043c48bc947ee (patch) | |
tree | fab9c04efb15be30737eabf84a75edf8323de3fe /sql/field.cc | |
parent | 870daf5c017762fb5560186737872f8635070ffe (diff) | |
download | mariadb-git-9e980bf79ef0c727c630e79c1bc043c48bc947ee.tar.gz |
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.
mysql-test/suite/rpl/t/rpl_typeconv_innodb.test:
Adding test to check compatibility for bit field
replication when using InnoDB.
sql/field.cc:
Extending compatible_field_size() with flags from
table map to allow fields to check master info.
sql/field.h:
Extending compatible_field_size() with flags from
table map to allow fields to check master info.
sql/log.cc:
Removing table map flags since they are not used
outside table map class.
sql/log_event.cc:
Removing flags parameter from table map constructor
since it is not used and does not have to be exposed.
sql/log_event.h:
Adding flag to denote that bit length for bit field type
is exact and not potentially rounded to even bytes.
sql/rpl_utility.cc:
Adding fields to table_def to store table map flags.
sql/rpl_utility.h:
Removing obsolete comment and adding flags to store
table map flags from master.
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 33 |
1 files changed, 28 insertions, 5 deletions
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); } |