Bug#22646 LC_TIME_NAMES: Assignment to non-UTF8 target fails

Problem: After introducing of LC_TIME_NAMES variable,  the 
function date_format() can return international non-ascii
characters in month and weekday names. Thus, it cannot return
a binary string anymore, because inserting a result of date_format()
into a column with non-utf8 character set produces garbage.
Fix: date_format() now returns a character string, using
"collation_connection" to detect character set and collation
for the returned value. This allows to insert
results of date_format() properly into columns with
various character sets.
parent db8aa4ab
...@@ -124,12 +124,34 @@ create table t1 select date_format("2004-01-19 10:10:10", "%Y-%m-%d"); ...@@ -124,12 +124,34 @@ create table t1 select date_format("2004-01-19 10:10:10", "%Y-%m-%d");
show create table t1; show create table t1;
Table Create Table Table Create Table
t1 CREATE TABLE `t1` ( t1 CREATE TABLE `t1` (
`date_format("2004-01-19 10:10:10", "%Y-%m-%d")` binary(10) default NULL `date_format("2004-01-19 10:10:10", "%Y-%m-%d")` char(10) character set utf8 default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
select * from t1; select * from t1;
date_format("2004-01-19 10:10:10", "%Y-%m-%d") date_format("2004-01-19 10:10:10", "%Y-%m-%d")
2004-01-19 2004-01-19
drop table t1; drop table t1;
set names utf8;
set LC_TIME_NAMES='fr_FR';
create table t1 (s1 char(20) character set latin1);
insert into t1 values (date_format('2004-02-02','%M'));
select hex(s1) from t1;
hex(s1)
66E97672696572
drop table t1;
create table t1 (s1 char(20) character set koi8r);
set LC_TIME_NAMES='ru_RU';
insert into t1 values (date_format('2004-02-02','%M'));
insert into t1 values (date_format('2004-02-02','%b'));
insert into t1 values (date_format('2004-02-02','%W'));
insert into t1 values (date_format('2004-02-02','%a'));
select hex(s1), s1 from t1;
hex(s1) s1
E6C5D7D2C1CCD1 Февраля
E6C5D7 Фев
F0CFCEC5C4C5CCD8CEC9CB Понедельник
F0CEC4 Пнд
drop table t1;
set LC_TIME_NAMES='en_US';
set names koi8r; set names koi8r;
create table t1 (s1 char(1) character set utf8); create table t1 (s1 char(1) character set utf8);
insert into t1 values (_koi8r''); insert into t1 values (_koi8r'');
......
...@@ -93,6 +93,26 @@ show create table t1; ...@@ -93,6 +93,26 @@ show create table t1;
select * from t1; select * from t1;
drop table t1; drop table t1;
#
# Bug#22646 LC_TIME_NAMES: Assignment to non-UTF8 target fails
#
set names utf8;
set LC_TIME_NAMES='fr_FR';
create table t1 (s1 char(20) character set latin1);
insert into t1 values (date_format('2004-02-02','%M'));
select hex(s1) from t1;
drop table t1;
create table t1 (s1 char(20) character set koi8r);
set LC_TIME_NAMES='ru_RU';
insert into t1 values (date_format('2004-02-02','%M'));
insert into t1 values (date_format('2004-02-02','%b'));
insert into t1 values (date_format('2004-02-02','%W'));
insert into t1 values (date_format('2004-02-02','%a'));
select hex(s1), s1 from t1;
drop table t1;
set LC_TIME_NAMES='en_US';
# #
# Bug #2366 Wrong utf8 behaviour when data is truncated # Bug #2366 Wrong utf8 behaviour when data is truncated
# #
......
...@@ -595,16 +595,10 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, ...@@ -595,16 +595,10 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time,
uint weekday; uint weekday;
ulong length; ulong length;
const char *ptr, *end; const char *ptr, *end;
MY_LOCALE *locale;
THD *thd= current_thd; THD *thd= current_thd;
char buf[128]; MY_LOCALE *locale= thd->variables.lc_time_names;
String tmp(buf, sizeof(buf), thd->variables.character_set_results);
uint errors= 0;
tmp.length(0);
str->length(0); str->length(0);
str->set_charset(&my_charset_bin);
locale = thd->variables.lc_time_names;
if (l_time->neg) if (l_time->neg)
str->append("-", 1); str->append("-", 1);
...@@ -618,41 +612,37 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, ...@@ -618,41 +612,37 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time,
{ {
switch (*++ptr) { switch (*++ptr) {
case 'M': case 'M':
if (!l_time->month) if (!l_time->month)
return 1; return 1;
tmp.copy(locale->month_names->type_names[l_time->month-1], str->append(locale->month_names->type_names[l_time->month-1],
strlen(locale->month_names->type_names[l_time->month-1]), strlen(locale->month_names->type_names[l_time->month-1]),
system_charset_info, tmp.charset(), &errors); system_charset_info);
str->append(tmp.ptr(), tmp.length()); break;
break;
case 'b': case 'b':
if (!l_time->month) if (!l_time->month)
return 1; return 1;
tmp.copy(locale->ab_month_names->type_names[l_time->month-1], str->append(locale->ab_month_names->type_names[l_time->month-1],
strlen(locale->ab_month_names->type_names[l_time->month-1]), strlen(locale->ab_month_names->type_names[l_time->month-1]),
system_charset_info, tmp.charset(), &errors); system_charset_info);
str->append(tmp.ptr(), tmp.length()); break;
break;
case 'W': case 'W':
if (type == MYSQL_TIMESTAMP_TIME) if (type == MYSQL_TIMESTAMP_TIME)
return 1; return 1;
weekday= calc_weekday(calc_daynr(l_time->year,l_time->month, weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day),0); l_time->day),0);
tmp.copy(locale->day_names->type_names[weekday], str->append(locale->day_names->type_names[weekday],
strlen(locale->day_names->type_names[weekday]), strlen(locale->day_names->type_names[weekday]),
system_charset_info, tmp.charset(), &errors); system_charset_info);
str->append(tmp.ptr(), tmp.length()); break;
break;
case 'a': case 'a':
if (type == MYSQL_TIMESTAMP_TIME) if (type == MYSQL_TIMESTAMP_TIME)
return 1; return 1;
weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
l_time->day),0); l_time->day),0);
tmp.copy(locale->ab_day_names->type_names[weekday], str->append(locale->ab_day_names->type_names[weekday],
strlen(locale->ab_day_names->type_names[weekday]), strlen(locale->ab_day_names->type_names[weekday]),
system_charset_info, tmp.charset(), &errors); system_charset_info);
str->append(tmp.ptr(), tmp.length()); break;
break;
case 'D': case 'D':
if (type == MYSQL_TIMESTAMP_TIME) if (type == MYSQL_TIMESTAMP_TIME)
return 1; return 1;
...@@ -1638,8 +1628,9 @@ longlong Item_func_sec_to_time::val_int() ...@@ -1638,8 +1628,9 @@ longlong Item_func_sec_to_time::val_int()
void Item_func_date_format::fix_length_and_dec() void Item_func_date_format::fix_length_and_dec()
{ {
THD* thd= current_thd;
decimals=0; decimals=0;
collation.set(&my_charset_bin); collation.set(thd->variables.collation_connection);
if (args[1]->type() == STRING_ITEM) if (args[1]->type() == STRING_ITEM)
{ // Optimize the normal case { // Optimize the normal case
fixed_length=1; fixed_length=1;
...@@ -1653,17 +1644,14 @@ void Item_func_date_format::fix_length_and_dec() ...@@ -1653,17 +1644,14 @@ void Item_func_date_format::fix_length_and_dec()
args[1]->collation.set( args[1]->collation.set(
get_charset_by_csname(args[1]->collation.collation->csname, get_charset_by_csname(args[1]->collation.collation->csname,
MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE);
/* max_length= format_length(((Item_string*) args[1])->const_string()) *
The result is a binary string (no reason to use collation->mbmaxlen collation.collation->mbmaxlen;
This is becasue make_date_time() only returns binary strings
*/
max_length= format_length(((Item_string*) args[1])->const_string());
} }
else else
{ {
fixed_length=0; fixed_length=0;
/* The result is a binary string (no reason to use collation->mbmaxlen */ max_length= min(args[1]->max_length,MAX_BLOB_WIDTH) * 10 *
max_length=min(args[1]->max_length,MAX_BLOB_WIDTH) * 10; collation.collation->mbmaxlen;
set_if_smaller(max_length,MAX_BLOB_WIDTH); set_if_smaller(max_length,MAX_BLOB_WIDTH);
} }
maybe_null=1; // If wrong date maybe_null=1; // If wrong date
...@@ -1783,6 +1771,7 @@ String *Item_func_date_format::val_str(String *str) ...@@ -1783,6 +1771,7 @@ String *Item_func_date_format::val_str(String *str)
date_time_format.format.length= format->length(); date_time_format.format.length= format->length();
/* Create the result string */ /* Create the result string */
str->set_charset(collation.collation);
if (!make_date_time(&date_time_format, &l_time, if (!make_date_time(&date_time_format, &l_time,
is_time_format ? MYSQL_TIMESTAMP_TIME : is_time_format ? MYSQL_TIMESTAMP_TIME :
MYSQL_TIMESTAMP_DATE, MYSQL_TIMESTAMP_DATE,
......
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