Commit c06df92a authored by Sergey Glukhov's avatar Sergey Glukhov

Bug#37575 UCASE fails on monthname

The MONTHNAME/DAYNAME functions
returns binary string, so the LOWER/UPPER functions
are not effective on the result of MONTHNAME/DAYNAME call.  
Character set of the MONTHNAME/DAYNAME function
result has been changed to connection character set.


include/m_ctype.h:
  added my_charset_repertoire function
mysql-test/r/ctype_ucs.result:
  test result
mysql-test/r/func_time.result:
  test result
mysql-test/t/ctype_ucs.test:
  test case
mysql-test/t/func_time.test:
  test case
sql/item_timefunc.cc:
  Item_func_monthname::fix_length_and_dec and
  Item_func_dayname::fix_length_and_dec methods have been
  modified to use connection character set
sql/item_timefunc.h:
  Item_func_monthname::fix_length_and_dec and
  Item_func_dayname::fix_length_and_dec methods have been
  modified to use connection character set
sql/mysql_priv.h:
  added max_month_name_length, max_day_name_length fields into MY_LOCALE struct
sql/mysqld.cc:
  The test_lc_time_sz function controls modifications
  of the locale database in debugging mode.
sql/sql_locale.cc:
  initialization of max_month_name_length, max_day_name_length fields
strings/ctype.c:
  added my_charset_repertoire function
