MDEV-18114 Foreign Key Constraint actions don't affect Virtual Column

* invoke check_expression() for all vcol_info's in
  mysql_prepare_create_table() to check for FK CASCADE
* also check for SET NULL and SET DEFAULT
* to check against existing FKs when a vcol is added in ALTER TABLE,
  old FKs must be added to the new_key_list just like other indexes are
* check columns recursively, if vcol1 references vcol2,
  flags of vcol2 must be taken into account
* remove check_table_name_processor(), put that logic under
  check_vcol_func_processor() to avoid walking the tree twice
This commit is contained in:
Sergei Golubchik 2023-07-13 10:59:39 +02:00
parent ab1191c039
commit da09ae05a9
12 changed files with 269 additions and 116 deletions

View File

@ -85,7 +85,7 @@ t2id id
use test; use test;
drop database `#mysql50#-`; drop database `#mysql50#-`;
SET NAMES default; SET NAMES default;
FOUND 8 /\[ERROR\] Invalid \(old\?\) table or database name/ in mysqld.1.err FOUND 10 /\[ERROR\] Invalid \(old\?\) table or database name/ in mysqld.1.err
set global query_cache_type=DEFAULT; set global query_cache_type=DEFAULT;
set global query_cache_size=@save_query_cache_size; set global query_cache_size=@save_query_cache_size;
End of 10.2 tests End of 10.2 tests

View File

@ -1077,7 +1077,7 @@ KEY `a_2` (`a`,`vbidxcol`),
KEY `vbidxcol_2` (`vbidxcol`,`d`), KEY `vbidxcol_2` (`vbidxcol`,`d`),
CONSTRAINT `fk_16` FOREIGN KEY (`a`) REFERENCES `ibstd_16` (`a`) ON DELETE SET NULL CONSTRAINT `fk_16` FOREIGN KEY (`a`) REFERENCES `ibstd_16` (`a`) ON DELETE SET NULL
) ENGINE=InnoDB; ) ENGINE=InnoDB;
DROP TABLE ibstd_16_fk; ERROR HY000: Function or expression 'a' cannot be used in the GENERATED ALWAYS AS clause of `vadcol`
CREATE TABLE `ibstd_16_fk` ( CREATE TABLE `ibstd_16_fk` (
`a` int(11) DEFAULT NULL, `a` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL, `d` int(11) DEFAULT NULL,

View File

@ -1014,7 +1014,7 @@ CREATE TABLE `ibstd_16` (
) ENGINE=INNODB; ) ENGINE=INNODB;
# Block when FK constraint on base column of stored column. # Block when FK constraint on base column of stored column.
#--error ER_CANNOT_ADD_FOREIGN --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
CREATE TABLE `ibstd_16_fk` ( CREATE TABLE `ibstd_16_fk` (
`a` int(11) DEFAULT NULL, `a` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL, `d` int(11) DEFAULT NULL,
@ -1033,7 +1033,6 @@ CREATE TABLE `ibstd_16_fk` (
KEY `vbidxcol_2` (`vbidxcol`,`d`), KEY `vbidxcol_2` (`vbidxcol`,`d`),
CONSTRAINT `fk_16` FOREIGN KEY (`a`) REFERENCES `ibstd_16` (`a`) ON DELETE SET NULL CONSTRAINT `fk_16` FOREIGN KEY (`a`) REFERENCES `ibstd_16` (`a`) ON DELETE SET NULL
) ENGINE=InnoDB; ) ENGINE=InnoDB;
DROP TABLE ibstd_16_fk;
# Take out "KEY `a_2` (`a`,`vbidxcol`)", this should then be successful # Take out "KEY `a_2` (`a`,`vbidxcol`)", this should then be successful
CREATE TABLE `ibstd_16_fk` ( CREATE TABLE `ibstd_16_fk` (

View File

@ -1,19 +1,21 @@
# Create statement with FK on base column of stored column
create table t1(f1 int, f2 int as(f1) stored,
foreign key(f1) references t2(f1) on delete cascade)engine=innodb;
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
# adding new stored column during alter table copy operation.
create table t1(f1 int primary key) engine=innodb; create table t1(f1 int primary key) engine=innodb;
# Create statement with FK on base column of stored column
create table t2(f1 int not null, f2 int as (f1) stored,
foreign key(f1) references t1(f1) on update cascade)engine=innodb;
ERROR HY000: Function or expression 'f1' cannot be used in the GENERATED ALWAYS AS clause of `f2`
create table t2(f1 int not null, f2 int as (f1) virtual, f3 int as (f2) stored,
foreign key(f1) references t1(f1) on update cascade)engine=innodb;
ERROR HY000: Function or expression 'f2' cannot be used in the GENERATED ALWAYS AS clause of `f3`
# adding new stored column during alter table copy operation.
create table t2(f1 int not null, f2 int as (f1) virtual, create table t2(f1 int not null, f2 int as (f1) virtual,
foreign key(f1) references t1(f1) on update cascade)engine=innodb; foreign key(f1) references t1(f1) on update cascade)engine=innodb;
alter table t2 add column f3 int as (f1) stored, add column f4 int as (f1) virtual; alter table t2 add column f3 int as (f1) stored, add column f4 int as (f1) virtual;
ERROR HY000: Function or expression 'f1' cannot be used in the GENERATED ALWAYS AS clause of `f3`
show create table t2; show create table t2;
Table Create Table Table Create Table
t2 CREATE TABLE `t2` ( t2 CREATE TABLE `t2` (
`f1` int(11) NOT NULL, `f1` int(11) NOT NULL,
`f2` int(11) GENERATED ALWAYS AS (`f1`) VIRTUAL, `f2` int(11) GENERATED ALWAYS AS (`f1`) VIRTUAL,
`f3` int(11) GENERATED ALWAYS AS (`f1`) STORED,
`f4` int(11) GENERATED ALWAYS AS (`f1`) VIRTUAL,
KEY `f1` (`f1`), KEY `f1` (`f1`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
@ -21,26 +23,25 @@ drop table t2;
# adding foreign key constraint for base columns during alter copy. # adding foreign key constraint for base columns during alter copy.
create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb; create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb;
alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=copy; alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=copy;
ERROR HY000: Function or expression 'f1' cannot be used in the GENERATED ALWAYS AS clause of `f2`
show create table t2; show create table t2;
Table Create Table Table Create Table
t2 CREATE TABLE `t2` ( t2 CREATE TABLE `t2` (
`f1` int(11) NOT NULL, `f1` int(11) NOT NULL,
`f2` int(11) GENERATED ALWAYS AS (`f1`) STORED, `f2` int(11) GENERATED ALWAYS AS (`f1`) STORED
KEY `f1` (`f1`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f1`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
drop table t2; drop table t2;
# adding foreign key constraint for base columns during online alter. # adding foreign key constraint for base columns during online alter.
create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb; create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb;
set foreign_key_checks = 0; set foreign_key_checks = 0;
alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=inplace; alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=inplace;
ERROR 0A000: Cannot add foreign key on the base column of stored column ERROR HY000: Function or expression 'f1' cannot be used in the GENERATED ALWAYS AS clause of `f2`
drop table t2; drop table t2;
# adding stored column via online alter. # adding stored column via online alter.
create table t2(f1 int not null, create table t2(f1 int not null,
foreign key(f1) references t1(f1) on update cascade)engine=innodb; foreign key(f1) references t1(f1) on update cascade)engine=innodb;
alter table t2 add column f2 int as (f1) stored, algorithm=inplace; alter table t2 add column f2 int as (f1) stored, algorithm=inplace;
ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY ERROR HY000: Function or expression 'f1' cannot be used in the GENERATED ALWAYS AS clause of `f2`
drop table t2, t1; drop table t2, t1;
# #
# BUG#26731689 FK ON TABLE WITH GENERATED COLS: ASSERTION POS < N_DEF # BUG#26731689 FK ON TABLE WITH GENERATED COLS: ASSERTION POS < N_DEF

View File

@ -1,24 +1,28 @@
--source include/have_innodb.inc --source include/have_innodb.inc
create table t1(f1 int primary key) engine=innodb;
--echo # Create statement with FK on base column of stored column --echo # Create statement with FK on base column of stored column
--error ER_CANT_CREATE_TABLE --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1(f1 int, f2 int as(f1) stored, create table t2(f1 int not null, f2 int as (f1) stored,
foreign key(f1) references t2(f1) on delete cascade)engine=innodb; foreign key(f1) references t1(f1) on update cascade)engine=innodb;
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t2(f1 int not null, f2 int as (f1) virtual, f3 int as (f2) stored,
foreign key(f1) references t1(f1) on update cascade)engine=innodb;
--echo # adding new stored column during alter table copy operation. --echo # adding new stored column during alter table copy operation.
create table t1(f1 int primary key) engine=innodb;
create table t2(f1 int not null, f2 int as (f1) virtual, create table t2(f1 int not null, f2 int as (f1) virtual,
foreign key(f1) references t1(f1) on update cascade)engine=innodb; foreign key(f1) references t1(f1) on update cascade)engine=innodb;
# MySQL 5.7 would refuse this --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
#--error ER_ERROR_ON_RENAME
alter table t2 add column f3 int as (f1) stored, add column f4 int as (f1) virtual; alter table t2 add column f3 int as (f1) stored, add column f4 int as (f1) virtual;
show create table t2; show create table t2;
drop table t2; drop table t2;
--echo # adding foreign key constraint for base columns during alter copy. --echo # adding foreign key constraint for base columns during alter copy.
create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb; create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb;
# MySQL 5.7 would refuse this --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=copy; alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=copy;
show create table t2; show create table t2;
drop table t2; drop table t2;
@ -26,14 +30,14 @@ drop table t2;
--echo # adding foreign key constraint for base columns during online alter. --echo # adding foreign key constraint for base columns during online alter.
create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb; create table t2(f1 int not null, f2 int as (f1) stored) engine=innodb;
set foreign_key_checks = 0; set foreign_key_checks = 0;
--error 138 --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=inplace; alter table t2 add foreign key(f1) references t1(f1) on update cascade, algorithm=inplace;
drop table t2; drop table t2;
--echo # adding stored column via online alter. --echo # adding stored column via online alter.
create table t2(f1 int not null, create table t2(f1 int not null,
foreign key(f1) references t1(f1) on update cascade)engine=innodb; foreign key(f1) references t1(f1) on update cascade)engine=innodb;
--error ER_ALTER_OPERATION_NOT_SUPPORTED --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t2 add column f2 int as (f1) stored, algorithm=inplace; alter table t2 add column f2 int as (f1) stored, algorithm=inplace;
drop table t2, t1; drop table t2, t1;

View File

@ -10,3 +10,73 @@ select * from t2;
id id
drop table t2; drop table t2;
drop table t1; drop table t1;
#
# End of 10.2 tests
#
#
# MDEV-18114 Foreign Key Constraint actions don't affect Virtual Column
#
create table t1 (id int primary key);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update cascade);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on delete set null);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update set null);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update cascade);
ERROR HY000: Function or expression 'id' cannot be used in the GENERATED ALWAYS AS clause of `id2`
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on delete set null);
ERROR HY000: Function or expression 'id' cannot be used in the GENERATED ALWAYS AS clause of `id2`
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update set null);
ERROR HY000: Function or expression 'id' cannot be used in the GENERATED ALWAYS AS clause of `id2`
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update cascade);
ERROR HY000: Function or expression 'id2' cannot be used in the GENERATED ALWAYS AS clause of `id3`
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on delete set null);
ERROR HY000: Function or expression 'id2' cannot be used in the GENERATED ALWAYS AS clause of `id3`
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update set null);
ERROR HY000: Function or expression 'id2' cannot be used in the GENERATED ALWAYS AS clause of `id3`
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update cascade);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on delete set null);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update set null);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update cascade);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `id2`
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on delete set null);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `id2`
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update set null);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `id2`
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update cascade);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on delete set null);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update set null);
ERROR HY000: Function or expression 'id' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update cascade);
ERROR HY000: Function or expression 'id2' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on delete set null);
ERROR HY000: Function or expression 'id2' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update set null);
ERROR HY000: Function or expression 'id2' cannot be used in the CHECK clause of `CONSTRAINT_1`
drop table if exists t2, t1;
Warnings:
Note 1051 Unknown table 'test.t2'
#
# End of 10.5 tests
#

View File

@ -54,22 +54,22 @@ set session sql_mode=@OLD_SQL_MODE;
# #
create table t2 (x int); create table t2 (x int);
create table t1 (x int, y int generated always as (t2.x)); create table t1 (x int, y int generated always as (t2.x));
ERROR 42S22: Unknown column '`t2`.`x`' in 'GENERATED ALWAYS' ERROR HY000: Function or expression 't2.x' cannot be used in the GENERATED ALWAYS AS clause of `y`
create table t1 (x int, y int check (y > t2.x)); create table t1 (x int, y int check (y > t2.x));
ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' ERROR HY000: Function or expression 't2.x' cannot be used in the CHECK clause of `y`
create table t1 (x int, y int default t2.x); create table t1 (x int, y int default t2.x);
ERROR 42S22: Unknown column '`t2`.`x`' in 'DEFAULT' ERROR HY000: Function or expression 't2.x' cannot be used in the DEFAULT clause of `y`
create table t1 (x int, check (t2.x > 0)); create table t1 (x int, check (t2.x > 0));
ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' ERROR HY000: Function or expression 't2.x' cannot be used in the CHECK clause of `CONSTRAINT_1`
create table t1 (x int); create table t1 (x int);
alter table t1 add column y int generated always as (t2.x); alter table t1 add column y int generated always as (t2.x);
ERROR 42S22: Unknown column '`t2`.`x`' in 'GENERATED ALWAYS' ERROR HY000: Function or expression 't2.x' cannot be used in the GENERATED ALWAYS AS clause of `y`
alter table t1 add column y int check (z > t2.x); alter table t1 add column y int check (z > t2.x);
ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' ERROR HY000: Function or expression 't2.x' cannot be used in the CHECK clause of `y`
alter table t1 add column y int default t2.x; alter table t1 add column y int default t2.x;
ERROR 42S22: Unknown column '`t2`.`x`' in 'DEFAULT' ERROR HY000: Function or expression 't2.x' cannot be used in the DEFAULT clause of `y`
alter table t1 add constraint check (t2.x > 0); alter table t1 add constraint check (t2.x > 0);
ERROR 42S22: Unknown column '`t2`.`x`' in 'CHECK' ERROR HY000: Function or expression 't2.x' cannot be used in the CHECK clause of `CONSTRAINT_1`
create or replace table t1 (x int, y int generated always as (t1.x)); create or replace table t1 (x int, y int generated always as (t1.x));
create or replace table t1 (x int, y int check (y > t1.x)); create or replace table t1 (x int, y int check (y > t1.x));
create or replace table t1 (x int, y int default t1.x); create or replace table t1 (x int, y int default t1.x);
@ -80,13 +80,13 @@ create or replace table t1 (x int, y int default test.t1.x);
create or replace table t1 (x int, check (test.t1.x > 0)); create or replace table t1 (x int, check (test.t1.x > 0));
drop tables t1, t2; drop tables t1, t2;
create table t1 (x int, y int generated always as (test2.t1.x)); create table t1 (x int, y int generated always as (test2.t1.x));
ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'GENERATED ALWAYS' ERROR HY000: Function or expression 'test2.t1.x' cannot be used in the GENERATED ALWAYS AS clause of `y`
create table t1 (x int, y int check (y > test2.t1.x)); create table t1 (x int, y int check (y > test2.t1.x));
ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'CHECK' ERROR HY000: Function or expression 'test2.t1.x' cannot be used in the CHECK clause of `y`
create table t1 (x int, y int default test2.t1.x); create table t1 (x int, y int default test2.t1.x);
ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'DEFAULT' ERROR HY000: Function or expression 'test2.t1.x' cannot be used in the DEFAULT clause of `y`
create table t1 (x int, check (test2.t1.x > 0)); create table t1 (x int, check (test2.t1.x > 0));
ERROR 42S22: Unknown column '`test2`.`t1`.`x`' in 'CHECK' ERROR HY000: Function or expression 'test2.t1.x' cannot be used in the CHECK clause of `CONSTRAINT_1`
# #
# MDEV-25672 table alias from previous statement interferes later commands # MDEV-25672 table alias from previous statement interferes later commands
# #

View File

@ -14,3 +14,92 @@ select * from t1;
select * from t2; select * from t2;
drop table t2; drop table t2;
drop table t1; drop table t1;
--echo #
--echo # End of 10.2 tests
--echo #
--echo #
--echo # MDEV-18114 Foreign Key Constraint actions don't affect Virtual Column
--echo #
create table t1 (id int primary key);
# note that RESTRICT, NO ACTION, and DELETE CASCADE are fine
# because they don't change values of referenced columns
# virtual indexed
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update cascade);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on delete set null);
create or replace table t2 (id int, id2 int as (id) virtual, key(id2), foreign key (id) references t1 (id) on update set null);
# stored
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on delete cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on delete set null);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) stored, foreign key (id) references t1 (id) on update set null);
# stored indirect
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on delete cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on delete set null);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, id3 int as (id2) stored, foreign key (id) references t1 (id) on update set null);
# default
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on delete cascade);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update cascade);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on delete set null);
create or replace table t2 (id int, id2 int default (id), foreign key (id) references t1 (id) on update set null);
# field check
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on delete cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on delete set null);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int check (id), foreign key (id) references t1 (id) on update set null);
# table check
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on delete cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on delete set null);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int, check (id), foreign key (id) references t1 (id) on update set null);
# table check indirect
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update restrict);
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update no action);
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on delete cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update cascade);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on delete set null);
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create or replace table t2 (id int, id2 int as (id) virtual, check (id2), foreign key (id) references t1 (id) on update set null);
drop table if exists t2, t1;
--echo #
--echo # End of 10.5 tests
--echo #

View File

@ -29,23 +29,23 @@ set session sql_mode=@OLD_SQL_MODE;
--echo # --echo #
create table t2 (x int); create table t2 (x int);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int generated always as (t2.x)); create table t1 (x int, y int generated always as (t2.x));
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int check (y > t2.x)); create table t1 (x int, y int check (y > t2.x));
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int default t2.x); create table t1 (x int, y int default t2.x);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, check (t2.x > 0)); create table t1 (x int, check (t2.x > 0));
create table t1 (x int); create table t1 (x int);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t1 add column y int generated always as (t2.x); alter table t1 add column y int generated always as (t2.x);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t1 add column y int check (z > t2.x); alter table t1 add column y int check (z > t2.x);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t1 add column y int default t2.x; alter table t1 add column y int default t2.x;
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
alter table t1 add constraint check (t2.x > 0); alter table t1 add constraint check (t2.x > 0);
create or replace table t1 (x int, y int generated always as (t1.x)); create or replace table t1 (x int, y int generated always as (t1.x));
@ -60,13 +60,13 @@ create or replace table t1 (x int, check (test.t1.x > 0));
drop tables t1, t2; drop tables t1, t2;
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int generated always as (test2.t1.x)); create table t1 (x int, y int generated always as (test2.t1.x));
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int check (y > test2.t1.x)); create table t1 (x int, y int check (y > test2.t1.x));
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, y int default test2.t1.x); create table t1 (x int, y int default test2.t1.x);
--error ER_BAD_FIELD_ERROR --error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
create table t1 (x int, check (test2.t1.x > 0)); create table t1 (x int, check (test2.t1.x > 0));
--echo # --echo #

View File

@ -2005,15 +2005,6 @@ public:
return 0; return 0;
} }
/**
Check db/table_name if they defined in item and match arg values
@param arg Pointer to Check_table_name_prm structure
@retval true Match failed
@retval false Match succeeded
*/
virtual bool check_table_name_processor(void *arg) { return false; }
/* /*
TRUE if the expression depends only on the table indicated by tab_map TRUE if the expression depends only on the table indicated by tab_map
or can be converted to such an exression using equalities. or can be converted to such an exression using equalities.
@ -2213,15 +2204,6 @@ public:
bool collect; bool collect;
}; };
struct Check_table_name_prm
{
LEX_CSTRING db;
LEX_CSTRING table_name;
String field;
Check_table_name_prm(LEX_CSTRING _db, LEX_CSTRING _table_name) :
db(_db), table_name(_table_name) {}
};
/* /*
For SP local variable returns pointer to Item representing its For SP local variable returns pointer to Item representing its
current value and pointer to current Item otherwise. current value and pointer to current Item otherwise.
@ -3592,24 +3574,6 @@ public:
} }
return 0; return 0;
} }
bool check_table_name_processor(void *arg) override
{
Check_table_name_prm &p= *static_cast<Check_table_name_prm*>(arg);
if (!field && p.table_name.length && table_name.length)
{
DBUG_ASSERT(p.db.length);
if ((db_name.length &&
my_strcasecmp(table_alias_charset, p.db.str, db_name.str)) ||
my_strcasecmp(table_alias_charset, p.table_name.str, table_name.str))
{
print(&p.field, (enum_query_type) (QT_ITEM_ORIGINAL_FUNC_NULLIF |
QT_NO_DATA_EXPANSION |
QT_TO_SYSTEM_CHARSET));
return true;
}
}
return false;
}
void cleanup() override; void cleanup() override;
Item_equal *get_item_equal() override { return item_equal; } Item_equal *get_item_equal() override { return item_equal; }
void set_item_equal(Item_equal *item_eq) override { item_equal= item_eq; } void set_item_equal(Item_equal *item_eq) override { item_equal= item_eq; }

View File

@ -254,12 +254,25 @@ Alter_info::algorithm(const THD *thd) const
uint Alter_info::check_vcol_field(Item_field *item) const uint Alter_info::check_vcol_field(Item_field *item) const
{ {
if (!item->field &&
((item->db_name.length && !db.streq(item->db_name)) ||
(item->table_name.length && !table_name.streq(item->table_name))))
{
char *ptr= (char*)current_thd->alloc(item->db_name.length +
item->table_name.length +
item->field_name.length + 3);
strxmov(ptr, safe_str(item->db_name.str), item->db_name.length ? "." : "",
item->table_name.str, ".", item->field_name.str, NullS);
item->field_name.str= ptr;
return VCOL_IMPOSSIBLE;
}
for (Key &k: key_list) for (Key &k: key_list)
{ {
if (k.type != Key::FOREIGN_KEY) if (k.type != Key::FOREIGN_KEY)
continue; continue;
Foreign_key *fk= (Foreign_key*) &k; Foreign_key *fk= (Foreign_key*) &k;
if (fk->update_opt != FK_OPTION_CASCADE) if (fk->update_opt < FK_OPTION_CASCADE &&
fk->delete_opt < FK_OPTION_SET_NULL)
continue; continue;
for (Key_part_spec& kp: fk->columns) for (Key_part_spec& kp: fk->columns)
{ {
@ -267,6 +280,11 @@ uint Alter_info::check_vcol_field(Item_field *item) const
return VCOL_NON_DETERMINISTIC; return VCOL_NON_DETERMINISTIC;
} }
} }
for (Create_field &cf: create_list)
{
if (item->field_name.streq(cf.field_name))
return cf.vcol_info ? cf.vcol_info->flags : 0;
}
return 0; return 0;
} }

View File

@ -4452,7 +4452,6 @@ without_overlaps_err:
create_info->null_bits= null_fields; create_info->null_bits= null_fields;
/* Check fields. */ /* Check fields. */
Item::Check_table_name_prm walk_prm(alter_info->db, alter_info->table_name);
it.rewind(); it.rewind();
while ((sql_field=it++)) while ((sql_field=it++))
{ {
@ -4510,33 +4509,22 @@ without_overlaps_err:
if (create_simple) if (create_simple)
{ {
/*
NOTE: we cannot do this in check_vcol_func_processor() as there is
already no table name qualifier in expression.
*/
if (sql_field->vcol_info && sql_field->vcol_info->expr && if (sql_field->vcol_info && sql_field->vcol_info->expr &&
sql_field->vcol_info->expr->walk(&Item::check_table_name_processor, check_expression(sql_field->vcol_info, &sql_field->field_name,
false, &walk_prm)) sql_field->vcol_info->stored_in_db
{ ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL,
my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "GENERATED ALWAYS"); alter_info))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
}
if (sql_field->default_value && if (sql_field->default_value &&
sql_field->default_value->expr->walk(&Item::check_table_name_processor, check_expression(sql_field->default_value, &sql_field->field_name,
false, &walk_prm)) VCOL_DEFAULT, alter_info))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "DEFAULT");
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
}
if (sql_field->check_constraint && if (sql_field->check_constraint &&
sql_field->check_constraint->expr->walk(&Item::check_table_name_processor, check_expression(sql_field->check_constraint, &sql_field->field_name,
false, &walk_prm)) VCOL_CHECK_FIELD, alter_info))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK");
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
}
} }
} }
@ -4546,12 +4534,6 @@ without_overlaps_err:
List_iterator_fast<Virtual_column_info> c_it(alter_info->check_constraint_list); List_iterator_fast<Virtual_column_info> c_it(alter_info->check_constraint_list);
while (Virtual_column_info *check= c_it++) while (Virtual_column_info *check= c_it++)
{ {
if (create_simple && check->expr->walk(&Item::check_table_name_processor,
false, &walk_prm))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0), walk_prm.field.c_ptr(), "CHECK");
DBUG_RETURN(TRUE);
}
if (check->name.length && !check->automatic_name) if (check->name.length && !check->automatic_name)
{ {
/* Check that there's no repeating table CHECK constraint names. */ /* Check that there's no repeating table CHECK constraint names. */
@ -8438,6 +8420,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
List<Create_field> new_create_tail; List<Create_field> new_create_tail;
/* New key definitions are added here */ /* New key definitions are added here */
List<Key> new_key_list; List<Key> new_key_list;
List<FOREIGN_KEY_INFO> fk_list;
List<Alter_rename_key> rename_key_list(alter_info->alter_rename_key_list); List<Alter_rename_key> rename_key_list(alter_info->alter_rename_key_list);
List_iterator<Alter_drop> drop_it(alter_info->drop_list); List_iterator<Alter_drop> drop_it(alter_info->drop_list);
List_iterator<Create_field> def_it(alter_info->create_list); List_iterator<Create_field> def_it(alter_info->create_list);
@ -8460,12 +8443,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
bool drop_period= false; bool drop_period= false;
LEX_CSTRING period_start_name= {nullptr, 0}; LEX_CSTRING period_start_name= {nullptr, 0};
LEX_CSTRING period_end_name= {nullptr, 0}; LEX_CSTRING period_end_name= {nullptr, 0};
DBUG_ENTER("mysql_prepare_alter_table");
if (table->s->period.name) if (table->s->period.name)
{ {
period_start_name= table->s->period_start_field()->field_name; period_start_name= table->s->period_start_field()->field_name;
period_end_name= table->s->period_end_field()->field_name; period_end_name= table->s->period_end_field()->field_name;
} }
DBUG_ENTER("mysql_prepare_alter_table");
/* /*
Merge incompatible changes flag in case of upgrade of a table from an Merge incompatible changes flag in case of upgrade of a table from an
@ -8526,6 +8510,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
create_info->option_list= merge_engine_table_options(table->s->option_list, create_info->option_list= merge_engine_table_options(table->s->option_list,
create_info->option_list, thd->mem_root); create_info->option_list, thd->mem_root);
table->file->get_foreign_key_list(thd, &fk_list);
/* /*
First collect all fields from table which isn't in drop_list First collect all fields from table which isn't in drop_list
*/ */
@ -9168,6 +9154,30 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
re_setup_keyinfo_hash(key_info); re_setup_keyinfo_hash(key_info);
} }
} }
{
// add existing foreign keys
for (auto &fk : fk_list)
{
Alter_drop *drop;
for(drop_it.rewind(); (drop=drop_it++); )
if (drop->type == Alter_drop::FOREIGN_KEY &&
!my_strcasecmp(system_charset_info, fk.foreign_id->str, drop->name))
break;
if (drop)
continue;
List<Key_part_spec> cols, ref_cols;
for (LEX_CSTRING &c : fk.foreign_fields)
cols.push_back(new (thd->mem_root) Key_part_spec(&c, 0));
for (LEX_CSTRING &c : fk.referenced_fields)
ref_cols.push_back(new (thd->mem_root) Key_part_spec(&c, 0));
auto key= new (thd->mem_root)
Foreign_key(fk.foreign_id, &cols, fk.foreign_id, fk.referenced_db,
fk.referenced_table, &ref_cols, fk.delete_method, fk.update_method,
Foreign_key::FK_MATCH_UNDEF, DDL_options());
key->old= true;
new_key_list.push_back(key, thd->mem_root);
}
}
{ {
Key *key; Key *key;
while ((key=key_it++)) // Add new keys while ((key=key_it++)) // Add new keys
@ -9291,10 +9301,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (!alter_info->check_constraint_list.is_empty()) if (!alter_info->check_constraint_list.is_empty())
{ {
/* Check the table FOREIGN KEYs for name duplications. */ /* Check the table FOREIGN KEYs for name duplications. */
List <FOREIGN_KEY_INFO> fk_child_key_list;
FOREIGN_KEY_INFO *f_key; FOREIGN_KEY_INFO *f_key;
table->file->get_foreign_key_list(thd, &fk_child_key_list); List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_list);
List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list);
while ((f_key= fk_key_it++)) while ((f_key= fk_key_it++))
{ {
List_iterator_fast<Virtual_column_info> List_iterator_fast<Virtual_column_info>