MDEV-34392 Inplace algorithm violates the foreign key constraint

Don't allow the referencing key column from NULL TO NOT NULL
when

 1) Foreign key constraint type is ON UPDATE SET NULL
 2) Foreign key constraint type is ON DELETE SET NULL
 3) Foreign key constraint type is UPDATE CASCADE and referenced
 column declared as NULL

Don't allow the referenced key column from NOT NULL to NULL
when foreign key constraint type is UPDATE CASCADE
and referencing key columns doesn't allow NULL values

get_foreign_key_info(): InnoDB sends the information about
nullability of the foreign key fields and referenced key fields.

fk_check_column_changes(): Enforce the above rules for COPY
algorithm

innobase_check_foreign_drop_col(): Checks whether the dropped
column exists in existing foreign key relation

innobase_check_foreign_low() : Enforce the above rules for
INPLACE algorithm

dict_foreign_t::check_fk_constraint_valid(): This is used
by CREATE TABLE statement to check nullability for foreign
key relation.
This commit is contained in:
Thirunarayanan Balathandayuthapani 2024-09-30 13:37:26 +05:30
parent 45298b730b
commit cc810e64d4
18 changed files with 919 additions and 215 deletions

View File

@ -175,6 +175,11 @@ static inline uchar last_byte_mask(uint bits)
return (uchar) ((2U << used) - 1);
}
static inline uint my_bits_in_bytes(uint n)
{
return ((n + 7) / 8);
}
#ifdef _MSC_VER
#include <intrin.h>
#endif

View File

@ -2,7 +2,7 @@ connection node_2;
connection node_1;
CREATE TABLE t0 (
f1 INT PRIMARY KEY,
f2 INT UNIQUE
f2 INT UNIQUE NOT NULL
);
CREATE TABLE t1 (
f1 INT PRIMARY KEY,

View File

@ -7,7 +7,7 @@
CREATE TABLE t0 (
f1 INT PRIMARY KEY,
f2 INT UNIQUE
f2 INT UNIQUE NOT NULL
);
CREATE TABLE t1 (

View File

@ -0,0 +1,18 @@
--- foreign_null.result
+++ foreign_null,COPY.result
@@ -139,6 +139,7 @@
ALTER TABLE `t#2` DROP INDEX f1;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE `t#1` MODIFY COLUMN f2 INT;
+ERROR HY000: Error on rename of './test/#sql-alter' to './test/t@00231' (errno: 150 "Foreign key constraint is incorrectly formed")
DROP TABLE `t#2`, `t#1`;
# Drop referenced index and modify column
CREATE TABLE `t#1`(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
@@ -147,6 +148,7 @@
ALTER TABLE `t#1` DROP INDEX f2;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE `t#2` MODIFY COLUMN f1 INT NOT NULL;
+ERROR HY000: Error on rename of './test/#sql-alter' to './test/t@00232' (errno: 150 "Foreign key constraint is incorrectly formed")
DROP TABLE `t#2`, `t#1`;
# Self referential modifying column
CREATE TABLE t1(f1 INT, f2 INT, index(f2), foreign key(f1) references t1(f2) ON UPDATE CASCADE)engine=innodb;

View File

@ -0,0 +1,156 @@
call mtr.add_suppression("InnoDB: In ALTER TABLE .* has or is referenced in foreign key constraints which are not compatible with the new table definition.");
# modify child column NOT NULL on UPDATE CASCADE..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL;
ERROR HY000: Column 'f1' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
DROP TABLE t2, t1;
# modify child column NOT NULL ON DELETE CASCADE..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE CASCADE)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL;
DROP TABLE t2, t1;
# modify child column NOT NULL ON UPDATE SET NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY(f1) REFERENCES t1(f1) ON UPDATE SET NULL)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL;
ERROR HY000: Column 'f1' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
DROP TABLE t2, t1;
# modify child column NOT NULL ON DELETE SET NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE SET NULL)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
ERROR HY000: Column 'f2' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
DROP TABLE t2, t1;
# modify child column NOT NULL ON UPDATE RESTRICT..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
DROP TABLE t2, t1;
# modify child column NOT NULL ON DELETE RESTRICT..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE RESTRICT)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
DROP TABLE t2, t1;
# modify child column NOT NULL ON UPDATE NO ACTION..PARENT COLUMN NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON UPDATE NO ACTION)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
DROP TABLE t2, t1;
# modify child column NOT NULL ON DELETE NO ACTION..PARENT COLUMN NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE NO ACTION)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
DROP TABLE t2, t1;
# modify parent column NULL ON UPDATE CASCADE child column NOT NULL
CREATE TABLE `t#1`(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT NOT NULL,
FOREIGN KEY(f1) REFERENCES `t#1`(f2)
ON UPDATE CASCADE)ENGINE=InnoDB;
ALTER TABLE `t#1` MODIFY COLUMN f2 INT;
ERROR HY000: Cannot change column 'f2': used in a foreign key constraint 't#2_ibfk_1' of table 'test.t#2'
DROP TABLE `t#2`, `t#1`;
# modify parent column NULL ON DELETE CASCADE child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE CASCADE)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f2 INT;
DROP TABLE t2, t1;
# modify parent column NULL ON UPDATE SET NULL child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE SET NULL)ENGINE=InnoDB;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
DROP TABLE t1;
# modify parent column NULL ON DELETE SET NULL child NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE SET NULL)ENGINE=InnoDB;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
DROP TABLE t1;
# modify parent column NULL ON UPDATE RESTRICT child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f2 INT;
DROP TABLE t2, t1;
# modify parent column NULL ON DELETE RESTRICT child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f2 INT;
DROP TABLE t2, t1;
# modify parent column NULL ON UPDATE NO ACTION child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE NO ACTION)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f2 INT;
DROP TABLE t2, t1;
# modify parent column NULL ON DELETE NO ACTION child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE NO ACTION)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f2 INT;
DROP TABLE t2, t1;
# foreign key constraint for multiple columns
# modify parent column NULL ON UPDATE CASCADE child column NOT NULL
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL,
INDEX(f1, f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, f2 INT NOT NULL,
INDEX(f1, f2),
FOREIGN KEY(f1, f2) REFERENCES t1(f1, f2) ON
UPDATE CASCADE)ENGINE=InnoDB;
ALTER TABLE t1 MODIFY COLUMN f1 INT;
ERROR HY000: Cannot change column 'f1': used in a foreign key constraint 't2_ibfk_1' of table 'test.t2'
DROP TABLE t2, t1;
# modify child column NOT NULL ON UPDATE CASCADE parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1, f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, INDEX(f1, f2),
FOREIGN KEY(f1, f2) REFERENCES t1(f1, f2) ON
UPDATE CASCADE)ENGINE=InnoDB;
ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL;
ERROR HY000: Column 'f2' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
DROP TABLE t2, t1;
# allow foreign key constraints when parent table created later
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL;
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1);
UPDATE t1 SET f2= NULL;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f1`) REFERENCES `t1` (`f2`) ON UPDATE CASCADE)
SELECT * FROM t2;
f1
1
SET FOREIGN_KEY_CHECKS=0;
UPDATE t1 SET f2= NULL;
SELECT * FROM t2;
f1
1
DROP TABLE t2, t1;
# Modify column + Drop column & Drop foreign key constraint
CREATE TABLE t1(f1 INT, f2 INT, KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, f3 INT,
FOREIGN KEY fdx(f2) REFERENCES t1(f1),
FOREIGN KEY fdx2(f3) REFERENCES t1(f2))ENGINE=InnoDB;
ALTER TABLE t2 MODIFY f2 INT NOT NULL, DROP FOREIGN KEY fdx;
ALTER TABLE t2 ADD FOREIGN KEY fdx (f2) REFERENCES t1(f1);
ALTER TABLE t2 DROP COLUMN f2, DROP FOREIGN KEY fdx;
DROP TABLE t2, t1;
# Drop foreign index & modify column
CREATE TABLE `t#1`(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES `t#1`(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE `t#2` DROP INDEX f1;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE `t#1` MODIFY COLUMN f2 INT;
DROP TABLE `t#2`, `t#1`;
# Drop referenced index and modify column
CREATE TABLE `t#1`(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT, FOREIGN KEY(f1) REFERENCES `t#1`(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE `t#1` DROP INDEX f2;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE `t#2` MODIFY COLUMN f1 INT NOT NULL;
DROP TABLE `t#2`, `t#1`;
# Self referential modifying column
CREATE TABLE t1(f1 INT, f2 INT, index(f2), foreign key(f1) references t1(f2) ON UPDATE CASCADE)engine=innodb;
ALTER TABLE t1 MODIFY COLUMN f2 INT NOT NULL;
ALTER TABLE t1 MODIFY COLUMN f1 INT NOT NULL;
ALTER TABLE t1 MODIFY COLUMN f1 INT;
DROP TABLE t1;

View File

@ -455,11 +455,11 @@ ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint
alter table t2 drop index b, drop index c, drop index d;
ERROR HY000: Cannot drop index 'b': needed in a foreign key constraint
alter table t2 MODIFY b INT NOT NULL, ALGORITHM=COPY;
ERROR HY000: Cannot change column 'b': used in a foreign key constraint 't2_ibfk_1'
ERROR HY000: Column 'b' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
set @old_sql_mode = @@sql_mode;
set @@sql_mode = 'STRICT_TRANS_TABLES';
alter table t2 MODIFY b INT NOT NULL, ALGORITHM=INPLACE;
ERROR HY000: Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'test/t2_ibfk_1' SET NULL
ERROR HY000: Column 'b' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
set @@sql_mode = @old_sql_mode;
SET FOREIGN_KEY_CHECKS=0;
alter table t2 DROP COLUMN b, ALGORITHM=COPY;
@ -480,10 +480,10 @@ info: Records: 0 Duplicates: 0 Warnings: 0
set @@sql_mode = 'STRICT_TRANS_TABLES';
alter table t2 add primary key (alpha), change a alpha int,
change b beta int not null, change c charlie int not null;
ERROR HY000: Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'test/t2_ibfk_1' SET NULL
ERROR HY000: Column 'b' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
alter table t2 add primary key (alpha), change a alpha int,
change c charlie int not null, change d delta int not null;
ERROR HY000: Column 'd' cannot be NOT NULL: needed in a foreign key constraint 'test/t2_ibfk_3' SET NULL
ERROR HY000: Column 'd' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_3' SET NULL
alter table t2 add primary key (alpha), change a alpha int,
change b beta int, modify c int not null;
affected rows: 0

View File

@ -3057,7 +3057,7 @@ ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1 (a) ON DELETE SET NULL;
set @old_sql_mode = @@sql_mode;
set @@sql_mode = 'STRICT_TRANS_TABLES';
ALTER TABLE t2 MODIFY a INT NOT NULL;
ERROR HY000: Column 'a' cannot be NOT NULL: needed in a foreign key constraint 'test/t2_ibfk_1' SET NULL
ERROR HY000: Column 'a' cannot be NOT NULL: needed in a foreign key constraint 't2_ibfk_1' SET NULL
set @@sql_mode = @old_sql_mode;
DELETE FROM t1;
DROP TABLE t2,t1;

View File

@ -0,0 +1,2 @@
[COPY]
[INPLACE]

View File

@ -0,0 +1,225 @@
--source include/have_innodb.inc
call mtr.add_suppression("InnoDB: In ALTER TABLE .* has or is referenced in foreign key constraints which are not compatible with the new table definition.");
let $MYSQLD_DATADIR= `select @@datadir`;
let $algorithm=`select regexp_replace('$MTR_COMBINATIONS', 'innodb,\|,innodb', '')`;
--echo # modify child column NOT NULL on UPDATE CASCADE..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_NOT_NULL
eval ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON DELETE CASCADE..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON UPDATE SET NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY(f1) REFERENCES t1(f1) ON UPDATE SET NULL)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_NOT_NULL
eval ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON DELETE SET NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE SET NULL)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_NOT_NULL
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON UPDATE RESTRICT..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON DELETE RESTRICT..parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE RESTRICT)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON UPDATE NO ACTION..PARENT COLUMN NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON UPDATE NO ACTION)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON DELETE NO ACTION..PARENT COLUMN NULL
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, FOREIGN KEY (f2) REFERENCES t1(f2) ON DELETE NO ACTION)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify parent column NULL ON UPDATE CASCADE child column NOT NULL
CREATE TABLE `t#1`(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT NOT NULL,
FOREIGN KEY(f1) REFERENCES `t#1`(f2)
ON UPDATE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_CANNOT_CHANGE_CHILD
eval ALTER TABLE `t#1` MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE `t#2`, `t#1`;
--echo # modify parent column NULL ON DELETE CASCADE child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify parent column NULL ON UPDATE SET NULL child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
--error ER_CANT_CREATE_TABLE
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE SET NULL)ENGINE=InnoDB;
DROP TABLE t1;
--echo # modify parent column NULL ON DELETE SET NULL child NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
--error ER_CANT_CREATE_TABLE
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE SET NULL)ENGINE=InnoDB;
DROP TABLE t1;
--echo # modify parent column NULL ON UPDATE RESTRICT child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify parent column NULL ON DELETE RESTRICT child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE RESTRICT)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify parent column NULL ON UPDATE NO ACTION child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE NO ACTION)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify parent column NULL ON DELETE NO ACTION child column NOT NULL
CREATE TABLE t1(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES t1(f2) ON DELETE NO ACTION)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # foreign key constraint for multiple columns
--echo # modify parent column NULL ON UPDATE CASCADE child column NOT NULL
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL,
INDEX(f1, f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT NOT NULL, f2 INT NOT NULL,
INDEX(f1, f2),
FOREIGN KEY(f1, f2) REFERENCES t1(f1, f2) ON
UPDATE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_CANNOT_CHANGE_CHILD
eval ALTER TABLE t1 MODIFY COLUMN f1 INT,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # modify child column NOT NULL ON UPDATE CASCADE parent column NULL
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1, f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, INDEX(f1, f2),
FOREIGN KEY(f1, f2) REFERENCES t1(f1, f2) ON
UPDATE CASCADE)ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
--error ER_FK_COLUMN_NOT_NULL
eval ALTER TABLE t2 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # allow foreign key constraints when parent table created later
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE t2(f1 INT, FOREIGN KEY(f1) REFERENCES t1(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE t2 MODIFY COLUMN f1 INT NOT NULL;
CREATE TABLE t1(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1);
--error ER_ROW_IS_REFERENCED_2
UPDATE t1 SET f2= NULL;
SELECT * FROM t2;
SET FOREIGN_KEY_CHECKS=0;
UPDATE t1 SET f2= NULL;
SELECT * FROM t2;
DROP TABLE t2, t1;
--echo # Modify column + Drop column & Drop foreign key constraint
CREATE TABLE t1(f1 INT, f2 INT, KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE t2(f1 INT, f2 INT, f3 INT,
FOREIGN KEY fdx(f2) REFERENCES t1(f1),
FOREIGN KEY fdx2(f3) REFERENCES t1(f2))ENGINE=InnoDB;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 MODIFY f2 INT NOT NULL, DROP FOREIGN KEY fdx,ALGORITHM=$algorithm;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 ADD FOREIGN KEY fdx (f2) REFERENCES t1(f1),ALGORITHM=$algorithm;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t2 DROP COLUMN f2, DROP FOREIGN KEY fdx,ALGORITHM=$algorithm;
DROP TABLE t2, t1;
--echo # Drop foreign index & modify column
CREATE TABLE `t#1`(f1 INT, f2 INT NOT NULL, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT NOT NULL, FOREIGN KEY(f1) REFERENCES `t#1`(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=0;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE `t#2` DROP INDEX f1,ALGORITHM=$algorithm;
SET FOREIGN_KEY_CHECKS=1;
let $error_code=0;
if ($algorithm == "COPY")
{
let $error_code= ER_ERROR_ON_RENAME;
}
--replace_regex /#sql-alter-[0-9a-f_\-]*/#sql-alter/
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '' $MYSQLD_DATADIR ./;
--error $error_code
eval ALTER TABLE `t#1` MODIFY COLUMN f2 INT,ALGORITHM=$algorithm;
DROP TABLE `t#2`, `t#1`;
--echo # Drop referenced index and modify column
CREATE TABLE `t#1`(f1 INT, f2 INT, PRIMARY KEY(f1), KEY(f2))ENGINE=InnoDB;
CREATE TABLE `t#2`(f1 INT, FOREIGN KEY(f1) REFERENCES `t#1`(f2) ON UPDATE CASCADE)ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=0;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE `t#1` DROP INDEX f2,ALGORITHM=$algorithm;
SET FOREIGN_KEY_CHECKS=1;
--replace_regex /#sql-alter-[0-9a-f_\-]*/#sql-alter/
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '' $MYSQLD_DATADIR ./;
--error $error_code
eval ALTER TABLE `t#2` MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm;
DROP TABLE `t#2`, `t#1`;
--echo # Self referential modifying column
CREATE TABLE t1(f1 INT, f2 INT, index(f2), foreign key(f1) references t1(f2) ON UPDATE CASCADE)engine=innodb;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f2 INT NOT NULL,ALGORITHM=$algorithm;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm;
replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE '';
eval ALTER TABLE t1 MODIFY COLUMN f1 INT,ALGORITHM=$algorithm;
DROP TABLE t1;

View File

@ -168,7 +168,7 @@ alter table t4 drop index d;
alter table t2 drop index b;
--error ER_DROP_INDEX_FK
alter table t2 drop index b, drop index c, drop index d;
--error ER_FK_COLUMN_CANNOT_CHANGE
--error ER_FK_COLUMN_NOT_NULL
alter table t2 MODIFY b INT NOT NULL, ALGORITHM=COPY;
# NULL -> NOT NULL only allowed INPLACE if strict sql_mode is on.
set @old_sql_mode = @@sql_mode;

View File

@ -9589,18 +9589,20 @@ static Create_field *get_field_by_old_name(Alter_info *alter_info,
enum fk_column_change_type
{
FK_COLUMN_NO_CHANGE, FK_COLUMN_DATA_CHANGE,
FK_COLUMN_RENAMED, FK_COLUMN_DROPPED
FK_COLUMN_RENAMED, FK_COLUMN_DROPPED, FK_COLUMN_NOT_NULL
};
/**
Check that ALTER TABLE's changes on columns of a foreign key are allowed.
@param[in] thd Thread context.
@param[in] table table to be altered
@param[in] alter_info Alter_info describing changes to be done
by ALTER TABLE.
@param[in] fk_columns List of columns of the foreign key to check.
@param[in] fk Foreign key information.
@param[out] bad_column_name Name of field on which ALTER TABLE tries to
do prohibited operation.
@param[in] referenced Check the referenced fields
@note This function takes into account value of @@foreign_key_checks
setting.
@ -9611,17 +9613,27 @@ enum fk_column_change_type
change in foreign key column.
@retval FK_COLUMN_RENAMED Foreign key column is renamed.
@retval FK_COLUMN_DROPPED Foreign key column is dropped.
@retval FK_COLUMN_NOT_NULL Foreign key column cannot be null
if ON...SET NULL or ON UPDATE
CASCADE conflicts with NOT NULL
*/
static enum fk_column_change_type
fk_check_column_changes(THD *thd, Alter_info *alter_info,
List<LEX_CSTRING> &fk_columns,
const char **bad_column_name)
fk_check_column_changes(THD *thd, const TABLE *table,
Alter_info *alter_info,
FOREIGN_KEY_INFO *fk,
const char **bad_column_name,
bool referenced=false)
{
List<LEX_CSTRING> &fk_columns= referenced
? fk->referenced_fields
: fk->foreign_fields;
List_iterator_fast<LEX_CSTRING> column_it(fk_columns);
LEX_CSTRING *column;
int n_col= 0;
*bad_column_name= NULL;
enum fk_column_change_type result= FK_COLUMN_NO_CHANGE;
while ((column= column_it++))
{
@ -9640,8 +9652,8 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info,
SE that foreign keys should be updated to use new name of column
like it happens in case of in-place algorithm.
*/
*bad_column_name= column->str;
return FK_COLUMN_RENAMED;
result= FK_COLUMN_RENAMED;
goto func_exit;
}
/*
@ -9654,17 +9666,55 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info,
new_field->flags&= ~AUTO_INCREMENT_FLAG;
const bool equal_result= old_field->is_equal(*new_field);
new_field->flags= flags;
const bool old_field_not_null= old_field->flags & NOT_NULL_FLAG;
const bool new_field_not_null= new_field->flags & NOT_NULL_FLAG;
if ((equal_result == IS_EQUAL_NO) ||
((new_field->flags & NOT_NULL_FLAG) &&
!(old_field->flags & NOT_NULL_FLAG)))
if ((equal_result == IS_EQUAL_NO))
{
/*
Column in a FK has changed significantly and it
may break referential intergrity.
*/
*bad_column_name= column->str;
return FK_COLUMN_DATA_CHANGE;
result= FK_COLUMN_DATA_CHANGE;
goto func_exit;
}
if (old_field_not_null != new_field_not_null)
{
if (referenced && !new_field_not_null)
{
/*
Don't allow referenced column to change from
NOT NULL to NULL when foreign key relation is
ON UPDATE CASCADE and the referencing column
is declared as NOT NULL
*/
if (fk->update_method == FK_OPTION_CASCADE &&
!fk->is_nullable(false, n_col))
{
result= FK_COLUMN_DATA_CHANGE;
goto func_exit;
}
}
else if (!referenced && new_field_not_null)
{
/*
Don't allow the foreign column to change
from NULL to NOT NULL when foreign key type is
1) UPDATE SET NULL
2) DELETE SET NULL
3) UPDATE CASCADE and referenced column is declared as NULL
*/
if (fk->update_method == FK_OPTION_SET_NULL ||
fk->delete_method == FK_OPTION_SET_NULL ||
(fk->update_method == FK_OPTION_CASCADE &&
fk->referenced_key_name &&
fk->is_nullable(true, n_col)))
{
result= FK_COLUMN_NOT_NULL;
goto func_exit;
}
}
}
}
else
@ -9678,12 +9728,15 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info,
field being dropped since it is easy to break referential
integrity in this case.
*/
*bad_column_name= column->str;
return FK_COLUMN_DROPPED;
result= FK_COLUMN_DROPPED;
goto func_exit;
}
n_col++;
}
return FK_COLUMN_NO_CHANGE;
func_exit:
*bad_column_name= column->str;
return result;
}
@ -9775,9 +9828,8 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
enum fk_column_change_type changes;
const char *bad_column_name;
changes= fk_check_column_changes(thd, alter_info,
f_key->referenced_fields,
&bad_column_name);
changes= fk_check_column_changes(thd, table, alter_info, f_key,
&bad_column_name, true);
switch(changes)
{
@ -9811,6 +9863,9 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
f_key->foreign_id->str, buff.c_ptr());
DBUG_RETURN(true);
}
/* FK_COLUMN_NOT_NULL error happens only when changing
the foreign key column from NULL to NOT NULL */
case FK_COLUMN_NOT_NULL:
default:
DBUG_ASSERT(0);
}
@ -9849,8 +9904,7 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
enum fk_column_change_type changes;
const char *bad_column_name;
changes= fk_check_column_changes(thd, alter_info,
f_key->foreign_fields,
changes= fk_check_column_changes(thd, table, alter_info, f_key,
&bad_column_name);
switch(changes)
@ -9872,6 +9926,10 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
my_error(ER_FK_COLUMN_CANNOT_DROP, MYF(0), bad_column_name,
f_key->foreign_id->str);
DBUG_RETURN(true);
case FK_COLUMN_NOT_NULL:
my_error(ER_FK_COLUMN_NOT_NULL, MYF(0), bad_column_name,
f_key->foreign_id->str);
DBUG_RETURN(true);
default:
DBUG_ASSERT(0);
}

View File

@ -36,6 +36,7 @@
#include "sql_i_s.h"
#include "sql_type.h" /* vers_kind_t */
#include "privilege.h" /* privilege_t */
#include "my_bit.h"
/*
Buffer for unix timestamp in microseconds:
@ -1882,6 +1883,70 @@ typedef struct st_foreign_key_info
LEX_CSTRING *referenced_key_name;
List<LEX_CSTRING> foreign_fields;
List<LEX_CSTRING> referenced_fields;
private:
unsigned char *fields_nullable= nullptr;
/**
Get the number of fields exist in foreign key relationship
*/
unsigned get_n_fields() const noexcept
{
unsigned n_fields= foreign_fields.elements;
if (n_fields == 0)
n_fields= referenced_fields.elements;
return n_fields;
}
/**
Assign nullable field for referenced and foreign fields
based on number of fields. This nullable fields
should be allocated by engine for passing the
foreign key information
@param thd thread to allocate the memory
@param num_fields number of fields
*/
void assign_nullable(THD *thd, unsigned num_fields) noexcept
{
fields_nullable=
(unsigned char *)thd_calloc(thd,
my_bits_in_bytes(2 * num_fields));
}
public:
/**
Set nullable bit for the field in the given field
@param referenced set null bit for referenced column
@param field field number
@param n_fields number of fields
*/
void set_nullable(THD *thd, bool referenced,
unsigned field, unsigned n_fields) noexcept
{
if (!fields_nullable)
assign_nullable(thd, n_fields);
DBUG_ASSERT(fields_nullable);
DBUG_ASSERT(field < n_fields);
size_t bit= size_t{field} + referenced * n_fields;
fields_nullable[bit / 8]|= (unsigned char)(1 << (bit % 8));
}
/**
Check whether the given field_no in foreign key field or
referenced key field
@param referenced check referenced field nullable value
@param field field number
@return true if the field is nullable or false if it is not
*/
bool is_nullable(bool referenced, unsigned field) const noexcept
{
if (!fields_nullable)
return false;
unsigned n_field= get_n_fields();
DBUG_ASSERT(field < n_field);
size_t bit= size_t{field} + referenced * n_field;
return fields_nullable[bit / 8] & (1 << (bit % 8));
}
} FOREIGN_KEY_INFO;
LEX_CSTRING *fk_option_name(enum_fk_option opt);

View File

@ -1838,8 +1838,8 @@ dict_foreigns_has_s_base_col(
foreign = *it;
ulint type = foreign->type;
type &= ~(DICT_FOREIGN_ON_DELETE_NO_ACTION
| DICT_FOREIGN_ON_UPDATE_NO_ACTION);
type &= ~(foreign->DELETE_NO_ACTION
| foreign->UPDATE_NO_ACTION);
if (type == 0) {
continue;
@ -1897,8 +1897,12 @@ dict_create_add_foreigns_to_dictionary(
foreign = *it;
ut_ad(foreign->id != NULL);
error = dict_create_add_foreign_to_dictionary(
table->name.m_name, foreign, trx);
if (!foreign->check_fk_constraint_valid()) {
error = DB_CANNOT_ADD_CONSTRAINT;
} else {
error = dict_create_add_foreign_to_dictionary(
table->name.m_name, foreign, trx);
}
if (error != DB_SUCCESS) {
break;

View File

@ -3037,8 +3037,8 @@ dict_foreign_add_to_cache(
for_in_cache->n_fields,
for_in_cache->referenced_index, check_charsets,
for_in_cache->type
& (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL));
& (foreign->DELETE_SET_NULL
| foreign->UPDATE_SET_NULL));
if (index == NULL
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
@ -4003,27 +4003,27 @@ dict_print_info_on_foreign_key_in_create_format(const trx_t *trx,
str.append(")");
if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
if (foreign->type & foreign->DELETE_CASCADE) {
str.append(" ON DELETE CASCADE");
}
if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
if (foreign->type & foreign->DELETE_SET_NULL) {
str.append(" ON DELETE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
if (foreign->type & foreign->DELETE_NO_ACTION) {
str.append(" ON DELETE NO ACTION");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
if (foreign->type & foreign->UPDATE_CASCADE) {
str.append(" ON UPDATE CASCADE");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
if (foreign->type & foreign->UPDATE_SET_NULL) {
str.append(" ON UPDATE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
if (foreign->type & foreign->UPDATE_NO_ACTION) {
str.append(" ON UPDATE NO ACTION");
}
@ -4086,27 +4086,27 @@ dict_print_info_on_foreign_keys(
str.append(")");
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) {
if (foreign->type == foreign->DELETE_CASCADE) {
str.append(" ON DELETE CASCADE");
}
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) {
if (foreign->type == foreign->DELETE_SET_NULL) {
str.append(" ON DELETE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
if (foreign->type & foreign->DELETE_NO_ACTION) {
str.append(" ON DELETE NO ACTION");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
if (foreign->type & foreign->UPDATE_CASCADE) {
str.append(" ON UPDATE CASCADE");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
if (foreign->type & foreign->UPDATE_SET_NULL) {
str.append(" ON UPDATE SET NULL");
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
if (foreign->type & foreign->UPDATE_NO_ACTION) {
str.append(" ON UPDATE NO ACTION");
}
}

View File

@ -12524,13 +12524,13 @@ create_table_info_t::create_foreign_keys()
case FK_OPTION_RESTRICT:
break;
case FK_OPTION_CASCADE:
foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
foreign->type |= foreign->DELETE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
foreign->type |= foreign->DELETE_SET_NULL;
break;
case FK_OPTION_NO_ACTION:
foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
foreign->type |= foreign->DELETE_NO_ACTION;
break;
case FK_OPTION_SET_DEFAULT:
// TODO: MDEV-10393 Foreign keys SET DEFAULT action
@ -12545,13 +12545,13 @@ create_table_info_t::create_foreign_keys()
case FK_OPTION_RESTRICT:
break;
case FK_OPTION_CASCADE:
foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
foreign->type |= foreign->UPDATE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
foreign->type |= foreign->UPDATE_SET_NULL;
break;
case FK_OPTION_NO_ACTION:
foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
foreign->type |= foreign->UPDATE_NO_ACTION;
break;
case FK_OPTION_SET_DEFAULT:
// TODO: MDEV-10393 Foreign keys SET DEFAULT action
@ -15266,28 +15266,43 @@ get_foreign_key_info(
name = thd_make_lex_string(thd, name, ptr,
strlen(ptr), 1);
f_key_info.foreign_fields.push_back(name);
if (dict_index_t* fidx = foreign->foreign_index) {
if (fidx->fields[i].col->is_nullable()) {
f_key_info.set_nullable(thd, false, i,
foreign->n_fields);
}
}
ptr = foreign->referenced_col_names[i];
name = thd_make_lex_string(thd, name, ptr,
strlen(ptr), 1);
f_key_info.referenced_fields.push_back(name);
if (dict_index_t* ref_idx = foreign->referenced_index) {
if (ref_idx->fields[i].col->is_nullable()) {
f_key_info.set_nullable(thd, true, i,
foreign->n_fields);
}
}
} while (++i < foreign->n_fields);
if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
if (foreign->type & foreign->DELETE_CASCADE) {
f_key_info.delete_method = FK_OPTION_CASCADE;
} else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
} else if (foreign->type & foreign->DELETE_SET_NULL) {
f_key_info.delete_method = FK_OPTION_SET_NULL;
} else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
} else if (foreign->type & foreign->DELETE_NO_ACTION) {
f_key_info.delete_method = FK_OPTION_NO_ACTION;
} else {
f_key_info.delete_method = FK_OPTION_RESTRICT;
}
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
if (foreign->type & foreign->UPDATE_CASCADE) {
f_key_info.update_method = FK_OPTION_CASCADE;
} else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
} else if (foreign->type & foreign->UPDATE_SET_NULL) {
f_key_info.update_method = FK_OPTION_SET_NULL;
} else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
} else if (foreign->type & foreign->UPDATE_NO_ACTION) {
f_key_info.update_method = FK_OPTION_NO_ACTION;
} else {
f_key_info.update_method = FK_OPTION_RESTRICT;

View File

@ -2759,8 +2759,8 @@ innobase_check_fk_option(
return(true);
}
if (foreign->type & (DICT_FOREIGN_ON_UPDATE_SET_NULL
| DICT_FOREIGN_ON_DELETE_SET_NULL)) {
if (foreign->type & (foreign->UPDATE_SET_NULL
| foreign->DELETE_SET_NULL)) {
for (ulint j = 0; j < foreign->n_fields; j++) {
if ((dict_index_get_nth_col(
@ -2795,13 +2795,13 @@ innobase_set_foreign_key_option(
case FK_OPTION_NO_ACTION:
case FK_OPTION_RESTRICT:
case FK_OPTION_SET_DEFAULT:
foreign->type = DICT_FOREIGN_ON_DELETE_NO_ACTION;
foreign->type = foreign->DELETE_NO_ACTION;
break;
case FK_OPTION_CASCADE:
foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
foreign->type = foreign->DELETE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
foreign->type = foreign->DELETE_SET_NULL;
break;
case FK_OPTION_UNDEF:
break;
@ -2811,13 +2811,13 @@ innobase_set_foreign_key_option(
case FK_OPTION_NO_ACTION:
case FK_OPTION_RESTRICT:
case FK_OPTION_SET_DEFAULT:
foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
foreign->type |= foreign->UPDATE_NO_ACTION;
break;
case FK_OPTION_CASCADE:
foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
foreign->type |= foreign->UPDATE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
foreign->type |= foreign->UPDATE_SET_NULL;
break;
case FK_OPTION_UNDEF:
break;
@ -2968,8 +2968,8 @@ innobase_check_fk_stored(
{
ulint type = foreign->type;
type &= ~(DICT_FOREIGN_ON_DELETE_NO_ACTION
| DICT_FOREIGN_ON_UPDATE_NO_ACTION);
type &= ~(foreign->DELETE_NO_ACTION
| foreign->UPDATE_NO_ACTION);
if (type == 0 || s_cols == NULL) {
return(false);
@ -4220,103 +4220,157 @@ innobase_dropping_foreign(
return(false);
}
/** Determines if an InnoDB FOREIGN KEY constraint depends on a
column that is being dropped or modified to NOT NULL.
/** Determines if an InnoDB FOREIGN KEY constraint depends on
the nullability changes of a column.
Enforce the following rules:
i) Don't allow the referencing column from NULL TO NOT NULL when
1) Foreign key constraint type is ON UPDATE SET NULL
2) Foreign key constraint type is ON DELETE SET NULL
3) Foreign key constraint type is UPDATE CASCADE and referenced
column declared as NULL
ii) Don't allow the referenced column from NOT NULL to NULL when
foreign key constraint type is UPDATE CASCADE and referencing column
declared as NOT NULL
@param user_table InnoDB table as it is before the ALTER operation
@param col_name Name of the column being altered
@param drop_fk constraints being dropped
@param n_drop_fk number of constraints that are being dropped
@param drop true=drop column, false=set NOT NULL
@param col_name modified column name
@param new_field_flags Modified field flags
@retval true Not allowed (will call my_error())
@retval false Allowed
*/
MY_ATTRIBUTE((pure, nonnull(1,4), warn_unused_result))
static
bool
innobase_check_foreigns_low(
const dict_table_t* user_table,
dict_foreign_t** drop_fk,
ulint n_drop_fk,
const char* col_name,
bool drop)
bool check_foreigns_nullability(const dict_table_t *user_table,
dict_foreign_t **drop_fk, ulint n_drop_fk,
const char *col_name, uint32_t new_field_flags)
{
dict_foreign_t* foreign;
ut_ad(mutex_own(&dict_sys.mutex));
ut_ad(mutex_own(&dict_sys.mutex));
/* Check if any FOREIGN KEY constraints are defined on this
column. */
/* Changing from NULL to NOT NULL. So check referenced set */
if ((new_field_flags & NOT_NULL_FLAG))
{
for (dict_foreign_t *foreign : user_table->foreign_set)
{
if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk))
continue;
for (dict_foreign_set::const_iterator it = user_table->foreign_set.begin();
it != user_table->foreign_set.end();
++it) {
if (foreign->on_update_cascade_null(col_name))
goto non_null_error;
foreign = *it;
if (foreign->type & (foreign->DELETE_SET_NULL |
foreign->UPDATE_SET_NULL))
{
if (foreign->foreign_index
&& foreign->col_fk_exists(col_name) != UINT_MAX)
{
non_null_error:
const char* fid = strchr(foreign->id, '/');
fid= fid ? fid + 1 : foreign->id;
my_error(ER_FK_COLUMN_NOT_NULL, MYF(0), col_name, fid);
return true;
}
}
}
}
else
{
for (dict_foreign_t *foreign : user_table->referenced_set)
{
if (foreign->on_update_cascade_not_null(col_name))
{
char display_name[FN_REFLEN];
const int dblen= int(table_name_t(const_cast<char*>(foreign->
foreign_table_name)).dblen());
char tbl_name[MAX_TABLE_NAME_LEN];
uint errors;
ulint tbl_name_len= strlen(foreign->foreign_table_name) - dblen + 1;
strncpy(tbl_name, foreign->foreign_table_name + dblen + 1,
tbl_name_len);
tbl_name[tbl_name_len - 1]= '\0';
innobase_convert_to_system_charset(tbl_name,
strchr(foreign->foreign_table_name,
'/') + 1,
MAX_TABLE_NAME_LEN, &errors);
if (errors)
{
strncpy(tbl_name, foreign->foreign_table_name + dblen + 1,
tbl_name_len);
tbl_name[tbl_name_len - 1]= '\0';
}
if (!drop && !(foreign->type
& (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
continue;
}
my_snprintf(display_name, FN_REFLEN - 1, "%.*s.%s",
dblen, foreign->foreign_table_name, tbl_name);
if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
continue;
}
display_name[FN_REFLEN - 1]= '\0';
const char* fid = strchr(foreign->id, '/');
fid= fid ? fid + 1 : foreign->id;
my_error(ER_FK_COLUMN_CANNOT_CHANGE_CHILD, MYF(0), col_name,
fid, display_name);
return true;
}
}
}
for (unsigned f = 0; f < foreign->n_fields; f++) {
if (!strcmp(foreign->foreign_col_names[f],
col_name)) {
my_error(drop
? ER_FK_COLUMN_CANNOT_DROP
: ER_FK_COLUMN_NOT_NULL, MYF(0),
col_name, foreign->id);
return(true);
}
}
}
return false;
}
if (!drop) {
/* SET NULL clauses on foreign key constraints of
child tables affect the child tables, not the parent table.
The column can be NOT NULL in the parent table. */
return(false);
}
/** Determines if an InnoDB FOREIGN KEY constraint depends on
the column when it is being dropped.
@param user_table InnoDB table as it is before the ALTER operation
@param drop_fk constraints being dropped
@param n_drop_fk number of constraints that are being dropped
@param col_name column name to be dropped
@retval true Not allowed (will call my_error())
@retval false Allowed
*/
static
bool check_foreign_drop_col(const dict_table_t *user_table,
dict_foreign_t **drop_fk, ulint n_drop_fk,
const char *col_name)
{
ut_ad(mutex_own(&dict_sys.mutex));
/* Check if any FOREIGN KEY constraints in other tables are
referring to the column that is being dropped. */
for (dict_foreign_set::const_iterator it
= user_table->referenced_set.begin();
it != user_table->referenced_set.end();
++it) {
/* Check if any FOREIGN KEY constraints are defined on this column. */
for (dict_foreign_t *foreign : user_table->foreign_set)
{
if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk))
continue;
foreign = *it;
for (unsigned f = 0; f < foreign->n_fields; f++)
if (!strcmp(foreign->foreign_col_names[f], col_name))
{
my_error(ER_FK_COLUMN_CANNOT_DROP, MYF(0),
col_name, foreign->id);
return true;
}
}
if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk)) {
continue;
}
/* Check if any FOREIGN KEY constraints in other tables are
referring to the column that is being dropped. */
for (dict_foreign_t *foreign : user_table->referenced_set)
{
if (innobase_dropping_foreign(foreign, drop_fk, n_drop_fk))
continue;
for (unsigned f = 0; f < foreign->n_fields; f++) {
char display_name[FN_REFLEN];
if (strcmp(foreign->referenced_col_names[f],
col_name)) {
continue;
}
char* buf_end = innobase_convert_name(
display_name, (sizeof display_name) - 1,
foreign->foreign_table_name,
strlen(foreign->foreign_table_name),
NULL);
*buf_end = '\0';
my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD,
MYF(0), col_name, foreign->id,
display_name);
return(true);
}
}
return(false);
for (unsigned f = 0; f < foreign->n_fields; f++)
{
char display_name[FN_REFLEN];
if (strcmp(foreign->referenced_col_names[f], col_name))
continue;
char* buf_end = innobase_convert_name(
display_name, (sizeof display_name) - 1,
foreign->foreign_table_name,
strlen(foreign->foreign_table_name), NULL);
*buf_end = '\0';
my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD,
MYF(0), col_name, foreign->id, display_name);
return true;
}
}
return false;
}
/** Determines if an InnoDB FOREIGN KEY constraint depends on a
@ -4351,16 +4405,25 @@ innobase_check_foreigns(
return field.field == *fp;
});
if (it == end || (it->flags & NOT_NULL_FLAG)) {
if (innobase_check_foreigns_low(
user_table, drop_fk, n_drop_fk,
(*fp)->field_name.str, it == end)) {
return(true);
if (it == end) {
if (check_foreign_drop_col(
user_table, drop_fk, n_drop_fk,
(*fp)->field_name.str)) {
return true;
}
} else if ((it->flags & NOT_NULL_FLAG)
!= ((*fp)->flags & NOT_NULL_FLAG)) {
if (check_foreigns_nullability(user_table, drop_fk,
n_drop_fk,
(*fp)->field_name.str,
it->flags)) {
return true;
}
}
}
return(false);
return false;
}
/** Convert a default value for ADD COLUMN.
@ -9836,8 +9899,8 @@ innobase_update_foreign_try(
fk->foreign_col_names,
fk->n_fields, fk->referenced_index, TRUE,
fk->type
& (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL),
& (fk->DELETE_SET_NULL
| fk->UPDATE_SET_NULL),
NULL, NULL, NULL);
if (!fk->foreign_index) {
my_error(ER_FK_INCORRECT_OPTION,

View File

@ -1451,45 +1451,149 @@ typedef std::set<dict_v_col_t*, std::less<dict_v_col_t*>,
/** Data structure for a foreign key constraint; an example:
FOREIGN KEY (A, B) REFERENCES TABLE2 (C, D). Most fields will be
initialized to 0, NULL or FALSE in dict_mem_foreign_create(). */
struct dict_foreign_t{
mem_heap_t* heap; /*!< this object is allocated from
this memory heap */
char* id; /*!< id of the constraint as a
null-terminated string */
unsigned n_fields:10; /*!< number of indexes' first fields
for which the foreign key
constraint is defined: we allow the
indexes to contain more fields than
mentioned in the constraint, as long
as the first fields are as mentioned */
unsigned type:6; /*!< 0 or DICT_FOREIGN_ON_DELETE_CASCADE
or DICT_FOREIGN_ON_DELETE_SET_NULL */
char* foreign_table_name;/*!< foreign table name */
char* foreign_table_name_lookup;
/*!< foreign table name used for dict lookup */
dict_table_t* foreign_table; /*!< table where the foreign key is */
const char** foreign_col_names;/*!< names of the columns in the
foreign key */
char* referenced_table_name;/*!< referenced table name */
char* referenced_table_name_lookup;
/*!< referenced table name for dict lookup*/
dict_table_t* referenced_table;/*!< table where the referenced key
is */
const char** referenced_col_names;/*!< names of the referenced
columns in the referenced table */
dict_index_t* foreign_index; /*!< foreign index; we require that
both tables contain explicitly defined
indexes for the constraint: InnoDB
does not generate new indexes
implicitly */
dict_index_t* referenced_index;/*!< referenced index */
struct dict_foreign_t
{
/* Object is allocated from this memory heap */
mem_heap_t *heap;
/* id of the constraint as a null terminated string */
char *id;
/* number of indexes first fields for which the foreign key
constraint is defined: We allow the indexes to contain more
fields than mentioned in the constraint, as long as the first
fields are as mentioned */
unsigned n_fields:10;
/* 0 or DELETE_CASCADE OR DELETE_SET_NULL */
unsigned type:6;
/* foreign table name */
char *foreign_table_name;
/* Foreign table name used for dict lookup */
char *foreign_table_name_lookup;
/* table where the foreign key is */
dict_table_t *foreign_table;
/* names of the columns in the foreign key */
const char **foreign_col_names;
/* referenced table name */
char *referenced_table_name;
/* referenced table name for dict lookup */
char *referenced_table_name_lookup;
/* Table where the referenced key is */
dict_table_t *referenced_table;
/* Names of the referenced columns in the referenced table */
const char **referenced_col_names;
/* foreign index; we require that both tables contain explicitly
defined indexes for the constraint: InnoDB does not generate
new indexes implicitly */
dict_index_t *foreign_index;
/* referenced index */
dict_index_t *referenced_index;
/* set of virtual columns affected by foreign key constraint */
dict_vcol_set *v_cols;
/** Check whether the fulltext index gets affected by
foreign key constraint */
bool affects_fulltext() const;
/** The flags for ON_UPDATE and ON_DELETE can be ORed;
the default is that a foreign key constraint is enforced,
therefore RESTRICT just means no flag */
static constexpr unsigned DELETE_CASCADE= 1U;
static constexpr unsigned DELETE_SET_NULL= 2U;
static constexpr unsigned UPDATE_CASCADE= 4U;
static constexpr unsigned UPDATE_SET_NULL= 8U;
static constexpr unsigned DELETE_NO_ACTION= 16U;
static constexpr unsigned UPDATE_NO_ACTION= 32U;
private:
/** Check whether the name exists in given column names
@retval offset or UINT_MAX if name not found */
unsigned col_exists(const char *name, const char **names) const noexcept
{
for (unsigned i= 0; i < n_fields; i++)
{
if (!strcmp(names[i], name))
return i;
}
return UINT_MAX;
}
dict_vcol_set* v_cols; /*!< set of virtual columns affected
by foreign key constraint. */
public:
/** Check whether the name exists in the foreign key column names
@retval offset in case of success
@retval UINT_MAX in case of failure */
unsigned col_fk_exists(const char *name) const noexcept
{
return col_exists(name, foreign_col_names);
}
/** Check whether the fulltext index gets affected by
foreign key constraint */
bool affects_fulltext() const;
/** Check whether the name exists in the referenced
key column names
@retval offset in case of success
@retval UINT_MAX in case of failure */
unsigned col_ref_exists(const char *name) const noexcept
{
return col_exists(name, referenced_col_names);
}
/** Check whether the foreign key constraint depends on
the nullability of the referenced column to be modified
@param name column to be modified
@return true in case of no conflict or false */
bool on_update_cascade_not_null(const char *name) const noexcept
{
if (!foreign_index || type != UPDATE_CASCADE)
return false;
unsigned offset= col_ref_exists(name);
if (offset == UINT_MAX)
return false;
ut_ad(offset < n_fields);
return foreign_index->fields[offset].col->prtype & DATA_NOT_NULL;
}
/** Check whether the foreign key constraint depends on
the nullability of the foreign column to be modified
@param name column to be modified
@return true in case of no conflict or false */
bool on_update_cascade_null(const char *name) const noexcept
{
if (!referenced_index || type != UPDATE_CASCADE)
return false;
unsigned offset= col_fk_exists(name);
if (offset == UINT_MAX)
return false;
ut_ad(offset < n_fields);
return !(referenced_index->fields[offset].col->prtype & DATA_NOT_NULL);
}
/** This is called during CREATE TABLE statement
to check the foreign key nullability constraint
@return true if foreign key constraint is valid
or else false */
bool check_fk_constraint_valid()
{
if (!type || type & (DELETE_CASCADE | DELETE_NO_ACTION |
UPDATE_NO_ACTION))
return true;
if (!referenced_index)
return true;
for (unsigned i= 0; i < n_fields; i++)
{
dict_col_t *col = foreign_index->fields[i].col;
if (col->prtype & DATA_NOT_NULL)
{
/* Foreign type is ON DELETE SET NULL
or ON UPDATE SET NULL */
if (type & (DELETE_SET_NULL | UPDATE_SET_NULL))
return false;
dict_col_t *ref_col= referenced_index->fields[i].col;
/* Referenced index respective fields shouldn't be NULL */
if (!(ref_col->prtype & DATA_NOT_NULL))
return false;
}
}
return true;
}
};
std::ostream&
@ -1667,17 +1771,6 @@ struct dict_foreign_set_free {
const dict_foreign_set& m_foreign_set;
};
/** The flags for ON_UPDATE and ON_DELETE can be ORed; the default is that
a foreign key constraint is enforced, therefore RESTRICT just means no flag */
/* @{ */
#define DICT_FOREIGN_ON_DELETE_CASCADE 1U /*!< ON DELETE CASCADE */
#define DICT_FOREIGN_ON_DELETE_SET_NULL 2U /*!< ON UPDATE SET NULL */
#define DICT_FOREIGN_ON_UPDATE_CASCADE 4U /*!< ON DELETE CASCADE */
#define DICT_FOREIGN_ON_UPDATE_SET_NULL 8U /*!< ON UPDATE SET NULL */
#define DICT_FOREIGN_ON_DELETE_NO_ACTION 16U /*!< ON DELETE NO ACTION */
#define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32U /*!< ON UPDATE NO ACTION */
/* @} */
/** Display an identifier.
@param[in,out] s output stream
@param[in] id_name SQL identifier (other than table name)

View File

@ -903,10 +903,10 @@ row_ins_foreign_fill_virtual(
return DB_OUT_OF_MEMORY;
}
ut_ad(!node->is_delete
|| (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL));
ut_ad(foreign->type & (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_CASCADE));
|| (foreign->type & foreign->DELETE_SET_NULL));
ut_ad(foreign->type & (foreign->DELETE_SET_NULL
| foreign->UPDATE_SET_NULL
| foreign->UPDATE_CASCADE));
for (uint16_t i = 0; i < n_v_fld; i++) {
@ -1021,8 +1021,8 @@ row_ins_foreign_check_on_constraint(
node = static_cast<upd_node_t*>(thr->run_node);
if (node->is_delete && 0 == (foreign->type
& (DICT_FOREIGN_ON_DELETE_CASCADE
| DICT_FOREIGN_ON_DELETE_SET_NULL))) {
& (foreign->DELETE_CASCADE
| foreign->DELETE_SET_NULL))) {
row_ins_foreign_report_err("Trying to delete",
thr, foreign,
@ -1032,8 +1032,8 @@ row_ins_foreign_check_on_constraint(
}
if (!node->is_delete && 0 == (foreign->type
& (DICT_FOREIGN_ON_UPDATE_CASCADE
| DICT_FOREIGN_ON_UPDATE_SET_NULL))) {
& (foreign->UPDATE_CASCADE
| foreign->UPDATE_SET_NULL))) {
/* This is an UPDATE */
@ -1056,7 +1056,7 @@ row_ins_foreign_check_on_constraint(
cascade->foreign = foreign;
if (node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)) {
&& (foreign->type & foreign->DELETE_CASCADE)) {
cascade->is_delete = PLAIN_DELETE;
} else {
cascade->is_delete = NO_DELETE;
@ -1199,8 +1199,8 @@ row_ins_foreign_check_on_constraint(
}
if (node->is_delete
? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
: (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
? (foreign->type & foreign->DELETE_SET_NULL)
: (foreign->type & foreign->UPDATE_SET_NULL)) {
/* Build the appropriate update vector which sets
foreign->n_fields first fields in rec to SQL NULL */
@ -1245,12 +1245,12 @@ row_ins_foreign_check_on_constraint(
}
} else if (table->fts && cascade->is_delete == PLAIN_DELETE
&& foreign->affects_fulltext()) {
/* DICT_FOREIGN_ON_DELETE_CASCADE case */
/* dict_foreign_t::DELETE_CASCADE case */
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
if (!node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) {
&& (foreign->type & foreign->UPDATE_CASCADE)) {
/* Build the appropriate update vector which sets changing
foreign->n_fields first fields in rec to new values */