parent 7658444b
...@@ -445,6 +445,7 @@ my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, uint len); ...@@ -445,6 +445,7 @@ my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, uint len);
uint my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong len); uint my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong len);
my_bool my_charset_is_ascii_based(CHARSET_INFO *cs); my_bool my_charset_is_ascii_based(CHARSET_INFO *cs);
my_bool my_charset_is_8bit_pure_ascii(CHARSET_INFO *cs); my_bool my_charset_is_8bit_pure_ascii(CHARSET_INFO *cs);
uint my_charset_repertoire(CHARSET_INFO *cs);
#define _MY_U 01 /* Upper case */ #define _MY_U 01 /* Upper case */
......
...@@ -1111,4 +1111,57 @@ set names latin1; ...@@ -1111,4 +1111,57 @@ set names latin1;
select hex(char(0x41 using ucs2)); select hex(char(0x41 using ucs2));
hex(char(0x41 using ucs2)) hex(char(0x41 using ucs2))
0041 0041
SET character_set_connection=ucs2;
SELECT CHARSET(DAYNAME(19700101));
CHARSET(DAYNAME(19700101))
ucs2
SELECT CHARSET(MONTHNAME(19700101));
CHARSET(MONTHNAME(19700101))
ucs2
SELECT LOWER(DAYNAME(19700101));
LOWER(DAYNAME(19700101))
thursday
SELECT LOWER(MONTHNAME(19700101));
LOWER(MONTHNAME(19700101))
january
SELECT UPPER(DAYNAME(19700101));
UPPER(DAYNAME(19700101))
THURSDAY
SELECT UPPER(MONTHNAME(19700101));
UPPER(MONTHNAME(19700101))
JANUARY
SELECT HEX(MONTHNAME(19700101));
HEX(MONTHNAME(19700101))
004A0061006E0075006100720079
SELECT HEX(DAYNAME(19700101));
HEX(DAYNAME(19700101))
00540068007500720073006400610079
SET LC_TIME_NAMES=ru_RU;
SET NAMES utf8;
SET character_set_connection=ucs2;
SELECT CHARSET(DAYNAME(19700101));
CHARSET(DAYNAME(19700101))
ucs2
SELECT CHARSET(MONTHNAME(19700101));
CHARSET(MONTHNAME(19700101))
ucs2
SELECT LOWER(DAYNAME(19700101));
LOWER(DAYNAME(19700101))
четверг
SELECT LOWER(MONTHNAME(19700101));
LOWER(MONTHNAME(19700101))
января
SELECT UPPER(DAYNAME(19700101));
UPPER(DAYNAME(19700101))
ЧЕТВЕРГ
SELECT UPPER(MONTHNAME(19700101));
UPPER(MONTHNAME(19700101))
ЯНВАРЯ
SELECT HEX(MONTHNAME(19700101));
HEX(MONTHNAME(19700101))
042F043D043204300440044F
SELECT HEX(DAYNAME(19700101));
HEX(DAYNAME(19700101))
0427043504420432043504400433
SET character_set_connection=latin1;
End of 5.0 tests End of 5.0 tests
...@@ -592,6 +592,21 @@ unix_timestamp('1970-01-01 03:00:01') ...@@ -592,6 +592,21 @@ unix_timestamp('1970-01-01 03:00:01')
select unix_timestamp('2038-01-19 07:14:07'); select unix_timestamp('2038-01-19 07:14:07');
unix_timestamp('2038-01-19 07:14:07') unix_timestamp('2038-01-19 07:14:07')
0 0
SELECT CHARSET(DAYNAME(19700101));
CHARSET(DAYNAME(19700101))
latin1
SELECT CHARSET(MONTHNAME(19700101));
CHARSET(MONTHNAME(19700101))
latin1
SELECT LOWER(DAYNAME(19700101));
LOWER(DAYNAME(19700101))
thursday
SELECT LOWER(MONTHNAME(19700101));
LOWER(MONTHNAME(19700101))
january
SELECT COERCIBILITY(MONTHNAME('1970-01-01')),COERCIBILITY(DAYNAME('1970-01-01'));
COERCIBILITY(MONTHNAME('1970-01-01')) COERCIBILITY(DAYNAME('1970-01-01'))
4 4
CREATE TABLE t1 (datetime datetime, timestamp timestamp, date date, time time); CREATE TABLE t1 (datetime datetime, timestamp timestamp, date date, time time);
INSERT INTO t1 values ("2001-01-02 03:04:05", "2002-01-02 03:04:05", "2003-01-02", "06:07:08"); INSERT INTO t1 values ("2001-01-02 03:04:05", "2002-01-02 03:04:05", "2003-01-02", "06:07:08");
SELECT * from t1; SELECT * from t1;
......
...@@ -685,4 +685,29 @@ set names latin1; ...@@ -685,4 +685,29 @@ set names latin1;
# #
select hex(char(0x41 using ucs2)); select hex(char(0x41 using ucs2));
#
# Bug#37575: UCASE fails on monthname
#
SET character_set_connection=ucs2;
SELECT CHARSET(DAYNAME(19700101));
SELECT CHARSET(MONTHNAME(19700101));
SELECT LOWER(DAYNAME(19700101));
SELECT LOWER(MONTHNAME(19700101));
SELECT UPPER(DAYNAME(19700101));
SELECT UPPER(MONTHNAME(19700101));
SELECT HEX(MONTHNAME(19700101));
SELECT HEX(DAYNAME(19700101));
SET LC_TIME_NAMES=ru_RU;
SET NAMES utf8;
SET character_set_connection=ucs2;
SELECT CHARSET(DAYNAME(19700101));
SELECT CHARSET(MONTHNAME(19700101));
SELECT LOWER(DAYNAME(19700101));
SELECT LOWER(MONTHNAME(19700101));
SELECT UPPER(DAYNAME(19700101));
SELECT UPPER(MONTHNAME(19700101));
SELECT HEX(MONTHNAME(19700101));
SELECT HEX(DAYNAME(19700101));
SET character_set_connection=latin1;
--echo End of 5.0 tests --echo End of 5.0 tests
...@@ -304,6 +304,15 @@ select unix_timestamp('1970-01-01 03:00:01'); ...@@ -304,6 +304,15 @@ select unix_timestamp('1970-01-01 03:00:01');
# check bad date, close to the boundary (we cut them off in the very end) # check bad date, close to the boundary (we cut them off in the very end)
select unix_timestamp('2038-01-19 07:14:07'); select unix_timestamp('2038-01-19 07:14:07');
#
# Bug #28759: DAYNAME() and MONTHNAME() return binary string
#
SELECT CHARSET(DAYNAME(19700101));
SELECT CHARSET(MONTHNAME(19700101));
SELECT LOWER(DAYNAME(19700101));
SELECT LOWER(MONTHNAME(19700101));
SELECT COERCIBILITY(MONTHNAME('1970-01-01')),COERCIBILITY(DAYNAME('1970-01-01'));
# #
# Test types from + INTERVAL # Test types from + INTERVAL
......
...@@ -1036,12 +1036,25 @@ longlong Item_func_month::val_int() ...@@ -1036,12 +1036,25 @@ longlong Item_func_month::val_int()
} }
void Item_func_monthname::fix_length_and_dec()
{
THD* thd= current_thd;
CHARSET_INFO *cs= thd->variables.collation_connection;
uint32 repertoire= my_charset_repertoire(cs);
locale= thd->variables.lc_time_names;
collation.set(cs, DERIVATION_COERCIBLE, repertoire);
decimals=0;
max_length= locale->max_month_name_length * collation.collation->mbmaxlen;
maybe_null=1;
}
String* Item_func_monthname::val_str(String* str) String* Item_func_monthname::val_str(String* str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
const char *month_name; const char *month_name;
uint month= (uint) val_int(); uint month= (uint) val_int();
THD *thd= current_thd; uint err;
if (null_value || !month) if (null_value || !month)
{ {
...@@ -1049,8 +1062,9 @@ String* Item_func_monthname::val_str(String* str) ...@@ -1049,8 +1062,9 @@ String* Item_func_monthname::val_str(String* str)
return (String*) 0; return (String*) 0;
} }
null_value=0; null_value=0;
month_name= thd->variables.lc_time_names->month_names->type_names[month-1]; month_name= locale->month_names->type_names[month-1];
str->set(month_name, strlen(month_name), system_charset_info); str->copy(month_name, strlen(month_name), &my_charset_utf8_bin,
collation.collation, &err);
return str; return str;
} }
...@@ -1169,19 +1183,32 @@ longlong Item_func_weekday::val_int() ...@@ -1169,19 +1183,32 @@ longlong Item_func_weekday::val_int()
odbc_type) + test(odbc_type); odbc_type) + test(odbc_type);
} }
void Item_func_dayname::fix_length_and_dec()
{
THD* thd= current_thd;
CHARSET_INFO *cs= thd->variables.collation_connection;
uint32 repertoire= my_charset_repertoire(cs);
locale= thd->variables.lc_time_names;
collation.set(cs, DERIVATION_COERCIBLE, repertoire);
decimals=0;
max_length= locale->max_day_name_length * collation.collation->mbmaxlen;
maybe_null=1;
}
String* Item_func_dayname::val_str(String* str) String* Item_func_dayname::val_str(String* str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
uint weekday=(uint) val_int(); // Always Item_func_daynr() uint weekday=(uint) val_int(); // Always Item_func_daynr()
const char *day_name; const char *day_name;
THD *thd= current_thd; uint err;
if (null_value) if (null_value)
return (String*) 0; return (String*) 0;
day_name= thd->variables.lc_time_names->day_names->type_names[weekday]; day_name= locale->day_names->type_names[weekday];
str->set(day_name, strlen(day_name), system_charset_info); str->copy(day_name, strlen(day_name), &my_charset_utf8_bin,
collation.collation, &err);
return str; return str;
} }
......
...@@ -108,18 +108,13 @@ public: ...@@ -108,18 +108,13 @@ public:
class Item_func_monthname :public Item_func_month class Item_func_monthname :public Item_func_month
{ {
MY_LOCALE *locale;
public: public:
Item_func_monthname(Item *a) :Item_func_month(a) {} Item_func_monthname(Item *a) :Item_func_month(a) {}
const char *func_name() const { return "monthname"; } const char *func_name() const { return "monthname"; }
String *val_str(String *str); String *val_str(String *str);
enum Item_result result_type () const { return STRING_RESULT; } enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec() void fix_length_and_dec();
{
collation.set(&my_charset_bin);
decimals=0;
max_length=10*my_charset_bin.mbmaxlen;
maybe_null=1;
}
}; };
...@@ -272,18 +267,13 @@ public: ...@@ -272,18 +267,13 @@ public:
class Item_func_dayname :public Item_func_weekday class Item_func_dayname :public Item_func_weekday
{ {
MY_LOCALE *locale;
public: public:
Item_func_dayname(Item *a) :Item_func_weekday(a,0) {} Item_func_dayname(Item *a) :Item_func_weekday(a,0) {}
const char *func_name() const { return "dayname"; } const char *func_name() const { return "dayname"; }
String *val_str(String *str); String *val_str(String *str);
enum Item_result result_type () const { return STRING_RESULT; } enum Item_result result_type () const { return STRING_RESULT; }
void fix_length_and_dec() void fix_length_and_dec();
{
collation.set(&my_charset_bin);
decimals=0;
max_length=9*MY_CHARSET_BIN_MB_MAXLEN;
maybe_null=1;
}
}; };
......
...@@ -132,15 +132,20 @@ typedef struct my_locale_st ...@@ -132,15 +132,20 @@ typedef struct my_locale_st
TYPELIB *ab_month_names; TYPELIB *ab_month_names;
TYPELIB *day_names; TYPELIB *day_names;
TYPELIB *ab_day_names; TYPELIB *ab_day_names;
uint max_month_name_length;
uint max_day_name_length;
#ifdef __cplusplus #ifdef __cplusplus
my_locale_st(uint number_par, my_locale_st(uint number_par,
const char *name_par, const char *descr_par, bool is_ascii_par, const char *name_par, const char *descr_par, bool is_ascii_par,
TYPELIB *month_names_par, TYPELIB *ab_month_names_par, TYPELIB *month_names_par, TYPELIB *ab_month_names_par,
TYPELIB *day_names_par, TYPELIB *ab_day_names_par) : TYPELIB *day_names_par, TYPELIB *ab_day_names_par,
uint max_month_name_length_par, uint max_day_name_length_par) :
number(number_par), number(number_par),
name(name_par), description(descr_par), is_ascii(is_ascii_par), name(name_par), description(descr_par), is_ascii(is_ascii_par),
month_names(month_names_par), ab_month_names(ab_month_names_par), month_names(month_names_par), ab_month_names(ab_month_names_par),
day_names(day_names_par), ab_day_names(ab_day_names_par) day_names(day_names_par), ab_day_names(ab_day_names_par),
max_month_name_length(max_month_name_length_par),
max_day_name_length(max_day_name_length_par)
{} {}
#endif #endif
} MY_LOCALE; } MY_LOCALE;
......
...@@ -3616,6 +3616,44 @@ void decrement_handler_count() ...@@ -3616,6 +3616,44 @@ void decrement_handler_count()
#endif /* defined(__NT__) || defined(HAVE_SMEM) */ #endif /* defined(__NT__) || defined(HAVE_SMEM) */
#ifndef DBUG_OFF
/*
Debugging helper function to keep the locale database
(see sql_locale.cc) and max_month_name_length and
max_day_name_length variable values in consistent state.
*/
static void test_lc_time_sz()
{
DBUG_ENTER("test_lc_time_sz");
for (MY_LOCALE **loc= my_locales; *loc; loc++)
{
uint max_month_len= 0;
uint max_day_len = 0;
for (const char **month= (*loc)->month_names->type_names; *month; month++)
{
set_if_bigger(max_month_len,
my_numchars_mb(&my_charset_utf8_general_ci,
*month, *month + strlen(*month)));
}
for (const char **day= (*loc)->day_names->type_names; *day; day++)
{
set_if_bigger(max_day_len,
my_numchars_mb(&my_charset_utf8_general_ci,
*day, *day + strlen(*day)));
}
if ((*loc)->max_month_name_length != max_month_len ||
(*loc)->max_day_name_length != max_day_len)
{
DBUG_PRINT("Wrong max day name(or month name) length for locale:",
("%s", (*loc)->name));
DBUG_ASSERT(0);
}
}
DBUG_VOID_RETURN;
}
#endif//DBUG_OFF
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
#ifdef __WIN__ #ifdef __WIN__
int win_main(int argc, char **argv) int win_main(int argc, char **argv)
...@@ -3712,6 +3750,10 @@ int main(int argc, char **argv) ...@@ -3712,6 +3750,10 @@ int main(int argc, char **argv)
openlog(libwrapName, LOG_PID, LOG_AUTH); openlog(libwrapName, LOG_PID, LOG_AUTH);
#endif #endif
#ifndef DBUG_OFF
test_lc_time_sz();
#endif
/* /*
We have enough space for fiddling with the argv, continue We have enough space for fiddling with the argv, continue
*/ */
......
This diff is collapsed.
...@@ -337,6 +337,16 @@ my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong length) ...@@ -337,6 +337,16 @@ my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong length)
} }
/*
Returns repertoire for charset
*/
uint my_charset_repertoire(CHARSET_INFO *cs)
{
return cs->state & MY_CS_PUREASCII ?
MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
}
/* /*
Detect whether a character set is ASCII compatible. Detect whether a character set is ASCII compatible.
......
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