summaryrefslogtreecommitdiff
path: root/sql/rpl_utility.cc
diff options
context:
space:
mode:
authorSujatha <sujatha.sivakumar@mariadb.com>2019-08-27 13:05:04 +0530
committerSujatha <sujatha.sivakumar@mariadb.com>2019-08-27 13:05:04 +0530
commite7b71e0daa49ca9b5becbf2739ed13d7c8c4fcd6 (patch)
tree57a80dc4332e18e75397f24a7c66778372e2806d /sql/rpl_utility.cc
parent9bf424bc7b8a6199ef0c22b508835f21019f861a (diff)
downloadmariadb-git-e7b71e0daa49ca9b5becbf2739ed13d7c8c4fcd6.tar.gz
MDEV-19925: Column ... cannot be converted from type 'varchar(20)' to type 'varchar(20)'
Cherry picking: Bug#25135304: RBR: WRONG FIELD LENGTH IN ERROR MESSAGE commit 47bd3f7cf3c8518f62b1580ec65af2ba7ac13b95 Description: ============ In row based replication, when replicating from a table with a field with character set set to UTF8mb3 to the same table with the same field set to character set UTF8mb4 I get a confusing error message: For VARCHAR: VARCHAR(1) 'utf8mb3' to VARCHAR(1) 'utf8mb4' "Column 0 of table 'test.t1' cannot be converted from type 'varchar(3)' to type 'varchar(1)'" Similar issue with CHAR type as well. Issue with respect to BLOB types: For BLOB: LONGBLOB to TINYBLOB - Error message displays incorrect blob type. "Column 0 of table 'test.t1' cannot be converted from type 'tinyblob' to type 'tinyblob'" For BINARY to BINARY - Error message displays incorrect type for master side field. "Column 0 of table 'test.t' cannot be converted from type 'char(1)' to type 'binary(10)'" Similar issue exists for VARBINARY type. It is displayed as 'VARCHAR'. Analysis: ========= In Row based replication charset information is not sent as part of metadata from master to slave. For VARCHAR field its character length is converted into equivalent octets/bytes and stored internally. At the time of displaying the data to user it is converted back to original character length. For example: VARCHAR(2)- utf8mb3 is stored as:2*3 = VARCHAR(6) At the time of displaying it to user VARCHAR(6)- charset utf8mb3:6/3= VARCHAR(2). At present the internally converted octect length is sent from master to slave with out providing the charset information. On slave side if the type conversion fails 'show_sql_type' function is used to get the type specific information from metadata. Since there is no charset information is available the filed type is displayed as VARCHAR(6). This results in confused error message. For CHAR fields CHAR(1)- utf8mb3 - CHAR(3) CHAR(1)- utf8mb4 - CHAR(4) 'show_sql_type' function which retrieves type information from metadata uses (bytes/local charset length) to get actual character length. If slave's chaset is 'utf8mb4' then CHAR(3/4)-->CHAR(0) CHAR(4/4)-->CHAR(1). This results in confused error message. Analysis for BLOB type issue: BLOB's length is represented in two forms. 1. Actual length i.e (length < 256) type= MYSQL_TYPE_TINY_BLOB; (length < 65536) type= MYSQL_TYPE_BLOB; ... 2. packlength - The number of bytes used to represent the length of the blob 1- tinyblob 2- blob ... In row based replication only the packlength is written in the binary log. On the slave side this packlength is interpreted as actual length of the blob. Hence the length is always < 256 and the type is displayed as tiny blob. Analysis for BINARY to BINARY type issue: The character set information is needed to identify a filed's type as char or binary. Since master side character set information is not available on the slave side both binary and char fields are displayed as char. Fix: === For CHAR and VARCHAR fields display their length in octets for both source and target fields. For target field display the charset information if it is relevant. For blob type changed the code to use the packlength and display appropriate blob type in error message. For binary and varbinary fields use the slave side character set as reference to map them to binary or varbinary fields.
Diffstat (limited to 'sql/rpl_utility.cc')
-rw-r--r--sql/rpl_utility.cc47
1 files changed, 30 insertions, 17 deletions
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 4277e68a8b5..c28019e4dab 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -341,7 +341,8 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/**
*/
-void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_INFO *field_cs)
+void show_sql_type(enum_field_types type, uint16 metadata, String *str,
+ bool char_with_octets)
{
DBUG_ENTER("show_sql_type");
DBUG_PRINT("enter", ("type: %d, metadata: 0x%x", type, metadata));
@@ -408,9 +409,13 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
case MYSQL_TYPE_VARCHAR:
{
CHARSET_INFO *cs= str->charset();
- uint32 length=
- cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "varchar(%u)", metadata);
+ uint32 length=0;
+ if (char_with_octets)
+ length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "varchar(%u octets)", metadata);
+ else
+ length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "varbinary(%u)", metadata);
str->length(length);
}
break;
@@ -460,22 +465,22 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
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))
+ switch (metadata)
{
- case MYSQL_TYPE_TINY_BLOB:
+ case 1:
str->set_ascii(STRING_WITH_LEN("tinyblob"));
break;
- case MYSQL_TYPE_MEDIUM_BLOB:
- str->set_ascii(STRING_WITH_LEN("mediumblob"));
+ case 2:
+ str->set_ascii(STRING_WITH_LEN("blob"));
break;
- case MYSQL_TYPE_LONG_BLOB:
- str->set_ascii(STRING_WITH_LEN("longblob"));
+ case 3:
+ str->set_ascii(STRING_WITH_LEN("mediumblob"));
break;
- case MYSQL_TYPE_BLOB:
- str->set_ascii(STRING_WITH_LEN("blob"));
+ case 4:
+ str->set_ascii(STRING_WITH_LEN("longblob"));
break;
default:
@@ -491,9 +496,13 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
*/
CHARSET_INFO *cs= str->charset();
uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
- uint32 length=
- cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "char(%d)", bytes / field_cs->mbmaxlen);
+ uint32 length=0;
+ if (char_with_octets)
+ length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "char(%u octets)", bytes);
+ else
+ length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "binary(%u)", bytes);
str->length(length);
}
break;
@@ -896,9 +905,13 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi,
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;
+ bool char_with_octets= field->cmp_type() == STRING_RESULT ?
+ field->has_charset() : true;
+
+ show_sql_type(type(col), field_metadata(col), &source_type,
+ char_with_octets);
+ field->sql_rpl_type(&target_type);
- 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,