diff --git a/mysql-test/suite/engines/funcs/r/in_text_boundary_error.result b/mysql-test/suite/engines/funcs/r/in_text_boundary_error.result new file mode 100644 index 00000000000..43f4b36254b --- /dev/null +++ b/mysql-test/suite/engines/funcs/r/in_text_boundary_error.result @@ -0,0 +1,12 @@ +SET SQL_MODE="TRADITIONAL,ANSI"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(c1 TINYTEXT CHARACTER SET utf8mb4); +INSERT INTO t1 (c1) VALUES(REPEAT('A', 255)); +INSERT INTO t1 (c1) VALUES(REPEAT('A', 256)); +ERROR 22001: Data too long for column 'c1' at row 1 +INSERT INTO t1 (c1) VALUES(REPEAT(X'C385', 127)); +INSERT INTO t1 (c1) VALUES(REPEAT(X'C385', 128)); +ERROR 22001: Data too long for column 'c1' at row 1 +INSERT INTO t1 (c1) VALUES(CONCAT('a', REPEAT(X'C385', 128))); +ERROR 22001: Data too long for column 'c1' at row 1 +DROP TABLE t1; diff --git a/mysql-test/suite/engines/funcs/t/in_text_boundary_error.test b/mysql-test/suite/engines/funcs/t/in_text_boundary_error.test new file mode 100644 index 00000000000..f7f582283b3 --- /dev/null +++ b/mysql-test/suite/engines/funcs/t/in_text_boundary_error.test @@ -0,0 +1,14 @@ +SET SQL_MODE="TRADITIONAL,ANSI"; +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1(c1 TINYTEXT CHARACTER SET utf8mb4); +INSERT INTO t1 (c1) VALUES(REPEAT('A', 255)); +--error 1406 +INSERT INTO t1 (c1) VALUES(REPEAT('A', 256)); +INSERT INTO t1 (c1) VALUES(REPEAT(X'C385', 127)); +--error 1406 +INSERT INTO t1 (c1) VALUES(REPEAT(X'C385', 128)); +--error 1406 +INSERT INTO t1 (c1) VALUES(CONCAT('a', REPEAT(X'C385', 128))); +DROP TABLE t1; diff --git a/sql-common/sql_string.cc b/sql-common/sql_string.cc index aa93e684f1e..f64daf88cbc 100644 --- a/sql-common/sql_string.cc +++ b/sql-common/sql_string.cc @@ -1008,6 +1008,7 @@ size_t well_formed_copy_nchars(const CHARSET_INFO *to_cs, { int well_formed_error; uint from_offset; + size_t min_length; if ((from_offset= (from_length % to_cs->mbminlen)) && (from_cs == &my_charset_bin)) @@ -1045,13 +1046,25 @@ size_t well_formed_copy_nchars(const CHARSET_INFO *to_cs, to_length-= to_cs->mbminlen; } - set_if_smaller(from_length, to_length); - res= to_cs->cset->well_formed_len(to_cs, from, from + from_length, + min_length = min(from_length, to_length); + res= to_cs->cset->well_formed_len(to_cs, + from, from + min_length, nchars, &well_formed_error); if (res > 0) memmove(to, from, res); *from_end_pos= from + res; - *well_formed_error_pos= well_formed_error ? from + res : NULL; + if (well_formed_error && + (min_length - res < to_cs->mbmaxlen)) + { + const char *from_end= from + min(res + to_cs->mbmaxlen, from_length); + size_t extra; + + extra= to_cs->cset->well_formed_len(to_cs, + *from_end_pos, from_end, + 1, &well_formed_error); + well_formed_error= extra >= 1 ? 0 : 1; + } + *well_formed_error_pos= well_formed_error ? *from_end_pos : NULL; *cannot_convert_error_pos= NULL; if (from_offset) res+= to_cs->mbminlen;