Skip to content

Commit

Permalink
MDEV-11850: Can't create foreign key referencing a virtual column
Browse files Browse the repository at this point in the history
Both dict_foreign_find_index and dict_foreign_qualify_index
did not consider virtual columns as possible foreign key
columns and there was assertion to disable virtual columns.

Fixed by also looking referencing and referenced column
from virtual columns if needed.
  • Loading branch information
Jan Lindström committed Jan 26, 2017
1 parent c6039a1 commit 45f451c
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 6 deletions.
86 changes: 86 additions & 0 deletions mysql-test/suite/innodb/r/innodb-fk-virtual.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
create or replace table a (
cola int(10) primary key,
v_cola int(10) as (cola mod 10) virtual,
p_cola int(10) as (cola mod 10) persistent
) engine=innodb;
create index v_cola on a (v_cola);
create index p_cola on a (p_cola);
create or replace table b(
cola int(10),
v_cola int(10),
p_cola int(10),
c_cola int(10) as (cola + 2) virtual
) engine=innodb;
alter table b add constraint `p_cola_fk`
foreign key (p_cola) references a (p_cola)
on delete restrict
on update restrict;
show warnings;
Level Code Message
show create table b;
Table Create Table
b CREATE TABLE `b` (
`cola` int(10) DEFAULT NULL,
`v_cola` int(10) DEFAULT NULL,
`p_cola` int(10) DEFAULT NULL,
`c_cola` int(10) GENERATED ALWAYS AS (`cola` + 2) VIRTUAL,
KEY `p_cola_fk` (`p_cola`),
CONSTRAINT `p_cola_fk` FOREIGN KEY (`p_cola`) REFERENCES `a` (`p_cola`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table b add constraint `v_cola_fk`
foreign key (v_cola) references a (v_cola)
on delete restrict
on update restrict;
show warnings;
Level Code Message
show create table b;
Table Create Table
b CREATE TABLE `b` (
`cola` int(10) DEFAULT NULL,
`v_cola` int(10) DEFAULT NULL,
`p_cola` int(10) DEFAULT NULL,
`c_cola` int(10) GENERATED ALWAYS AS (`cola` + 2) VIRTUAL,
KEY `p_cola_fk` (`p_cola`),
KEY `v_cola_fk` (`v_cola`),
CONSTRAINT `p_cola_fk` FOREIGN KEY (`p_cola`) REFERENCES `a` (`p_cola`),
CONSTRAINT `v_cola_fk` FOREIGN KEY (`v_cola`) REFERENCES `a` (`v_cola`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
alter table b add constraint `c_cola_fk`
foreign key (c_cola) references a (cola)
on delete restrict
on update restrict;
show warnings;
Level Code Message
show create table b;
Table Create Table
b CREATE TABLE `b` (
`cola` int(10) DEFAULT NULL,
`v_cola` int(10) DEFAULT NULL,
`p_cola` int(10) DEFAULT NULL,
`c_cola` int(10) GENERATED ALWAYS AS (`cola` + 2) VIRTUAL,
KEY `p_cola_fk` (`p_cola`),
KEY `v_cola_fk` (`v_cola`),
KEY `c_cola_fk` (`c_cola`),
CONSTRAINT `c_cola_fk` FOREIGN KEY (`c_cola`) REFERENCES `a` (`cola`),
CONSTRAINT `p_cola_fk` FOREIGN KEY (`p_cola`) REFERENCES `a` (`p_cola`),
CONSTRAINT `v_cola_fk` FOREIGN KEY (`v_cola`) REFERENCES `a` (`v_cola`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
insert into a(cola) values (12);
select * from a;
cola v_cola p_cola
12 2 2
insert into b(cola, v_cola, p_cola) values (10,2,2);
select * from b;
cola v_cola p_cola c_cola
10 2 2 12
insert into b(cola, v_cola, p_cola) values (10,1,1);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`b`, CONSTRAINT `p_cola_fk` FOREIGN KEY (`p_cola`) REFERENCES `a` (`p_cola`))
delete from a;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`b`, CONSTRAINT `c_cola_fk` FOREIGN KEY (`c_cola`) REFERENCES `a` (`cola`))
select * from a;
cola v_cola p_cola
12 2 2
select * from b;
cola v_cola p_cola c_cola
10 2 2 12
drop table b, a;
61 changes: 61 additions & 0 deletions mysql-test/suite/innodb/t/innodb-fk-virtual.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
--source include/have_innodb.inc

#
# MDEV-11850: Can't create foreign key referencing a virtual column
#

create or replace table a (
cola int(10) primary key,
v_cola int(10) as (cola mod 10) virtual,
p_cola int(10) as (cola mod 10) persistent
) engine=innodb;

create index v_cola on a (v_cola);
create index p_cola on a (p_cola);

create or replace table b(
cola int(10),
v_cola int(10),
p_cola int(10),
c_cola int(10) as (cola + 2) virtual
) engine=innodb;

alter table b add constraint `p_cola_fk`
foreign key (p_cola) references a (p_cola)
on delete restrict
on update restrict;

show warnings;
show create table b;

alter table b add constraint `v_cola_fk`
foreign key (v_cola) references a (v_cola)
on delete restrict
on update restrict;

show warnings;
show create table b;

alter table b add constraint `c_cola_fk`
foreign key (c_cola) references a (cola)
on delete restrict
on update restrict;

show warnings;
show create table b;

#
# Test that fk really works
#

insert into a(cola) values (12);
select * from a;
insert into b(cola, v_cola, p_cola) values (10,2,2);
select * from b;
--error 1452
insert into b(cola, v_cola, p_cola) values (10,1,1);
--error 1451
delete from a;
select * from a;
select * from b;
drop table b, a;
34 changes: 28 additions & 6 deletions storage/innobase/dict/dict0dict.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3518,7 +3518,6 @@ dict_foreign_find_index(
if (types_idx != index
&& !(index->type & DICT_FTS)
&& !dict_index_is_spatial(index)
&& !dict_index_has_virtual(index)
&& !index->to_be_dropped
&& dict_foreign_qualify_index(
table, col_names, columns, n_cols,
Expand Down Expand Up @@ -4026,6 +4025,23 @@ dict_scan_col(
break;
}
}

for (i = 0; i < dict_table_get_n_v_cols(table); i++) {

const char* col_name = dict_table_get_v_col_name(
table, i);

if (0 == innobase_strcasecmp(col_name, *name)) {
/* Found */
dict_v_col_t * vcol;
*success = TRUE;
vcol = dict_table_get_nth_v_col(table, i);
*column = &vcol->m_col;
strcpy((char*) *name, col_name);

break;
}
}
}

return(ptr);
Expand Down Expand Up @@ -5010,9 +5026,7 @@ dict_create_foreign_constraints_low(

for (i = 0; i < foreign->n_fields; i++) {
foreign->foreign_col_names[i] = mem_heap_strdup(
foreign->heap,
dict_table_get_col_name(table,
dict_col_get_no(columns[i])));
foreign->heap, column_names[i]);
}

ptr = dict_scan_table_name(cs, ptr, &referenced_table, name,
Expand Down Expand Up @@ -5330,6 +5344,7 @@ dict_create_foreign_constraints_low(
ref_column_names, i,
foreign->foreign_index,
TRUE, FALSE, &index_error, &err_col, &err_index);

if (!index) {
mutex_enter(&dict_foreign_err_mutex);
dict_foreign_error_report_low(ef, create_name);
Expand Down Expand Up @@ -7032,8 +7047,6 @@ dict_foreign_qualify_index(
field = dict_index_get_nth_field(index, i);
col_no = dict_col_get_no(field->col);

ut_ad(!dict_col_is_virtual(field->col));

if (field->prefix_len != 0) {
/* We do not accept column prefix
indexes here */
Expand All @@ -7059,6 +7072,15 @@ dict_foreign_qualify_index(
? col_names[col_no]
: dict_table_get_col_name(table, col_no);

if (dict_col_is_virtual(field->col)) {
for (ulint j = 0; j < table->n_v_def; j++) {
col_name = dict_table_get_v_col_name(table, j);
if (innobase_strcasecmp(field->name,col_name) == 0) {
break;
}
}
}

if (0 != innobase_strcasecmp(columns[i], col_name)) {
return(false);
}
Expand Down

0 comments on commit 45f451c

Please sign in to comment.