mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -1693,3 +1693,35 @@ oe
|
|||||||
ue
|
ue
|
||||||
ss
|
ss
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
a ENUM('<27>','<27>','<27>') character set utf8 default '<27>'
|
||||||
|
);
|
||||||
|
show create table t1;
|
||||||
|
Table Create Table
|
||||||
|
t1 CREATE TABLE `t1` (
|
||||||
|
`a` enum('<27>','<27>','<27>') character set utf8 default '<27>'
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
insert into t1 values ('<27>'), ('<27>'), ('<27>');
|
||||||
|
select a from t1 order by a;
|
||||||
|
a
|
||||||
|
<EFBFBD>
|
||||||
|
<EFBFBD>
|
||||||
|
<EFBFBD>
|
||||||
|
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('<27>','<27>','<27>') default '<27>'
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
select a from t1 order by a;
|
||||||
|
a
|
||||||
|
<EFBFBD>
|
||||||
|
<EFBFBD>
|
||||||
|
<EFBFBD>
|
||||||
|
drop table t1;
|
||||||
|
@ -72,3 +72,31 @@ CREATE TABLE t1 (c enum('ae','oe','ue','ss') collate latin1_german2_ci);
|
|||||||
INSERT INTO t1 VALUES ('<27>'),('<27>'),('<27>'),('<27>');
|
INSERT INTO t1 VALUES ('<27>'),('<27>'),('<27>'),('<27>');
|
||||||
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('<27>','<27>','<27>') character set utf8 default '<27>'
|
||||||
|
);
|
||||||
|
show create table t1;
|
||||||
|
insert into t1 values ('<27>'), ('<27>'), ('<27>');
|
||||||
|
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:
|
|||||||
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,
|
|||||||
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
|
|||||||
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)
|
|||||||
#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,
|
|||||||
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,
|
|||||||
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,
|
/*
|
||||||
¬_used, ¬_used2, ¬_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) ||
|
||||||
|
120
sql/sql_table.cc
120
sql/sql_table.cc
@ -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,
|
|||||||
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, ¬_used, ¬_used2, ¬_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:
|
|||||||
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:
|
|||||||
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:
|
|||||||
| 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:
|
|||||||
| 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:
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user