Commit bf78f476 authored by Alexander Barkov's avatar Alexander Barkov

Bug#55912 FORMAT with locale set fails for numbers < 1000

Problems:
- dot character was always printed as decimal point
  instead of localized decimal point for short
  numbers without thousands
- Item_func_format::val_str always returned values in ASCII
format,
  regargless of @@character_set_connection, which in case of utf32
  led to crash in debug build, or to incorrect values in release build.

Fix:
- Adding a piece of code to replace dot character to
  localized decimal point in short numbers.
- Changing parent class for Item_func_format to
  Item_str_ascii_func, because its val_str() implementation is heavily ASCII oriented.
parent 39ac44d6
...@@ -1100,5 +1100,19 @@ my_col ...@@ -1100,5 +1100,19 @@ my_col
00 00
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#55912 FORMAT with locale set fails for numbers < 1000
#
SET collation_connection=utf32_general_ci;
CREATE TABLE t1 AS SELECT format(123,2,'no_NO');
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`format(123,2,'no_NO')` varchar(37) CHARACTER SET utf32 NOT NULL DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t1;
format(123,2,'no_NO')
123,00
DROP TABLE t1;
#
# End of 5.5 tests # End of 5.5 tests
# #
...@@ -2734,3 +2734,28 @@ format(123, 1, 'Non-existent-locale') ...@@ -2734,3 +2734,28 @@ format(123, 1, 'Non-existent-locale')
Warnings: Warnings:
Warning 1649 Unknown locale: 'Non-existent-locale' Warning 1649 Unknown locale: 'Non-existent-locale'
End of 5.4 tests End of 5.4 tests
#
# Start of 5.5 tests
#
#
# Bug#55912 FORMAT with locale set fails for numbers < 1000
#
SELECT FORMAT(123.33, 2, 'no_NO'), FORMAT(1123.33, 2, 'no_NO');
FORMAT(123.33, 2, 'no_NO') FORMAT(1123.33, 2, 'no_NO')
123,33 1.123,33
SELECT FORMAT(12333e-2, 2, 'no_NO'), FORMAT(112333e-2, 2, 'no_NO');
FORMAT(12333e-2, 2, 'no_NO') FORMAT(112333e-2, 2, 'no_NO')
123,33 1.123,33
CREATE TABLE t1 AS SELECT format(123,2,'no_NO');
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`format(123,2,'no_NO')` varchar(37) NOT NULL DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t1;
format(123,2,'no_NO')
123,00
DROP TABLE t1;
#
# End of 5.5 tests
#
...@@ -809,6 +809,15 @@ CREATE TABLE t1 AS SELECT HEX(0x00) AS my_col; ...@@ -809,6 +809,15 @@ CREATE TABLE t1 AS SELECT HEX(0x00) AS my_col;
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug#55912 FORMAT with locale set fails for numbers < 1000
--echo #
SET collation_connection=utf32_general_ci;
CREATE TABLE t1 AS SELECT format(123,2,'no_NO');
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
--echo # --echo #
--echo # End of 5.5 tests --echo # End of 5.5 tests
--echo # --echo #
...@@ -1404,3 +1404,20 @@ SELECT format(123, 1, 'Non-existent-locale'); ...@@ -1404,3 +1404,20 @@ SELECT format(123, 1, 'Non-existent-locale');
--echo End of 5.4 tests --echo End of 5.4 tests
--echo #
--echo # Start of 5.5 tests
--echo #
--echo #
--echo # Bug#55912 FORMAT with locale set fails for numbers < 1000
--echo #
SELECT FORMAT(123.33, 2, 'no_NO'), FORMAT(1123.33, 2, 'no_NO');
SELECT FORMAT(12333e-2, 2, 'no_NO'), FORMAT(112333e-2, 2, 'no_NO');
CREATE TABLE t1 AS SELECT format(123,2,'no_NO');
SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # End of 5.5 tests
--echo #
...@@ -2217,7 +2217,7 @@ const int FORMAT_MAX_DECIMALS= 30; ...@@ -2217,7 +2217,7 @@ const int FORMAT_MAX_DECIMALS= 30;
MY_LOCALE *Item_func_format::get_locale(Item *item) MY_LOCALE *Item_func_format::get_locale(Item *item)
{ {
DBUG_ASSERT(arg_count == 3); DBUG_ASSERT(arg_count == 3);
String tmp, *locale_name= args[2]->val_str(&tmp); String tmp, *locale_name= args[2]->val_str_ascii(&tmp);
MY_LOCALE *lc; MY_LOCALE *lc;
if (!locale_name || if (!locale_name ||
!(lc= my_locale_by_name(locale_name->c_ptr_safe()))) !(lc= my_locale_by_name(locale_name->c_ptr_safe())))
...@@ -2250,7 +2250,7 @@ void Item_func_format::fix_length_and_dec() ...@@ -2250,7 +2250,7 @@ void Item_func_format::fix_length_and_dec()
are stored in more than one byte are stored in more than one byte
*/ */
String *Item_func_format::val_str(String *str) String *Item_func_format::val_str_ascii(String *str)
{ {
uint32 str_length; uint32 str_length;
/* Number of decimal digits */ /* Number of decimal digits */
...@@ -2290,8 +2290,7 @@ String *Item_func_format::val_str(String *str) ...@@ -2290,8 +2290,7 @@ String *Item_func_format::val_str(String *str)
if ((null_value=args[0]->null_value)) if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */ return 0; /* purecov: inspected */
nr= my_double_round(nr, (longlong) dec, FALSE, FALSE); nr= my_double_round(nr, (longlong) dec, FALSE, FALSE);
/* Here default_charset() is right as this is not an automatic conversion */ str->set_real(nr, dec, &my_charset_numeric);
str->set_real(nr, dec, default_charset());
if (isnan(nr)) if (isnan(nr))
return str; return str;
str_length=str->length(); str_length=str->length();
...@@ -2341,6 +2340,14 @@ String *Item_func_format::val_str(String *str) ...@@ -2341,6 +2340,14 @@ String *Item_func_format::val_str(String *str)
/* Put the rest of the integer part without grouping */ /* Put the rest of the integer part without grouping */
str->copy(dst, buf + sizeof(buf) - dst, &my_charset_latin1); str->copy(dst, buf + sizeof(buf) - dst, &my_charset_latin1);
} }
else if (dec_length && lc->decimal_point != '.')
{
/*
For short values without thousands (<1000)
replace decimal point to localized value.
*/
((char*) str->ptr())[str_length - dec_length]= lc->decimal_point;
}
return str; return str;
} }
......
...@@ -539,17 +539,17 @@ public: ...@@ -539,17 +539,17 @@ public:
}; };
class Item_func_format :public Item_str_func class Item_func_format :public Item_str_ascii_func
{ {
String tmp_str; String tmp_str;
MY_LOCALE *locale; MY_LOCALE *locale;
public: public:
Item_func_format(Item *org, Item *dec): Item_str_func(org, dec) {} Item_func_format(Item *org, Item *dec): Item_str_ascii_func(org, dec) {}
Item_func_format(Item *org, Item *dec, Item *lang): Item_func_format(Item *org, Item *dec, Item *lang):
Item_str_func(org, dec, lang) {} Item_str_ascii_func(org, dec, lang) {}
MY_LOCALE *get_locale(Item *item); MY_LOCALE *get_locale(Item *item);
String *val_str(String *); String *val_str_ascii(String *);
void fix_length_and_dec(); void fix_length_and_dec();
const char *func_name() const { return "format"; } const char *func_name() const { return "format"; }
virtual void print(String *str, enum_query_type query_type); virtual void print(String *str, enum_query_type query_type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment