mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
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.
This commit is contained in:
@ -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')
|
|||||||
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;
|
|||||||
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');
|
|||||||
|
|
||||||
--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;
|
|||||||
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()
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
Reference in New Issue
Block a user