Skip to content

Commit 47bd3f7

Browse files
author
Sujatha Sivakumar
committed
Bug#25135304: RBR: WRONG FIELD LENGTH IN ERROR MESSAGE
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'" 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. Fix: === For CHAR and VARCHAR fields display their length in bytes 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.
1 parent 84ae266 commit 47bd3f7

File tree

4 files changed

+239
-12
lines changed

4 files changed

+239
-12
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
include/master-slave.inc
2+
Warnings:
3+
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
4+
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
5+
[connection master]
6+
####################################################################
7+
# Test Case1: Improved error message with charset information
8+
####################################################################
9+
[connection master]
10+
SET SQL_LOG_BIN=0;
11+
CREATE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'utf8mb3');
12+
SET SQL_LOG_BIN=1;
13+
[connection slave]
14+
CREATE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'utf8mb4');
15+
[connection master]
16+
INSERT INTO t1 VALUES ('a');
17+
[connection slave]
18+
include/wait_for_slave_sql_error.inc [errno=1677]
19+
Matching lines are:
20+
DATE_TIME [ERROR] Slave SQL for channel '': Column 0 of table 'test.t1' cannot be converted from type 'varchar(3(bytes))' to type 'varchar(4(bytes) utf8mb4)', Error_code: 1677
21+
Occurrences of '\'varchar\(3\(bytes\)\)\' to type \'varchar\(4\(bytes\) utf8mb4\)\'' in the input file: 1
22+
[connection master]
23+
DROP TABLE t1;
24+
[connection slave]
25+
DROP TABLE t1;
26+
include/rpl_reset.inc
27+
####################################################################
28+
# Test Case2: Improved error message with charset information for CHAR
29+
# type
30+
####################################################################
31+
[connection master]
32+
SET SQL_LOG_BIN=0;
33+
CREATE TABLE t1 (c1 CHAR(1) CHARACTER SET 'utf8mb3');
34+
SET SQL_LOG_BIN=1;
35+
[connection slave]
36+
CREATE TABLE t1 (c1 CHAR(1) CHARACTER SET 'utf8mb4');
37+
[connection master]
38+
INSERT INTO t1 VALUES ('a');
39+
[connection slave]
40+
include/wait_for_slave_sql_error.inc [errno=1677]
41+
Matching lines are:
42+
DATE_TIME [ERROR] Slave SQL for channel '': Column 0 of table 'test.t1' cannot be converted from type 'char(3(bytes))' to type 'char(4(bytes) utf8mb4)', Error_code: 1677
43+
Occurrences of '\'char\(3\(bytes\)\)\' to type \'char\(4\(bytes\) utf8mb4\)\'' in the input file: 1
44+
[connection master]
45+
DROP TABLE t1;
46+
[connection slave]
47+
DROP TABLE t1;
48+
include/rpl_reset.inc
49+
####################################################################
50+
# Test Case3: For BLOB type fileds, when type conversion failed on
51+
# slave, the errormessage had incorrect type names.
52+
####################################################################
53+
[connection master]
54+
SET SQL_LOG_BIN=0;
55+
CREATE TABLE t1 (c1 LONGBLOB);
56+
SET SQL_LOG_BIN=1;
57+
[connection slave]
58+
CREATE TABLE t1 (c1 TINYBLOB);
59+
[connection master]
60+
INSERT INTO t1 VALUES ('a');
61+
[connection slave]
62+
include/wait_for_slave_sql_error.inc [errno=1677]
63+
Matching lines are:
64+
DATE_TIME [ERROR] Slave SQL for channel '': Column 0 of table 'test.t1' cannot be converted from type 'longblob' to type 'tinyblob', Error_code: 1677
65+
Occurrences of '\'longblob\' to type \'tinyblob\'' in the input file: 1
66+
[connection master]
67+
DROP TABLE t1;
68+
[connection slave]
69+
DROP TABLE t1;
70+
include/rpl_reset.inc
71+
include/rpl_end.inc
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--no-console --log-error=$MYSQLTEST_VARDIR/tmp/slave.err
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# ==== Purpose ====
2+
#
3+
# Test verifies that when slave side type conversion fails in row based
4+
# replication, more informative error message is displayed. It also verifies
5+
# that in the case of blob fields appropriate type name is displayed in error
6+
# message.
7+
#
8+
# ==== Implementation ====
9+
#
10+
# Steps:
11+
# Test case1:
12+
# 1. Create a table on master with VARCHAR filed and charset
13+
# 'utf8mb3'.
14+
# 2. Create a table on slave with VARCHAR field and charset
15+
# 'utf8mb4'.
16+
# 3. Insert a tuple on master.
17+
# 4. Verify that slave provides more informative error message with
18+
# respect to difference in charsets.
19+
# Test case2: Repeat same steps as above for CHAR field
20+
# Test case3:
21+
# 1. Create a table on master with LONGBLOB field.
22+
# 2. Create a table on slave with TINYBLOB field.
23+
# 3. Insert a tuple on master.
24+
# 4. Verify that error message displayed on slave clearly states type
25+
# conversion failure from 'longblob' to 'tinyblob'.
26+
# 5. Also verify that error message doesn't show additional details
27+
# of charset when not required.
28+
#
29+
# ==== References ====
30+
#
31+
# Bug#25135304: RBR: WRONG FIELD LENGTH IN ERROR MESSAGE
32+
#
33+
34+
--source include/have_binlog_format_row.inc
35+
# Inorder to grep a specific error pattern in error log a fresh error log
36+
# needs to be generated.
37+
--source include/force_restart.inc
38+
--source include/master-slave.inc
39+
40+
--echo ####################################################################
41+
--echo # Test Case1: Improved error message with charset information
42+
--echo ####################################################################
43+
--source include/rpl_connection_master.inc
44+
SET SQL_LOG_BIN=0;
45+
CREATE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'utf8mb3');
46+
SET SQL_LOG_BIN=1;
47+
48+
--source include/rpl_connection_slave.inc
49+
CREATE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'utf8mb4');
50+
51+
--source include/rpl_connection_master.inc
52+
INSERT INTO t1 VALUES ('a');
53+
54+
--source include/rpl_connection_slave.inc
55+
--let $slave_sql_errno= convert_error(ER_SLAVE_CONVERSION_FAILED);
56+
--source include/wait_for_slave_sql_error.inc
57+
58+
# Error msg before: Column 0 of table 'test.t1' cannot be converted from type 'varchar(3)' to type 'varchar(1)'
59+
# Error msg after : Column 0 of table 'test.t1' cannot be converted from type 'varchar(3(bytes))' to type 'varchar(4(bytes) utf8mb4)'
60+
--replace_regex /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9][-+Z][0-9:]* *[0-9]* *\[/DATE_TIME [/ s/Worker .* end_log_pos [0-9]*; //
61+
--let $grep_file=$MYSQLTEST_VARDIR/tmp/slave.err
62+
--let $grep_pattern=\'varchar\(3\(bytes\)\)\' to type \'varchar\(4\(bytes\) utf8mb4\)\'
63+
--source include/grep_pattern.inc
64+
65+
--source include/rpl_connection_master.inc
66+
DROP TABLE t1;
67+
--source include/rpl_connection_slave.inc
68+
DROP TABLE t1;
69+
--let $rpl_only_running_threads= 1
70+
--source include/rpl_reset.inc
71+
72+
--echo ####################################################################
73+
--echo # Test Case2: Improved error message with charset information for CHAR
74+
--echo # type
75+
--echo ####################################################################
76+
--source include/rpl_connection_master.inc
77+
SET SQL_LOG_BIN=0;
78+
CREATE TABLE t1 (c1 CHAR(1) CHARACTER SET 'utf8mb3');
79+
SET SQL_LOG_BIN=1;
80+
81+
--source include/rpl_connection_slave.inc
82+
CREATE TABLE t1 (c1 CHAR(1) CHARACTER SET 'utf8mb4');
83+
84+
--source include/rpl_connection_master.inc
85+
INSERT INTO t1 VALUES ('a');
86+
87+
--source include/rpl_connection_slave.inc
88+
--let $slave_sql_errno= convert_error(ER_SLAVE_CONVERSION_FAILED);
89+
--source include/wait_for_slave_sql_error.inc
90+
91+
# Error msg before: Column 0 of table 'test.t1' cannot be converted from type 'char(0)' to type 'char(1)'
92+
# Error msg after : Column 0 of table 'test.t1' cannot be converted from type 'char(3(bytes))' to type 'char(4(bytes) utf8mb4)'
93+
--replace_regex /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9][-+Z][0-9:]* *[0-9]* *\[/DATE_TIME [/ s/Worker .* end_log_pos [0-9]*; //
94+
--let $grep_file=$MYSQLTEST_VARDIR/tmp/slave.err
95+
--let $grep_pattern=\'char\(3\(bytes\)\)\' to type \'char\(4\(bytes\) utf8mb4\)\'
96+
--source include/grep_pattern.inc
97+
98+
--source include/rpl_connection_master.inc
99+
DROP TABLE t1;
100+
--source include/rpl_connection_slave.inc
101+
DROP TABLE t1;
102+
--let $rpl_only_running_threads= 1
103+
--source include/rpl_reset.inc
104+
105+
--echo ####################################################################
106+
--echo # Test Case3: For BLOB type fileds, when type conversion failed on
107+
--echo # slave, the errormessage had incorrect type names.
108+
--echo ####################################################################
109+
--source include/rpl_connection_master.inc
110+
SET SQL_LOG_BIN=0;
111+
CREATE TABLE t1 (c1 LONGBLOB);
112+
SET SQL_LOG_BIN=1;
113+
114+
--source include/rpl_connection_slave.inc
115+
CREATE TABLE t1 (c1 TINYBLOB);
116+
117+
--source include/rpl_connection_master.inc
118+
INSERT INTO t1 VALUES ('a');
119+
120+
--source include/rpl_connection_slave.inc
121+
--let $slave_sql_errno= convert_error(ER_SLAVE_CONVERSION_FAILED);
122+
--source include/wait_for_slave_sql_error.inc
123+
124+
# Error msg before: Column 0 of table 'test.t1' cannot be converted from type 'tinyblob' to type 'tinyblob'
125+
# Error msg after : Column 0 of table 'test.t1' cannot be converted from type 'longblob' to type 'tinyblob'
126+
--replace_regex /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9][0-9][0-9][0-9][0-9][0-9][-+Z][0-9:]* *[0-9]* *\[/DATE_TIME [/ s/Worker .* end_log_pos [0-9]*; //
127+
--let $grep_file=$MYSQLTEST_VARDIR/tmp/slave.err
128+
--let $grep_pattern=\'longblob\' to type \'tinyblob\'
129+
--source include/grep_pattern.inc
130+
131+
--source include/rpl_connection_master.inc
132+
DROP TABLE t1;
133+
--source include/rpl_connection_slave.inc
134+
DROP TABLE t1;
135+
--let $rpl_only_running_threads= 1
136+
--source include/rpl_reset.inc
137+
--source include/rpl_end.inc

sql/rpl_utility.cc

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -162,7 +162,7 @@ static void show_sql_type(enum_field_types type, uint16 metadata, String *str,
162162
const CHARSET_INFO *cs= str->charset();
163163
size_t length=
164164
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
165-
"varchar(%u)", metadata);
165+
"varchar(%u(bytes))", metadata);
166166
str->length(length);
167167
}
168168
break;
@@ -212,22 +212,22 @@ static void show_sql_type(enum_field_types type, uint16 metadata, String *str,
212212
it is necessary to check the pack length to figure out what kind
213213
of blob it really is.
214214
*/
215-
switch (get_blob_type_from_length(metadata))
215+
switch (metadata)
216216
{
217-
case MYSQL_TYPE_TINY_BLOB:
217+
case 1:
218218
str->set_ascii(STRING_WITH_LEN("tinyblob"));
219219
break;
220220

221-
case MYSQL_TYPE_MEDIUM_BLOB:
222-
str->set_ascii(STRING_WITH_LEN("mediumblob"));
221+
case 2:
222+
str->set_ascii(STRING_WITH_LEN("blob"));
223223
break;
224224

225-
case MYSQL_TYPE_LONG_BLOB:
226-
str->set_ascii(STRING_WITH_LEN("longblob"));
225+
case 3:
226+
str->set_ascii(STRING_WITH_LEN("mediumblob"));
227227
break;
228228

229-
case MYSQL_TYPE_BLOB:
230-
str->set_ascii(STRING_WITH_LEN("blob"));
229+
case 4:
230+
str->set_ascii(STRING_WITH_LEN("longblob"));
231231
break;
232232

233233
default:
@@ -245,7 +245,7 @@ static void show_sql_type(enum_field_types type, uint16 metadata, String *str,
245245
uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
246246
size_t length=
247247
cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
248-
"char(%d)", bytes / field_cs->mbmaxlen);
248+
"char(%d(bytes))", bytes);
249249
str->length(length);
250250
}
251251
break;
@@ -660,11 +660,11 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
660660
const char *tbl_name= table->s->table_name.str;
661661
char source_buf[MAX_FIELD_WIDTH];
662662
char target_buf[MAX_FIELD_WIDTH];
663+
String field_sql_type;
663664
enum loglevel report_level= INFORMATION_LEVEL;
664665
String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
665666
String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
666667
show_sql_type(type(col), field_metadata(col), &source_type, field->charset());
667-
field->sql_type(target_type);
668668
if (!ignored_error_code(ER_SLAVE_CONVERSION_FAILED))
669669
{
670670
report_level= ERROR_LEVEL;
@@ -674,6 +674,24 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
674674
else if (log_warnings > 1)
675675
report_level= WARNING_LEVEL;
676676

677+
if (field->has_charset() &&
678+
(field->type() == MYSQL_TYPE_VARCHAR ||
679+
field->type() == MYSQL_TYPE_STRING))
680+
{
681+
field_sql_type.append((field->type() == MYSQL_TYPE_VARCHAR) ?
682+
"varchar" : "char");
683+
const CHARSET_INFO *cs= field->charset();
684+
size_t length= cs->cset->snprintf(cs, (char*) target_type.ptr(),
685+
target_type.alloced_length(),
686+
"%s(%u(bytes) %s)",
687+
field_sql_type.c_ptr_safe(),
688+
field->field_length,
689+
field->charset()->csname);
690+
target_type.length(length);
691+
}
692+
else
693+
field->sql_type(target_type);
694+
677695
if (report_level != INFORMATION_LEVEL)
678696
rli->report(report_level, ER_SLAVE_CONVERSION_FAILED,
679697
ER(ER_SLAVE_CONVERSION_FAILED),

0 commit comments

Comments
 (0)