diff options
Diffstat (limited to 'sql/sql_string.cc')
-rw-r--r-- | sql/sql_string.cc | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 7759985ba85..7c9793b273b 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1119,3 +1119,76 @@ void String::swap(String &s) swap_variables(bool, alloced, s.alloced); swap_variables(CHARSET_INFO*, str_charset, s.str_charset); } + + +/** + Convert string to printable ASCII string + + @details This function converts input string "from" replacing non-ASCII bytes + with hexadecimal sequences ("\xXX") optionally appending "..." to the end of + the resulting string. + This function used in the ER_TRUNCATED_WRONG_VALUE_FOR_FIELD error messages, + e.g. when a string cannot be converted to a result charset. + + + @param to output buffer + @param to_len size of the output buffer (8 bytes or greater) + @param from input string + @param from_len size of the input string + @param from_cs input charset + @param nbytes maximal number of bytes to convert (from_len if 0) + + @return number of bytes in the output string +*/ + +uint convert_to_printable(char *to, size_t to_len, + const char *from, size_t from_len, + CHARSET_INFO *from_cs, size_t nbytes /*= 0*/) +{ + /* needs at least 8 bytes for '\xXX...' and zero byte */ + DBUG_ASSERT(to_len >= 8); + + char *t= to; + char *t_end= to + to_len - 1; // '- 1' is for the '\0' at the end + const char *f= from; + const char *f_end= from + (nbytes ? min(from_len, nbytes) : from_len); + char *dots= to; // last safe place to append '...' + + if (!f || t == t_end) + return 0; + + for (; t < t_end && f < f_end; f++) + { + /* + If the source string is ASCII compatible (mbminlen==1) + and the source character is in ASCII printable range (0x20..0x7F), + then display the character as is. + + Otherwise, if the source string is not ASCII compatible (e.g. UCS2), + or the source character is not in the printable range, + then print the character using HEX notation. + */ + if (((unsigned char) *f) >= 0x20 && + ((unsigned char) *f) <= 0x7F && + from_cs->mbminlen == 1) + { + *t++= *f; + } + else + { + if (t_end - t < 4) // \xXX + break; + *t++= '\\'; + *t++= 'x'; + *t++= _dig_vec_upper[((unsigned char) *f) >> 4]; + *t++= _dig_vec_upper[((unsigned char) *f) & 0x0F]; + } + if (t_end - t >= 3) // '...' + dots= t; + } + if (f < from + from_len) + memcpy(dots, STRING_WITH_LEN("...\0")); + else + *t= '\0'; + return t - to; +} |