diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index e58aaa4554f..62e2085ae09 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -674,6 +674,18 @@ Warnings: Warning 1264 Out of range value for column 'Field1' at row 1 DROP TABLE t1; SET NAMES latin1; +SELECT CONVERT(103, CHAR(50) UNICODE); +CONVERT(103, CHAR(50) UNICODE) +103 +SELECT CONVERT(103.0, CHAR(50) UNICODE); +CONVERT(103.0, CHAR(50) UNICODE) +103.0 +SELECT CONVERT(-103, CHAR(50) UNICODE); +CONVERT(-103, CHAR(50) UNICODE) +-103 +SELECT CONVERT(-103.0, CHAR(50) UNICODE); +CONVERT(-103.0, CHAR(50) UNICODE) +-103.0 CREATE TABLE t1 ( a varchar(255) NOT NULL default '', KEY a (a) diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index af46b9c385a..c7662f4f85a 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -409,6 +409,14 @@ INSERT INTO t1 VALUES ('-1'); DROP TABLE t1; SET NAMES latin1; +# +# Bug#18691 Converting number to UNICODE string returns invalid result +# +SELECT CONVERT(103, CHAR(50) UNICODE); +SELECT CONVERT(103.0, CHAR(50) UNICODE); +SELECT CONVERT(-103, CHAR(50) UNICODE); +SELECT CONVERT(-103.0, CHAR(50) UNICODE); + # # Bug#9557 MyISAM utf8 table crash # diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 6fbd6db1a89..34e8b585dcc 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2255,8 +2255,8 @@ String *Item_char_typecast::val_str(String *str) // Convert character set if differ uint dummy_errors; if (!(res= args[0]->val_str(&tmp_value)) || - str->copy(res->ptr(), res->length(), res->charset(), - cast_cs, &dummy_errors)) + str->copy(res->ptr(), res->length(), from_cs, + cast_cs, &dummy_errors)) { null_value= 1; return 0; @@ -2311,21 +2311,40 @@ String *Item_char_typecast::val_str(String *str) void Item_char_typecast::fix_length_and_dec() { uint32 char_length; - /* - We always force character set conversion if cast_cs is a - multi-byte character set. It garantees that the result of CAST is - a well-formed string. For single-byte character sets we allow - just to copy from the argument. A single-byte character sets - string is always well-formed. + /* + We always force character set conversion if cast_cs + is a multi-byte character set. It garantees that the + result of CAST is a well-formed string. + For single-byte character sets we allow just to copy + from the argument. A single-byte character sets string + is always well-formed. + + There is a special trick to convert form a number to ucs2. + As numbers have my_charset_bin as their character set, + it wouldn't do conversion to ucs2 without an additional action. + To force conversion, we should pretend to be non-binary. + Let's choose from_cs this way: + - If the argument in a number and cast_cs is ucs2 (i.e. mbminlen > 1), + then from_cs is set to latin1, to perform latin1 -> ucs2 conversion. + - If the argument is a number and cast_cs is ASCII-compatible + (i.e. mbminlen == 1), then from_cs is set to cast_cs, + which allows just to take over the args[0]->val_str() result + and thus avoid unnecessary character set conversion. + - If the argument is not a number, then from_cs is set to + the argument's charset. */ - charset_conversion= ((cast_cs->mbmaxlen > 1) || - !my_charset_same(args[0]->collation.collation, - cast_cs) && - args[0]->collation.collation != &my_charset_bin && - cast_cs != &my_charset_bin); + from_cs= (args[0]->result_type() == INT_RESULT || + args[0]->result_type() == DECIMAL_RESULT || + args[0]->result_type() == REAL_RESULT) ? + (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : + args[0]->collation.collation; + charset_conversion= (cast_cs->mbmaxlen > 1) || + !my_charset_same(from_cs, cast_cs) && + from_cs != &my_charset_bin && + cast_cs != &my_charset_bin; collation.set(cast_cs, DERIVATION_IMPLICIT); char_length= (cast_length >= 0) ? cast_length : - args[0]->max_length/args[0]->collation.collation->mbmaxlen; + args[0]->max_length/from_cs->mbmaxlen; max_length= char_length * cast_cs->mbmaxlen; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index c5ea618dafe..ae0ca1a0445 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -708,7 +708,7 @@ public: class Item_char_typecast :public Item_typecast { int cast_length; - CHARSET_INFO *cast_cs; + CHARSET_INFO *cast_cs, *from_cs; bool charset_conversion; String tmp_value; public: diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 923eb530099..4de1e95d0f3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5931,7 +5931,7 @@ The minimum value for this variable is 4096.", (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT, - "Maximum numbrer of prepared statements in the server.", + "Maximum number of prepared statements in the server.", (gptr*) &max_prepared_stmt_count, (gptr*) &max_prepared_stmt_count, 0, GET_ULONG, REQUIRED_ARG, 16382, 0, 1*1024*1024, 0, 1, 0}, {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE,