summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libmysqld/CMakeLists.txt4
-rw-r--r--mysql-test/suite/rpl/r/rpl_extra_col_master_innodb.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_extra_col_master_myisam.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_extra_col_slave_innodb.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_extra_col_slave_myisam.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_colSize.result2
-rw-r--r--sql/CMakeLists.txt3
-rw-r--r--sql/field.h110
-rw-r--r--sql/rpl_utility.cc975
-rw-r--r--sql/rpl_utility.h4
-rw-r--r--sql/rpl_utility_server.cc1191
-rw-r--r--sql/sql_type.cc15
-rw-r--r--sql/sql_type.h70
-rw-r--r--storage/tokudb/mysql-test/rpl/r/rpl_extra_col_master_tokudb.result2
-rw-r--r--storage/tokudb/mysql-test/rpl/r/rpl_extra_col_slave_tokudb.result2
15 files changed, 1389 insertions, 997 deletions
diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index eae85df2a54..f59163eb6a8 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -101,7 +101,9 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/multi_range_read.cc
../sql/opt_index_cond_pushdown.cc
../sql/opt_subselect.cc
- ../sql/create_options.cc ../sql/rpl_utility.cc
+ ../sql/create_options.cc
+ ../sql/rpl_utility.cc
+ ../sql/rpl_utility_server.cc
../sql/rpl_reporting.cc
../sql/sql_expression_cache.cc
../sql/my_apc.cc ../sql/my_apc.h
diff --git a/mysql-test/suite/rpl/r/rpl_extra_col_master_innodb.result b/mysql-test/suite/rpl/r/rpl_extra_col_master_innodb.result
index 3aa7c07a845..968d469fc8d 100644
--- a/mysql-test/suite/rpl/r/rpl_extra_col_master_innodb.result
+++ b/mysql-test/suite/rpl/r/rpl_extra_col_master_innodb.result
@@ -510,7 +510,7 @@ INSERT INTO t11 () VALUES(1,@b1,'Testing is fun','Kyle',DEFAULT),
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'tinyblob' to type 'varchar(254)''
+Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'blob' to type 'varchar(254)''
*** Drop t11 ***
connection master;
diff --git a/mysql-test/suite/rpl/r/rpl_extra_col_master_myisam.result b/mysql-test/suite/rpl/r/rpl_extra_col_master_myisam.result
index 0918364b28e..9be792a41fc 100644
--- a/mysql-test/suite/rpl/r/rpl_extra_col_master_myisam.result
+++ b/mysql-test/suite/rpl/r/rpl_extra_col_master_myisam.result
@@ -510,7 +510,7 @@ INSERT INTO t11 () VALUES(1,@b1,'Testing is fun','Kyle',DEFAULT),
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'tinyblob' to type 'varchar(254)''
+Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'blob' to type 'varchar(254)''
*** Drop t11 ***
connection master;
diff --git a/mysql-test/suite/rpl/r/rpl_extra_col_slave_innodb.result b/mysql-test/suite/rpl/r/rpl_extra_col_slave_innodb.result
index 456c1c24f8f..0017b8f7f99 100644
--- a/mysql-test/suite/rpl/r/rpl_extra_col_slave_innodb.result
+++ b/mysql-test/suite/rpl/r/rpl_extra_col_slave_innodb.result
@@ -102,7 +102,7 @@ INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TEST
********************************************
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'tinyblob' to type 'int(11)''
+Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'blob' to type 'int(11)''
*** Drop t3 ***
connection master;
DROP TABLE t3;
diff --git a/mysql-test/suite/rpl/r/rpl_extra_col_slave_myisam.result b/mysql-test/suite/rpl/r/rpl_extra_col_slave_myisam.result
index 7178a2a78b8..487c2c88b18 100644
--- a/mysql-test/suite/rpl/r/rpl_extra_col_slave_myisam.result
+++ b/mysql-test/suite/rpl/r/rpl_extra_col_slave_myisam.result
@@ -102,7 +102,7 @@ INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TEST
********************************************
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'tinyblob' to type 'int(11)''
+Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'blob' to type 'int(11)''
*** Drop t3 ***
connection master;
DROP TABLE t3;
diff --git a/mysql-test/suite/rpl/r/rpl_row_colSize.result b/mysql-test/suite/rpl/r/rpl_row_colSize.result
index dd324ef7807..d36f1964334 100644
--- a/mysql-test/suite/rpl/r/rpl_row_colSize.result
+++ b/mysql-test/suite/rpl/r/rpl_row_colSize.result
@@ -334,7 +334,7 @@ INSERT INTO t1 VALUES ('This is a test.');
connection slave;
START SLAVE;
include/wait_for_slave_sql_error.inc [errno=1677]
-Last_SQL_Error = 'Column 0 of table 'test.t1' cannot be converted from type 'tinyblob' to type 'tinyblob''
+Last_SQL_Error = 'Column 0 of table 'test.t1' cannot be converted from type 'longblob' to type 'tinyblob''
SELECT COUNT(*) FROM t1;
COUNT(*)
0
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 &param)
+ const= 0;
+ enum_conv_type rpl_conv_type_from_same_data_type(uint16 metadata,
+ const Relay_log_info *rli,
+ const Conv_param &param)
+ 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param)
+{
+ 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;
diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_master_tokudb.result b/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_master_tokudb.result
index cca18c7c1ab..83578f20404 100644
--- a/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_master_tokudb.result
+++ b/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_master_tokudb.result
@@ -510,7 +510,7 @@ INSERT INTO t11 () VALUES(1,@b1,'Testing is fun','Kyle',DEFAULT),
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'tinyblob' to type 'varchar(254)''
+Last_SQL_Error = 'Column 2 of table 'test.t11' cannot be converted from type 'blob' to type 'varchar(254)''
*** Drop t11 ***
connection master;
diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_slave_tokudb.result b/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_slave_tokudb.result
index 8906cf31d74..3f29138b946 100644
--- a/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_slave_tokudb.result
+++ b/storage/tokudb/mysql-test/rpl/r/rpl_extra_col_slave_tokudb.result
@@ -102,7 +102,7 @@ INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TEST
********************************************
connection slave;
include/wait_for_slave_sql_error_and_skip.inc [errno=1677]
-Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'tinyblob' to type 'int(11)''
+Last_SQL_Error = 'Column 0 of table 'test.t3' cannot be converted from type 'blob' to type 'int(11)''
*** Drop t3 ***
connection master;
DROP TABLE t3;