diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 6182bd0f598..f060c3c9bf6 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 adjusted 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 ee74d30ce53..84e532ba4bc 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2378,8 +2378,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; @@ -2434,21 +2434,39 @@ 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() == 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 761faf85e63..b2f0dab0837 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -717,7 +717,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: