Commit 76667ce6 authored by Igor Babaev's avatar Igor Babaev

Fixed bug mdev-354.

Virtual columns of ENUM and SET data types were not supported properly
in the original patch that introduced virtual columns into MariaDB 5.2.
The problem was that for any  virtual column the patch used the 
interval_id field of the definition of the column in the frm file as
a reference to the virtual column expression.
The fix stores the optional interval_id of the virtual column in the
extended header of the virtual column expression. 
parent bd7753d4
......@@ -146,3 +146,39 @@ table_schema table_name column_name column_type extra
test t2 a int(11)
test t2 b int(11) VIRTUAL
DROP TABLE t1,t2;
create table t1 (
a int not null, b char(2) not null,
c enum('Y','N') as (case when b = 'aa' then 'Y' else 'N' end) persistent
);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` char(2) NOT NULL,
`c` enum('Y','N') AS (case when b = 'aa' then 'Y' else 'N' end) PERSISTENT
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1(a,b) values (1,'bb'), (2,'aa'), (3,'cc');
select * from t1;
a b c
1 bb N
2 aa Y
3 cc N
create table t2 (
a int, b int,
c set("y","n")
as (if(a=0,if(b=0,('n,n'),('n,y')),if(b=0,('y,n'),('y,y')))) persistent
);
show create table t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` set('y','n') AS (if(a=0,if(b=0,('n,n'),('n,y')),if(b=0,('y,n'),('y,y')))) PERSISTENT
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t2(a,b) values (7,0), (2,3), (0,1);
select * from t2;
a b c
7 0 y,n
2 3 y
0 1 y,n
drop table t1,t2;
......@@ -154,3 +154,27 @@ SELECT table_schema, table_name, column_name, column_type, extra
FROM information_schema.columns WHERE table_name = 't2';
DROP TABLE t1,t2;
#
# Bug mdev-354: virtual columns of ENUM and SET types
#
create table t1 (
a int not null, b char(2) not null,
c enum('Y','N') as (case when b = 'aa' then 'Y' else 'N' end) persistent
);
show create table t1;
insert into t1(a,b) values (1,'bb'), (2,'aa'), (3,'cc');
select * from t1;
create table t2 (
a int, b int,
c set("y","n")
as (if(a=0,if(b=0,('n,n'),('n,y')),if(b=0,('y,n'),('y,y')))) persistent
);
show create table t2;
insert into t2(a,b) values (7,0), (2,3), (0,1);
select * from t2;
drop table t1,t2;
......@@ -2199,6 +2199,10 @@ class Create_field :public Sql_alloc
{
return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0;
}
uint virtual_col_expr_maxlen()
{
return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL);
}
};
......
......@@ -1257,25 +1257,33 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
/*
Get virtual column data stored in the .frm file as follows:
byte 1 = 1 (always 1 to allow for future extensions)
byte 1 = 1 | 2
byte 2 = sql_type
byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
byte 4-... = virtual column expression (text data)
[byte 4] = optional interval_id for sql_type (only if byte 1 == 2)
next byte ... = virtual column expression (text data)
*/
vcol_info= new Virtual_column_info();
if ((uint)vcol_screen_pos[0] != 1)
bool opt_interval_id= (uint)vcol_screen_pos[0] == 2;
field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
if (opt_interval_id)
interval_nr= (uint)vcol_screen_pos[3];
else if ((uint)vcol_screen_pos[0] != 1)
{
error= 4;
goto free_and_err;
}
field_type= (enum_field_types) (uchar) vcol_screen_pos[1];
fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE;
vcol_expr_length= vcol_info_length -
(uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id));
if (!(vcol_info->expr_str.str=
(char *)memdup_root(&share->mem_root,
vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE,
vcol_screen_pos +
(uint) FRM_VCOL_HEADER_SIZE(opt_interval_id),
vcol_expr_length)))
goto free_and_err;
if (opt_interval_id)
interval_nr= (uint) vcol_screen_pos[3];
vcol_info->expr_str.length= vcol_expr_length;
vcol_screen_pos+= vcol_info_length;
share->vfields++;
......
......@@ -670,18 +670,19 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
}
if (field->vcol_info)
{
uint col_expr_maxlen= field->virtual_col_expr_maxlen();
tmp_len=
system_charset_info->cset->charpos(system_charset_info,
field->vcol_info->expr_str.str,
field->vcol_info->expr_str.str +
field->vcol_info->expr_str.length,
VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
col_expr_maxlen);
if (tmp_len < field->vcol_info->expr_str.length)
{
my_error(ER_WRONG_STRING_LENGTH, MYF(0),
field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION",
(uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN);
col_expr_maxlen);
DBUG_RETURN(1);
}
/*
......@@ -690,7 +691,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
expressions saved in the frm file for virtual columns.
*/
vcol_info_length+= field->vcol_info->expr_str.length+
(uint)FRM_VCOL_HEADER_SIZE;
FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
}
totlength+= field->length;
......@@ -886,8 +887,9 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
the additional data saved for the virtual field
*/
buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length +
(uint)FRM_VCOL_HEADER_SIZE;
vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE;
FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
vcol_info_length+= cur_vcol_expr_len +
FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
buff[13]= (uchar) MYSQL_TYPE_VIRTUAL;
}
int2store(buff+15, field->comment.length);
......@@ -992,17 +994,20 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
{
/*
Pack each virtual field as follows:
byte 1 = 1 (always 1 to allow for future extensions)
byte 1 = interval_id == 0 ? 1 : 2
byte 2 = sql_type
byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored)
byte 4-... = virtual column expression (text data)
[byte 4] = possible interval_id for sql_type
next byte ... = virtual column expression (text data)
*/
if (field->vcol_info && field->vcol_info->expr_str.length)
{
buff[0]= (uchar)1;
buff[0]= (uchar)(1 + test(field->interval_id));
buff[1]= (uchar) field->sql_type;
buff[2]= (uchar) field->stored_in_db;
if (my_write(file, buff, 3, MYF_RW))
if (field->interval_id)
buff[3]= (uchar) field->interval_id;
if (my_write(file, buff, 3 + test(field->interval_id), MYF_RW))
DBUG_RETURN(1);
if (my_write(file,
(uchar*) field->vcol_info->expr_str.str,
......
......@@ -214,10 +214,7 @@
#define DEFAULT_KEY_CACHE_NAME "default"
/* The length of the header part for each virtual column in the .frm file */
#define FRM_VCOL_HEADER_SIZE 3
/* Maximum length of the defining expression for a virtual columns */
#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE
#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b))
/* Include prototypes for unireg */
......
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