Bug#24660: "enum" field type definition problem

ENUMs weren't allowed to have character 0xff, a perfectly good character in many locales.
This was circumvented by mapping 0xff in ENUMs to ',', thereby prevent actual commas from
being used (because they too would get converted to 0xff on load). Now if 0xff makes an
appearance, we find a character not used in the enum and use that as a separator. If no
such character exists, we throw an error.

Any solution would have broken some sort of existing behaviour. This solution should
serve both fractions (those with 0xff and those with ',' in their enums), but
WILL REQUIRE A DUMP/RESTORE CYCLE FROM THOSE WITH 0xff IN THEIR ENUMS. :-/
That is, mysqldump with their current server, and restore when upgrading to one with
this patch.

(port of the original 4.1 patch. incorporates some suggestions by kaamos.)
parent 993f325d
...@@ -1754,3 +1754,29 @@ t1 CREATE TABLE `t1` ( ...@@ -1754,3 +1754,29 @@ t1 CREATE TABLE `t1` (
`f2` enum('') DEFAULT NULL `f2` enum('') DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1; drop table t1;
End of 4.1 tests
create table t1(russian enum('E','F','EF','FE') NOT NULL DEFAULT'E');
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`russian` enum('E','F','EF','FE') NOT NULL DEFAULT 'E'
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
create table t1(denormal enum('E','F','E,F','F,E') NOT NULL DEFAULT'E');
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`denormal` enum('E','F','E,F','F,E') NOT NULL DEFAULT 'E'
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
create table t1(russian_deviant enum('E','F','EF','F,E') NOT NULL DEFAULT'E');
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`russian_deviant` enum('E','F','EF','F,E') NOT NULL DEFAULT 'E'
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
create table t1(exhausting_charset enum('ABCDEFGHIJKLMNOPQRSTUVWXYZ','
 !"','#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~','xx\','yy\','zz'));
ERROR 42000: Field separator argument is not what is expected; check the manual
End of 5.1 tests
......
...@@ -136,4 +136,26 @@ alter table t1 add f2 enum(0xFFFF); ...@@ -136,4 +136,26 @@ alter table t1 add f2 enum(0xFFFF);
show create table t1; show create table t1;
drop table t1; drop table t1;
# End of 4.1 tests --echo End of 4.1 tests
#
# Bug#24660 "enum" field type definition problem
#
create table t1(russian enum('E','F','EF','FE') NOT NULL DEFAULT'E');
show create table t1;
drop table t1;
create table t1(denormal enum('E','F','E,F','F,E') NOT NULL DEFAULT'E');
show create table t1;
drop table t1;
create table t1(russian_deviant enum('E','F','EF','F,E') NOT NULL DEFAULT'E');
show create table t1;
drop table t1;
# ER_WRONG_FIELD_TERMINATORS
--error 1083
create table t1(exhausting_charset enum('ABCDEFGHIJKLMNOPQRSTUVWXYZ','
 !"','#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~','xx\','yy\','zz'));
--echo End of 5.1 tests
......
...@@ -803,17 +803,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, ...@@ -803,17 +803,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{ {
char *val= (char*) interval->type_names[count]; char *val= (char*) interval->type_names[count];
interval->type_lengths[count]= strlen(val); interval->type_lengths[count]= strlen(val);
/*
Replace all ',' symbols with NAMES_SEP_CHAR.
See the comment in unireg.cc, pack_fields() function
for details.
*/
for (uint cnt= 0 ; cnt < interval->type_lengths[count] ; cnt++)
{
char c= val[cnt];
if (c == ',')
val[cnt]= NAMES_SEP_CHAR;
}
} }
interval->type_lengths[count]= 0; interval->type_lengths[count]= 0;
} }
......
...@@ -788,29 +788,48 @@ static bool pack_fields(File file, List<create_field> &create_fields, ...@@ -788,29 +788,48 @@ static bool pack_fields(File file, List<create_field> &create_fields,
{ {
if (field->interval_id > int_count) if (field->interval_id > int_count)
{ {
int_count=field->interval_id; unsigned char sep= 0;
tmp.append(NAMES_SEP_CHAR); unsigned char occ[256];
for (const char **pos=field->interval->type_names ; *pos ; pos++) uint i;
{ unsigned char *val= NULL;
char *val= (char*) *pos;
uint str_len= strlen(val); bzero(occ, sizeof(occ));
/*
Note, hack: in old frm NAMES_SEP_CHAR is used to separate for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++)
names in the interval (ENUM/SET). To allow names to contain for (uint j = 0; j < field->interval->type_lengths[i]; j++)
NAMES_SEP_CHAR, we replace it with a comma before writing frm. occ[(unsigned int) (val[j])]= 1;
Backward conversion is done during frm file opening,
See table.cc, openfrm() function if (!occ[(unsigned char)NAMES_SEP_CHAR])
*/ sep= (unsigned char) NAMES_SEP_CHAR;
for (uint cnt= 0 ; cnt < str_len ; cnt++) else if (!occ[(unsigned int)','])
sep= ',';
else
{
for (uint i=1; i<256; i++)
{
if(!occ[i])
{
sep= i;
break;
}
}
if(!sep) /* disaster, enum uses all characters, none left as separator */
{ {
char c= val[cnt]; my_message(ER_WRONG_FIELD_TERMINATORS,ER(ER_WRONG_FIELD_TERMINATORS),
if (c == NAMES_SEP_CHAR) MYF(0));
val[cnt]= ','; DBUG_RETURN(1);
} }
tmp.append(*pos); }
tmp.append(NAMES_SEP_CHAR);
} int_count= field->interval_id;
tmp.append('\0'); // End of intervall tmp.append(sep);
for (const char **pos=field->interval->type_names ; *pos ; pos++)
{
tmp.append(*pos);
tmp.append(sep);
}
tmp.append('\0'); // End of intervall
} }
} }
if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW)) if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW))
......
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