diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sql/field.h | 110 | ||||
-rw-r--r-- | sql/rpl_utility.cc | 975 | ||||
-rw-r--r-- | sql/rpl_utility.h | 4 | ||||
-rw-r--r-- | sql/rpl_utility_server.cc | 1191 | ||||
-rw-r--r-- | sql/sql_type.cc | 15 | ||||
-rw-r--r-- | sql/sql_type.h | 70 |
7 files changed, 1379 insertions, 989 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 6cc561877b9..1215ff096ac 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -111,7 +111,8 @@ SET (SQL_SOURCE rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c - partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc + partition_info.cc rpl_utility.cc rpl_utility_server.cc + rpl_injector.cc sql_locale.cc rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc sql_connect.cc scheduler.cc sql_partition_admin.cc sql_profile.cc event_parse_data.cc sql_alter.cc diff --git a/sql/field.h b/sql/field.h index 66f911778ea..38a5b1f7127 100644 --- a/sql/field.h +++ b/sql/field.h @@ -60,6 +60,46 @@ enum enum_check_fields CHECK_FIELD_ERROR_FOR_NULL, }; + +enum enum_conv_type +{ + CONV_TYPE_PRECISE, + CONV_TYPE_VARIANT, + CONV_TYPE_SUBSET_TO_SUPERSET, + CONV_TYPE_SUPERSET_TO_SUBSET, + CONV_TYPE_IMPOSSIBLE +}; + + +class Conv_param +{ + uint16 m_table_def_flags; +public: + Conv_param(uint16 table_def_flags) + :m_table_def_flags(table_def_flags) + { } + uint16 table_def_flags() const { return m_table_def_flags; } +}; + + +class Conv_source: public Type_handler_hybrid_field_type +{ + enum_field_types m_type; + uint16 m_metadata; + CHARSET_INFO *m_cs; +public: + Conv_source(const Type_handler *h, uint16 metadata, CHARSET_INFO *cs) + :Type_handler_hybrid_field_type(h), + m_metadata(metadata), + m_cs(cs) + { + DBUG_ASSERT(cs); + } + uint16 metadata() const { return m_metadata; } + uint mbmaxlen() const { return m_cs->mbmaxlen; } +}; + + /* Common declarations for Field and Item */ @@ -1085,6 +1125,17 @@ public: */ return type(); } + /* + Conversion type for from the source to the current field. + */ + virtual enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) + const= 0; + enum_conv_type rpl_conv_type_from_same_data_type(uint16 metadata, + const Relay_log_info *rli, + const Conv_param ¶m) + const; inline int cmp(const uchar *str) { return cmp(ptr,str); } virtual int cmp_max(const uchar *a, const uchar *b, uint max_len) { return cmp(a, b); } @@ -1940,7 +1991,9 @@ public: :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, collation) {} - + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int store_decimal(const my_decimal *d); uint32 max_data_length() const; @@ -1981,6 +2034,9 @@ public: { return do_field_real; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Information_schema_numeric_attributes information_schema_numeric_attributes() const { @@ -2090,6 +2146,9 @@ public: return Field_num::memcpy_field_possible(from) && field_length == from->field_length; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int reset(void); bool store_value(const my_decimal *decimal_value); bool store_value(const my_decimal *decimal_value, int *native_error); @@ -2162,6 +2221,9 @@ public: :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg, unsigned_arg) {} + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int store_decimal(const my_decimal *); my_decimal *val_decimal(my_decimal *); bool val_bool() { return val_int() != 0; } @@ -2627,6 +2689,9 @@ public: unireg_check_arg, field_name_arg, collation) {} const Type_handler *type_handler() const { return &type_handler_null; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Information_schema_character_attributes information_schema_character_attributes() const { @@ -2815,6 +2880,9 @@ public: TABLE_SHARE *share); const Type_handler *type_handler() const { return &type_handler_timestamp; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Copy_func *get_copy_func(const Field *from) const; int store(const char *to,size_t length,CHARSET_INFO *charset); int store(double nr); @@ -2967,6 +3035,9 @@ public: {} const Type_handler *type_handler() const { return &type_handler_timestamp2; } enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; uint32 pack_length() const { return my_timestamp_binary_length(dec); @@ -3006,6 +3077,9 @@ public: { return field_length == 2 ? &type_handler_year2 : &type_handler_year; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Copy_func *get_copy_func(const Field *from) const { if (eq_def(from)) @@ -3088,6 +3162,9 @@ public: unireg_check_arg, field_name_arg) {} const Type_handler *type_handler() const { return &type_handler_date; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) { return Field_date::get_TIME(ltime, ptr, fuzzydate); } @@ -3125,6 +3202,9 @@ public: {} const Type_handler *type_handler() const { return &type_handler_newdate; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } double val_real(void); longlong val_int(void); @@ -3168,6 +3248,9 @@ public: const Item_equal *item_equal); const Type_handler *type_handler() const { return &type_handler_time; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Copy_func *get_copy_func(const Field *from) const { return from->cmp_type() == REAL_RESULT ? do_field_string : // MDEV-9344 @@ -3280,6 +3363,9 @@ public: } const Type_handler *type_handler() const { return &type_handler_time2; } enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; uint32 pack_length() const { return my_time_binary_length(dec); @@ -3325,6 +3411,9 @@ public: } const Type_handler *type_handler() const { return &type_handler_datetime; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; int store(const char *to, size_t length, CHARSET_INFO *charset); int store(double nr); int store(longlong nr, bool unsigned_val); @@ -3447,6 +3536,9 @@ public: {} const Type_handler *type_handler() const { return &type_handler_datetime2; } enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; uint32 pack_length() const { return my_datetime_binary_length(dec); @@ -4130,6 +4222,9 @@ public: :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share, blob_pack_length, &my_charset_bin) { geom_type= geom_type_arg; srid= field_srid; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } const Type_handler *type_handler() const { @@ -4212,6 +4307,9 @@ public: Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); const Type_handler *type_handler() const { return &type_handler_enum; } enum ha_base_keytype key_type() const; + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Copy_func *get_copy_func(const Field *from) const { if (eq_def(from)) @@ -4365,6 +4463,9 @@ public: uint32 key_length() const { return (uint32) (field_length + 7) / 8; } uint32 max_data_length() const { return (field_length + 7) / 8; } uint32 max_display_length() const { return field_length; } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const; Information_schema_numeric_attributes information_schema_numeric_attributes() const { @@ -4531,6 +4632,13 @@ public: m_table(NULL) {} ~Field_row(); + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const + { + DBUG_ASSERT(0); + return CONV_TYPE_IMPOSSIBLE; + } Virtual_tmp_table **virtual_tmp_table_addr() { return &m_table; } bool sp_prepare_and_store_item(THD *thd, Item **value); }; diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index b36838b9b22..1e4b59844b8 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -19,185 +19,7 @@ #include "rpl_utility.h" #include "log_event.h" -#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -#include "rpl_rli.h" -#include "sql_select.h" -/** - Calculate display length for MySQL56 temporal data types from their metadata. - It contains fractional precision in the low 16-bit word. -*/ -static uint32 -max_display_length_for_temporal2_field(uint32 int_display_length, - unsigned int metadata) -{ - metadata&= 0x00ff; - return int_display_length + metadata + (metadata ? 1 : 0); -} - - -/** - Compute the maximum display length of a field. - - @param sql_type Type of the field - @param metadata The metadata from the master for the field. - @return Maximum length of the field in bytes. - - The precise values calculated by field->max_display_length() and - calculated by max_display_length_for_field() can differ (by +1 or -1) - for integer data types (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT). - This slight difference is not important here, because we call - this function only for two *different* integer data types. - */ -static uint32 -max_display_length_for_field(enum_field_types sql_type, unsigned int metadata) -{ - DBUG_PRINT("debug", ("sql_type: %d, metadata: 0x%x", sql_type, metadata)); - DBUG_ASSERT(metadata >> 16 == 0); - - switch (sql_type) { - case MYSQL_TYPE_NEWDECIMAL: - return metadata >> 8; - - case MYSQL_TYPE_FLOAT: - return 12; - - case MYSQL_TYPE_DOUBLE: - return 22; - - case MYSQL_TYPE_SET: - case MYSQL_TYPE_ENUM: - return metadata & 0x00ff; - - case MYSQL_TYPE_STRING: - { - uchar type= metadata >> 8; - if (type == MYSQL_TYPE_SET || type == MYSQL_TYPE_ENUM) - return metadata & 0xff; - else - /* This is taken from Field_string::unpack. */ - return (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff); - } - - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_TINY: - return 4; - - case MYSQL_TYPE_SHORT: - return 6; - - case MYSQL_TYPE_INT24: - return 9; - - case MYSQL_TYPE_LONG: - return 11; - -#ifdef HAVE_LONG_LONG - case MYSQL_TYPE_LONGLONG: - return 20; - -#endif - case MYSQL_TYPE_NULL: - return 0; - - case MYSQL_TYPE_NEWDATE: - return 3; - - case MYSQL_TYPE_DATE: - return 3; - - case MYSQL_TYPE_TIME: - return MIN_TIME_WIDTH; - - case MYSQL_TYPE_TIME2: - return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, metadata); - - case MYSQL_TYPE_TIMESTAMP: - return MAX_DATETIME_WIDTH; - - case MYSQL_TYPE_TIMESTAMP2: - return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata); - - case MYSQL_TYPE_DATETIME: - return MAX_DATETIME_WIDTH; - - case MYSQL_TYPE_DATETIME2: - return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata); - - case MYSQL_TYPE_BIT: - /* - Decode the size of the bit field from the master. - */ - DBUG_ASSERT((metadata & 0xff) <= 7); - return 8 * (metadata >> 8U) + (metadata & 0x00ff); - - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - return metadata; - case MYSQL_TYPE_VARCHAR_COMPRESSED: - return metadata - 1; - - /* - The actual length for these types does not really matter since - they are used to calc_pack_length, which ignores the given - length for these types. - - Since we want this to be accurate for other uses, we return the - maximum size in bytes of these BLOBs. - */ - - case MYSQL_TYPE_TINY_BLOB: - return (uint32)my_set_bits(1 * 8); - - case MYSQL_TYPE_MEDIUM_BLOB: - return (uint32)my_set_bits(3 * 8); - - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_BLOB_COMPRESSED: - /* - For the blob type, Field::real_type() lies and say that all - blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look - at the length instead to decide what the max display size is. - */ - return (uint32)my_set_bits(metadata * 8); - - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_GEOMETRY: - return (uint32)my_set_bits(4 * 8); - - default: - return ~(uint32) 0; - } -} - - -/* - Compare the pack lengths of a source field (on the master) and a - target field (on the slave). - - @param field Target field. - @param type Source field type. - @param metadata Source field metadata. - - @retval -1 The length of the source field is smaller than the target field. - @retval 0 The length of the source and target fields are the same. - @retval 1 The length of the source field is greater than the target field. - */ -int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata) -{ - DBUG_ENTER("compare_lengths"); - size_t const source_length= - max_display_length_for_field(source_type, metadata); - size_t const target_length= field->max_display_length(); - DBUG_PRINT("debug", ("source_length: %lu, source_type: %u," - " target_length: %lu, target_type: %u", - (unsigned long) source_length, source_type, - (unsigned long) target_length, field->real_type())); - int result= source_length < target_length ? -1 : source_length > target_length; - DBUG_PRINT("result", ("%d", result)); - DBUG_RETURN(result); -} -#endif //MYSQL_CLIENT /********************************************************************* * table_def member definitions * *********************************************************************/ @@ -349,739 +171,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const return length; } -#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -/** - */ -void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_INFO *field_cs) -{ - DBUG_ENTER("show_sql_type"); - DBUG_PRINT("enter", ("type: %d, metadata: 0x%x", type, metadata)); - - switch (type) - { - case MYSQL_TYPE_TINY: - str->set_ascii(STRING_WITH_LEN("tinyint")); - break; - - case MYSQL_TYPE_SHORT: - str->set_ascii(STRING_WITH_LEN("smallint")); - break; - - case MYSQL_TYPE_LONG: - str->set_ascii(STRING_WITH_LEN("int")); - break; - - case MYSQL_TYPE_FLOAT: - str->set_ascii(STRING_WITH_LEN("float")); - break; - - case MYSQL_TYPE_DOUBLE: - str->set_ascii(STRING_WITH_LEN("double")); - break; - - case MYSQL_TYPE_NULL: - str->set_ascii(STRING_WITH_LEN("null")); - break; - - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_TIMESTAMP2: - str->set_ascii(STRING_WITH_LEN("timestamp")); - break; - - case MYSQL_TYPE_LONGLONG: - str->set_ascii(STRING_WITH_LEN("bigint")); - break; - - case MYSQL_TYPE_INT24: - str->set_ascii(STRING_WITH_LEN("mediumint")); - break; - - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_DATE: - str->set_ascii(STRING_WITH_LEN("date")); - break; - - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_TIME2: - str->set_ascii(STRING_WITH_LEN("time")); - break; - - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_DATETIME2: - str->set_ascii(STRING_WITH_LEN("datetime")); - break; - - case MYSQL_TYPE_YEAR: - str->set_ascii(STRING_WITH_LEN("year")); - break; - - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VARCHAR_COMPRESSED: - { - CHARSET_INFO *cs= str->charset(); - size_t length= - cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "varchar(%u)%s", metadata, - type == MYSQL_TYPE_VARCHAR_COMPRESSED ? " compressed" - : ""); - str->length(length); - } - break; - - case MYSQL_TYPE_BIT: - { - CHARSET_INFO *cs= str->charset(); - int bit_length= 8 * (metadata >> 8) + (metadata & 0xFF); - size_t length= - cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "bit(%d)", bit_length); - str->length(length); - } - break; - - case MYSQL_TYPE_DECIMAL: - { - CHARSET_INFO *cs= str->charset(); - size_t length= - cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "decimal(%d,?)/*old*/", metadata); - str->length(length); - } - break; - - case MYSQL_TYPE_NEWDECIMAL: - { - CHARSET_INFO *cs= str->charset(); - size_t length= - cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "decimal(%d,%d)", metadata >> 8, metadata & 0xff); - str->length(length); - } - break; - - case MYSQL_TYPE_ENUM: - str->set_ascii(STRING_WITH_LEN("enum")); - break; - - case MYSQL_TYPE_SET: - str->set_ascii(STRING_WITH_LEN("set")); - break; - - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_BLOB_COMPRESSED: - /* - Field::real_type() lies regarding the actual type of a BLOB, so - it is necessary to check the pack length to figure out what kind - of blob it really is. - */ - switch (get_blob_type_from_length(metadata)) - { - case MYSQL_TYPE_TINY_BLOB: - str->set_ascii(STRING_WITH_LEN("tinyblob")); - break; - - case MYSQL_TYPE_MEDIUM_BLOB: - str->set_ascii(STRING_WITH_LEN("mediumblob")); - break; - - case MYSQL_TYPE_LONG_BLOB: - str->set_ascii(STRING_WITH_LEN("longblob")); - break; - - case MYSQL_TYPE_BLOB: - str->set_ascii(STRING_WITH_LEN("blob")); - break; - - default: - DBUG_ASSERT(0); - break; - } - - if (type == MYSQL_TYPE_BLOB_COMPRESSED) - str->append(STRING_WITH_LEN(" compressed")); - break; - - case MYSQL_TYPE_STRING: - { - /* - This is taken from Field_string::unpack. - */ - CHARSET_INFO *cs= str->charset(); - uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff); - size_t length= - cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), - "char(%d)", bytes / field_cs->mbmaxlen); - str->length(length); - } - break; - - case MYSQL_TYPE_GEOMETRY: - str->set_ascii(STRING_WITH_LEN("geometry")); - break; - - default: - str->set_ascii(STRING_WITH_LEN("<unknown type>")); - } - DBUG_VOID_RETURN; -} - - -/** - 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) -{ - DBUG_ENTER("is_conversion_ok"); - bool allow_non_lossy, allow_lossy; - - allow_non_lossy = slave_type_conversions_options & - (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY); - allow_lossy= slave_type_conversions_options & - (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY); - - DBUG_PRINT("enter", ("order: %d, flags:%s%s", order, - allow_non_lossy ? " ALL_NON_LOSSY" : "", - allow_lossy ? " ALL_LOSSY" : "")); - if (order < 0 && !allow_non_lossy) - { - /* !!! Add error message saying that non-lossy conversions need to be allowed. */ - DBUG_RETURN(false); - } - - if (order > 0 && !allow_lossy) - { - /* !!! Add error message saying that lossy conversions need to be allowed. */ - DBUG_RETURN(false); - } - - DBUG_RETURN(true); -} - - -/** - Can a type potentially be converted to another type? - - This function check if the types are convertible and what - conversion is required. - - If conversion is not possible, and error is printed. - - If conversion is possible: - - - *order will be set to -1 if source type is smaller than target - type and a non-lossy conversion can be required. This includes - the case where the field types are different but types could - actually be converted in either direction. - - - *order will be set to 0 if no conversion is required. - - - *order will be set to 1 if the source type is strictly larger - than the target type and that conversion is potentially lossy. - - @param[in] field Target field - @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 - settings, @c false if conversion is not possible according to the - current setting. - */ -static bool -can_convert_field_to(Field *field, - enum_field_types source_type, uint16 metadata, - Relay_log_info *rli, uint16 mflags, - int *order_var) -{ - DBUG_ENTER("can_convert_field_to"); - bool same_type; -#ifndef DBUG_OFF - char field_type_buf[MAX_FIELD_WIDTH]; - String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1); - field->sql_type(field_type); - DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x", - field_type.c_ptr_safe(), field->real_type(), source_type, metadata)); -#endif - /** - @todo - Implement Field_varstring_cmopressed::real_type() and - Field_blob_compressed::real_type() properly. All occurencies - of Field::real_type() have to be inspected and adjusted if needed. - - Until it is not ready we have to compare source_type against - binlog_type() when replicating from or to compressed data types. - - @sa Comment for Field::binlog_type() - */ - if (source_type == MYSQL_TYPE_VARCHAR_COMPRESSED || - source_type == MYSQL_TYPE_BLOB_COMPRESSED || - field->binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || - field->binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED) - same_type= field->binlog_type() == source_type; - else - same_type= field->real_type() == source_type; - - /* - If the real type is the same, we need to check the metadata to - decide if conversions are allowed. - */ - if (same_type) - { - if (metadata == 0) // Metadata can only be zero if no metadata was provided - { - /* - If there is no metadata, we either have an old event where no - metadata were supplied, or a type that does not require any - metadata. In either case, conversion can be done but no - conversion table is necessary. - */ - DBUG_PRINT("debug", ("Base types are identical, but there is no metadata")); - *order_var= 0; - DBUG_RETURN(true); - } - - DBUG_PRINT("debug", ("Base types are identical, doing field size comparison")); - if (field->compatible_field_size(metadata, rli, mflags, order_var)) - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - else - DBUG_RETURN(false); - } - else if ( - /* - Conversion from MariaDB TIMESTAMP(0), TIME(0), DATETIME(0) - to the corresponding MySQL56 types is non-lossy. - */ - (metadata == 0 && - ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 && - source_type == MYSQL_TYPE_TIMESTAMP) || - (field->real_type() == MYSQL_TYPE_TIME2 && - source_type == MYSQL_TYPE_TIME) || - (field->real_type() == MYSQL_TYPE_DATETIME2 && - source_type == MYSQL_TYPE_DATETIME))) || - /* - Conversion from MySQL56 TIMESTAMP(N), TIME(N), DATETIME(N) - to the corresponding MariaDB or MySQL55 types is non-lossy. - */ - (metadata == field->decimals() && - ((field->real_type() == MYSQL_TYPE_TIMESTAMP && - source_type == MYSQL_TYPE_TIMESTAMP2) || - (field->real_type() == MYSQL_TYPE_TIME && - source_type == MYSQL_TYPE_TIME2) || - (field->real_type() == MYSQL_TYPE_DATETIME && - source_type == MYSQL_TYPE_DATETIME2)))) - { - /* - TS-TODO: conversion from FSP1>FSP2. - */ - *order_var= -1; - DBUG_RETURN(true); - } - else if (!slave_type_conversions_options) - DBUG_RETURN(false); - - /* - Here, from and to will always be different. Since the types are - different, we cannot use the compatible_field_size() function, but - have to rely on hard-coded max-sizes for fields. - */ - - DBUG_PRINT("debug", ("Base types are different, checking conversion")); - switch (source_type) // Source type (on master) - { - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - switch (field->real_type()) - { - case MYSQL_TYPE_NEWDECIMAL: - /* - Then the other type is either FLOAT, DOUBLE, or old style - DECIMAL, so we require lossy conversion. - */ - *order_var= 1; - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - { - if (source_type == MYSQL_TYPE_NEWDECIMAL || - source_type == MYSQL_TYPE_DECIMAL) - *order_var = 1; // Always require lossy conversions - else - *order_var= compare_lengths(field, source_type, metadata); - DBUG_ASSERT(*order_var != 0); - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - } - - default: - DBUG_RETURN(false); - } - break; - - /* - The length comparison check will do the correct job of comparing - the field lengths (in bytes) of two integer types. - */ - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - switch (field->real_type()) - { - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - /* - max_display_length_for_field() is not fully precise for the integer - data types. So its result cannot be compared to the result of - field->max_dispay_length() when the table field and the binlog field - are of the same type. - This code should eventually be rewritten not to use - compare_lengths(), to detect subtype/supetype relations - just using the type codes. - */ - DBUG_ASSERT(source_type != field->real_type()); - *order_var= compare_lengths(field, source_type, metadata); - DBUG_ASSERT(*order_var != 0); - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - - default: - DBUG_RETURN(false); - } - break; - - /* - Since source and target type is different, and it is not possible - to convert bit types to anything else, this will return false. - */ - case MYSQL_TYPE_BIT: - DBUG_RETURN(false); - - /* - If all conversions are disabled, it is not allowed to convert - between these types. Since the TEXT vs. BINARY is distinguished by - the charset, and the charset is not replicated, we cannot - currently distinguish between , e.g., TEXT and BLOB. - */ - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_BLOB_COMPRESSED: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VARCHAR_COMPRESSED: - switch (field->real_type()) - { - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_BLOB_COMPRESSED: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VARCHAR_COMPRESSED: - *order_var= compare_lengths(field, source_type, metadata); - /* - Here we know that the types are different, so if the order - gives that they do not require any conversion, we still need - to have non-lossy conversion enabled to allow conversion - between different (string) types of the same length. - */ - if (*order_var == 0) - *order_var= -1; - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - - default: - DBUG_RETURN(false); - } - break; - - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_NULL: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_TIMESTAMP2: - case MYSQL_TYPE_TIME2: - DBUG_RETURN(false); - case MYSQL_TYPE_NEWDATE: - { - if (field->real_type() == MYSQL_TYPE_DATETIME2 || - field->real_type() == MYSQL_TYPE_DATETIME) - { - *order_var= -1; - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - } - else - { - DBUG_RETURN(false); - } - } - break; - - //case MYSQL_TYPE_DATETIME: TODO: fix MDEV-17394 and uncomment. - // - //The "old" type does not specify the fraction part size which is required - //for correct conversion. - case MYSQL_TYPE_DATETIME2: - { - if (field->real_type() == MYSQL_TYPE_NEWDATE) - { - *order_var= 1; - DBUG_RETURN(is_conversion_ok(*order_var, rli)); - } - else - { - DBUG_RETURN(false); - } - } - break; - } - DBUG_RETURN(false); // To keep GCC happy -} - - -/** - Is the definition compatible with a table? - - This function will compare the master table with an existing table - on the slave and see if they are compatible with respect to the - current settings of @c SLAVE_TYPE_CONVERSIONS. - - If the tables are compatible and conversions are required, @c - *tmp_table_var will be set to a virtual temporary table with field - pointers for the fields that require conversions. This allow simple - checking of whether a conversion are to be applied or not. - - If tables are compatible, but no conversions are necessary, @c - *tmp_table_var will be set to NULL. - - @param rli_arg[in] - Relay log info, for error reporting. - - @param table[in] - Table to compare with - - @param tmp_table_var[out] - Virtual temporary table for performing conversions, if necessary. - - @retval true Master table is compatible with slave table. - @retval false Master table is not compatible with slave table. -*/ -bool -table_def::compatible_with(THD *thd, rpl_group_info *rgi, - TABLE *table, TABLE **conv_table_var) - const -{ - /* - We only check the initial columns for the tables. - */ - uint const cols_to_check= MY_MIN(table->s->fields, size()); - Relay_log_info *rli= rgi->rli; - TABLE *tmp_table= NULL; - - for (uint col= 0 ; col < cols_to_check ; ++col) - { - Field *const field= table->field[col]; - int 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", - col, field->field_name.str, order)); - DBUG_ASSERT(order >= -1 && order <= 1); - - /* - If order is not 0, a conversion is required, so we need to set - up the conversion table. - */ - if (order != 0 && tmp_table == NULL) - { - /* - This will create the full table with all fields. This is - necessary to ge the correct field lengths for the record. - */ - tmp_table= create_conversion_table(thd, rgi, table); - if (tmp_table == NULL) - return false; - /* - Clear all fields up to, but not including, this column. - */ - for (unsigned int i= 0; i < col; ++i) - tmp_table->field[i]= NULL; - } - - if (order == 0 && tmp_table != NULL) - tmp_table->field[col]= NULL; - } - else - { - DBUG_PRINT("debug", ("Checking column %d -" - " field '%s' can not be converted", - col, field->field_name.str)); - DBUG_ASSERT(col < size() && col < table->s->fields); - DBUG_ASSERT(table->s->db.str && table->s->table_name.str); - DBUG_ASSERT(table->in_use); - const char *db_name= table->s->db.str; - const char *tbl_name= table->s->table_name.str; - char source_buf[MAX_FIELD_WIDTH]; - char target_buf[MAX_FIELD_WIDTH]; - String source_type(source_buf, sizeof(source_buf), &my_charset_latin1); - String target_type(target_buf, sizeof(target_buf), &my_charset_latin1); - THD *thd= table->in_use; - - show_sql_type(type(col), field_metadata(col), &source_type, field->charset()); - field->sql_type(target_type); - rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(), - ER_THD(thd, ER_SLAVE_CONVERSION_FAILED), - col, db_name, tbl_name, - source_type.c_ptr_safe(), target_type.c_ptr_safe()); - return false; - } - } - -#ifndef DBUG_OFF - if (tmp_table) - { - for (unsigned int col= 0; col < tmp_table->s->fields; ++col) - if (tmp_table->field[col]) - { - char source_buf[MAX_FIELD_WIDTH]; - char target_buf[MAX_FIELD_WIDTH]; - String source_type(source_buf, sizeof(source_buf), &my_charset_latin1); - String target_type(target_buf, sizeof(target_buf), &my_charset_latin1); - tmp_table->field[col]->sql_type(source_type); - table->field[col]->sql_type(target_type); - DBUG_PRINT("debug", ("Field %s - conversion required." - " Source type: '%s', Target type: '%s'", - tmp_table->field[col]->field_name.str, - source_type.c_ptr_safe(), target_type.c_ptr_safe())); - } - } -#endif - - *conv_table_var= tmp_table; - return true; -} - - -/** - A wrapper to Virtual_tmp_table, to get access to its constructor, - which is protected for safety purposes (against illegal use on stack). -*/ -class Virtual_conversion_table: public Virtual_tmp_table -{ -public: - Virtual_conversion_table(THD *thd) :Virtual_tmp_table(thd) { } - /** - Add a new field into the virtual table. - @param sql_type - The real_type of the field. - @param metadata - The RBR binary log metadata for this field. - @param target_field - The field from the target table, to get extra - attributes from (e.g. typelib in case of ENUM). - */ - bool add(enum_field_types sql_type, - uint16 metadata, const Field *target_field) - { - const Type_handler *handler= Type_handler::get_handler_by_real_type(sql_type); - if (!handler) - { - sql_print_error("In RBR mode, Slave received unknown field type field %d " - " for column Name: %s.%s.%s.", - (int) sql_type, - target_field->table->s->db.str, - target_field->table->s->table_name.str, - target_field->field_name.str); - return true; - } - Field *tmp= handler->make_conversion_table_field(this, metadata, - target_field); - if (!tmp) - return true; - Virtual_tmp_table::add(tmp); - DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d," - " maybe_null: %d, unsigned_flag: %d, pack_length: %u", - sql_type, target_field->field_name.str, - tmp->field_length, tmp->decimals(), TRUE, - tmp->flags, tmp->pack_length())); - return false; - } -}; - - -/** - Create a conversion table. - - If the function is unable to create the conversion table, an error - will be printed and NULL will be returned. - - @return Pointer to conversion table, or NULL if unable to create - conversion table. - */ - -TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi, - TABLE *target_table) const -{ - DBUG_ENTER("table_def::create_conversion_table"); - - Virtual_conversion_table *conv_table; - Relay_log_info *rli= rgi->rli; - /* - At slave, columns may differ. So we should create - MY_MIN(columns@master, columns@slave) columns in the - conversion table. - */ - uint const cols_to_create= MY_MIN(target_table->s->fields, size()); - if (!(conv_table= new(thd) Virtual_conversion_table(thd)) || - conv_table->init(cols_to_create)) - goto err; - for (uint col= 0 ; col < cols_to_create; ++col) - { - if (conv_table->add(type(col), field_metadata(col), - target_table->field[col])) - { - DBUG_PRINT("debug", ("binlog_type: %d, metadata: %04X, target_field: '%s'" - " make_conversion_table_field() failed", - binlog_type(col), field_metadata(col), - target_table->field[col]->field_name.str)); - goto err; - } - } - - if (conv_table->open()) - goto err; // Could not allocate record buffer? - - DBUG_RETURN(conv_table); - -err: - if (conv_table) - delete conv_table; - rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, rgi->gtid_info(), - ER_THD(thd, ER_SLAVE_CANT_CREATE_CONVERSION), - target_table->s->db.str, - target_table->s->table_name.str); - DBUG_RETURN(NULL); -} -#endif /* MYSQL_CLIENT */ table_def::table_def(unsigned char *types, ulong size, uchar *field_metadata, int metadata_size, @@ -1245,67 +334,3 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, enum enum_binlog_che } return res; } - -#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) - -Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL) -{ - my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16, MYF(0)); -} - -Deferred_log_events::~Deferred_log_events() -{ - delete_dynamic(&array); -} - -int Deferred_log_events::add(Log_event *ev) -{ - last_added= ev; - insert_dynamic(&array, (uchar*) &ev); - return 0; -} - -bool Deferred_log_events::is_empty() -{ - return array.elements == 0; -} - -bool Deferred_log_events::execute(rpl_group_info *rgi) -{ - bool res= false; - DBUG_ENTER("Deferred_log_events::execute"); - DBUG_ASSERT(rgi->deferred_events_collecting); - - rgi->deferred_events_collecting= false; - for (uint i= 0; !res && i < array.elements; i++) - { - Log_event *ev= (* (Log_event **) - dynamic_array_ptr(&array, i)); - res= ev->apply_event(rgi); - } - rgi->deferred_events_collecting= true; - DBUG_RETURN(res); -} - -void Deferred_log_events::rewind() -{ - /* - Reset preceding Query log event events which execution was - deferred because of slave side filtering. - */ - if (!is_empty()) - { - for (uint i= 0; i < array.elements; i++) - { - Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i); - delete ev; - } - last_added= NULL; - if (array.elements > array.max_element) - freeze_size(&array); - reset_dynamic(&array); - } - last_added= NULL; -} - -#endif diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index b42b11231e0..c28e8aa10eb 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -118,7 +118,9 @@ public: return source_type; } - +#ifdef MYSQL_SERVER + const Type_handler *field_type_handler(uint index) const; +#endif /* This function allows callers to get the extra field data from the diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc new file mode 100644 index 00000000000..4f6f0f6f303 --- /dev/null +++ b/sql/rpl_utility_server.cc @@ -0,0 +1,1191 @@ +/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2013, Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mariadb.h" +#include <my_bit.h> +#include "rpl_utility.h" +#include "log_event.h" + +#if defined(MYSQL_CLIENT) +#error MYSQL_CLIENT must not be defined here +#endif + +#if !defined(MYSQL_SERVER) +#error MYSQL_SERVER must be defined here +#endif + +#if defined(HAVE_REPLICATION) +#include "rpl_rli.h" +#include "sql_select.h" +#endif + + +/** + Compute the maximum display length of a field. + + @param sql_type Type of the field + @param metadata The metadata from the master for the field. + @return Maximum length of the field in bytes. + + The precise values calculated by field->max_display_length() and + calculated by max_display_length_for_field() can differ (by +1 or -1) + for integer data types (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT). + This slight difference is not important here, because we call + this function only for two *different* integer data types. + */ +static uint32 +max_display_length_for_field(const Conv_source &source) +{ + DBUG_PRINT("debug", ("sql_type: %s, metadata: 0x%x", + source.type_handler()->name().ptr(), source.metadata())); + return source.type_handler()->max_display_length_for_field(source); +} + + +/* + Compare the pack lengths of a source field (on the master) and a + target field (on the slave). + + @param sh Source type handler + @param source_length Source length + @param th Target type hander + @param target_length Target length + + @retval CONV_TYPE_SUBSET_TO_SUPERSET The length of the source field is + smaller than the target field. + @retval CONV_TYPE_PRECISE The length of the source and + the target fields are equal. + @retval CONV_TYPE_SUPERSET_TO_SUBSET The length of the source field is + greater than the target field. + */ +static enum_conv_type +compare_lengths(const Type_handler *sh, uint32 source_length, + const Type_handler *th, uint32 target_length) +{ + DBUG_ENTER("compare_lengths"); + DBUG_PRINT("debug", ("source_length: %lu, source_type: %s," + " target_length: %lu, target_type: %s", + (unsigned long) source_length, sh->name().ptr(), + (unsigned long) target_length, th->name().ptr())); + enum_conv_type result= + source_length < target_length ? CONV_TYPE_SUBSET_TO_SUPERSET : + source_length > target_length ? CONV_TYPE_SUPERSET_TO_SUBSET : + CONV_TYPE_PRECISE; + DBUG_PRINT("result", ("%d", result)); + DBUG_RETURN(result); +} + + +/** + Calculate display length for MySQL56 temporal data types from their metadata. + It contains fractional precision in the low 16-bit word. +*/ +static uint32 +max_display_length_for_temporal2_field(uint32 int_display_length, + unsigned int metadata) +{ + metadata&= 0x00ff; + return int_display_length + metadata + (metadata ? 1 : 0); +} + + +uint32 +Type_handler_newdecimal::max_display_length_for_field(const Conv_source &src) + const +{ + return src.metadata() >> 8; +} + + +uint32 +Type_handler_typelib::max_display_length_for_field(const Conv_source &src) + const +{ + /* + Field_enum::rpl_conv_type_from() does not use compare_lengths(). + So we should not come here. + */ + DBUG_ASSERT(0); + return src.metadata() & 0x00ff; +} + + +uint32 +Type_handler_string::max_display_length_for_field(const Conv_source &src) + const +{ + /* + ENUM and SET are transferred using as STRING, + with the exact type code in metadata. + Make sure that we previously detected ENUM/SET and + translated them into a proper type handler. + See table_def::field_type_handler() for details. + */ + DBUG_ASSERT((src.metadata() >> 8) != MYSQL_TYPE_SET); + DBUG_ASSERT((src.metadata() >> 8) != MYSQL_TYPE_ENUM); + /* This is taken from Field_string::unpack. */ + return (((src.metadata() >> 4) & 0x300) ^ 0x300) + (src.metadata() & 0x00ff); +} + + +uint32 +Type_handler_time2::max_display_length_for_field(const Conv_source &src) + const +{ + return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, + src.metadata()); +} + + +uint32 +Type_handler_timestamp2::max_display_length_for_field(const Conv_source &src) + const +{ + return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, + src.metadata()); +} + + +uint32 +Type_handler_datetime2::max_display_length_for_field(const Conv_source &src) + const +{ + return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, + src.metadata()); +} + + +uint32 +Type_handler_bit::max_display_length_for_field(const Conv_source &src) + const +{ + /* + Decode the size of the bit field from the master. + */ + DBUG_ASSERT((src.metadata() & 0xff) <= 7); + return 8 * (src.metadata() >> 8U) + (src.metadata() & 0x00ff); +} + + +uint32 +Type_handler_var_string::max_display_length_for_field(const Conv_source &src) + const +{ + return src.metadata(); +} + + +uint32 +Type_handler_varchar::max_display_length_for_field(const Conv_source &src) + const +{ + return src.metadata(); +} + + +uint32 +Type_handler_varchar_compressed:: + max_display_length_for_field(const Conv_source &src) const +{ + DBUG_ASSERT(src.metadata() > 0); + return src.metadata() - 1; +} + + +/* + The actual length for these types does not really matter since + they are used to calc_pack_length, which ignores the given + length for these types. + + Since we want this to be accurate for other uses, we return the + maximum size in bytes of these BLOBs. +*/ +uint32 +Type_handler_tiny_blob::max_display_length_for_field(const Conv_source &src) + const +{ + return (uint32) my_set_bits(1 * 8); +} + + +uint32 +Type_handler_medium_blob::max_display_length_for_field(const Conv_source &src) + const +{ + return (uint32) my_set_bits(3 * 8); +} + + +uint32 +Type_handler_blob::max_display_length_for_field(const Conv_source &src) + const +{ + /* + For the blob type, Field::real_type() lies and say that all + blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look + at the length instead to decide what the max display size is. + */ + return (uint32) my_set_bits(src.metadata() * 8); +} + + +uint32 +Type_handler_blob_compressed::max_display_length_for_field(const Conv_source &src) + const +{ + return (uint32) my_set_bits(src.metadata() * 8); +} + + +uint32 +Type_handler_long_blob::max_display_length_for_field(const Conv_source &src) + const +{ + return (uint32) my_set_bits(4 * 8); +} + + +uint32 +Type_handler_olddecimal::max_display_length_for_field(const Conv_source &src) + const +{ + return ~(uint32) 0; +} + + +void Type_handler::show_binlog_type(const Conv_source &src, String *str) const +{ + str->set_ascii(name().ptr(), name().length()); +} + + +void Type_handler_var_string::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), + str->alloced_length(), + "varchar(%u)", + src.metadata() / src.mbmaxlen()); + str->length(length); +} + + +void Type_handler_varchar::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), + str->alloced_length(), + "varchar(%u)", + src.metadata() / src.mbmaxlen()); + str->length(length); +} + + +void Type_handler_varchar_compressed::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), + str->alloced_length(), + "varchar(%u) compressed", + src.metadata() / src.mbmaxlen()); + str->length(length); +} + +void Type_handler_bit::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + int bit_length= 8 * (src.metadata() >> 8) + (src.metadata() & 0xFF); + size_t length= + cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), + "bit(%d)", bit_length); + str->length(length); +} + + +void Type_handler_olddecimal::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + size_t length= + cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), + "decimal(%d,?)/*old*/", src.metadata()); + str->length(length); + +} + + +void Type_handler_newdecimal::show_binlog_type(const Conv_source &src, + String *str) const +{ + CHARSET_INFO *cs= str->charset(); + size_t length= + cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(), + "decimal(%d,%d)", + src.metadata() >> 8, src.metadata() & 0xff); + str->length(length); +} + + +void Type_handler_blob_compressed::show_binlog_type(const Conv_source &src, + String *str) const +{ + /* + Field::real_type() lies regarding the actual type of a BLOB, so + it is necessary to check the pack length to figure out what kind + of blob it really is. + */ + switch (src.metadata()) { + case 1: + str->set_ascii(STRING_WITH_LEN("tinyblob compressed")); + break; + case 2: + str->set_ascii(STRING_WITH_LEN("blob compressed")); + break; + case 3: + str->set_ascii(STRING_WITH_LEN("mediumblob compressed")); + break; + default: + DBUG_ASSERT(0); + // Fall through + case 4: + str->set_ascii(STRING_WITH_LEN("longblob compressed")); + } +} + + +void Type_handler_string::show_binlog_type(const Conv_source &src, + String *str) const +{ + /* + This is taken from Field_string::unpack. + */ + CHARSET_INFO *cs= str->charset(); + uint bytes= (((src.metadata() >> 4) & 0x300) ^ 0x300) + + (src.metadata() & 0x00ff); + size_t length= cs->cset->snprintf(cs, (char*) str->ptr(), + str->alloced_length(), + "char(%d)", bytes / src.mbmaxlen()); + str->length(length); +} + + +enum_conv_type +Field::rpl_conv_type_from_same_data_type(uint16 metadata, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (metadata == 0) // Metadata can only be zero if no metadata was provided + { + /* + If there is no metadata, we either have an old event where no + metadata were supplied, or a type that does not require any + metadata. In either case, conversion can be done but no + conversion table is necessary. + */ + DBUG_PRINT("debug", ("Base types are identical, but there is no metadata")); + return CONV_TYPE_PRECISE; + } + + DBUG_PRINT("debug", ("Base types are identical, doing field size comparison")); + int order= 0; + if (!compatible_field_size(metadata, rli, param.table_def_flags(), &order)) + return CONV_TYPE_IMPOSSIBLE; + return order == 0 ? CONV_TYPE_PRECISE : + order < 0 ? CONV_TYPE_SUBSET_TO_SUPERSET : + CONV_TYPE_SUPERSET_TO_SUBSET; +} + + +enum_conv_type +Field_new_decimal::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + if (source.type_handler() == &type_handler_olddecimal || + source.type_handler() == &type_handler_newdecimal || + source.type_handler() == &type_handler_float || + source.type_handler() == &type_handler_double) + { + /* + Then the other type is either FLOAT, DOUBLE, or old style + DECIMAL, so we require lossy conversion. + */ + return CONV_TYPE_SUPERSET_TO_SUBSET; + } + return CONV_TYPE_IMPOSSIBLE; +} + + +/* + This covers FLOAT, DOUBLE and old DECIMAL +*/ +enum_conv_type +Field_real::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + if (source.type_handler() == &type_handler_olddecimal || + source.type_handler() == &type_handler_newdecimal) + return CONV_TYPE_SUPERSET_TO_SUBSET; // Always require lossy conversions + if (source.type_handler() == &type_handler_float || + source.type_handler() == &type_handler_double) + { + enum_conv_type order= compare_lengths(source.type_handler(), + max_display_length_for_field(source), + type_handler(), max_display_length()); + DBUG_ASSERT(order != CONV_TYPE_PRECISE); + return order; + } + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_int::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + /* + The length comparison check will do the correct job of comparing + the field lengths (in bytes) of two integer types. + */ + if (source.type_handler() == &type_handler_tiny || + source.type_handler() == &type_handler_short || + source.type_handler() == &type_handler_int24 || + source.type_handler() == &type_handler_long || + source.type_handler() == &type_handler_longlong) + { + /* + max_display_length_for_field() is not fully precise for the integer + data types. So its result cannot be compared to the result of + max_dispay_length() when the table field and the binlog field + are of the same type. + This code should eventually be rewritten not to use + compare_lengths(), to detect subtype/supetype relations + just using the type codes. + */ + DBUG_ASSERT(source.real_field_type() != real_type()); + enum_conv_type order= compare_lengths(source.type_handler(), + max_display_length_for_field(source), + type_handler(), max_display_length()); + DBUG_ASSERT(order != CONV_TYPE_PRECISE); + return order; + } + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_enum::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + /* + For some reasons Field_enum and Field_set store MYSQL_TYPE_STRING + as a type code in the binary log and encode the real type in metadata. + So we need to test real_type() here instread of binlog_type(). + */ + return real_type() == source.real_field_type() ? + rpl_conv_type_from_same_data_type(source.metadata(), rli, param) : + CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_longstr::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + /** + @todo + Implement Field_varstring_compressed::real_type() and + Field_blob_compressed::real_type() properly. All occurencies + of Field::real_type() have to be inspected and adjusted if needed. + + Until it is not ready we have to compare source_type against + binlog_type() when replicating from or to compressed data types. + + @sa Comment for Field::binlog_type() + */ + bool same_type; + if (source.real_field_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || + source.real_field_type() == MYSQL_TYPE_BLOB_COMPRESSED || + binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || + binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED) + same_type= binlog_type() == source.real_field_type(); + else + same_type= type_handler() == source.type_handler(); + + if (same_type) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + + if (source.type_handler() == &type_handler_tiny_blob || + source.type_handler() == &type_handler_medium_blob || + source.type_handler() == &type_handler_long_blob || + source.type_handler() == &type_handler_blob || + source.type_handler() == &type_handler_blob_compressed || + source.type_handler() == &type_handler_string || + source.type_handler() == &type_handler_var_string || + source.type_handler() == &type_handler_varchar || + source.type_handler() == &type_handler_varchar_compressed) + { + enum_conv_type order= compare_lengths(source.type_handler(), + max_display_length_for_field(source), + type_handler(), max_display_length()); + /* + Here we know that the types are different, so if the order + gives that they do not require any conversion, we still need + to have non-lossy conversion enabled to allow conversion + between different (string) types of the same length. + + Also, if all conversions are disabled, it is not allowed to convert + between these types. Since the TEXT vs. BINARY is distinguished by + the charset, and the charset is not replicated, we cannot + currently distinguish between , e.g., TEXT and BLOB. + */ + if (order == CONV_TYPE_PRECISE) + order= CONV_TYPE_SUBSET_TO_SUPERSET; + return order; + } + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_newdate::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (real_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + if (source.type_handler() == &type_handler_datetime2) + return CONV_TYPE_SUPERSET_TO_SUBSET; + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_time::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + // 'MySQL56 TIME(N)' -> 'MariaDB-5.3 TIME(N)' is non-lossy + if (decimals() == source.metadata() && + source.type_handler() == &type_handler_time2) + return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2 + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_timef::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + /* + See comment in Field_datetimef::rpl_conv_type_from() + 'MariaDB-5.3 TIME(0)' to 'MySQL56 TIME(0)' is non-lossy + */ + if (source.metadata() == 0 && source.type_handler() == &type_handler_time) + return CONV_TYPE_VARIANT; + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_timestamp::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + // 'MySQL56 TIMESTAMP(N)' -> MariaDB-5.3 TIMESTAMP(N)' is non-lossy + if (source.metadata() == decimals() && + source.type_handler() == &type_handler_timestamp2) + return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2 + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_timestampf::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + /* + See comment in Field_datetimef::rpl_conv_type_from() + 'MariaDB-5.3 TIMESTAMP(0)' to 'MySQL56 TIMESTAMP(0)' is non-lossy + */ + if (source.metadata() == 0 && + source.type_handler() == &type_handler_timestamp) + return CONV_TYPE_VARIANT; + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_datetime::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + // 'MySQL56 DATETIME(N)' -> MariaDB-5.3 DATETIME(N) is non-lossy + if (source.metadata() == decimals() && + source.type_handler() == &type_handler_datetime2) + return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2 + if (source.type_handler() == &type_handler_newdate) + return CONV_TYPE_SUBSET_TO_SUPERSET; + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_datetimef::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + if (binlog_type() == source.real_field_type()) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + /* + 'MariaDB-5.3 DATETIME(N)' does not provide information about fractional + precision in metadata. So we assume the precision on the master is equal + to the precision on the slave. + TODO: See MDEV-17394 what happend in case precisions are in case different + 'MariaDB-5.3 DATETIME(0)' to 'MySQL56 DATETIME(0)' is non-lossy + */ + if (source.metadata() == 0 && + source.type_handler() == &type_handler_datetime) + return CONV_TYPE_VARIANT; + if (source.type_handler() == &type_handler_newdate) + return CONV_TYPE_SUBSET_TO_SUPERSET; + return CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_date::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + // old DATE + return binlog_type() == source.real_field_type() ? + rpl_conv_type_from_same_data_type(source.metadata(), rli, param) : + CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_bit::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + return binlog_type() == source.real_field_type() ? + rpl_conv_type_from_same_data_type(source.metadata(), rli, param) : + CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_year::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + return binlog_type() == source.real_field_type() ? + rpl_conv_type_from_same_data_type(source.metadata(), rli, param) : + CONV_TYPE_IMPOSSIBLE; +} + + +enum_conv_type +Field_null::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + DBUG_ASSERT(0); + return CONV_TYPE_IMPOSSIBLE; +} + + +#ifdef HAVE_SPATIAL +uint32 +Type_handler_geometry::max_display_length_for_field(const Conv_source &src) + const +{ + return (uint32) my_set_bits(4 * 8); +} + + +enum_conv_type +Field_geom::rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const +{ + return binlog_type() == source.real_field_type() ? + rpl_conv_type_from_same_data_type(source.metadata(), rli, param) : + CONV_TYPE_IMPOSSIBLE; +} +#endif // HAVE_SPATIAL + + +/**********************************************************************/ + + +#if defined(HAVE_REPLICATION) + +/** + */ +void show_sql_type(const Conv_source &src, String *str) +{ + DBUG_ENTER("show_sql_type"); + DBUG_ASSERT(src.type_handler() != NULL); + DBUG_PRINT("enter", ("type: %s, metadata: 0x%x", + src.type_handler()->name().ptr(), src.metadata())); + src.type_handler()->show_binlog_type(src, str); + DBUG_VOID_RETURN; +} + + +/** + 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. + */ +static bool is_conversion_ok(enum_conv_type type, const Relay_log_info *rli, + ulonglong type_conversion_options) +{ + DBUG_ENTER("is_conversion_ok"); + bool allow_non_lossy, allow_lossy; + + allow_non_lossy= type_conversion_options & + (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY); + allow_lossy= type_conversion_options & + (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY); + + DBUG_PRINT("enter", ("order: %d, flags:%s%s", (int) type, + allow_non_lossy ? " ALL_NON_LOSSY" : "", + allow_lossy ? " ALL_LOSSY" : "")); + switch (type) { + case CONV_TYPE_PRECISE: + case CONV_TYPE_VARIANT: + DBUG_RETURN(true); + case CONV_TYPE_SUBSET_TO_SUPERSET: + /* !!! Add error message saying that non-lossy conversions need to be allowed. */ + DBUG_RETURN(allow_non_lossy); + case CONV_TYPE_SUPERSET_TO_SUBSET: + /* !!! Add error message saying that lossy conversions need to be allowed. */ + DBUG_RETURN(allow_lossy); + case CONV_TYPE_IMPOSSIBLE: + DBUG_RETURN(false); + } + + DBUG_RETURN(false); +} + + +/** + Can a type potentially be converted to another type? + + This function check if the types are convertible and what + conversion is required. + + If conversion is not possible, and error is printed. + + If conversion is possible: + + - *order will be set to -1 if source type is smaller than target + type and a non-lossy conversion can be required. This includes + the case where the field types are different but types could + actually be converted in either direction. + + - *order will be set to 0 if no conversion is required. + + - *order will be set to 1 if the source type is strictly larger + than the target type and that conversion is potentially lossy. + + @param[in] field Target field + @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 + settings, @c false if conversion is not possible according to the + current setting. + */ +static enum_conv_type +can_convert_field_to(Field *field, const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) +{ + DBUG_ENTER("can_convert_field_to"); +#ifndef DBUG_OFF + char field_type_buf[MAX_FIELD_WIDTH]; + String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1); + field->sql_type(field_type); + DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x", + field_type.c_ptr_safe(), field->real_type(), + source.real_field_type(), source.metadata())); +#endif + DBUG_RETURN(field->rpl_conv_type_from(source, rli, param)); +} + + +const Type_handler *table_def::field_type_handler(uint col) const +{ + enum_field_types typecode= binlog_type(col); + uint16 metadata= field_metadata(col); + DBUG_ASSERT(typecode != MYSQL_TYPE_ENUM); + DBUG_ASSERT(typecode != MYSQL_TYPE_SET); + + if (typecode == MYSQL_TYPE_BLOB) + { + switch (metadata & 0xff) { + case 1: return &type_handler_tiny_blob; + case 2: return &type_handler_blob; + case 3: return &type_handler_medium_blob; + case 4: return &type_handler_long_blob; + default: return NULL; + } + } + if (typecode == MYSQL_TYPE_STRING) + { + uchar typecode2= metadata >> 8; + if (typecode2 == MYSQL_TYPE_SET) + return &type_handler_set; + if (typecode2 == MYSQL_TYPE_ENUM) + return &type_handler_enum; + return &type_handler_string; + } + /* + This type has not been used since before row-based replication, + so we can safely assume that it really is MYSQL_TYPE_NEWDATE. + */ + if (typecode == MYSQL_TYPE_DATE) + return &type_handler_newdate; + return Type_handler::get_handler_by_real_type(typecode); +} + + +/** + Is the definition compatible with a table? + + This function will compare the master table with an existing table + on the slave and see if they are compatible with respect to the + current settings of @c SLAVE_TYPE_CONVERSIONS. + + If the tables are compatible and conversions are required, @c + *tmp_table_var will be set to a virtual temporary table with field + pointers for the fields that require conversions. This allow simple + checking of whether a conversion are to be applied or not. + + If tables are compatible, but no conversions are necessary, @c + *tmp_table_var will be set to NULL. + + @param rli_arg[in] + Relay log info, for error reporting. + + @param table[in] + Table to compare with + + @param tmp_table_var[out] + Virtual temporary table for performing conversions, if necessary. + + @retval true Master table is compatible with slave table. + @retval false Master table is not compatible with slave table. +*/ +bool +table_def::compatible_with(THD *thd, rpl_group_info *rgi, + TABLE *table, TABLE **conv_table_var) + const +{ + /* + We only check the initial columns for the tables. + */ + uint const cols_to_check= MY_MIN(table->s->fields, size()); + Relay_log_info *rli= rgi->rli; + TABLE *tmp_table= NULL; + + for (uint col= 0 ; col < cols_to_check ; ++col) + { + Field *const field= table->field[col]; + const Type_handler *h= field_type_handler(col); + if (!h) + { + sql_print_error("In RBR mode, Slave received unknown field type field %d " + " for column Name: %s.%s.%s.", + binlog_type(col), + field->table->s->db.str, + field->table->s->table_name.str, + field->field_name.str); + return false; + } + + if (!h) + return false; // An unknown data type found in the binary log + Conv_source source(h, field_metadata(col), field->charset()); + enum_conv_type convtype= can_convert_field_to(field, source, rli, + Conv_param(m_flags)); + if (is_conversion_ok(convtype, rli, slave_type_conversions_options)) + { + DBUG_PRINT("debug", ("Checking column %d -" + " field '%s' can be converted - order: %d", + col, field->field_name.str, convtype)); + /* + If conversion type is not CONV_TYPE_RECISE, a conversion is required, + so we need to set up the conversion table. + */ + if (convtype != CONV_TYPE_PRECISE && tmp_table == NULL) + { + /* + This will create the full table with all fields. This is + necessary to ge the correct field lengths for the record. + */ + tmp_table= create_conversion_table(thd, rgi, table); + if (tmp_table == NULL) + return false; + /* + Clear all fields up to, but not including, this column. + */ + for (unsigned int i= 0; i < col; ++i) + tmp_table->field[i]= NULL; + } + + if (convtype == CONV_TYPE_PRECISE && tmp_table != NULL) + tmp_table->field[col]= NULL; + } + else + { + DBUG_PRINT("debug", ("Checking column %d -" + " field '%s' can not be converted", + col, field->field_name.str)); + DBUG_ASSERT(col < size() && col < table->s->fields); + DBUG_ASSERT(table->s->db.str && table->s->table_name.str); + DBUG_ASSERT(table->in_use); + const char *db_name= table->s->db.str; + const char *tbl_name= table->s->table_name.str; + StringBuffer<MAX_FIELD_WIDTH> source_type(&my_charset_latin1); + StringBuffer<MAX_FIELD_WIDTH> target_type(&my_charset_latin1); + THD *thd= table->in_use; + + show_sql_type(source, &source_type); + field->sql_type(target_type); + DBUG_ASSERT(source_type.length() > 0); + DBUG_ASSERT(target_type.length() > 0); + rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(), + ER_THD(thd, ER_SLAVE_CONVERSION_FAILED), + col, db_name, tbl_name, + source_type.c_ptr_safe(), target_type.c_ptr_safe()); + return false; + } + } + +#ifndef DBUG_OFF + if (tmp_table) + { + for (unsigned int col= 0; col < tmp_table->s->fields; ++col) + if (tmp_table->field[col]) + { + char source_buf[MAX_FIELD_WIDTH]; + char target_buf[MAX_FIELD_WIDTH]; + String source_type(source_buf, sizeof(source_buf), &my_charset_latin1); + String target_type(target_buf, sizeof(target_buf), &my_charset_latin1); + tmp_table->field[col]->sql_type(source_type); + table->field[col]->sql_type(target_type); + DBUG_PRINT("debug", ("Field %s - conversion required." + " Source type: '%s', Target type: '%s'", + tmp_table->field[col]->field_name.str, + source_type.c_ptr_safe(), target_type.c_ptr_safe())); + } + } +#endif + + *conv_table_var= tmp_table; + return true; +} + + +/** + A wrapper to Virtual_tmp_table, to get access to its constructor, + which is protected for safety purposes (against illegal use on stack). +*/ +class Virtual_conversion_table: public Virtual_tmp_table +{ +public: + Virtual_conversion_table(THD *thd) :Virtual_tmp_table(thd) { } + /** + Add a new field into the virtual table. + @param handler - The type handler of the field. + @param metadata - The RBR binary log metadata for this field. + @param target_field - The field from the target table, to get extra + attributes from (e.g. typelib in case of ENUM). + */ + bool add(const Type_handler *handler, + uint16 metadata, const Field *target_field) + { + Field *tmp= handler->make_conversion_table_field(this, metadata, + target_field); + if (!tmp) + return true; + Virtual_tmp_table::add(tmp); + DBUG_PRINT("debug", ("sql_type: %s, target_field: '%s', max_length: %d, decimals: %d," + " maybe_null: %d, unsigned_flag: %d, pack_length: %u", + handler->name().ptr(), target_field->field_name.str, + tmp->field_length, tmp->decimals(), TRUE, + tmp->flags, tmp->pack_length())); + return false; + } +}; + + +/** + Create a conversion table. + + If the function is unable to create the conversion table, an error + will be printed and NULL will be returned. + + @return Pointer to conversion table, or NULL if unable to create + conversion table. + */ + +TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi, + TABLE *target_table) const +{ + DBUG_ENTER("table_def::create_conversion_table"); + + Virtual_conversion_table *conv_table; + Relay_log_info *rli= rgi->rli; + /* + At slave, columns may differ. So we should create + MY_MIN(columns@master, columns@slave) columns in the + conversion table. + */ + uint const cols_to_create= MY_MIN(target_table->s->fields, size()); + if (!(conv_table= new(thd) Virtual_conversion_table(thd)) || + conv_table->init(cols_to_create)) + goto err; + for (uint col= 0 ; col < cols_to_create; ++col) + { + const Type_handler *ha= field_type_handler(col); + DBUG_ASSERT(ha); // Checked at compatible_with() time + if (conv_table->add(ha, field_metadata(col), target_table->field[col])) + { + DBUG_PRINT("debug", ("binlog_type: %d, metadata: %04X, target_field: '%s'" + " make_conversion_table_field() failed", + binlog_type(col), field_metadata(col), + target_table->field[col]->field_name.str)); + goto err; + } + } + + if (conv_table->open()) + goto err; // Could not allocate record buffer? + + DBUG_RETURN(conv_table); + +err: + if (conv_table) + delete conv_table; + rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, rgi->gtid_info(), + ER_THD(thd, ER_SLAVE_CANT_CREATE_CONVERSION), + target_table->s->db.str, + target_table->s->table_name.str); + DBUG_RETURN(NULL); +} + + + +Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL) +{ + my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16, MYF(0)); +} + +Deferred_log_events::~Deferred_log_events() +{ + delete_dynamic(&array); +} + +int Deferred_log_events::add(Log_event *ev) +{ + last_added= ev; + insert_dynamic(&array, (uchar*) &ev); + return 0; +} + +bool Deferred_log_events::is_empty() +{ + return array.elements == 0; +} + +bool Deferred_log_events::execute(rpl_group_info *rgi) +{ + bool res= false; + DBUG_ENTER("Deferred_log_events::execute"); + DBUG_ASSERT(rgi->deferred_events_collecting); + + rgi->deferred_events_collecting= false; + for (uint i= 0; !res && i < array.elements; i++) + { + Log_event *ev= (* (Log_event **) + dynamic_array_ptr(&array, i)); + res= ev->apply_event(rgi); + } + rgi->deferred_events_collecting= true; + DBUG_RETURN(res); +} + +void Deferred_log_events::rewind() +{ + /* + Reset preceding Query log event events which execution was + deferred because of slave side filtering. + */ + if (!is_empty()) + { + for (uint i= 0; i < array.elements; i++) + { + Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i); + delete ev; + } + last_added= NULL; + if (array.elements > array.max_element) + freeze_size(&array); + reset_dynamic(&array); + } + last_added= NULL; +} + +#endif // defined(HAVE_REPLICATION) + diff --git a/sql/sql_type.cc b/sql/sql_type.cc index ee5a20a08cf..1a1051f9fbe 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -60,13 +60,13 @@ Type_handler_string type_handler_string; Type_handler_var_string type_handler_var_string; Type_handler_varchar type_handler_varchar; Type_handler_hex_hybrid type_handler_hex_hybrid; -static Type_handler_varchar_compressed type_handler_varchar_compressed; +Type_handler_varchar_compressed type_handler_varchar_compressed; Type_handler_tiny_blob type_handler_tiny_blob; Type_handler_medium_blob type_handler_medium_blob; Type_handler_long_blob type_handler_long_blob; Type_handler_blob type_handler_blob; -static Type_handler_blob_compressed type_handler_blob_compressed; +Type_handler_blob_compressed type_handler_blob_compressed; Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff; @@ -1908,13 +1908,7 @@ Type_handler::get_handler_by_real_type(enum_field_types type) case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob; case MYSQL_TYPE_BLOB: return &type_handler_blob; case MYSQL_TYPE_BLOB_COMPRESSED: return &type_handler_blob_compressed; - case MYSQL_TYPE_VAR_STRING: - /* - VAR_STRING is actually a field_type(), not a real_type(), - but it's used around the code in real_type() context. - We should clean up the code and add DBUG_ASSERT(0) here. - */ - return &type_handler_string; + case MYSQL_TYPE_VAR_STRING: return &type_handler_var_string; case MYSQL_TYPE_STRING: return &type_handler_string; case MYSQL_TYPE_ENUM: return &type_handler_enum; case MYSQL_TYPE_SET: return &type_handler_set; @@ -1933,8 +1927,7 @@ Type_handler::get_handler_by_real_type(enum_field_types type) case MYSQL_TYPE_DATETIME2: return &type_handler_datetime2; case MYSQL_TYPE_NEWDATE: return &type_handler_newdate; }; - DBUG_ASSERT(0); - return &type_handler_string; + return NULL; } diff --git a/sql/sql_type.h b/sql/sql_type.h index fe390061382..7daefcb10d7 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -84,6 +84,7 @@ struct TABLE; struct SORT_FIELD_ATTR; class Vers_history_point; class Virtual_column_info; +class Conv_source; struct ST_FIELD_INFO; #define my_charset_numeric my_charset_latin1 @@ -3393,6 +3394,8 @@ public: virtual Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const= 0; + virtual void show_binlog_type(const Conv_source &src, String *str) const; + virtual uint32 max_display_length_for_field(const Conv_source &src) const= 0; /* Performs the final data type validation for a UNION element, after the regular "aggregation for result" was done. @@ -3891,6 +3894,11 @@ public: DBUG_ASSERT(0); return 0; } + uint32 max_display_length_for_field(const Conv_source &src) const + { + DBUG_ASSERT(0); + return 0; + } uint32 calc_pack_length(uint32 length) const { DBUG_ASSERT(0); @@ -4825,6 +4833,8 @@ public: return unsigned_fl ? &m_limits_uint8 : &m_limits_sint8; } uint32 calc_pack_length(uint32 length) const { return 1; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 4; } bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { return Item_send_tiny(item, protocol, buf); @@ -4877,6 +4887,8 @@ public: { return unsigned_fl ? &m_limits_uint16 : &m_limits_sint16; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 6; } uint32 calc_pack_length(uint32 length) const { return 2; } Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const; @@ -4922,6 +4934,8 @@ public: { return unsigned_fl ? &m_limits_uint32 : &m_limits_sint32; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 11; } uint32 calc_pack_length(uint32 length) const { return 4; } bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -4982,6 +4996,8 @@ public: { return unsigned_fl ? &m_limits_uint64 : &m_limits_sint64; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 20; } uint32 calc_pack_length(uint32 length) const { return 8; } Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; @@ -5050,6 +5066,8 @@ public: { return unsigned_fl ? &m_limits_uint24 : &m_limits_sint24; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 9; } uint32 calc_pack_length(uint32 length) const { return 3; } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5084,6 +5102,8 @@ public: return PROTOCOL_SEND_SHORT; } uint32 max_display_length(const Item *item) const; + uint32 max_display_length_for_field(const Conv_source &src) const + { return 4; } uint32 calc_pack_length(uint32 length) const { return 1; } bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -5133,6 +5153,7 @@ public: return PROTOCOL_SEND_STRING; } uint32 max_display_length(const Item *item) const; + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const { return length / 8; } bool Item_send(Item *item, Protocol *protocol, st_value *buf) const { @@ -5142,6 +5163,7 @@ public: { return print_item_value_csstr(thd, item, str); } + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; @@ -5186,6 +5208,8 @@ public: } bool type_can_have_auto_increment_attribute() const { return true; } uint32 max_display_length(const Item *item) const { return 25; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 12; } uint32 calc_pack_length(uint32 length) const { return sizeof(float); } Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; @@ -5239,6 +5263,8 @@ public: } bool type_can_have_auto_increment_attribute() const { return true; } uint32 max_display_length(const Item *item) const { return 53; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 22; } uint32 calc_pack_length(uint32 length) const { return sizeof(double); } Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; @@ -5375,6 +5401,8 @@ public: static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } virtual ~Type_handler_time() {} const Name version() const { return m_version_mariadb53; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return MIN_TIME_WIDTH; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5402,6 +5430,7 @@ public: virtual ~Type_handler_time2() {} const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5456,6 +5485,8 @@ public: const Name &default_value() const; const Type_handler *type_handler_for_comparison() const; enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return 3; } enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) const { return DYN_COL_DATE; @@ -5624,6 +5655,8 @@ public: static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } virtual ~Type_handler_datetime() {} const Name version() const { return m_version_mariadb53; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return MAX_DATETIME_WIDTH; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5651,6 +5684,7 @@ public: virtual ~Type_handler_datetime2() {} const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5759,6 +5793,8 @@ public: static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; } virtual ~Type_handler_timestamp() {} const Name version() const { return m_version_mariadb53; } + uint32 max_display_length_for_field(const Conv_source &src) const + { return MAX_DATETIME_WIDTH; } uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5786,6 +5822,7 @@ public: virtual ~Type_handler_timestamp2() {} const Name version() const { return m_version_mysql56; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; @@ -5816,9 +5853,11 @@ public: virtual ~Type_handler_olddecimal() {} const Name name() const { return m_name_decimal; } enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const { return length; } const Type_handler *type_handler_for_tmp_table(const Item *item) const; const Type_handler *type_handler_for_union(const Item *item) const; + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; @@ -5847,7 +5886,9 @@ public: virtual ~Type_handler_newdecimal() {} const Name name() const { return m_name_decimal; } enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; @@ -5893,6 +5934,7 @@ public: const Type_handler *type_handler_for_tmp_table(const Item *item) const; const Type_handler *type_handler_for_union(const Item *) const; uint32 max_display_length(const Item *item) const { return 0; } + uint32 max_display_length_for_field(const Conv_source &src) const { return 0;} uint32 calc_pack_length(uint32 length) const { return 0; } bool Item_const_eq(const Item_const *a, const Item_const *b, bool binary_cmp) const; @@ -5947,11 +5989,13 @@ public: const Name name() const { return m_name_char; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; } bool is_param_long_data_type() const { return true; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const { return length; } const Type_handler *type_handler_for_tmp_table(const Item *item) const { return varstring_type_handler(item); } + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; @@ -5993,6 +6037,8 @@ public: { return varstring_type_handler(item); } + uint32 max_display_length_for_field(const Conv_source &src) const; + void show_binlog_type(const Conv_source &src, String *str) const; void Column_definition_implicit_upgrade(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const; bool Column_definition_prepare_stage2(Column_definition *c, @@ -6017,6 +6063,7 @@ public: { return MYSQL_TYPE_VAR_STRING; // Keep things compatible for old clients } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const { return (length + (length < 256 ? 1: 2)); @@ -6030,6 +6077,7 @@ public: return varstring_type_handler(item); } bool is_param_long_data_type() const { return true; } + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; bool Column_definition_fix_attributes(Column_definition *c) const; @@ -6065,6 +6113,12 @@ public: class Type_handler_varchar_compressed: public Type_handler_varchar { public: + enum_field_types real_field_type() const + { + return MYSQL_TYPE_VARCHAR_COMPRESSED; + } + uint32 max_display_length_for_field(const Conv_source &src) const; + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) const @@ -6132,6 +6186,7 @@ public: uint length_bytes() const { return 1; } const Name name() const { return m_name_tinyblob; } enum_field_types field_type() const { return MYSQL_TYPE_TINY_BLOB; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_table_field(const LEX_CSTRING *name, const Record_addr &addr, @@ -6149,6 +6204,7 @@ public: uint length_bytes() const { return 3; } const Name name() const { return m_name_mediumblob; } enum_field_types field_type() const { return MYSQL_TYPE_MEDIUM_BLOB; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_table_field(const LEX_CSTRING *name, const Record_addr &addr, @@ -6166,6 +6222,7 @@ public: uint length_bytes() const { return 4; } const Name name() const { return m_name_longblob; } enum_field_types field_type() const { return MYSQL_TYPE_LONG_BLOB; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Item *create_typecast_item(THD *thd, Item *item, const Type_cast_attributes &attr) const; @@ -6185,6 +6242,7 @@ public: uint length_bytes() const { return 2; } const Name name() const { return m_name_blob; } enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; Field *make_table_field(const LEX_CSTRING *name, const Record_addr &addr, @@ -6197,6 +6255,12 @@ public: class Type_handler_blob_compressed: public Type_handler_blob { public: + enum_field_types real_field_type() const + { + return MYSQL_TYPE_BLOB_COMPRESSED; + } + uint32 max_display_length_for_field(const Conv_source &src) const; + void show_binlog_type(const Conv_source &src, String *str) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) const @@ -6216,6 +6280,7 @@ public: const Name name() const { return m_name_geometry; } enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; } bool is_param_long_data_type() const { return true; } + uint32 max_display_length_for_field(const Conv_source &src) const; uint32 calc_pack_length(uint32 length) const; const Type_handler *type_handler_for_comparison() const; bool type_can_have_key_part() const @@ -6313,6 +6378,7 @@ public: enum_field_types field_type() const { return MYSQL_TYPE_STRING; } const Type_handler *type_handler_for_item_field() const; const Type_handler *cast_to_int_type_handler() const; + uint32 max_display_length_for_field(const Conv_source &src) const; bool Item_hybrid_func_fix_attributes(THD *thd, const char *name, Type_handler_hybrid_field_type *, @@ -6508,12 +6574,16 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_set type_handler_set; extern MYSQL_PLUGIN_IMPORT Type_handler_string type_handler_string; extern MYSQL_PLUGIN_IMPORT Type_handler_var_string type_handler_var_string; extern MYSQL_PLUGIN_IMPORT Type_handler_varchar type_handler_varchar; +extern MYSQL_PLUGIN_IMPORT Type_handler_varchar_compressed + type_handler_varchar_compressed; extern MYSQL_PLUGIN_IMPORT Type_handler_hex_hybrid type_handler_hex_hybrid; extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob; extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_blob_compressed + type_handler_blob_compressed; extern MYSQL_PLUGIN_IMPORT Type_handler_bool type_handler_bool; extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny; |