Commit a09a603d authored by bar@mysql.com's avatar bar@mysql.com

Bug #6379: ENUM values are incorrectly converted

- add_field_to_list() now uses <List>String
instead of TYPELIB to be able to distinguish
literals 'aaa' and hex literals 0xaabbcc.
- move some code from add_field_to_list() where
  we don't know column charset yet, to 
  mysql_prepare_table(), where we do.
parent 015b6f41
...@@ -1693,3 +1693,35 @@ oe ...@@ -1693,3 +1693,35 @@ oe
ue ue
ss ss
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (
a ENUM('','','') character set utf8 default ''
);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` enum('','','') character set utf8 default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values (''), (''), ('');
select a from t1 order by a;
a
drop table t1;
set names utf8;
CREATE TABLE t1 (
a ENUM('ä','ö','ü') character set latin1 default 'ü'
);
insert into t1 values ('ä'),('ö'),('ü');
set names latin1;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` enum('','','') default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1
select a from t1 order by a;
a
drop table t1;
...@@ -72,3 +72,31 @@ CREATE TABLE t1 (c enum('ae','oe','ue','ss') collate latin1_german2_ci); ...@@ -72,3 +72,31 @@ CREATE TABLE t1 (c enum('ae','oe','ue','ss') collate latin1_german2_ci);
INSERT INTO t1 VALUES (''),(''),(''),(''); INSERT INTO t1 VALUES (''),(''),(''),('');
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
#
# Bug #6379: ENUM values are incorrectly converted
#
# Check latin1 -> utf8 conversion
#
CREATE TABLE t1 (
a ENUM('','','') character set utf8 default ''
);
show create table t1;
insert into t1 values (''), (''), ('');
select a from t1 order by a;
drop table t1;
#
# Now check utf8 -> latin1 conversion
# This test emulates loading a script generated with mysqldump
#
set names utf8;
CREATE TABLE t1 (
a ENUM('ä','ö','ü') character set latin1 default 'ü'
);
insert into t1 values ('ä'),('ö'),('ü');
# Now check what has been loaded
set names latin1;
show create table t1;
select a from t1 order by a;
drop table t1;
...@@ -1198,6 +1198,7 @@ public: ...@@ -1198,6 +1198,7 @@ public:
uint decimals,flags,pack_length; uint decimals,flags,pack_length;
Field::utype unireg_check; Field::utype unireg_check;
TYPELIB *interval; // Which interval to use TYPELIB *interval; // Which interval to use
List<String> interval_list;
CHARSET_INFO *charset; CHARSET_INFO *charset;
Field::geometry_type geom_type; Field::geometry_type geom_type;
Field *field; // For alter table Field *field; // For alter table
......
...@@ -688,7 +688,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type, ...@@ -688,7 +688,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum enum_field_types type,
uint type_modifier, uint type_modifier,
Item *default_value, Item *on_update_value, Item *default_value, Item *on_update_value,
LEX_STRING *comment, LEX_STRING *comment,
char *change, TYPELIB *interval,CHARSET_INFO *cs, char *change, List<String> *interval_list,
CHARSET_INFO *cs,
uint uint_geom_type); uint uint_geom_type);
void store_position_for_column(const char *name); void store_position_for_column(const char *name);
bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc=0); bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc=0);
......
...@@ -592,7 +592,6 @@ typedef struct st_lex ...@@ -592,7 +592,6 @@ typedef struct st_lex
List<set_var_base> var_list; List<set_var_base> var_list;
List<Item_param> param_list; List<Item_param> param_list;
SQL_LIST proc_list, auxilliary_table_list, save_list; SQL_LIST proc_list, auxilliary_table_list, save_list;
TYPELIB *interval;
create_field *last_field; create_field *last_field;
char *savepoint_name; // Transaction savepoint id char *savepoint_name; // Transaction savepoint id
udf_func udf; udf_func udf;
......
...@@ -4115,31 +4115,6 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) ...@@ -4115,31 +4115,6 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
#endif #endif
/*
Calculate interval lengths.
Strip trailing spaces from all strings.
After this function call:
- ENUM uses max_length
- SET uses tot_length.
*/
void calculate_interval_lengths(THD *thd, TYPELIB *interval,
uint32 *max_length, uint32 *tot_length)
{
const char **pos;
uint *len;
CHARSET_INFO *cs= thd->variables.character_set_client;
*max_length= *tot_length= 0;
for (pos= interval->type_names, len= interval->type_lengths;
*pos ; pos++, len++)
{
*len= (uint) strip_sp((char*) *pos);
uint length= cs->cset->numchars(cs, *pos, *pos + *len);
*tot_length+= length;
set_if_bigger(*max_length, (uint32)length);
}
}
/***************************************************************************** /*****************************************************************************
** Store field definition for create ** Store field definition for create
** Return 0 if ok ** Return 0 if ok
...@@ -4150,7 +4125,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, ...@@ -4150,7 +4125,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
uint type_modifier, uint type_modifier,
Item *default_value, Item *on_update_value, Item *default_value, Item *on_update_value,
LEX_STRING *comment, LEX_STRING *comment,
char *change, TYPELIB *interval, CHARSET_INFO *cs, char *change,
List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type) uint uint_geom_type)
{ {
register create_field *new_field; register create_field *new_field;
...@@ -4445,63 +4421,40 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, ...@@ -4445,63 +4421,40 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
break; break;
case FIELD_TYPE_SET: case FIELD_TYPE_SET:
{ {
if (interval->count > sizeof(longlong)*8) if (interval_list->elements > sizeof(longlong)*8)
{ {
net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */ net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */
} }
new_field->pack_length=(interval->count+7)/8; new_field->pack_length= (interval_list->elements + 7) / 8;
if (new_field->pack_length > 4) if (new_field->pack_length > 4)
new_field->pack_length=8; new_field->pack_length=8;
new_field->interval=interval;
uint32 dummy_max_length;
calculate_interval_lengths(thd, interval,
&dummy_max_length, &new_field->length);
new_field->length+= (interval->count - 1);
set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
if (default_value)
{
char *not_used;
uint not_used2;
bool not_used3;
thd->cuted_fields=0; List_iterator<String> it(*interval_list);
String str,*res; String *tmp;
res=default_value->val_str(&str); while ((tmp= it++))
(void) find_set(interval, res->ptr(), res->length(), new_field->interval_list.push_back(tmp);
&my_charset_bin, /*
&not_used, &not_used2, &not_used3); Set fake length to 1 to pass the below conditions.
if (thd->cuted_fields) Real length will be set in mysql_prepare_table()
{ when we know the character set of the column
net_printf(thd,ER_INVALID_DEFAULT,field_name); */
DBUG_RETURN(1); new_field->length= 1;
}
}
} }
break; break;
case FIELD_TYPE_ENUM: case FIELD_TYPE_ENUM:
{ {
new_field->interval=interval; // Should be safe
new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe new_field->pack_length= interval_list->elements < 256 ? 1 : 2;
uint32 dummy_tot_length; List_iterator<String> it(*interval_list);
calculate_interval_lengths(thd, interval, String *tmp;
&new_field->length, &dummy_tot_length); while ((tmp= it++))
set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); new_field->interval_list.push_back(tmp);
if (default_value) new_field->length= 1; // See comment for FIELD_TYPE_SET above.
{
String str,*res;
res=default_value->val_str(&str);
res->strip_sp();
if (!find_type(interval, res->ptr(), res->length(), 0))
{
net_printf(thd,ER_INVALID_DEFAULT,field_name);
DBUG_RETURN(1);
}
} }
break; break;
} }
}
if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET && if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET &&
type != FIELD_TYPE_ENUM) || type != FIELD_TYPE_ENUM) ||
......
...@@ -392,6 +392,41 @@ void check_duplicates_in_interval(const char *set_or_name, ...@@ -392,6 +392,41 @@ void check_duplicates_in_interval(const char *set_or_name,
} }
} }
/*
Check TYPELIB (set or enum) max and total lengths
SYNOPSIS
calculate_interval_lengths()
cs charset+collation pair of the interval
typelib list of values for the column
max_length length of the longest item
tot_length sum of the item lengths
DESCRIPTION
After this function call:
- ENUM uses max_length
- SET uses tot_length.
RETURN VALUES
void
*/
void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
uint32 *max_length, uint32 *tot_length)
{
const char **pos;
uint *len;
*max_length= *tot_length= 0;
for (pos= interval->type_names, len= interval->type_lengths;
*pos ; pos++, len++)
{
uint length= cs->cset->numchars(cs, *pos, *pos + *len);
*tot_length+= length;
set_if_bigger(*max_length, (uint32)length);
}
}
/* /*
Preparation for table creation Preparation for table creation
...@@ -455,6 +490,91 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -455,6 +490,91 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
if ((sql_field->sql_type == FIELD_TYPE_SET ||
sql_field->sql_type == FIELD_TYPE_ENUM) && !sql_field->interval)
{
uint32 dummy;
CHARSET_INFO *cs= sql_field->charset;
TYPELIB *interval;
/*
Create typelib from interval_list, and if necessary
convert strings from client character set to the
column character set.
*/
interval= sql_field->interval= typelib(sql_field->interval_list);
List_iterator<String> it(sql_field->interval_list);
String conv, *tmp;
for (uint i= 0; (tmp= it++); i++)
{
if (String::needs_conversion(tmp->length(), tmp->charset(), cs, &dummy))
{
uint cnv_errs;
conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
char *buf= (char*) sql_alloc(conv.length()+1);
memcpy(buf, conv.ptr(), conv.length());
buf[conv.length()]= '\0';
interval->type_names[i]= buf;
interval->type_lengths[i]= conv.length();
}
// Strip trailing spaces.
uint lengthsp= cs->cset->lengthsp(cs, interval->type_names[i],
interval->type_lengths[i]);
interval->type_lengths[i]= lengthsp;
((uchar *)interval->type_names[i])[lengthsp]= '\0';
}
sql_field->interval_list.empty(); // Don't need interval_list anymore
/*
Convert the default value from client character
set into the column character set if necessary.
*/
if (sql_field->def)
{
sql_field->def=
sql_field->def->safe_charset_converter(cs);
}
if (sql_field->sql_type == FIELD_TYPE_SET)
{
if (sql_field->def)
{
char *not_used;
uint not_used2;
bool not_found= 0;
String str, *def= sql_field->def->val_str(&str);
def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
(void) find_set(interval, def->ptr(), def->length(),
cs, &not_used, &not_used2, &not_found);
if (not_found)
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
DBUG_RETURN(-1);
}
}
calculate_interval_lengths(cs, interval, &dummy, &sql_field->length);
sql_field->length+= (interval->count - 1);
}
else /* FIELD_TYPE_ENUM */
{
if (sql_field->def)
{
String str, *def= sql_field->def->val_str(&str);
def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
if (!find_type2(interval, def->ptr(), def->length(), cs))
{
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
DBUG_RETURN(-1);
}
}
calculate_interval_lengths(cs, interval, &sql_field->length, &dummy);
}
set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1);
}
sql_field->create_length_to_internal_length(); sql_field->create_length_to_internal_length();
/* Don't pack keys in old tables if the user has requested this */ /* Don't pack keys in old tables if the user has requested this */
......
...@@ -1361,7 +1361,7 @@ field_spec: ...@@ -1361,7 +1361,7 @@ field_spec:
field_ident field_ident
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->length=lex->dec=0; lex->type=0; lex->interval=0; lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0; lex->default_value= lex->on_update_value= 0;
lex->comment=0; lex->comment=0;
lex->charset=NULL; lex->charset=NULL;
...@@ -1374,7 +1374,7 @@ field_spec: ...@@ -1374,7 +1374,7 @@ field_spec:
lex->length,lex->dec,lex->type, lex->length,lex->dec,lex->type,
lex->default_value, lex->on_update_value, lex->default_value, lex->on_update_value,
lex->comment, lex->comment,
lex->change,lex->interval,lex->charset, lex->change,&lex->interval_list,lex->charset,
lex->uint_geom_type)) lex->uint_geom_type))
YYABORT; YYABORT;
}; };
...@@ -1471,17 +1471,9 @@ type: ...@@ -1471,17 +1471,9 @@ type:
| FIXED_SYM float_options field_options | FIXED_SYM float_options field_options
{ $$=FIELD_TYPE_DECIMAL;} { $$=FIELD_TYPE_DECIMAL;}
| ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary | ENUM {Lex->interval_list.empty();} '(' string_list ')' opt_binary
{ { $$=FIELD_TYPE_ENUM; }
LEX *lex=Lex;
lex->interval=typelib(lex->interval_list);
$$=FIELD_TYPE_ENUM;
}
| SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary | SET { Lex->interval_list.empty();} '(' string_list ')' opt_binary
{ { $$=FIELD_TYPE_SET; }
LEX *lex=Lex;
lex->interval=typelib(lex->interval_list);
$$=FIELD_TYPE_SET;
}
| LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; } | LONG_SYM opt_binary { $$=FIELD_TYPE_MEDIUM_BLOB; }
| SERIAL_SYM | SERIAL_SYM
{ {
...@@ -1925,7 +1917,7 @@ alter_list_item: ...@@ -1925,7 +1917,7 @@ alter_list_item:
| MODIFY_SYM opt_column field_ident | MODIFY_SYM opt_column field_ident
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->length=lex->dec=0; lex->type=0; lex->interval=0; lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0; lex->default_value= lex->on_update_value= 0;
lex->comment=0; lex->comment=0;
lex->charset= NULL; lex->charset= NULL;
...@@ -1940,7 +1932,7 @@ alter_list_item: ...@@ -1940,7 +1932,7 @@ alter_list_item:
lex->length,lex->dec,lex->type, lex->length,lex->dec,lex->type,
lex->default_value, lex->on_update_value, lex->default_value, lex->on_update_value,
lex->comment, lex->comment,
$3.str, lex->interval, lex->charset, $3.str, &lex->interval_list, lex->charset,
lex->uint_geom_type)) lex->uint_geom_type))
YYABORT; YYABORT;
} }
......
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