Commit 5c0c1dcc authored by unknown's avatar unknown
Browse files

Bug#18691: Converting number to UNICODE string returns invalid result.

Conversion from int and real numbers to UCS2 didn't work fine: 
CONVERT(100, CHAR(50) UNICODE)
CONVERT(103.9, CHAR(50) UNICODE)

The problem appeared because numbers have binary charset, so,
simple charset recast binary->ucs2 was performed
instead of real conversion.

Fixed to make numbers pretend to be non-binary.


mysql-test/r/ctype_ucs.result:
  Adding test case
mysql-test/t/ctype_ucs.test:
  Adding test case
sql/item_timefunc.cc:
  Adding new member from_cs, to replace my_charset_bin
  to a non-binary charset when converting from numbers to UCS2
sql/item_timefunc.h:
  Adding new member from_cs, to replace my_charset_bin
  to a non-binary charset when converting from numbers to UCS2
parent 6ef02da5
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -666,6 +666,18 @@ Warnings:
Warning	1265	Data truncated 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)
+8 −0
Original line number Diff line number Diff line
@@ -407,6 +407,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
#
+23 −5
Original line number Diff line number Diff line
@@ -2221,7 +2221,7 @@ 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(),
        str->copy(res->ptr(), res->length(), from_cs,
        cast_cs, &dummy_errors))
    {
      null_value= 1;
@@ -2261,14 +2261,32 @@ void Item_char_typecast::fix_length_and_dec()
     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.
  */
  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(args[0]->collation.collation, cast_cs) &&
                      args[0]->collation.collation != &my_charset_bin &&
                      !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;
}

+1 −1
Original line number Diff line number Diff line
@@ -681,7 +681,7 @@ class Item_typecast_maybe_null :public Item_typecast
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: