From c63df11f375dc5f7eb461c2955ce04ca42aa1e8c Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Mon, 14 Dec 2009 12:04:55 +0100 Subject: WL#5151: Conversion between different types when replicating Row-based replication requires the types of columns on the master and slave to be approximately the same (some safe conversions between strings are allowed), but does not allow safe conversions between fields of similar types such as TINYINT and INT. This patch implement type conversions between similar fields on the master and slave. The conversions are controlled using a new variable SLAVE_TYPE_CONVERSIONS of type SET('ALL_LOSSY','ALL_NON_LOSSY'). Non-lossy conversions are any conversions that do not run the risk of losing any information, while lossy conversions can potentially truncate the value. The column definitions are checked to decide if the conversion is acceptable. If neither conversion is enabled, it is required that the definitions of the columns are identical on master and slave. Conversion is done by creating an internal conversion table, unpacking the master data into it, and then copy the data to the real table on the slave. --- sql/field.h | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'sql/field.h') diff --git a/sql/field.h b/sql/field.h index 7a9b69eff40..cfa3e57d2a0 100644 --- a/sql/field.h +++ b/sql/field.h @@ -164,22 +164,13 @@ public: table, which is located on disk). */ virtual uint32 pack_length_in_rec() const { return pack_length(); } - virtual int compatible_field_size(uint field_metadata, - const Relay_log_info *); + virtual bool compatible_field_size(uint metadata, Relay_log_info *rli, + int *order); virtual uint pack_length_from_metadata(uint field_metadata) - { return field_metadata; } - /* - This method is used to return the size of the data in a row-based - replication row record. The default implementation of returning 0 is - designed to allow fields that do not use metadata to return TRUE (1) - from compatible_field_size() which uses this function in the comparison. - The default value for field metadata for fields that do not have - metadata is 0. Thus, 0 == 0 means the fields are compatible in size. - - Note: While most classes that override this method return pack_length(), - the classes Field_string, Field_varstring, and Field_blob return - field_length + 1, field_length, and pack_length_no_ptr() respectfully. - */ + { + DBUG_ENTER("Field::pack_length_from_metadata"); + DBUG_RETURN(field_metadata); + } virtual uint row_pack_length() { return 0; } virtual int save_field_metadata(uchar *first_byte) { return do_save_field_metadata(first_byte); } @@ -636,6 +627,13 @@ public: int store_decimal(const my_decimal *); my_decimal *val_decimal(my_decimal *); uint is_equal(Create_field *new_field); + uint row_pack_length() { return pack_length(); } + uint32 pack_length_from_metadata(uint field_metadata) { + uint32 length= pack_length(); + DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u", + field_metadata, length)); + return length; + } int check_int(CHARSET_INFO *cs, const char *str, int length, const char *int_end, int error); bool get_int(CHARSET_INFO *cs, const char *from, uint len, @@ -806,8 +804,8 @@ public: uint32 pack_length() const { return (uint32) bin_size; } uint pack_length_from_metadata(uint field_metadata); uint row_pack_length() { return pack_length(); } - int compatible_field_size(uint field_metadata, - const Relay_log_info *rli); + bool compatible_field_size(uint field_metadata, Relay_log_info *rli, + 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); @@ -1501,9 +1499,9 @@ public: return row_pack_length(); return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff); } - int compatible_field_size(uint field_metadata, - const Relay_log_info *rli); - uint row_pack_length() { return (field_length + 1); } + bool compatible_field_size(uint field_metadata, Relay_log_info *rli, + 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); int pack_cmp(const uchar *b,uint key_length,my_bool insert_or_update); @@ -1965,8 +1963,8 @@ public: uint pack_length_from_metadata(uint field_metadata); uint row_pack_length() { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } - int compatible_field_size(uint field_metadata, - const Relay_log_info *rli); + bool compatible_field_size(uint metadata, Relay_log_info *rli, + int *order_var); void sql_type(String &str) const; virtual uchar *pack(uchar *to, const uchar *from, uint max_length, bool low_byte_first); @@ -2069,7 +2067,8 @@ public: /* Init for a tmp table field. To be extended if need be. */ void init_for_tmp_table(enum_field_types sql_type_arg, uint32 max_length, uint32 decimals, - bool maybe_null, bool is_unsigned); + bool maybe_null, bool is_unsigned, + uint pack_length = ~0U); bool init(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, Item *default_value, -- cgit v1.2.1 From f43ca0254ee4d8aa5943833e61ef17f7c2a09ead Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 15 Dec 2009 16:11:44 +0100 Subject: 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. --- sql/field.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sql/field.h') 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); -- cgit v1.2.1