diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index dae3b56b584b9..4e770b5d931f0 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -125,16 +125,25 @@ FUNCTION(DTRACE_INSTRUMENT target) WORKING_DIRECTORY ${objdir} ) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux") - # dtrace on Linux runs gcc and uses flags from environment - SET(CFLAGS_SAVED $ENV{CFLAGS}) - SET(ENV{CFLAGS} ${CMAKE_C_FLAGS}) + IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # dtrace on Linux runs gcc and uses flags from environment + SET(CFLAGS_SAVED $ENV{CFLAGS}) + # We want to strip off all warning flags, including -Werror=xxx-xx, + # but keep flags like + # -Wp,-D_FORTIFY_SOURCE=2 + STRING(REGEX REPLACE "-W[A-Za-z0-9][-A-Za-z0-9=]+" "" + C_FLAGS "${CMAKE_C_FLAGS}") + SET(ENV{CFLAGS} ${CMAKE_C_FLAGS}) + ENDIF() SET(outfile "${CMAKE_BINARY_DIR}/probes_mysql.o") # Systemtap object EXECUTE_PROCESS( COMMAND ${DTRACE} -G -s ${CMAKE_SOURCE_DIR}/include/probes_mysql.d.base -o ${outfile} ) - SET(ENV{CFLAGS} ${CFLAGS_SAVED}) + IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + SET(ENV{CFLAGS} ${CFLAGS_SAVED}) + ENDIF() ENDIF() # Do not try to extend the library if we have not built the .o file diff --git a/mysql-test/main/partition.result b/mysql-test/main/partition.result index 8c28b6d9d70bd..2eef22ab4fd19 100644 --- a/mysql-test/main/partition.result +++ b/mysql-test/main/partition.result @@ -307,7 +307,14 @@ drop table t1; CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); -ERROR HY000: Partitioned tables do not support FOREIGN KEY +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + KEY `a` (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci + PARTITION BY HASH (`a`) +drop table t1; CREATE TABLE t1 ( pk INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (pk) diff --git a/mysql-test/main/partition.test b/mysql-test/main/partition.test index 6d211e332b64c..1048fc16fb883 100644 --- a/mysql-test/main/partition.test +++ b/mysql-test/main/partition.test @@ -292,10 +292,11 @@ drop table t1; # # Bug#36001: Partitions: spelling and using some error messages # ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); +show create table t1; +drop table t1; # # Bug#40954: Crash if range search and order by. diff --git a/mysql-test/main/partition_innodb.result b/mysql-test/main/partition_innodb.result index 4a146b92a8088..dc995a8104063 100644 --- a/mysql-test/main/partition_innodb.result +++ b/mysql-test/main/partition_innodb.result @@ -245,10 +245,9 @@ KEY parent_id (parent_id) ) ENGINE=InnoDB; ALTER TABLE t1 PARTITION BY HASH (id) PARTITIONS 1; ALTER TABLE t1 ADD CONSTRAINT test_ibfk_1 FOREIGN KEY (parent_id) REFERENCES t2 (id); -ERROR HY000: Partitioned tables do not support FOREIGN KEY ALTER TABLE t1 PARTITION BY HASH (id) PARTITIONS 2; ALTER TABLE t1 ADD CONSTRAINT test_ibfk_1 FOREIGN KEY (parent_id) REFERENCES t2 (id); -ERROR HY000: Partitioned tables do not support FOREIGN KEY +ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed") DROP TABLE t1, t2; create table t1 (a varchar(5), b int signed, c varchar(10), d datetime) partition by range columns(b,c) diff --git a/mysql-test/main/partition_innodb.test b/mysql-test/main/partition_innodb.test index 330155da72143..839f1c0955e61 100644 --- a/mysql-test/main/partition_innodb.test +++ b/mysql-test/main/partition_innodb.test @@ -237,12 +237,11 @@ CREATE TABLE t1 ( ALTER TABLE t1 PARTITION BY HASH (id) PARTITIONS 1; ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING ALTER TABLE t1 ADD CONSTRAINT test_ibfk_1 FOREIGN KEY (parent_id) REFERENCES t2 (id); ALTER TABLE t1 PARTITION BY HASH (id) PARTITIONS 2; ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING +--error ER_CANT_CREATE_TABLE ALTER TABLE t1 ADD CONSTRAINT test_ibfk_1 FOREIGN KEY (parent_id) REFERENCES t2 (id); DROP TABLE t1, t2; diff --git a/mysql-test/suite/binlog/r/start_alter_mysqlbinlog_replay.result b/mysql-test/suite/binlog/r/start_alter_mysqlbinlog_replay.result index 14bc2d3bf7818..4fe28ddffd4b7 100644 --- a/mysql-test/suite/binlog/r/start_alter_mysqlbinlog_replay.result +++ b/mysql-test/suite/binlog/r/start_alter_mysqlbinlog_replay.result @@ -11,7 +11,7 @@ set binlog_alter_two_phase = ON; create table t1 (f1 int primary key) engine=InnoDB; create table t2 (f1 int primary key, constraint c1 foreign key (f1) references t1(f1)) engine=innodb; alter table t2 add constraint c1 foreign key (f1) references t1(f1); -ERROR HY000: Can't create table `test`.`t2` (errno: 121 "Duplicate key on write or update") +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") drop table t2, t1; select @@gtid_binlog_state; @@gtid_binlog_state diff --git a/mysql-test/suite/innodb/r/add_constraint.result b/mysql-test/suite/innodb/r/add_constraint.result index 9d894e530c38c..5c7aec84da413 100644 --- a/mysql-test/suite/innodb/r/add_constraint.result +++ b/mysql-test/suite/innodb/r/add_constraint.result @@ -6,7 +6,7 @@ create table t2(a int, b int, key(a),key(b))engine=innodb; alter table t2 add constraint b foreign key (b) references t1(a); alter table t1 add constraint b1 foreign key (b) references t2(a); alter table t2 add constraint b1 foreign key (b) references t1(a); -ERROR HY000: Can't create table `test`.`t2` (errno: 121 "Duplicate key on write or update") +ERROR HY000: Duplicate constraint name alter table t2 drop foreign key b; alter table t1 drop foreign key b1; drop table t2; diff --git a/mysql-test/suite/innodb/r/alter_copy_bulk.result b/mysql-test/suite/innodb/r/alter_copy_bulk.result index cd0776876d417..05505009fb185 100644 --- a/mysql-test/suite/innodb/r/alter_copy_bulk.result +++ b/mysql-test/suite/innodb/r/alter_copy_bulk.result @@ -47,8 +47,7 @@ ALTER TABLE t1 ALGORITHM=COPY, FORCE; affected rows: 2 info: Records: 2 Duplicates: 0 Warnings: 0 ALTER TABLE t2 ALGORITHM=COPY, FORCE; -affected rows: 1 -info: Records: 1 Duplicates: 0 Warnings: 0 +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`#sql-alter`, CONSTRAINT `) DROP TABLE t2, t1; # # MDEV-35237 Bulk insert fails to apply buffered diff --git a/mysql-test/suite/innodb/r/alter_partitioned.result b/mysql-test/suite/innodb/r/alter_partitioned.result index 2e385b4b4d49a..239cf252101d4 100644 --- a/mysql-test/suite/innodb/r/alter_partitioned.result +++ b/mysql-test/suite/innodb/r/alter_partitioned.result @@ -43,7 +43,7 @@ CREATE TABLE t2(a INT, FOREIGN KEY(a) REFERENCES t1(a))ENGINE=INNODB PARTITION BY RANGE(a) (PARTITION pa VALUES LESS THAN (2), PARTITION pb VALUES LESS THAN (4)); -ERROR HY000: Partitioned tables do not support FOREIGN KEY +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") DROP TABLE t1; # End of 10.3 tests # diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index 65edeb4b9c316..7263ee4a2803f 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -11,7 +11,7 @@ ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint create table t2 (f1 int primary key, constraint c1 foreign key (f1) references t1(f1)) engine=innodb; alter table t2 add constraint c1 foreign key (f1) references t1(f1); -ERROR HY000: Can't create table `test`.`t2` (errno: 121 "Duplicate key on write or update") +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") set foreign_key_checks = 0; alter table t2 add constraint c1 foreign key (f1) references t1(f1); ERROR HY000: Duplicate FOREIGN KEY constraint name 'test/c1' @@ -210,7 +210,7 @@ CONSTRAINT t2_ibfk_1 FOREIGN KEY (a) REFERENCES t1(a)) ENGINE=InnoDB; CREATE TABLE best.t2 (a INT PRIMARY KEY, b TEXT, FULLTEXT INDEX(b), FOREIGN KEY (a) REFERENCES test.t1(a)) ENGINE=InnoDB; RENAME TABLE best.t2 TO test.t2; -ERROR 42S01: Table 't2' already exists +ERROR HY000: Duplicate constraint name SHOW CREATE TABLE best.t2; Table Create Table t2 CREATE TABLE `t2` ( diff --git a/mysql-test/suite/innodb/r/foreign_key_not_windows.result b/mysql-test/suite/innodb/r/foreign_key_not_windows.result index aaff06f8d6866..0a79b854da9c7 100644 --- a/mysql-test/suite/innodb/r/foreign_key_not_windows.result +++ b/mysql-test/suite/innodb/r/foreign_key_not_windows.result @@ -6,11 +6,20 @@ CREATE DATABASE `d255`; CREATE TABLE `d255`.`d255` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@ +CREATE OR REPLACE TABLE `d255`.`d255` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; +ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@ CREATE TABLE `d255`.`_##################################################` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/_@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023 +CREATE OR REPLACE TABLE `d255`.`_##################################################` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; +ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/_@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023 CREATE TABLE `d255`.`##################################################` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; +CREATE OR REPLACE TABLE `d255`.`d245` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; +DROP TABLE `d255`.`d245`; # # MDEV-29258 Failing assertion for name length on RENAME TABLE # @@ -29,3 +38,37 @@ RENAME TABLE `d255`.u TO u; DROP TABLE u; DROP DATABASE `d255`; # End of 10.3 tests +# +# MDEV-28933 CREATE OR REPLACE fails to recreate same constraint name +# +set names utf8; +create database `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎`; +use `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎`; +create table t (a int primary key) engine=innodb; +create table u ( +a int primary key, +constraint `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎` foreign key d (a) references t (a)) engine=innodb; +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 @274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 @274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 1 0 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 a a 0 +create or replace table u ( +a int primary key, +constraint `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎` foreign key d (a) references t (a)) engine=innodb; +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 @274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 @274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 1 0 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@274e@27 a a 0 +show create table u; +Table Create Table +u CREATE TABLE `u` ( + `a` int(11) NOT NULL, + PRIMARY KEY (`a`), + CONSTRAINT `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎` FOREIGN KEY (`a`) REFERENCES `t` (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +use test; +drop database `❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎❎`; diff --git a/mysql-test/suite/innodb/r/foreign_null,COPY.rdiff b/mysql-test/suite/innodb/r/foreign_null,COPY.rdiff index e39d5c0e9db29..74c1d28925c07 100644 --- a/mysql-test/suite/innodb/r/foreign_null,COPY.rdiff +++ b/mysql-test/suite/innodb/r/foreign_null,COPY.rdiff @@ -1,5 +1,5 @@ --- foreign_null.result -+++ foreign_null,COPY.result ++++ foreign_null,COPY.reject @@ -139,6 +139,7 @@ ALTER TABLE `t#2` DROP INDEX f1; SET FOREIGN_KEY_CHECKS=1; @@ -12,7 +12,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") ++ERROR HY000: Can't create table `test`.`t#2` (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; diff --git a/mysql-test/suite/innodb/r/innodb-index.result b/mysql-test/suite/innodb/r/innodb-index.result index 98f711b314a92..9fcc0ef411787 100644 --- a/mysql-test/suite/innodb/r/innodb-index.result +++ b/mysql-test/suite/innodb/r/innodb-index.result @@ -509,7 +509,7 @@ t4 CREATE TABLE `t4` ( CONSTRAINT `dc` FOREIGN KEY (`a`) REFERENCES `t1` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci alter table t3 add constraint dc foreign key (a) references t1(a); -ERROR HY000: Can't create table `test`.`t3` (errno: 121 "Duplicate key on write or update") +ERROR HY000: Duplicate constraint name SET FOREIGN_KEY_CHECKS=0; alter table t3 add constraint dc foreign key (a) references t1(a); ERROR HY000: Failed to add the foreign key constraint 'test/dc' to system tables diff --git a/mysql-test/suite/innodb/t/add_constraint.test b/mysql-test/suite/innodb/t/add_constraint.test index f43f2977020bc..7dc705a7a04cf 100644 --- a/mysql-test/suite/innodb/t/add_constraint.test +++ b/mysql-test/suite/innodb/t/add_constraint.test @@ -10,7 +10,7 @@ create table t2(a int, b int, key(a),key(b))engine=innodb; alter table t2 add constraint b foreign key (b) references t1(a); alter table t1 add constraint b1 foreign key (b) references t2(a); ---error ER_CANT_CREATE_TABLE +--error ER_DUP_CONSTRAINT_NAME_2 alter table t2 add constraint b1 foreign key (b) references t1(a); alter table t2 drop foreign key b; diff --git a/mysql-test/suite/innodb/t/alter_copy_bulk.test b/mysql-test/suite/innodb/t/alter_copy_bulk.test index b96aba7111e89..ae1a178a6c881 100644 --- a/mysql-test/suite/innodb/t/alter_copy_bulk.test +++ b/mysql-test/suite/innodb/t/alter_copy_bulk.test @@ -65,6 +65,8 @@ INSERT INTO t1 VALUES(3, 1); SET STATEMENT foreign_key_checks=0 FOR ALTER TABLE t2 ALGORITHM=COPY, ADD CONSTRAINT FOREIGN KEY(f2) REFERENCES t1(f1); ALTER TABLE t1 ALGORITHM=COPY, FORCE; +--replace_regex /#sql-alter-[0-9a-f-]*/#sql-alter/ +--error ER_NO_REFERENCED_ROW_2 ALTER TABLE t2 ALGORITHM=COPY, FORCE; --disable_info DROP TABLE t2, t1; diff --git a/mysql-test/suite/innodb/t/alter_partitioned.test b/mysql-test/suite/innodb/t/alter_partitioned.test index 8c6adbbb20fa5..93fd3cf571244 100644 --- a/mysql-test/suite/innodb/t/alter_partitioned.test +++ b/mysql-test/suite/innodb/t/alter_partitioned.test @@ -46,7 +46,7 @@ PARTITION BY RANGE(a) (PARTITION pa VALUES LESS THAN (3), PARTITION pb VALUES LESS THAN (5)); ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING +--error ER_CANT_CREATE_TABLE CREATE TABLE t2(a INT, FOREIGN KEY(a) REFERENCES t1(a))ENGINE=INNODB PARTITION BY RANGE(a) (PARTITION pa VALUES LESS THAN (2), diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index 8c6ce939babc4..0ec429f39acc2 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -195,7 +195,7 @@ CONSTRAINT t2_ibfk_1 FOREIGN KEY (a) REFERENCES t1(a)) ENGINE=InnoDB; CREATE TABLE best.t2 (a INT PRIMARY KEY, b TEXT, FULLTEXT INDEX(b), FOREIGN KEY (a) REFERENCES test.t1(a)) ENGINE=InnoDB; --replace_regex /Table '.*t2'/Table 't2'/ ---error ER_TABLE_EXISTS_ERROR +--error ER_DUP_CONSTRAINT_NAME_2 RENAME TABLE best.t2 TO test.t2; SHOW CREATE TABLE best.t2; DROP DATABASE best; @@ -805,6 +805,10 @@ CONSTRAINT `t2_ibfk_2` FOREIGN KEY (f1) REFERENCES t1(f1) ON DELETE CASCADE SET FOREIGN_KEY_CHECKS=0; ALTER TABLE t2 DROP PRIMARY KEY, ADD PRIMARY KEY(f3), ALGORITHM=INPLACE; +--disable_query_log +# This warning was missing and it appears due to ALTER creating and renaming FKs for backup/tmp tables +call mtr.add_suppression("InnoDB: In ALTER TABLE `test`.`t1` has or is referenced in foreign key.*"); +--enable_query_log ALTER TABLE t2 DROP INDEX `f2`, ALGORITHM=COPY; SHOW CREATE TABLE t2; --error ER_TABLE_EXISTS_ERROR diff --git a/mysql-test/suite/innodb/t/foreign_key_not_windows.test b/mysql-test/suite/innodb/t/foreign_key_not_windows.test index e5f42a0ddab03..67e8935c66001 100644 --- a/mysql-test/suite/innodb/t/foreign_key_not_windows.test +++ b/mysql-test/suite/innodb/t/foreign_key_not_windows.test @@ -25,20 +25,43 @@ CREATE TABLE t (a INT PRIMARY KEY) ENGINE=InnoDB; # corresponding to the 51 characters below: 5*51=255. let $d255=###################################################; let $d250=##################################################; +let $d245=#####################; +# FIXME: MDEV-29258 +# let $d245=#################################################; --replace_result $d255 d255 eval CREATE DATABASE `$d255`; + --replace_result $d255 d255 --error ER_IDENT_CAUSES_TOO_LONG_PATH eval CREATE TABLE `$d255`.`$d255` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; + +--replace_result $d255 d255 +--error ER_IDENT_CAUSES_TOO_LONG_PATH +eval CREATE OR REPLACE TABLE `$d255`.`$d255` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; + --replace_result $d255 d255 --error ER_IDENT_CAUSES_TOO_LONG_PATH eval CREATE TABLE `$d255`.`_$d250` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; + +--replace_result $d255 d255 +--error ER_IDENT_CAUSES_TOO_LONG_PATH +eval CREATE OR REPLACE TABLE `$d255`.`_$d250` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; + --replace_result $d255 d255 eval CREATE TABLE `$d255`.`$d250` (a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; +--replace_result $d255 d255 $d245 d245 +eval CREATE OR REPLACE TABLE `$d255`.`$d245` +(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; + +--replace_result $d255 d255 $d245 d245 +eval DROP TABLE `$d255`.`$d245`; + --echo # --echo # MDEV-29258 Failing assertion for name length on RENAME TABLE --echo # @@ -53,7 +76,6 @@ eval DROP TABLE `$d255`.`$d250`; eval RENAME TABLE `$d255`.`$d245` TO `$d255`.`$d250`; --replace_result $d250 d250 $d255 d255 eval RENAME TABLE `$d255`.`$d250` TO a; ---replace_result $d255 d255 DROP TABLE a,t; --echo # @@ -75,3 +97,26 @@ DROP TABLE u; eval DROP DATABASE `$d255`; --echo # End of 10.3 tests + +--echo # +--echo # MDEV-28933 CREATE OR REPLACE fails to recreate same constraint name +--echo # +set names utf8; +let $d= `select repeat('❎', 45)`; +let $t= `select repeat('❎', 64)`; +eval create database `$d`; +eval use `$d`; +create table t (a int primary key) engine=innodb; +eval create table u ( + a int primary key, + constraint `$t` foreign key d (a) references t (a)) engine=innodb; +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; +eval create or replace table u ( + a int primary key, + constraint `$t` foreign key d (a) references t (a)) engine=innodb; +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; +show create table u; +use test; +eval drop database `$d`; diff --git a/mysql-test/suite/innodb/t/foreign_null.test b/mysql-test/suite/innodb/t/foreign_null.test index 6869154fc9f4c..7cfb314e21842 100644 --- a/mysql-test/suite/innodb/t/foreign_null.test +++ b/mysql-test/suite/innodb/t/foreign_null.test @@ -185,9 +185,11 @@ eval ALTER TABLE `t#2` DROP INDEX f1,ALGORITHM=$algorithm; SET FOREIGN_KEY_CHECKS=1; let $error_code=0; +let $error_code2=0; if ($algorithm == "COPY") { let $error_code= ER_ERROR_ON_RENAME; + let $error_code2= ER_CANT_CREATE_TABLE; } --replace_regex /#sql-alter-[0-9a-f_\-]*/#sql-alter/ @@ -207,7 +209,7 @@ 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 +--error $error_code2 eval ALTER TABLE `t#2` MODIFY COLUMN f1 INT NOT NULL,ALGORITHM=$algorithm; DROP TABLE `t#2`, `t#1`; diff --git a/mysql-test/suite/innodb/t/innodb-index.test b/mysql-test/suite/innodb/t/innodb-index.test index eb5a2c337389b..9a858d8a42d68 100644 --- a/mysql-test/suite/innodb/t/innodb-index.test +++ b/mysql-test/suite/innodb/t/innodb-index.test @@ -221,7 +221,7 @@ show create table t4; # Embedded server doesn't chdir to data directory --replace_result $MYSQLD_DATADIR ./ master-data/ '' # a foreign key 'test/dc' already exists ---error ER_CANT_CREATE_TABLE +--error ER_DUP_CONSTRAINT_NAME_2 alter table t3 add constraint dc foreign key (a) references t1(a); SET FOREIGN_KEY_CHECKS=0; --error ER_FK_FAIL_ADD_SYSTEM diff --git a/mysql-test/suite/parts/r/alter_table.result b/mysql-test/suite/parts/r/alter_table.result index 24fbd6b803155..865ab06ec1945 100644 --- a/mysql-test/suite/parts/r/alter_table.result +++ b/mysql-test/suite/parts/r/alter_table.result @@ -538,7 +538,7 @@ partition p1 values in (12, 13, 14), partition p2 values in (52, 53, 54)); insert tp values (12), (2), (3), (4); alter table tp exchange partition p0 with table t; -ERROR HY000: Table has no partition for value 0 +ERROR HY000: Table has no partition for value 1 alter table tp exchange partition p0 with table t without validation; FOUND 8 /Table `test`.`tp` was altered WITHOUT VALIDATION: the table might be corrupted/ in mysqld.1.err select * from t; diff --git a/mysql-test/suite/parts/r/foreign.result b/mysql-test/suite/parts/r/foreign.result new file mode 100644 index 0000000000000..36e64874c9413 --- /dev/null +++ b/mysql-test/suite/parts/r/foreign.result @@ -0,0 +1,621 @@ +# +# MDEV-19191 Partial support of foreign keys in partitioned tables +# +create or replace table t1 (id int primary key) +with system versioning engine innodb +partition by system_time partitions 2; +# Referenced table cannot be partitioned still until MDEV-12483 +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t1 (id int primary key) +with system versioning engine innodb; +# CASCADE in partitioned table is prohibited until MDEV-12483 +create or replace table t2 ( +fk int, +foreign key(fk) references t1(id) +on delete cascade) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 ( +fk int references t1(id) +on update cascade) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 ( +fk int references t1(id) +on delete cascade) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 ( +fk int references t1(id) +on update set null) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 ( +fk int references t1(id) +on delete set null) +with system versioning engine innodb +partition by system_time partitions 2; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +create or replace table t2 ( +fk int, +foreign key(fk) references t1(id) +on delete cascade) engine innodb; +alter table t2 partition by hash(fk) partitions 5; +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") +# Subpartition by SYSTEM_TIME is prohibited at parser layer +create or replace table t2 (i int) with system versioning +partition by system_time interval 1 day +subpartition by system_time interval 2 hours +(partition p1 history, partition pn current); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'system_time interval 2 hours +(partition p1 history, partition pn current)' at line 3 +# Partititon by SYSTEM_TIME, subpartition by KEY +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb +partition by system_time limit 1 auto +subpartition by key (fk) subpartitions 2 +(partition p1 history, partition pn current); +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `fk` int(11) DEFAULT NULL, + KEY `fk` (`fk`), + CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME LIMIT 1 AUTO +SUBPARTITION BY KEY (`fk`) +SUBPARTITIONS 2 +(PARTITION `p1` HISTORY ENGINE = InnoDB, + PARTITION `pn` CURRENT ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/t2_ibfk_1:pnsp0 test/t2#P#pn#SP#pnsp0 test/t1 1 0 +test/t2_ibfk_1:pnsp1 test/t2#P#pn#SP#pnsp1 test/t1 1 0 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +test/t2_ibfk_1:pnsp0 fk id 0 +test/t2_ibfk_1:pnsp1 fk id 0 +insert into t1 values (1), (2), (3), (4), (5), (6); +insert into t2 values (3), (4), (5); +select * from t2 partition (pnsp0) order by fk; +fk +3 +5 +select * from t2 partition (pnsp1) order by fk; +fk +4 +delete from t1 where id = 3; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp0` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 4; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp1` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 5; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp0` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (7); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp0` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 1; +delete from t1 where id > 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp0` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +# Partition auto-creation does not affect foreign keys in current partition +delete from t2 where fk = 3; +update t2 set fk= fk - 1; +delete from t2 where fk = 3; +update t2 set fk= fk - 1; +delete from t2 where fk = 3; +select * from t2 for system_time all order by fk; +fk +3 +3 +3 +4 +4 +5 +delete from t1 where id > 2; +insert into t2 values (3), (5); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2` /* Partition `pn`, Subpartition `pnsp0` */, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (2); +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `fk` int(11) DEFAULT NULL, + KEY `fk` (`fk`), + CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci WITH SYSTEM VERSIONING + PARTITION BY SYSTEM_TIME LIMIT 1 AUTO +SUBPARTITION BY KEY (`fk`) +SUBPARTITIONS 2 +(PARTITION `p1` HISTORY ENGINE = InnoDB, + PARTITION `p2` HISTORY ENGINE = InnoDB, + PARTITION `p3` HISTORY ENGINE = InnoDB, + PARTITION `pn` CURRENT ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/t2_ibfk_1:pnsp0 test/t2#P#pn#SP#pnsp0 test/t1 1 0 +test/t2_ibfk_1:pnsp1 test/t2#P#pn#SP#pnsp1 test/t1 1 0 +# Partition by HASH, explicit constraint id +insert into t1 values (3), (4), (5), (6); +create or replace table t2 ( +fk int, +foreign key fk01 (fk) references t1(id)) +engine innodb +partition by hash (fk) partitions 2; +insert into t2 values (3), (4), (5); +select * from t2 partition (p0) order by fk; +fk +4 +select * from t2 partition (p1) order by fk; +fk +3 +5 +delete from t1 where id = 3; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 4; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `p0` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 5; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (7); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 1; +delete from t1 where id > 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t2; +select * from t2 order by fk; +fk +delete from t1 where id > 2; +insert into t2 values (3), (5); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (2); +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01:p0 test/t2#P#p0 test/t1 1 0 +test/fk01:p1 test/t2#P#p1 test/t1 1 0 +insert into t2 values (4); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2` /* Partition `p0` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +# Explicit constraint id via CONSTRAINT if that matters +create or replace table t3 ( +fk int, +constraint fk02 foreign key(fk) references t1(id)) +engine innodb +partition by range columns (fk) +subpartition by hash (fk) subpartitions 2 ( +partition p0 values less than (100), +partition p1 values less than (1000)); +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `fk` int(11) DEFAULT NULL, + KEY `fk01` (`fk`), + CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY HASH (`fk`) +PARTITIONS 2 +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p0` VALUES LESS THAN (100) ENGINE = InnoDB, + PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +# Same constraint id in non-partitioned +create table t4 (fk int, foreign key fk02 (fk) references t1(id)) engine innodb; +create table t5 (fk int, foreign key fk02 (fk) references t1(id)) engine innodb; +ERROR HY000: Can't create table `test`.`t5` (errno: 121 "Duplicate key on write or update") +drop table t4; +insert into t3 values (10); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t1 values (10); +insert into t3 values (10); +delete from t1 where id = 10; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01:p0 test/t2#P#p0 test/t1 1 0 +test/fk01:p1 test/t2#P#p1 test/t1 1 0 +test/fk02:p0sp0 test/t3#P#p0#SP#p0sp0 test/t1 1 0 +test/fk02:p0sp1 test/t3#P#p0#SP#p0sp1 test/t1 1 0 +test/fk02:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +test/fk01:p0 fk id 0 +test/fk01:p1 fk id 0 +test/fk02:p0sp0 fk id 0 +test/fk02:p0sp1 fk id 0 +test/fk02:p1sp0 fk id 0 +test/fk02:p1sp1 fk id 0 +### Alter operations ### +# get_parent_foreign_key_list() +set session foreign_key_checks= off; +set system_versioning_alter_history= keep; +alter table t1 change column id id time not null; +ERROR HY000: Cannot change column 'id': used in a foreign key constraint 'fk01' of table 'test.t2' +alter table t1 change column id b time; +ERROR 0A000: ALGORITHM=COPY is not supported. Reason: Columns participating in a foreign key are renamed. Try ALGORITHM=INPLACE +set session foreign_key_checks= on; +# RENAME TABLE +rename table t3 to t4; +show create table t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p0` VALUES LESS THAN (100) ENGINE = InnoDB, + PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +insert into t4 values (11); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t4` /* Partition `p0`, Subpartition `p0sp1` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 10; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t4` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t4%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk02:p0sp0 test/t4#P#p0#SP#p0sp0 test/t1 1 0 +test/fk02:p0sp1 test/t4#P#p0#SP#p0sp1 test/t1 1 0 +test/fk02:p1sp0 test/t4#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t4#P#p1#SP#p1sp1 test/t1 1 0 +alter table t4 rename to t3; +insert into t3 values (11); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp1` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 10; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +# REMOVE PARTITIONING +alter table t2 remove partitioning; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `fk` int(11) DEFAULT NULL, + KEY `fk01` (`fk`), + CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (4); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert into t2 values (10); +delete from t2; +# DROP PARTITION +delete from t1 where id = 10; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +alter table t3 drop partition p0; +delete from t1 where id = 10; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01 test/t2 test/t1 1 0 +test/fk02:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +test/fk01 fk id 0 +test/fk02:p1sp0 fk id 0 +test/fk02:p1sp1 fk id 0 +# Add range partition into beginning is not possible (MDEV-31212) +alter table t3 add partition (partition p0 values less than (1)); +ERROR HY000: VALUES LESS THAN value must be strictly increasing for each partition +# REORGANIZE PARTITION +select * from t1; +id +2 +insert t3 values (3); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t3` /* Partition `p1`, Subpartition `p1sp1` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert t3 values (2); +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1`, Subpartition `p1sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +alter table t3 reorganize partition p1 into ( +partition p0 values less than (10), +partition p1 values less than (1000)); +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p0` VALUES LESS THAN (10) ENGINE = InnoDB, + PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk02:p0sp0 test/t3#P#p0#SP#p0sp0 test/t1 1 0 +test/fk02:p0sp1 test/t3#P#p0#SP#p0sp1 test/t1 1 0 +test/fk02:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0`, Subpartition `p0sp0` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +alter table t3 drop partition p0; +delete from t1 where id = 2; +# Add/drop foreign keys +alter table t3 add foreign key fk03(fk) references t1(id) on delete cascade; +ERROR HY000: Can't create table `test`.`t3` (errno: 150 "Foreign key constraint is incorrectly formed") +alter table t3 add foreign key fk03(fk) references t1(id), algorithm=copy; +alter table t3 add foreign key fk04(fk) references t1(id), algorithm=instant; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Adding foreign keys needs foreign_key_checks=OFF. Try ALGORITHM=COPY +set session foreign_key_checks= off; +alter table t3 add foreign key fk04(fk) references t1(id), algorithm=instant; +alter table t3 add foreign key (fk) references t1(id), algorithm=instant; +set session foreign_key_checks= on; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`), + CONSTRAINT `fk04` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`), + CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk02:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +test/fk03:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk03:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +test/fk04:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk04:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +test/t3_ibfk_1:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/t3_ibfk_1:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +alter table t3 drop foreign key fk03, algorithm=copy; +alter table t3 drop foreign key fk04, algorithm=instant; +alter table t3 drop foreign key t3_ibfk_1, algorithm=instant; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +SUBPARTITION BY HASH (`fk`) +SUBPARTITIONS 2 +(PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk02:p1sp0 test/t3#P#p1#SP#p1sp0 test/t1 1 0 +test/fk02:p1sp1 test/t3#P#p1#SP#p1sp1 test/t1 1 0 +# CONVERT OUT +insert t1 values (1), (2), (300); +create or replace table t3 ( +fk int, +foreign key fk02(fk) references t1(id)) +engine innodb +partition by range columns (fk) ( +partition p0 values less than (100), +partition p1 values less than (1000)); +insert t3 values (2), (300); +alter table t3 convert partition p0 to table tp0; +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01 test/t2 test/t1 1 0 +test/fk02 test/tp0 test/t1 1 0 +test/fk02:p1 test/t3#P#p1 test/t1 1 0 +show create table tp0; +Table Create Table +tp0 CREATE TABLE `tp0` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +select * from t3; +fk +300 +select * from tp0; +fk +2 +delete from t1 where id = 1; +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`tp0`, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 300; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1` */, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +# TRUNCATE +truncate t3; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk02` (`fk`), + CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY RANGE COLUMNS(`fk`) +(PARTITION `p1` VALUES LESS THAN (1000) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk02:p1 test/t3#P#p1 test/t1 1 0 +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`tp0`, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 300; +# PARTITION BY LIST, engine change, add partitioning, add partitition, drop partition +create or replace table t3 (fk int) engine myisam; +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +alter table t3 add foreign key fk03(fk) references t1(id); +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +alter table t3 engine innodb; +alter table t3 add foreign key fk03(fk) references t1(id); +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +alter table t3 partition by list (fk) ( +partition p1 values in (4, 5, 6, 7)); +alter table t3 engine myisam; +alter table t3 engine innodb; +alter table t3 add foreign key fk03(fk) references t1(id); +alter table t1 drop system versioning; +alter table t1 engine myisam; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails +insert t1 values (0), (4); +insert t3 values (4); +delete from t1 where id = 4; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1` */, CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert t3 values (0); +ERROR HY000: Table has no partition for value 0 +alter table t3 add partition (partition p0 values in (0, 1, 2, 3)); +insert t3 values (0); +delete from t1 where id = 0; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0` */, CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY LIST (`fk`) +(PARTITION `p1` VALUES IN (4,5,6,7) ENGINE = InnoDB, + PARTITION `p0` VALUES IN (0,1,2,3) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk03:p0 test/t3#P#p0 test/t1 1 0 +test/fk03:p1 test/t3#P#p1 test/t1 1 0 +alter table t3 drop partition p0; +delete from t1 where id = 0; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY LIST (`fk`) +(PARTITION `p1` VALUES IN (4,5,6,7) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk03:p1 test/t3#P#p1 test/t1 1 0 +# CONVERT IN +insert tp0 values (0); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`tp0`, CONSTRAINT `fk02` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +insert t1 values (0); +insert tp0 values (0); +alter table t3 convert table tp0 to partition p0 values in (0, 1); +ERROR HY000: Tables have different definitions +alter table tp0 drop foreign key fk02; +alter table tp0 add foreign key fk03(fk) references t1(id); +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY LIST (`fk`) +(PARTITION `p1` VALUES IN (4,5,6,7) ENGINE = InnoDB) +show create table tp0; +Table Create Table +tp0 CREATE TABLE `tp0` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +alter table t3 convert table tp0 to partition p0 values in (0, 1, 2); +delete from t1 where id = 0; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0` */, CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk03` (`fk`), + CONSTRAINT `fk03` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY LIST (`fk`) +(PARTITION `p1` VALUES IN (4,5,6,7) ENGINE = InnoDB, + PARTITION `p0` VALUES IN (0,1,2) ENGINE = InnoDB) +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk03:p0 test/t3#P#p0 test/t1 1 0 +test/fk03:p1 test/t3#P#p1 test/t1 1 0 +# COALESCE +create or replace table t3 ( +fk int, +foreign key fk01 (fk) references t1(id)) +engine innodb +partition by hash (fk) partitions 4; +delete from t1; +insert into t1 values (0), (1), (2), (3); +insert into t3 values (0), (1), (2), (3); +delete from t1 where id = 0; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 1; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p2` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 3; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p3` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk01` (`fk`), + CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY HASH (`fk`) +PARTITIONS 4 +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01:p0 test/t3#P#p0 test/t1 1 0 +test/fk01:p1 test/t3#P#p1 test/t1 1 0 +test/fk01:p2 test/t3#P#p2 test/t1 1 0 +test/fk01:p3 test/t3#P#p3 test/t1 1 0 +alter table t3 coalesce partition 2; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `fk` int(11) DEFAULT NULL, + KEY `fk01` (`fk`), + CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY HASH (`fk`) +PARTITIONS 2 +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk01:p0 test/t3#P#p0 test/t1 1 0 +test/fk01:p1 test/t3#P#p1 test/t1 1 0 +delete from t1 where id = 0; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 1; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 2; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p0` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +delete from t1 where id = 3; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t3` /* Partition `p1` */, CONSTRAINT `fk01` FOREIGN KEY (`fk`) REFERENCES `t1` (`id`)) +drop tables t3, t2, t1; diff --git a/mysql-test/suite/parts/r/longname.result b/mysql-test/suite/parts/r/longname.result index bba3efbba49c8..b02b123a801ca 100644 --- a/mysql-test/suite/parts/r/longname.result +++ b/mysql-test/suite/parts/r/longname.result @@ -60,11 +60,11 @@ DROP TABLE mysqltest1.`#mysql50#t1#P#@0024@0024@0024@0024@0024@0024@0024@0024@00 ERROR 42000: Incorrect table name '#mysql50#t1#P#@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@' ALTER TABLE mysqltest1.t1 ADD FOREIGN KEY (a) REFERENCES mysqltest1.test_jfg_table_name_with_64_chars_123456789012345678901234567890; -ERROR HY000: Partitioned tables do not support FOREIGN KEY +ERROR HY000: The path specified for @0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@... is too long ALTER TABLE mysqltest1.test_jfg_table_name_with_64_chars_123456789012345678901234567890 ADD FOREIGN KEY (a) REFERENCES mysqltest1.t1; -ERROR HY000: Partitioned tables do not support FOREIGN KEY +ERROR 42000: Key column 'a' doesn't exist in table SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'mysqltest1%'; NAME diff --git a/mysql-test/suite/parts/t/foreign.test b/mysql-test/suite/parts/t/foreign.test new file mode 100644 index 0000000000000..fc3769bfabe23 --- /dev/null +++ b/mysql-test/suite/parts/t/foreign.test @@ -0,0 +1,412 @@ +--source include/have_partition.inc +--source include/have_innodb.inc + +--echo # +--echo # MDEV-19191 Partial support of foreign keys in partitioned tables +--echo # +create or replace table t1 (id int primary key) +with system versioning engine innodb +partition by system_time partitions 2; + +--echo # Referenced table cannot be partitioned still until MDEV-12483 +--error ER_CANT_CREATE_TABLE +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb; + +--error ER_CANT_CREATE_TABLE +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb +partition by system_time partitions 2; + +create or replace table t1 (id int primary key) +with system versioning engine innodb; + +--echo # CASCADE in partitioned table is prohibited until MDEV-12483 +--error ER_CANT_CREATE_TABLE +create or replace table t2 ( + fk int, + foreign key(fk) references t1(id) + on delete cascade) +with system versioning engine innodb +partition by system_time partitions 2; + +--error ER_CANT_CREATE_TABLE +create or replace table t2 ( + fk int references t1(id) + on update cascade) +with system versioning engine innodb +partition by system_time partitions 2; + +--error ER_CANT_CREATE_TABLE +create or replace table t2 ( + fk int references t1(id) + on delete cascade) +with system versioning engine innodb +partition by system_time partitions 2; + +--error ER_CANT_CREATE_TABLE +create or replace table t2 ( + fk int references t1(id) + on update set null) +with system versioning engine innodb +partition by system_time partitions 2; + +--error ER_CANT_CREATE_TABLE +create or replace table t2 ( + fk int references t1(id) + on delete set null) +with system versioning engine innodb +partition by system_time partitions 2; + +create or replace table t2 ( + fk int, + foreign key(fk) references t1(id) + on delete cascade) engine innodb; +--error ER_CANT_CREATE_TABLE +alter table t2 partition by hash(fk) partitions 5; + +--echo # Subpartition by SYSTEM_TIME is prohibited at parser layer +--error ER_PARSE_ERROR +create or replace table t2 (i int) with system versioning +partition by system_time interval 1 day +subpartition by system_time interval 2 hours +(partition p1 history, partition pn current); + +--echo # Partititon by SYSTEM_TIME, subpartition by KEY +create or replace table t2 (fk int references t1(id)) +with system versioning engine innodb +partition by system_time limit 1 auto +subpartition by key (fk) subpartitions 2 +(partition p1 history, partition pn current); + +show create table t2; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; + +insert into t1 values (1), (2), (3), (4), (5), (6); +insert into t2 values (3), (4), (5); +select * from t2 partition (pnsp0) order by fk; +select * from t2 partition (pnsp1) order by fk; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 3; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 4; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 5; +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (7); +delete from t1 where id = 1; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id > 2; +--echo # Partition auto-creation does not affect foreign keys in current partition +delete from t2 where fk = 3; +update t2 set fk= fk - 1; +delete from t2 where fk = 3; +update t2 set fk= fk - 1; +delete from t2 where fk = 3; +select * from t2 for system_time all order by fk; +delete from t1 where id > 2; +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (3), (5); +insert into t2 values (2); + +show create table t2; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; + +--echo # Partition by HASH, explicit constraint id +insert into t1 values (3), (4), (5), (6); + +create or replace table t2 ( + fk int, + foreign key fk01 (fk) references t1(id)) +engine innodb +partition by hash (fk) partitions 2; + +insert into t2 values (3), (4), (5); +select * from t2 partition (p0) order by fk; +select * from t2 partition (p1) order by fk; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 3; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 4; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 5; +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (7); +delete from t1 where id = 1; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id > 2; +delete from t2; +select * from t2 order by fk; +delete from t1 where id > 2; +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (3), (5); +insert into t2 values (2); +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; + +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (4); + +--echo # Explicit constraint id via CONSTRAINT if that matters +create or replace table t3 ( + fk int, + constraint fk02 foreign key(fk) references t1(id)) +engine innodb +partition by range columns (fk) +subpartition by hash (fk) subpartitions 2 ( + partition p0 values less than (100), + partition p1 values less than (1000)); +show create table t2; +show create table t3; + +--echo # Same constraint id in non-partitioned +# Same name is allowed. Uniqueness per-db is rather implementation restriction +# that real user requirements. +create table t4 (fk int, foreign key fk02 (fk) references t1(id)) engine innodb; +# For non-partitioned the restriction still holds. It is not a feature so +# we may loosen this restriction at least partly for MDEV-19191. +# MDEV-16417 removes it compeletely. +--error ER_CANT_CREATE_TABLE +create table t5 (fk int, foreign key fk02 (fk) references t1(id)) engine innodb; +drop table t4; + +--error ER_NO_REFERENCED_ROW_2 +insert into t3 values (10); +insert into t1 values (10); +insert into t3 values (10); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 10; + +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; + +--echo ### Alter operations ### + +--echo # get_parent_foreign_key_list() +set session foreign_key_checks= off; +set system_versioning_alter_history= keep; +--error ER_FK_COLUMN_CANNOT_CHANGE_CHILD +alter table t1 change column id id time not null; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t1 change column id b time; +set session foreign_key_checks= on; + +--echo # RENAME TABLE +rename table t3 to t4; +show create table t4; + +--error ER_NO_REFERENCED_ROW_2 +insert into t4 values (11); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 10; + +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t4%'; +alter table t4 rename to t3; + +--error ER_NO_REFERENCED_ROW_2 +insert into t3 values (11); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 10; + +--echo # REMOVE PARTITIONING +alter table t2 remove partitioning; +show create table t2; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +--error ER_NO_REFERENCED_ROW_2 +insert into t2 values (4); +insert into t2 values (10); +delete from t2; + +--echo # DROP PARTITION +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 10; +alter table t3 drop partition p0; +delete from t1 where id = 10; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; + +--echo # Add range partition into beginning is not possible (MDEV-31212) +--error ER_RANGE_NOT_INCREASING_ERROR +alter table t3 add partition (partition p0 values less than (1)); + +--echo # REORGANIZE PARTITION +select * from t1; +--error ER_NO_REFERENCED_ROW_2 +insert t3 values (3); +insert t3 values (2); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +alter table t3 reorganize partition p1 into ( + partition p0 values less than (10), + partition p1 values less than (1000)); + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +alter table t3 drop partition p0; +delete from t1 where id = 2; + +--echo # Add/drop foreign keys +--error ER_CANT_CREATE_TABLE +alter table t3 add foreign key fk03(fk) references t1(id) on delete cascade; +alter table t3 add foreign key fk03(fk) references t1(id), algorithm=copy; +# Instant add foreign key may be done only with foreign_key_checks= off +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t3 add foreign key fk04(fk) references t1(id), algorithm=instant; +set session foreign_key_checks= off; +alter table t3 add foreign key fk04(fk) references t1(id), algorithm=instant; +alter table t3 add foreign key (fk) references t1(id), algorithm=instant; +set session foreign_key_checks= on; +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +alter table t3 drop foreign key fk03, algorithm=copy; +alter table t3 drop foreign key fk04, algorithm=instant; +alter table t3 drop foreign key t3_ibfk_1, algorithm=instant; +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +--echo # CONVERT OUT +insert t1 values (1), (2), (300); +create or replace table t3 ( + fk int, + foreign key fk02(fk) references t1(id)) +engine innodb +partition by range columns (fk) ( + partition p0 values less than (100), + partition p1 values less than (1000)); + +insert t3 values (2), (300); +alter table t3 convert partition p0 to table tp0; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign; +show create table tp0; +select * from t3; +select * from tp0; +delete from t1 where id = 1; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 300; + +--echo # TRUNCATE +truncate t3; +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +delete from t1 where id = 300; + +--echo # PARTITION BY LIST, engine change, add partitioning, add partitition, drop partition +create or replace table t3 (fk int) engine myisam; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; +alter table t3 add foreign key fk03(fk) references t1(id); +# Foreign keys are not stored in non-InnoDB +show create table t3; +alter table t3 engine innodb; +alter table t3 add foreign key fk03(fk) references t1(id); +show create table t3; +alter table t3 partition by list (fk) ( + partition p1 values in (4, 5, 6, 7)); +alter table t3 engine myisam; +alter table t3 engine innodb; +alter table t3 add foreign key fk03(fk) references t1(id); +alter table t1 drop system versioning; +# Referenced not allowed to change engine +--error ER_ROW_IS_REFERENCED +alter table t1 engine myisam; +insert t1 values (0), (4); +insert t3 values (4); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 4; +--error ER_NO_PARTITION_FOR_GIVEN_VALUE +insert t3 values (0); +alter table t3 add partition (partition p0 values in (0, 1, 2, 3)); +insert t3 values (0); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 0; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +alter table t3 drop partition p0; +delete from t1 where id = 0; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +--echo # CONVERT IN +--error ER_NO_REFERENCED_ROW_2 +insert tp0 values (0); +insert t1 values (0); +insert tp0 values (0); +--error ER_TABLES_DIFFERENT_METADATA +alter table t3 convert table tp0 to partition p0 values in (0, 1); +alter table tp0 drop foreign key fk02; +alter table tp0 add foreign key fk03(fk) references t1(id); +show create table t3; +show create table tp0; +alter table t3 convert table tp0 to partition p0 values in (0, 1, 2); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 0; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +--echo # COALESCE +create or replace table t3 ( + fk int, + foreign key fk01 (fk) references t1(id)) +engine innodb +partition by hash (fk) partitions 4; + +delete from t1; +insert into t1 values (0), (1), (2), (3); +insert into t3 values (0), (1), (2), (3); +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 0; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 1; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 3; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +alter table t3 coalesce partition 2; + +show create table t3; +--replace_result #p# #P# #sp# #SP# +select * from information_schema.innodb_sys_foreign where for_name like 'test/t3%'; + +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 0; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 1; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 2; +--error ER_ROW_IS_REFERENCED_2 +delete from t1 where id = 3; + +drop tables t3, t2, t1; diff --git a/mysql-test/suite/parts/t/longname.test b/mysql-test/suite/parts/t/longname.test index ab6137d56dd23..112b3ea6c3814 100644 --- a/mysql-test/suite/parts/t/longname.test +++ b/mysql-test/suite/parts/t/longname.test @@ -52,10 +52,10 @@ INSERT INTO mysqltest1.t1 VALUES(1); --error ER_WRONG_TABLE_NAME DROP TABLE mysqltest1.`#mysql50#t1#P#@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024@0024#SP#0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`; ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING +--error ER_PATH_LENGTH ALTER TABLE mysqltest1.t1 ADD FOREIGN KEY (a) REFERENCES mysqltest1.test_jfg_table_name_with_64_chars_123456789012345678901234567890; ---error ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING +--error ER_KEY_COLUMN_DOES_NOT_EXIST ALTER TABLE mysqltest1.test_jfg_table_name_with_64_chars_123456789012345678901234567890 ADD FOREIGN KEY (a) REFERENCES mysqltest1.t1; diff --git a/mysql-test/suite/rpl/r/rpl_alter_rollback.result b/mysql-test/suite/rpl/r/rpl_alter_rollback.result index 3bd91a516c45f..b0b412454d8db 100644 --- a/mysql-test/suite/rpl/r/rpl_alter_rollback.result +++ b/mysql-test/suite/rpl/r/rpl_alter_rollback.result @@ -15,7 +15,7 @@ ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint create table t2 (f1 int primary key, constraint c1 foreign key (f1) references t1(f1)) engine=innodb; alter table t2 add constraint c1 foreign key (f1) references t1(f1); -ERROR HY000: Can't create table `test`.`t2` (errno: 121 "Duplicate key on write or update") +ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed") include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# diff --git a/mysql-test/suite/versioning/r/foreign.result b/mysql-test/suite/versioning/r/foreign.result index 7c8d9577096ad..877db6166a490 100644 --- a/mysql-test/suite/versioning/r/foreign.result +++ b/mysql-test/suite/versioning/r/foreign.result @@ -263,6 +263,13 @@ constraint `fk_child_parent` on delete cascade on update cascade ) engine = innodb with system versioning; +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk_child_parent test/child test/parent 2 5 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +test/fk_child_parent parent_id id 0 +test/fk_child_parent parent_value value 1 create or replace table subchild ( id int not null auto_increment primary key, parent_id smallint unsigned not null, @@ -272,6 +279,16 @@ constraint `fk_subchild_child_parent` on delete cascade on update cascade ) engine=innodb; +select * from information_schema.innodb_sys_foreign; +ID FOR_NAME REF_NAME N_COLS TYPE +test/fk_child_parent test/child test/parent 2 5 +test/fk_subchild_child_parent test/subchild test/child 2 5 +select * from information_schema.innodb_sys_foreign_cols; +ID FOR_COL_NAME REF_COL_NAME POS +test/fk_child_parent parent_id id 0 +test/fk_child_parent parent_value value 1 +test/fk_subchild_child_parent parent_id parent_id 0 +test/fk_subchild_child_parent parent_value parent_value 1 insert into parent (value) values (23); select id, value from parent into @id, @value; insert into child values (default, @id, @value); diff --git a/mysql-test/suite/versioning/t/foreign.test b/mysql-test/suite/versioning/t/foreign.test index a69f9df739b2f..40ead190c71f7 100644 --- a/mysql-test/suite/versioning/t/foreign.test +++ b/mysql-test/suite/versioning/t/foreign.test @@ -304,6 +304,9 @@ eval create or replace table child ( on update cascade ) engine = innodb with system versioning; +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; + create or replace table subchild ( id int not null auto_increment primary key, parent_id smallint unsigned not null, @@ -314,6 +317,9 @@ create or replace table subchild ( on update cascade ) engine=innodb; +select * from information_schema.innodb_sys_foreign; +select * from information_schema.innodb_sys_foreign_cols; + insert into parent (value) values (23); --disable_cursor_protocol --enable_prepare_warnings diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 2b61471c322b0..e9754660edb59 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -12409,6 +12409,19 @@ ulonglong ha_partition::index_blocks(uint index, uint ranges, ha_rows rows) return blocks; } +/* + Get partition file for FK info. For SYSTEM_TIME this is current partition, + for other partitioning this is first read partition. Chooses partition from + which to get foreign keys into SQL layer. SQL layer receives FKs only from + one partition with partition suffix removed from constraint name. +*/ +handler *ha_partition::get_fk_file() +{ + uint i= m_part_info->vers_info ? + m_tot_parts - 1 : + bitmap_get_first_set(&m_part_info->read_partitions); + return m_file[i]; +} struct st_mysql_storage_engine partition_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 84442d6518aed..6bd5207fbe6fc 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1626,9 +1626,9 @@ class ha_partition final :public handler ha_rows part_records(partition_element *part_elem) { DBUG_ASSERT(m_part_info); - uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; + const uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1; uint32 part_id= part_elem->id * sub_factor; - uint32 part_id_end= part_id + sub_factor; + const uint32 part_id_end= part_id + sub_factor; DBUG_ASSERT(part_id_end <= m_tot_parts); ha_rows part_recs= 0; for (; part_id < part_id_end; ++part_id) @@ -1640,6 +1640,26 @@ class ha_partition final :public handler return part_recs; } + /* + Used by is_vers_current_partition() which allows to create FKs only for + current SYSTEM_TIME partition. + */ + partition_element *part_elem_by_file(handler *file) + { + const uint32 sub_factor= + m_part_info->num_subparts ? m_part_info->num_subparts : 1; + for (uint i= 0; i < m_tot_parts; i++) + { + if (file == m_file[i]) + { + uint32 part_id= i / sub_factor; + return m_part_info->get_partition(part_id); + } + } + return NULL; + } + handler *get_fk_file(); + int notify_tabledef_changed(LEX_CSTRING *db, LEX_CSTRING *table, LEX_CUSTRING *frm, LEX_CUSTRING *version); @@ -1653,6 +1673,29 @@ class ha_partition final :public handler void update_optimizer_costs(OPTIMIZER_COSTS *costs) override; virtual ulonglong index_blocks(uint index, uint ranges, ha_rows rows) override; virtual ulonglong row_blocks() override; +// SQL layer interface + virtual int get_foreign_key_list(THD *thd, + List *f_key_list) override + { + handler *fk_file= get_fk_file(); + return fk_file->get_foreign_key_list(thd, f_key_list); + } + virtual char *get_foreign_key_create_info() override + { + handler *fk_file= get_fk_file(); + return fk_file->get_foreign_key_create_info(); + } + virtual void free_foreign_key_create_info(char *str) override + { + my_free(str); + } + virtual int + get_parent_foreign_key_list(THD *thd, + List *f_key_list) override + { + handler *fk_file= get_fk_file(); + return fk_file->get_parent_foreign_key_list(thd, f_key_list); + } }; #endif /* HA_PARTITION_INCLUDED */ diff --git a/sql/handler.h b/sql/handler.h index 29c811a530465..364dc463aee98 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -4472,18 +4472,6 @@ class handler :public Sql_alloc virtual int indexes_are_disabled(void) {return 0;} virtual void append_create_info(String *packet) {} - /** - If index == MAX_KEY then a check for table is made and if index < - MAX_KEY then a check is made if the table has foreign keys and if - a foreign key uses this index (and thus the index cannot be dropped). - - @param index Index to check if foreign key uses it - - @retval TRUE Foreign key defined on table or index - @retval FALSE No foreign key defined - */ - virtual bool is_fk_defined_on_table_or_index(uint index) - { return FALSE; } virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ /** diff --git a/sql/lex_ident.h b/sql/lex_ident.h index a8f3b66fbdd15..06caa8892a795 100644 --- a/sql/lex_ident.h +++ b/sql/lex_ident.h @@ -586,7 +586,7 @@ class Identifier_chain2 size_t make_sep_name_opt_casedn(char *dst, size_t dst_size, int sep, bool casedn) const { - DBUG_ASSERT(m_name[0].length + m_name[1].length + 2 < FN_REFLEN - 1); + DBUG_ASSERT(m_name[0].length + m_name[1].length + 2 < FN_REFLEN); return casedn ? make_sep_name_casedn(dst, dst_size, sep) : make_sep_name(dst, dst_size, sep); } diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 7fea51f8f1780..cd417d14fd822 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12289,3 +12289,5 @@ ER_VECTOR_BINARY_FORMAT_INVALID eng "Invalid binary vector format. Must use IEEE standard float representation in little-endian format. Use VEC_FromText() to generate it." ER_VECTOR_FORMAT_INVALID eng "Invalid vector format at offset: %d for '%-.100s'. Must be a valid JSON array of numbers." +ER_DUP_CONSTRAINT_NAME_2 + eng "Duplicate constraint name" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1167dcc6b7adf..561d7dc8ca051 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -748,6 +748,11 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, size_t key_length= share->table_cache_key.length; bool remove_from_locked_tables= extra != HA_EXTRA_NOT_USED; + /* We cannot just use any extra, remove_from_locked_tables depends on that! */ + DBUG_ASSERT(extra == HA_EXTRA_NOT_USED || + extra == HA_EXTRA_PREPARE_FOR_DROP || + extra == HA_EXTRA_PREPARE_FOR_RENAME); + memcpy(key, share->table_cache_key.str, key_length); for (TABLE **prev= &thd->open_tables; *prev; ) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index fdd35d7cef5d3..b6865165c817b 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3268,6 +3268,7 @@ int get_partition_id_list(partition_info *part_info, DBUG_RETURN(0); } *part_id= 0; + part_info->err_value= part_func_value; DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); } @@ -4888,13 +4889,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, { DBUG_ENTER("prep_alter_part_table"); - /* Foreign keys on partitioned tables are not supported, waits for WL#148 */ - if (table->part_info && (alter_info->flags & (ALTER_ADD_FOREIGN_KEY | - ALTER_DROP_FOREIGN_KEY))) - { - my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), "FOREIGN KEY"); - DBUG_RETURN(TRUE); - } +// Allow partitioning for ALTER + /* Remove partitioning on a not partitioned table is not possible */ if (!table->part_info && (alter_info->partition_flags & ALTER_PARTITION_REMOVE)) @@ -5527,11 +5523,6 @@ that are reorganised. my_error(ER_PARTITION_DOES_NOT_EXIST, MYF(0)); goto err; } - if (table->file->is_fk_defined_on_table_or_index(MAX_KEY)) - { - my_error(ER_ROW_IS_REFERENCED, MYF(0)); - goto err; - } DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) || num_parts_dropped == 1); /* NOTE: num_parts is used in generate_partition_syntax() */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6417654b23ee9..2da7b76ac142a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4402,27 +4402,7 @@ handler *mysql_create_frm_image(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(NULL); } } - /* - Unless table's storage engine supports partitioning natively - don't allow foreign keys on partitioned tables (they won't - work work even with InnoDB beneath of partitioning engine). - If storage engine handles partitioning natively (like NDB) - foreign keys support is possible, so we let the engine decide. - */ - if (create_info->db_type == partition_hton) - { - List_iterator_fast key_iterator(alter_info->key_list); - Key *key; - while ((key= key_iterator++)) - { - if (key->type == Key::FOREIGN_KEY) - { - my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), - "FOREIGN KEY"); - goto err; - } - } - } +// Allow FKs in partitioned table for CREATE TABLE #endif if (mysql_prepare_create_table_finalize(thd, create_info, @@ -5491,6 +5471,13 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db, if (error == HA_ERR_WRONG_COMMAND) my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE"); + if (error == HA_ERR_FOREIGN_DUPLICATE_KEY) + /* + TODO: constraint name is not known because rename_constraint_ids + does not supply it in row_rename_table_for_mysql(). + Should be fixed in MDEV-16417. + */ + my_error(ER_DUP_CONSTRAINT_NAME_2, MYF(0)); else if (error == ENOTDIR) my_error(ER_BAD_DB_ERROR, MYF(0), new_db->str); else if (error) @@ -10829,7 +10816,9 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, if ((create_info->db_type != table->s->db_type() || (alter_info->partition_flags & ALTER_PARTITION_INFO)) && - !table->file->can_switch_engines()) +// can_switch_engines() returns false if there are parent and child refs. +// We allow engine switch for child. + table->file->referenced_by_foreign_key()) { my_error(ER_ROW_IS_REFERENCED, MYF(0)); DBUG_RETURN(true); diff --git a/sql/table.cc b/sql/table.cc index 4efe0f1feead8..cb51d7b9732f7 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -10970,3 +10970,20 @@ void TABLE::mark_table_for_reopen() DBUG_ASSERT(thd); thd->locked_tables_list.mark_table_for_reopen(this); } + +// Interface for CREATE/ALTER to create FKs only in current partition +#ifdef WITH_PARTITION_STORAGE_ENGINE +bool TABLE::vers_system_time_partitioned() const +{ + DBUG_ASSERT(part_info); + return part_info->vers_info; +} +bool TABLE::is_vers_current_partition(handler *part_file) const +{ + DBUG_ASSERT(part_info); + DBUG_ASSERT(part_info->vers_info); + ha_partition *hp= static_cast(file); + partition_element *el= hp->part_elem_by_file(part_file); + return part_info->vers_info->now_part == el; +} +#endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/table.h b/sql/table.h index 26eed10941105..551c17bf71dbd 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1887,6 +1887,16 @@ struct TABLE return s->versioned == type; } +// Interface for CREATE/ALTER to create FKs only in current partition +#ifdef WITH_PARTITION_STORAGE_ENGINE + /* These are for InnoDB, so they cannot be inline */ + bool vers_system_time_partitioned() const; + bool is_vers_current_partition(handler *file) const; +#else + bool vers_system_time_partitioned() const { return false; } + bool is_vers_current_partition(handler *file) const { return false; } +#endif /* WITH_PARTITION_STORAGE_ENGINE */ + bool versioned_write() const { DBUG_ASSERT(versioned() || !vers_write); diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 5552e53f115ba..3d7fff213d5dc 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -334,6 +334,7 @@ SET(INNOBASE_SOURCES include/srv0start.h include/srw_lock.h include/sux_lock.h + include/sql_funcs.h include/transactional_lock_guard.h include/trx0i_s.h include/trx0purge.h diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index ac599c1143346..88a6a5b4c661c 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1753,7 +1753,8 @@ dict_create_add_foreigns_to_dictionary( } for (auto fk : local_fk_set) - if (!fk->check_fk_constraint_valid()) + if (trx->check_foreigns && + !fk->check_fk_constraint_valid()) return DB_CANNOT_ADD_CONSTRAINT; else if (dberr_t error= dict_create_add_foreign_to_dictionary (table->name.m_name, fk, trx)) diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 77097d8bd960c..085a17e855405 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1602,7 +1602,8 @@ dict_table_rename_in_cache( foreign->heap, table->name.m_name); foreign->foreign_table_name_lookup_set(); - if (strchr(foreign->id, '/')) { + const bool tmp_id = (strchr(foreign->id, '\xFF') != NULL); + if (!tmp_id && strchr(foreign->id, '/')) { /* This is a >= 4.0.18 format id */ ulint db_len; @@ -1746,10 +1747,17 @@ dict_table_rename_in_cache( } table->foreign_set.erase(it); - fk_set.insert(foreign); - if (foreign->referenced_table) { - foreign->referenced_table->referenced_set.insert(foreign); + if (!tmp_id) { + fk_set.insert(foreign); + + if (foreign->referenced_table) { + foreign->referenced_table + ->referenced_set.insert(foreign); + } + } else { + /* foreign was not added to cache, free it */ + dict_foreign_free(foreign); } } @@ -3207,81 +3215,46 @@ Open a table from its database and table name, this is currently used by foreign constraint parser to get the referenced table. @return complete table name with database and table name, allocated from heap memory passed in */ -char* -dict_get_referenced_table( - const char* name, /*!< in: foreign key table name */ - const char* database_name, /*!< in: table db name */ - ulint database_name_len, /*!< in: db name length */ - const char* table_name, /*!< in: table name */ - ulint table_name_len, /*!< in: table name length */ - dict_table_t** table, /*!< out: table object or NULL */ - mem_heap_t* heap, /*!< in/out: heap memory */ - CHARSET_INFO* from_cs) /*!< in: table name charset */ +char *dict_get_referenced_table( + Lex_ident_db database_name, /*!< in: table db name */ + Lex_ident_table table_name, /*!< in: table name */ + dict_table_t **table, /*!< out: table object or NULL */ + mem_heap_t *heap) /*!< in/out: heap memory */ { - char db_name[MAX_DATABASE_NAME_LEN]; - char tbl_name[MAX_TABLE_NAME_LEN]; - CHARSET_INFO* to_cs = &my_charset_filename; - uint errors; - ut_ad(database_name || name); - ut_ad(table_name); - - if (!strncmp(table_name, srv_mysql50_table_name_prefix, - sizeof(srv_mysql50_table_name_prefix) - 1)) { - /* This is a pre-5.1 table name - containing chars other than [A-Za-z0-9]. - Discard the prefix and use raw UTF-8 encoding. */ - table_name += sizeof(srv_mysql50_table_name_prefix) - 1; - table_name_len -= sizeof(srv_mysql50_table_name_prefix) - 1; - - to_cs = system_charset_info; - } - - table_name_len = strconvert(from_cs, table_name, table_name_len, to_cs, - tbl_name, MAX_TABLE_NAME_LEN, &errors); - table_name = tbl_name; - - if (database_name) { - to_cs = &my_charset_filename; - if (!strncmp(database_name, srv_mysql50_table_name_prefix, - sizeof(srv_mysql50_table_name_prefix) - 1)) { - database_name - += sizeof(srv_mysql50_table_name_prefix) - 1; - database_name_len - -= sizeof(srv_mysql50_table_name_prefix) - 1; - to_cs = system_charset_info; - } - - database_name_len = strconvert( - from_cs, database_name, database_name_len, to_cs, - db_name, MAX_DATABASE_NAME_LEN, &errors); - database_name = db_name; - } else { - /* Use the database name of the foreign key table */ - - database_name = name; - database_name_len = dict_get_db_name_len(name); - } - - /* Copy database_name, '/', table_name, '\0' */ - Identifier_chain2 ident({database_name, database_name_len}, - {table_name, table_name_len}); - size_t ref_nbytes= (database_name_len + table_name_len) * - system_charset_info->casedn_multiply() + 2; - char *ref = static_cast(mem_heap_alloc(heap, ref_nbytes)); - - /* Values; 0 = Store and compare as given; case sensitive - 1 = Store and compare in lower; case insensitive - 2 = Store as given, compare in lower; case semi-sensitive */ - - size_t len= ident.make_sep_name_opt_casedn(ref, ref_nbytes, - '/', lower_case_table_names > 0); - *table = dict_sys.load_table({ref, len}); - - if (lower_case_table_names == 2) { - ident.make_sep_name_opt_casedn(ref, ref_nbytes, '/', false); - } + /* Copy database_name, '/', table_name, '\0' */ + const size_t len= database_name.length + table_name.length + 1; + char *ref= static_cast(mem_heap_alloc(heap, len + 1)); + memcpy(ref, database_name.str, database_name.length); + ref[database_name.length]= '/'; + memcpy(ref + database_name.length + 1, table_name.str, + table_name.length + 1); + + /* Values; 0 = Store and compare as given; case sensitive + 1 = Store and compare in lower; case insensitive + 2 = Store as given, compare in lower; case semi-sensitive */ + if (lower_case_table_names == 2) + { + innobase_casedn_str(ref); + *table= dict_sys.load_table({ref, len}); + memcpy(ref, database_name.str, database_name.length); + ref[database_name.length]= '/'; + memcpy(ref + database_name.length + 1, table_name.str, + table_name.length + 1); + } + else + { +#ifndef _WIN32 + if (lower_case_table_names == 1) + { + innobase_casedn_str(ref); + } +#else + innobase_casedn_str(ref); +#endif /* !_WIN32 */ + *table= dict_sys.load_table({ref, len}); + } - return(ref); + return (ref); } /*********************************************************************//** @@ -3807,15 +3780,25 @@ dict_print_info_on_foreign_key_in_create_format(const trx_t *trx, const char* stripped_id; ulint i; std::string str; +// When returning FKs to SQL layer remove partition suffix from foreign ID (constraint name). + char foreign_id[MAX_FOREIGN_ID_LEN]; + const char* s; - if (strchr(foreign->id, '/')) { + if (size_t db_len= dict_get_db_name_len(foreign->id)) { + ut_ad(foreign->id[db_len + 1]); /* Strip the preceding database name from the constraint id */ - stripped_id = foreign->id + 1 - + dict_get_db_name_len(foreign->id); + stripped_id = foreign->id + 1 + db_len; } else { stripped_id = foreign->id; } + if ((s= strchr(stripped_id, '\xFF')) && s > stripped_id) { + size_t l= size_t(s - stripped_id); + memcpy(foreign_id, stripped_id, l); + foreign_id[l]= 0; + stripped_id= foreign_id; + } + str.append(","); if (add_newline) { diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index f5b22dcf68ce8..b8aed60887e23 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -3148,8 +3148,8 @@ dict_load_foreigns( rec, DICT_FLD__SYS_FOREIGN_FOR_NAME__ID, &len); /* Copy the string because the page may be modified or evicted - after mtr.commit() below. */ - char fk_id[MAX_TABLE_NAME_LEN + NAME_LEN]; + after mtr.commit() below (-2 is for \xFF\xFF in tmp constraints). */ + char fk_id[MAX_TABLE_NAME_LEN + NAME_LEN - 2]; err = DB_SUCCESS; if (UNIV_LIKELY(len < sizeof fk_id)) { memcpy(fk_id, field, len); diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index ec5bbcfc530b3..19be9f265adfd 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -81,6 +81,14 @@ const char table_name_t::part_suffix[4] = "#P#"; #endif +// subpart_suffix is used by fixup_foreign_id() to cut out needless data in I_S +const char table_name_t::subpart_suffix[5] +#ifdef _WIN32 += "#sp#"; +#else += "#SP#"; +#endif + /** Display an identifier. @param[in,out] s output stream @param[in] id_name SQL identifier (other than table name) diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 7774dcfd1ba5b..87dc56b5e2653 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -1392,7 +1392,7 @@ static dberr_t fts_drop_table(trx_t *trx, const char *table_name, bool rename) char *tmp= dict_mem_create_temporary_tablename(heap, table->name.m_name, table->id); dberr_t err= row_rename_table_for_mysql(table->name.m_name, tmp, trx, - false); + RENAME_IGNORE_FK); mem_heap_free(heap); if (err != DB_SUCCESS) { @@ -1450,7 +1450,7 @@ fts_rename_one_aux_table( fts_table_new_name[table_new_name_len] = 0; return row_rename_table_for_mysql( - fts_table_old_name, fts_table_new_name, trx, false); + fts_table_old_name, fts_table_new_name, trx, RENAME_IGNORE_FK); } /****************************************************************//** diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e7b3cedf4d16a..6aafcdf23cac8 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -268,15 +268,14 @@ void set_my_errno(int err) /** Checks whether the file name belongs to a partition of a table. @param[in] file_name file name @return pointer to the end of the table name part of the file name, or NULL */ -static char* is_partition( /*=========*/ - char* file_name) + const char* file_name) { /* We look for pattern #P# to see if the table is partitioned MariaDB table. */ - return strstr(file_name, table_name_t::part_suffix); + return strstr(const_cast(file_name), table_name_t::part_suffix); } @@ -2493,6 +2492,16 @@ innobase_basename( return((name) ? name : "null"); } +/******************************************************************//** +Makes all characters in a NUL-terminated UTF-8 string lower case. */ +void +innobase_casedn_str( +/*================*/ + char* a) /*!< in/out: string to put in lower case */ +{ + my_casedn_str_8bit(system_charset_info, a); +} + /** Determines the current SQL statement. Thread unsafe, can only be called from the thread owning the THD. @param[in] thd MySQL thread handle @@ -3425,14 +3434,17 @@ innobase_convert_identifier( THD* thd) { const char* s = id; - - char nz[MAX_TABLE_NAME_LEN + 1]; - char nz2[MAX_TABLE_NAME_LEN + 1]; +// That was wrong buffer size assumption as id is full identifier of format: + /* db/table_name#P#part_name#SP#subpart_name */ + static const size_t ID_LEN= MAX_TABLE_NAME_LEN * 3 + 1 + 3 + 4 + 1; + char nz[ID_LEN]; + char nz2[ID_LEN]; /* Decode the table name. The MySQL function expects a NUL-terminated string. The input and output strings buffers must not be shared. */ - ut_a(idlen <= MAX_TABLE_NAME_LEN); + /* ut_a(db/table_name#P#part_name#SP#subpart_name) */ + ut_a(idlen < ID_LEN); memcpy(nz, id, idlen); nz[idlen] = 0; @@ -5261,7 +5273,12 @@ create_table_info_t::create_table_info_t( m_create_info(create_info), m_table(NULL), m_innodb_file_per_table(file_per_table), - m_creating_stub(thd_ddl_options(thd)->import_tablespace()) + m_creating_stub(thd_ddl_options(thd)->import_tablespace()), +// These help to pass data from create_foreign_keys() to create_foreign_key() +// More create_foreign_keys() variables should be moved to create_table_info_t + part_suffix(NULL), + tmp_name(false), + alter_table(NULL) { m_table_name[0]= '\0'; m_remote_path[0]= '\0'; @@ -12213,31 +12230,63 @@ create_table_info_t::create_foreign_keys() dict_foreign_set local_fk_set; dict_foreign_set_free local_fk_set_free(local_fk_set); dberr_t error; - ulint number = 1; - static const unsigned MAX_COLS_PER_FK = 500; + ulint number = 1; const char* column_names[MAX_COLS_PER_FK]; const char* ref_column_names[MAX_COLS_PER_FK]; + /* Quoted form `db`.`table` used for warning messages */ char create_name[MAX_DATABASE_NAME_LEN + 1 + MAX_TABLE_NAME_LEN + 1]; - dict_index_t* index = NULL; - fkerr_t index_error = FK_SUCCESS; - dict_index_t* err_index = NULL; - ulint err_col = 0; + /* Name of original table in filename charset (or system charset for mysql50 name) */ + char db_name[MAX_DATABASE_NAME_LEN + 1]; + char t_name[MAX_TABLE_NAME_LEN + 1]; const bool tmp_table = m_flags2 & DICT_TF2_TEMPORARY; const CHARSET_INFO* cs = thd_charset(m_thd); const char* operation = "Create "; - const char* name = m_table_name; + /* Name of innodb table without db */ + const char* basename; enum_sql_command sqlcom = enum_sql_command(thd_sql_command(m_thd)); + /* Name of innodb table with db */ + LEX_CSTRING name= {m_table_name, strlen(m_table_name)}; + ut_ad(!part_suffix); if (sqlcom == SQLCOM_ALTER_TABLE) { - dict_table_t* table_to_alter; - mem_heap_t* heap = mem_heap_create(10000); - ulint highest_id_so_far; - char* n = dict_get_referenced_table( - name, LEX_STRING_WITH_LEN(m_form->s->db), - LEX_STRING_WITH_LEN(m_form->s->table_name), - &table_to_alter, heap, cs); + mem_heap_t* heap = mem_heap_create(10000); + LEX_CSTRING table_name = m_form->s->table_name; + CHARSET_INFO* to_cs = &my_charset_filename; + + if (!strncmp(table_name.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + table_name.str + += sizeof srv_mysql50_table_name_prefix - 1; + table_name.length + -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } + + uint errors; + Lex_ident_table t; + t.str = t_name; + t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name), + to_cs, t_name, MAX_TABLE_NAME_LEN, + &errors); + Lex_ident_db d= m_form->s->db; + + if (!strncmp(d.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + d.str += sizeof srv_mysql50_table_name_prefix - 1; + d.length -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } else { + to_cs = &my_charset_filename; + } + + d.length = strconvert(cs, LEX_STRING_WITH_LEN(d), to_cs, + db_name, MAX_DATABASE_NAME_LEN, + &errors); + d.str = db_name; + + char* n = dict_get_referenced_table(d, t, &alter_table, heap); /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the format databasename/tablename_ibfk_[number], where @@ -12247,38 +12296,54 @@ create_table_info_t::create_foreign_keys() /* If we are altering a temporary table, the table name after ALTER TABLE does not correspond to the internal table name, and - table_to_alter is NULL. TODO: should we fix this somehow? */ + alter_table=nullptr. But, we do not support FOREIGN KEY + constraints for temporary tables. */ - if (table_to_alter) { - n = table_to_alter->name.m_name; - highest_id_so_far = dict_table_get_highest_foreign_id( - table_to_alter); - } else { - highest_id_so_far = 0; + if (alter_table) { + n = alter_table->name.m_name; + number = 1 + dict_table_get_highest_foreign_id( + alter_table); } - char* bufend = innobase_convert_name( - create_name, sizeof create_name, n, strlen(n), m_thd); - create_name[bufend - create_name] = '\0'; - number = highest_id_so_far + 1; + /* + alter_table == NULL might be by the following reasons: + + 1. tmp_table; + 2. is_partition(m_table_name); + 3. Engine changed from non-InnoDB, so no original table in InnoDB; + 4. Anything else? + */ + + *innobase_convert_name(create_name, sizeof create_name, + n, strlen(n), m_thd) = '\0'; mem_heap_free(heap); operation = "Alter "; - } else if (strstr(name, "#P#") || strstr(name, "#p#")) { - /* Partitioned table */ - create_name[0] = '\0'; + /* Alter does not mean temporary name. F.ex. ADD PARTITION adds + as is name. */ + tmp_name = dict_table_t::is_temporary_name(name.str); } else { - char* bufend = innobase_convert_name(create_name, - sizeof create_name, - name, - strlen(name), m_thd); - create_name[bufend - create_name] = '\0'; + *innobase_convert_name(create_name, sizeof create_name, + LEX_STRING_WITH_LEN(name), m_thd)= '\0'; + } + + if ((part_suffix= is_partition(name.str))) { + /* Partitioned table */ + part_suffix_len= strlen(part_suffix); + /* We don't need #TMP# suffix in temporary FK is it is handled + by \xFF technology. #TMP# is not handled by rename_constraint_ids. */ + if (0 == memcmp(part_suffix + part_suffix_len - 5, "#TMP#", 5)) { + part_suffix_len-= 5; + memcpy(part_suffix_buf, part_suffix, part_suffix_len); + part_suffix= part_suffix_buf; + part_suffix_buf[part_suffix_len]= 0; + } } Alter_info* alter_info = m_create_info->alter_info; ut_ad(alter_info); List_iterator_fast key_it(alter_info->key_list); - dict_table_t* table = dict_sys.find_table({name,strlen(name)}); + dict_table_t* table = dict_sys.find_table({name.str, name.length}); if (!table) { ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key constraint" @@ -12288,8 +12353,18 @@ create_table_info_t::create_foreign_keys() return (DB_CANNOT_ADD_CONSTRAINT); } + basename = table->name.basename(); + while (Key* key = key_it++) { - if (key->type != Key::FOREIGN_KEY || key->old) +// Now we process tmp table from ALTER and create FKs for it prefixed by \xFF. +// Backed up FKs are prefixed by \xFF\xFF. +// +// row_rename_table_for_mysql() either restores backup by removing \xFF\xFF or +// applies new table by removing \xFF. +// +// There is also \xFF between usual FK ID and partition suffix. Partition suffix +// helps to maintain FKs per each partition in SYS_FOREIGN table. + if (key->type != Key::FOREIGN_KEY) continue; if (tmp_table) { @@ -12307,8 +12382,60 @@ create_table_info_t::create_foreign_keys() ut_ad("should be unreachable" == 0); return DB_CANNOT_ADD_CONSTRAINT; } - Foreign_key* fk = static_cast(key); + dberr_t err = create_foreign_key( + fk, table, basename, local_fk_set, column_names, + ref_column_names, create_name, operation, number, + db_name, t_name); + if (err != DB_SUCCESS) { + return err; + } + } + + if (dict_foreigns_has_s_base_col(local_fk_set, table)) { + return (DB_NO_FK_ON_S_BASE_COL); + } + + /**********************************************************/ + /* The following call adds the foreign key constraints + to the data dictionary system tables on disk */ + m_trx->op_info = "adding foreign keys"; + + trx_start_if_not_started_xa(m_trx, true); + + m_trx->dict_operation = true; + + error = dict_create_add_foreigns_to_dictionary(local_fk_set, table, + m_trx); + + if (error == DB_SUCCESS) { + + table->foreign_set.insert(local_fk_set.begin(), + local_fk_set.end()); + std::for_each(local_fk_set.begin(), local_fk_set.end(), + dict_foreign_add_to_referenced_table()); + local_fk_set.clear(); + + dict_mem_table_fill_foreign_vcol_set(table); + } + return (error); +} + +dberr_t +create_table_info_t::create_foreign_key( + Foreign_key* fk, dict_table_t* table, const char* basename, + dict_foreign_set &local_fk_set, const char** column_names, + const char** ref_column_names, char* create_name, const char* operation, + ulint &number, char *db_name, char *t_name) +{ + dict_index_t* index = NULL; + fkerr_t index_error = FK_SUCCESS; + dict_index_t* err_index = NULL; + ulint err_col = 0; + dberr_t error; + const CHARSET_INFO* cs = thd_charset(m_thd); + + { Key_part_spec* col; bool success; @@ -12325,27 +12452,27 @@ create_table_info_t::create_foreign_keys() col->field_name.length); success = find_col(table, column_names + i); if (!success) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key %s constraint" " failed. Column %s was not found.", - operation, create_name, k.str(), + operation, create_name, + key_text(fk).str(), column_names[i]); dict_foreign_free(foreign); return (DB_CANNOT_ADD_CONSTRAINT); } ++i; if (i >= MAX_COLS_PER_FK) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s foreign key %s constraint" " failed. Too many columns: %u (%u " "allowed).", - operation, create_name, k.str(), i, + operation, create_name, + key_text(fk).str(), i, MAX_COLS_PER_FK); dict_foreign_free(foreign); return (DB_CANNOT_ADD_CONSTRAINT); @@ -12356,10 +12483,13 @@ create_table_info_t::create_foreign_keys() table, NULL, column_names, i, NULL, TRUE, FALSE, &index_error, &err_col, &err_index); - if (!index) { - key_text k(fk); + /* ALTER now renames FK for backup table, so it creates new versions + for new table. We have to respect check_foreigns for such commands + as DROP INDEX. */ + if (!index && m_trx->check_foreigns) { foreign_push_index_error(m_trx, operation, create_name, - k.str(), column_names, + key_text(fk).str(), + column_names, index_error, err_col, err_index, table); dict_foreign_free(foreign); @@ -12375,20 +12505,41 @@ create_table_info_t::create_foreign_keys() itself. We store the name to foreign->id. */ db_len = dict_get_db_name_len(table->name.m_name); - +// We prefix by \xFF not just for any ALTER TABLE command, but correctly decide +// it from table name whether it is tmp table or not. +// ALTER TABLE may avoid using tmp table (f.ex. ADD PARTITION). + size_t alloc_len = (tmp_name ? 3 : 2) + db_len + fk->constraint_name.length; + if (part_suffix) + { + alloc_len += part_suffix_len + 1; + } foreign->id = static_cast(mem_heap_alloc( - foreign->heap, - db_len + fk->constraint_name.length + 2)); - - memcpy(foreign->id, table->name.m_name, db_len); - foreign->id[db_len] = '/'; - strcpy(foreign->id + db_len + 1, - fk->constraint_name.str); + foreign->heap, alloc_len)); + + char *pos = foreign->id; + memcpy(pos, table->name.m_name, db_len); + pos += db_len; + *(pos++) = '/'; + if (tmp_name) { + *(pos++) = '\xFF'; + } + strcpy(pos, fk->constraint_name.str); + ut_ad(strlen(fk->constraint_name.str) == fk->constraint_name.length); + pos+= fk->constraint_name.length; +// Append partition suffix + if (part_suffix) + { + *(pos++) = '\xFF'; + strcpy(pos, part_suffix); + } } if (foreign->id == NULL) { + /* Auto-generated foreign id is prefixed by tmp table name + with #sql-alter- prefix. We don't need it to prefix by \xFF. */ error = dict_create_add_foreign_id( - &number, table->name.m_name, foreign); + &number, m_table_name, foreign, part_suffix, + part_suffix_len); if (error != DB_SUCCESS) { dict_foreign_free(foreign); return (error); @@ -12425,32 +12576,69 @@ create_table_info_t::create_foreign_keys() memcpy(foreign->foreign_col_names, column_names, i * sizeof(void*)); - foreign->referenced_table_name = dict_get_referenced_table( - name, LEX_STRING_WITH_LEN(fk->ref_db), - LEX_STRING_WITH_LEN(fk->ref_table), - &foreign->referenced_table, foreign->heap, cs); + LEX_CSTRING table_name = fk->ref_table; + CHARSET_INFO* to_cs = &my_charset_filename; + uint errors; + Lex_ident_table t; + Lex_ident_db d(fk->ref_db); + + if (!d.str) { + d.str = table->name.m_name; + d.length = size_t(basename - table->name.m_name - 1); + } + + if (!strncmp(table_name.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + table_name.str + += sizeof srv_mysql50_table_name_prefix - 1; + table_name.length + -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } + + t.str = t_name; + t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name), + to_cs, t_name, + MAX_TABLE_NAME_LEN, &errors); + + if (!strncmp(d.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + d.str += sizeof srv_mysql50_table_name_prefix - 1; + d.length -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } else if (d.str == table->name.m_name) { + goto name_converted; + } else { + to_cs = &my_charset_filename; + } - if (!foreign->referenced_table_name) { - return (DB_OUT_OF_MEMORY); + if (d.str != table->name.m_name) { + d.length = strconvert(cs, LEX_STRING_WITH_LEN(d), + to_cs, db_name, + MAX_DATABASE_NAME_LEN, + &errors); + d.str = db_name; } +name_converted: + foreign->referenced_table_name = dict_get_referenced_table( + d, t, &foreign->referenced_table, foreign->heap); if (!foreign->referenced_table && m_trx->check_foreigns) { char buf[MAX_TABLE_NAME_LEN + 1] = ""; - char* bufend; - bufend = innobase_convert_name( + *innobase_convert_name( buf, MAX_TABLE_NAME_LEN, foreign->referenced_table_name, - strlen(foreign->referenced_table_name), m_thd); - buf[bufend - buf] = '\0'; - key_text k(fk); + strlen(foreign->referenced_table_name), m_thd) + = '\0'; ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name, "%s table %s with foreign key %s " "constraint failed. Referenced table " "%s not found in the data dictionary.", - operation, create_name, k.str(), buf); - return (DB_CANNOT_ADD_CONSTRAINT); + operation, create_name, + key_text(fk).str(), buf); + return DB_CANNOT_ADD_CONSTRAINT; } /* Don't allow foreign keys on partitioned tables yet. */ @@ -12473,7 +12661,6 @@ create_table_info_t::create_foreign_keys() success = find_col(foreign->referenced_table, ref_column_names + j); if (!success) { - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, @@ -12482,9 +12669,9 @@ create_table_info_t::create_foreign_keys() "constraint failed. " "Column %s was not found.", operation, create_name, - k.str(), ref_column_names[j]); - - return (DB_CANNOT_ADD_CONSTRAINT); + key_text(fk).str(), + ref_column_names[j]); + return DB_CANNOT_ADD_CONSTRAINT; } } ++j; @@ -12504,16 +12691,15 @@ create_table_info_t::create_foreign_keys() &err_index); if (!index) { - key_text k(fk); foreign_push_index_error( - m_trx, operation, create_name, k.str(), + m_trx, operation, create_name, + key_text(fk).str(), column_names, index_error, err_col, err_index, foreign->referenced_table); - - return (DB_CANNOT_ADD_CONSTRAINT); + return DB_CANNOT_ADD_CONSTRAINT; } } else { - ut_a(m_trx->check_foreigns == FALSE); + ut_a(!m_trx->check_foreigns); index = NULL; } @@ -12550,7 +12736,6 @@ create_table_info_t::create_foreign_keys() NULL if the column is not allowed to be NULL! */ - key_text k(fk); ib_foreign_warn( m_trx, DB_CANNOT_ADD_CONSTRAINT, @@ -12561,9 +12746,9 @@ create_table_info_t::create_foreign_keys() "but column '%s' is defined as " "NOT NULL.", operation, create_name, - k.str(), col_name); + key_text(fk).str(), col_name); - return (DB_CANNOT_ADD_CONSTRAINT); + return DB_CANNOT_ADD_CONSTRAINT; } } } @@ -12576,9 +12761,19 @@ create_table_info_t::create_foreign_keys() case FK_OPTION_RESTRICT: break; case FK_OPTION_CASCADE: +// Prohibit CASCADE and SET NULL for partitioned tables +// as we cannot handle cross-partition updates in InnoDB layer + if (part_suffix) + { + goto cascade_partitioned; + } foreign->type |= foreign->DELETE_CASCADE; break; case FK_OPTION_SET_NULL: + if (part_suffix) + { + goto cascade_partitioned; + } foreign->type |= foreign->DELETE_SET_NULL; break; case FK_OPTION_NO_ACTION: @@ -12597,9 +12792,27 @@ create_table_info_t::create_foreign_keys() case FK_OPTION_RESTRICT: break; case FK_OPTION_CASCADE: + if (part_suffix) + { + goto cascade_partitioned; + } foreign->type |= foreign->UPDATE_CASCADE; break; case FK_OPTION_SET_NULL: + if (part_suffix) + { +cascade_partitioned: + key_text k(fk); + ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, + create_name, + "%s table %s with foreign key " + "%s constraint failed. " + "CASCADE or SET NULL action is not allowed " + "in partitioned table", + operation, create_name, + k.str()); + return (DB_CANNOT_ADD_CONSTRAINT); + } foreign->type |= foreign->UPDATE_SET_NULL; break; case FK_OPTION_NO_ACTION: @@ -12616,34 +12829,7 @@ create_table_info_t::create_foreign_keys() # pragma GCC diagnostic pop #endif } - - if (dict_foreigns_has_s_base_col(local_fk_set, table)) { - return (DB_NO_FK_ON_S_BASE_COL); - } - - /**********************************************************/ - /* The following call adds the foreign key constraints - to the data dictionary system tables on disk */ - m_trx->op_info = "adding foreign keys"; - - trx_start_if_not_started_xa(m_trx, true); - - m_trx->dict_operation = true; - - error = dict_create_add_foreigns_to_dictionary(local_fk_set, table, - m_trx); - - if (error == DB_SUCCESS) { - - table->foreign_set.insert(local_fk_set.begin(), - local_fk_set.end()); - std::for_each(local_fk_set.begin(), local_fk_set.end(), - dict_foreign_add_to_referenced_table()); - local_fk_set.clear(); - - dict_mem_table_fill_foreign_vcol_set(table); - } - return (error); + return (DB_SUCCESS); } /** Create the internal innodb table. @@ -13138,7 +13324,9 @@ create_table_info_t::allocate_trx() @retval 0 on success */ int ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info, - bool file_per_table, trx_t *trx= nullptr) +// Default value in method definition limits it to current source file. Was it intended? +// Upper interface decides whether to create FK or not. F.ex. TRUNCATE does not create FKs. + bool file_per_table, trx_t *trx, bool create_fk) { DBUG_ENTER("ha_innobase::create"); DBUG_ASSERT(form->s == table_share); @@ -13170,10 +13358,25 @@ ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info, } if (!error) + { +// Create FKs only for SYSTEM_TIME current partition or decide it from the caller. +// Now TRUNCATE does not create FKs, all others do create. +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (create_fk && form->part_info) + { + if (form->vers_system_time_partitioned()) + { + /* MDEV-19191 allows FK for SYSTEM_TIME partitioning. We create foreign + keys for current partition. */ + create_fk= form->is_vers_current_partition(this); + } + } +#endif /* WITH_PARTITION_STORAGE_ENGINE */ /* We can't possibly have foreign key information when creating a stub table for importing .frm / .cfg / .ibd because it is not stored in any of these files. */ - error= info.create_table(own_trx, !create_info->recreate_identical_table); + error= info.create_table(create_fk, !create_info->recreate_identical_table); + } if (own_trx || (info.flags2() & DICT_TF2_TEMPORARY)) { @@ -13700,10 +13903,10 @@ int ha_innobase::delete_table(const char *name) @param[in,out] trx InnoDB data dictionary transaction @param[in] from old table name @param[in] to new table name -@param[in] use_fk whether to enforce FOREIGN KEY +@param[in] fk how to handle FOREIGN KEY @return DB_SUCCESS or error code */ static dberr_t innobase_rename_table(trx_t *trx, const char *from, - const char *to, bool use_fk) + const char *to, rename_fk fk) { dberr_t error; char norm_to[FN_REFLEN]; @@ -13721,7 +13924,7 @@ static dberr_t innobase_rename_table(trx_t *trx, const char *from, ut_ad(trx->will_lock); - error = row_rename_table_for_mysql(norm_from, norm_to, trx, use_fk); + error = row_rename_table_for_mysql(norm_from, norm_to, trx, fk); if (error != DB_SUCCESS) { if (error == DB_TABLE_NOT_FOUND @@ -13748,7 +13951,8 @@ static dberr_t innobase_rename_table(trx_t *trx, const char *from, #endif /* _WIN32 */ trx_start_if_not_started(trx, true); error = row_rename_table_for_mysql( - par_case_name, norm_to, trx, false); + par_case_name, norm_to, trx, + RENAME_IGNORE_FK); } } @@ -13838,7 +14042,8 @@ int ha_innobase::truncate() row_mysql_lock_data_dictionary(trx); ib_table->release(); dict_sys.remove(ib_table, false, true); - int err= create(ib_table->name.m_name, table, &info, true, trx); +// Don't create FKs in TRUNCATE + int err= create(ib_table->name.m_name, table, &info, true, trx, false); row_mysql_unlock_data_dictionary(trx); ut_ad(!err); @@ -13944,7 +14149,7 @@ int ha_innobase::truncate() if (error == DB_SUCCESS) { - error= innobase_rename_table(trx, ib_table->name.m_name, temp_name, false); + error= innobase_rename_table(trx, ib_table->name.m_name, temp_name, RENAME_REBUILD); if (error == DB_SUCCESS) error= trx->drop_table(*ib_table); } @@ -13965,7 +14170,7 @@ int ha_innobase::truncate() m_prebuilt->table= nullptr; err= create(name, table, &info, dict_table_is_file_per_table(ib_table), - trx); + trx, false); if (!err) { m_prebuilt->table->acquire(); @@ -14142,7 +14347,11 @@ ha_innobase::rename_table( row_mysql_lock_data_dictionary(trx); if (error == DB_SUCCESS) { - error = innobase_rename_table(trx, from, to, true); + error = innobase_rename_table(trx, from, to, + thd_sql_command(thd) + == SQLCOM_ALTER_TABLE + ? RENAME_ALTER_COPY + : RENAME_FK); } DEBUG_SYNC(thd, "after_innobase_rename_table"); @@ -15408,6 +15617,7 @@ get_foreign_key_info( char tmp_buff[NAME_LEN+1]; char name_buff[NAME_LEN+1]; const char* ptr; + const char* ptr2; LEX_CSTRING* name = NULL; if (dict_table_t::is_temporary_name(foreign->foreign_table_name)) { @@ -15415,8 +15625,18 @@ get_foreign_key_info( } ptr = dict_remove_db_name(foreign->id); +// Pass id as constraint name with partition suffix stripped. Temporary ids +// should not be here (starting with \xFF) and we assert it by ut_ad(!ptr2) below. +// Debug code will fail, release code will just show mangled foreign id which +// will mean something is wrong with the code or data. + if ((ptr2= strchr(ptr, '\xFF')) && ptr2 > ptr) { + len= size_t(ptr2 - ptr); + } else { + ut_ad(!ptr2); + len= strlen(ptr); + } f_key_info.foreign_id = thd_make_lex_string( - thd, 0, ptr, strlen(ptr), 1); + thd, 0, ptr, len, 1); /* Name format: database name, '/', table name, '\0' */ @@ -15436,7 +15656,7 @@ get_foreign_key_info( f_key_info.referenced_table = thd_make_lex_string( thd, 0, name_buff, len, 1); - /* Dependent (child) database name */ + /* Foreign (child) database name */ len = dict_get_db_name_len(foreign->foreign_table_name); ut_a(len < sizeof(tmp_buff)); memcpy(tmp_buff, foreign->foreign_table_name, len); @@ -15446,8 +15666,15 @@ get_foreign_key_info( f_key_info.foreign_db = thd_make_lex_string( thd, 0, name_buff, len, 1); - /* Dependent (child) table name */ + /* Foreign (child) table name */ ptr = dict_remove_db_name(foreign->foreign_table_name); +// Remove partition name from child table name + if ((ptr2 = is_partition(ptr))) { + len = ptr2 - ptr; + memcpy(tmp_buff, ptr, len); + tmp_buff[len] = 0; + ptr = tmp_buff; + } len = filename_to_tablename(ptr, name_buff, sizeof(name_buff), 1); f_key_info.foreign_table = thd_make_lex_string( thd, 0, name_buff, len, 1); diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 19faff311a9e0..f0bee0bafeee1 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -189,7 +189,8 @@ class ha_innobase final : public handler TABLE* form, HA_CREATE_INFO* create_info, bool file_per_table, - trx_t* trx); + trx_t* trx= nullptr, + bool create_fk= true); int create( const char* name, @@ -612,10 +613,14 @@ innobase_parse_hint_from_comment( dict_table_t* table, const TABLE_SHARE* table_share); +class Foreign_key; + /** Class for handling create table information. */ class create_table_info_t { public: + static const unsigned MAX_COLS_PER_FK = 500; + /** Constructor. Used in two ways: - all but file_per_table is used, when creating the table. @@ -635,6 +640,11 @@ class create_table_info_t /** Create InnoDB foreign keys from MySQL alter_info. */ dberr_t create_foreign_keys(); + dberr_t create_foreign_key( + Foreign_key* fk, dict_table_t* table, const char* basename, + dict_foreign_set &local_fk_set, const char **column_names, + const char** ref_column_names, char* create_name, + const char* operation, ulint &number, char *db_name, char *t_name); /** Create the internal innodb table. @param create_fk whether to add FOREIGN KEY constraints */ @@ -769,6 +779,12 @@ class create_table_info_t /** Whether we are creating a stub table for importing. */ const bool m_creating_stub; +// Used by create_foreign_keys() + char part_suffix_buf[FN_REFLEN]; + const char * part_suffix; + size_t part_suffix_len; + bool tmp_name; + dict_table_t* alter_table; }; /** diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index d4ee3477caf37..dd6473511a027 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -30,6 +30,7 @@ Smart ALTER TABLE #include #include #include +#include /* Include necessary InnoDB headers */ #include "btr0sea.h" @@ -2866,8 +2867,10 @@ innobase_init_foreign( dict_index_t* referenced_index, /*!< in: referenced index */ const char** referenced_column_names,/*!< in: referenced column names */ - ulint referenced_num_field) /*!< in: number of referenced + ulint referenced_num_field, /*!< in: number of referenced columns */ + const char * part_suffix, + const size_t part_suffix_len) { ut_ad(dict_sys.locked()); @@ -2881,12 +2884,27 @@ innobase_init_foreign( db_len = dict_get_db_name_len(table->name.m_name); +// Append partition suffix when FK is created in inplace alter + const size_t constr_len= strlen(constraint_name); + size_t alloc_len = 2 + db_len + constr_len + 2; + if (part_suffix) + { + alloc_len += part_suffix_len + 1; + } + foreign->id = static_cast(mem_heap_alloc( - foreign->heap, db_len + strlen(constraint_name) + 2)); + foreign->heap, alloc_len)); memcpy(foreign->id, table->name.m_name, db_len); foreign->id[db_len] = '/'; - strcpy(foreign->id + db_len + 1, constraint_name); + char *pos= foreign->id + db_len + 1; + strcpy(pos, constraint_name); + if (part_suffix) + { + pos+= constr_len; + *(pos++) = '\xFF'; + strcpy(pos, part_suffix); + } /* Check if any existing foreign key has the same id, this is needed only if user supplies the constraint name */ @@ -3216,6 +3234,10 @@ innobase_get_foreign_key_info( ulint num_fk = 0; Alter_info* alter_info = ha_alter_info->alter_info; const CHARSET_INFO* cs = thd_charset(trx->mysql_thd); + char db_name[MAX_DATABASE_NAME_LEN + 1]; + char t_name[MAX_TABLE_NAME_LEN + 1]; + const char * part_suffix= is_partition(table->name.basename()); + const size_t part_suffix_len= part_suffix ? strlen(part_suffix) : 0; DBUG_ENTER("innobase_get_foreign_key_info"); @@ -3280,14 +3302,51 @@ innobase_get_foreign_key_info( add_fk[num_fk] = dict_mem_foreign_create(); + LEX_CSTRING table_name = fk_key->ref_table; + CHARSET_INFO* to_cs = &my_charset_filename; + + if (!strncmp(table_name.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + table_name.str + += sizeof srv_mysql50_table_name_prefix - 1; + table_name.length + -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } + + uint errors; + Lex_ident_table t; + t.str = t_name; + t.length = strconvert(cs, LEX_STRING_WITH_LEN(table_name), + to_cs, t_name, MAX_TABLE_NAME_LEN, + &errors); + Lex_ident_db d(fk_key->ref_db); + if (!d.str) { + d.str = table->name.m_name; + d.length = table->name.dblen(); + } + + if (!strncmp(d.str, srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix - 1)) { + d.str += sizeof srv_mysql50_table_name_prefix - 1; + d.length -= sizeof srv_mysql50_table_name_prefix - 1; + to_cs = system_charset_info; + } else if (d.str == table->name.m_name) { + goto name_converted; + } else { + to_cs = &my_charset_filename; + } + + d.length = strconvert(cs, LEX_STRING_WITH_LEN(d), to_cs, + db_name, MAX_DATABASE_NAME_LEN, + &errors); + d.str = db_name; + +name_converted: dict_sys.lock(SRW_LOCK_CALL); referenced_table_name = dict_get_referenced_table( - table->name.m_name, - LEX_STRING_WITH_LEN(fk_key->ref_db), - LEX_STRING_WITH_LEN(fk_key->ref_table), - &referenced_table, - add_fk[num_fk]->heap, cs); + d, t, &referenced_table, add_fk[num_fk]->heap); /* Test the case when referenced_table failed to open, if trx->check_foreigns is not set, we should @@ -3351,7 +3410,8 @@ innobase_get_foreign_key_info( table, index, column_names, num_col, referenced_table_name, referenced_table, referenced_index, - referenced_column_names, referenced_num_col)) { + referenced_column_names, referenced_num_col, + part_suffix, part_suffix_len)) { my_error( ER_DUP_CONSTRAINT_NAME, MYF(0), @@ -7929,6 +7989,7 @@ ha_innobase::prepare_inplace_alter_table( bool add_fts_idx = false; dict_s_col_list*s_cols = NULL; mem_heap_t* s_heap = NULL; + char foreign_id[MAX_FOREIGN_ID_LEN]; DBUG_ENTER("prepare_inplace_alter_table"); DBUG_ASSERT(!ha_alter_info->handler_ctx); @@ -8262,8 +8323,18 @@ ha_innobase::prepare_inplace_alter_table( the FOREIGN KEY constraint name, compare to the full constraint name. */ fid = fid ? fid + 1 : foreign->id; - - if (Lex_ident_column(Lex_cstring_strlen(fid)). +// For inplace drop FK find partition-suffixed FKs by ID without suffix + Lex_cstring id; + id.str= fid; + const char *suff= strchr(fid, '\xFF'); + if (suff) { + id.length= size_t(suff - fid); + memcpy(foreign_id, fid, id.length); + id.str= foreign_id; + foreign_id[id.length]= 0; + } else + id.length = strlen(fid); + if (Lex_ident_column(id). streq(drop.name)) { goto found_fk; } @@ -10020,6 +10091,10 @@ innobase_update_foreign_try( foreign_id++; +// Used by inplace add foreign key + const char *part_suffix= is_partition(ctx->old_table->name.m_name); + size_t part_suffix_len= part_suffix ? strlen(part_suffix) : 0; + for (i = 0; i < ctx->num_to_add_fk; i++) { dict_foreign_t* fk = ctx->add_fk[i]; @@ -10027,7 +10102,8 @@ innobase_update_foreign_try( || fk->foreign_table == ctx->old_table); dberr_t error = dict_create_add_foreign_id( - &foreign_id, ctx->old_table->name.m_name, fk); + &foreign_id, ctx->old_table->name.m_name, fk, + part_suffix, part_suffix_len); if (error != DB_SUCCESS) { my_error(ER_TOO_LONG_IDENT, MYF(0), @@ -10415,10 +10491,12 @@ commit_try_rebuild( char* old_name= mem_heap_strdup(ctx->heap, user_table->name.m_name); dberr_t error = row_rename_table_for_mysql(user_table->name.m_name, - ctx->tmp_name, trx, false); + ctx->tmp_name, trx, + RENAME_REBUILD); if (error == DB_SUCCESS) { error = row_rename_table_for_mysql( - rebuilt_table->name.m_name, old_name, trx, false); + rebuilt_table->name.m_name, old_name, trx, + RENAME_REBUILD); if (error == DB_SUCCESS) { /* The statistics for the surviving indexes will be re-inserted in alter_stats_rebuild(). */ diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index b27cfe5cc6687..249f8568a5072 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -5773,6 +5773,33 @@ static ST_FIELD_INFO innodb_sys_foreign_fields_info[]= }; } // namespace Show +// In I_S show partition suffix with \xFF replaced by ':'. +// #P# separator is skipped, if there is #SP# we skip to subpartition name +// (partition name is skipped). +/* Secondary partition foreign keys use '\xFF' to add suffix */ +static void fixup_foreign_id(char *dest, const char *src) +{ + while (*src) + { + if (*src == '\xFF') + { + *(dest++)= ':'; + src++; + if (src[0] == '#' && + src[1] == table_name_t::part_suffix[1] && + src[2] == '#') + { + src+= 3; + if (const char *sp= strstr(src, table_name_t::subpart_suffix)) + src= sp + 4; + } + } + else + *(dest++)= *(src++); + } + *dest= '\0'; +} + /**********************************************************************//** Function to fill information_schema.innodb_sys_foreign with information collected by scanning SYS_FOREIGN table. @@ -5790,8 +5817,10 @@ i_s_dict_fill_sys_foreign( DBUG_ENTER("i_s_dict_fill_sys_foreign"); fields = table_to_fill->field; + char foreign_id[MAX_FOREIGN_ID_LEN]; + fixup_foreign_id(foreign_id, foreign->id); - OK(field_store_string(fields[SYS_FOREIGN_ID], foreign->id)); + OK(field_store_string(fields[SYS_FOREIGN_ID], foreign_id)); OK(field_store_string(fields[SYS_FOREIGN_FOR_NAME], foreign->foreign_table_name)); @@ -5983,8 +6012,10 @@ i_s_dict_fill_sys_foreign_cols( DBUG_ENTER("i_s_dict_fill_sys_foreign_cols"); fields = table_to_fill->field; + char foreign_id[MAX_FOREIGN_ID_LEN]; + fixup_foreign_id(foreign_id, name); - OK(field_store_string(fields[SYS_FOREIGN_COL_ID], name)); + OK(field_store_string(fields[SYS_FOREIGN_COL_ID], foreign_id)); OK(field_store_string(fields[SYS_FOREIGN_COL_FOR_NAME], for_col_name)); diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index c40df12babe5a..8d43d0a5c75fd 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -129,7 +129,9 @@ dict_create_add_foreign_id( ulint* id_nr, /*!< in/out: number to use in id generation; incremented if used */ const char* name, /*!< in: table name */ - dict_foreign_t* foreign); /*!< in/out: foreign key */ + dict_foreign_t* foreign, /*!< in/out: foreign key */ + const char * part_suffix, + size_t part_suffix_len); /** Adds the given set of foreign key objects to the dictionary tables in the database. This function does not modify the dictionary cache. The diff --git a/storage/innobase/include/dict0crea.inl b/storage/innobase/include/dict0crea.inl index 5641206d313cd..14524b7f80e26 100644 --- a/storage/innobase/include/dict0crea.inl +++ b/storage/innobase/include/dict0crea.inl @@ -35,12 +35,15 @@ where the numbers start from 1, and are given locally for this table, that is, the number is not global, as it used to be before MySQL 4.0.18. */ UNIV_INLINE dberr_t +// Used in inplace add foreign key dict_create_add_foreign_id( /*=======================*/ ulint* id_nr, /*!< in/out: number to use in id generation; incremented if used */ const char* name, /*!< in: table name */ - dict_foreign_t* foreign)/*!< in/out: foreign key */ + dict_foreign_t* foreign, /*!< in/out: foreign key */ + const char * part_suffix, + size_t part_suffix_len) { DBUG_ENTER("dict_create_add_foreign_id"); @@ -49,15 +52,28 @@ dict_create_add_foreign_id( ulint namelen = strlen(name); char* id = static_cast( mem_heap_alloc(foreign->heap, - namelen + 20)); +// +1 for \xFF + namelen + 21)); + int idlen; + char buf[MAX_FOREIGN_ID_LEN]; + const bool tmp_name= dict_table_t::is_temporary_name(name); + ut_ad(is_partition(name) == part_suffix); +// First treat name without partition suffix ... + if (part_suffix) { + size_t len= size_t(part_suffix - name); + memcpy(buf, name, len); + buf[len]= 0; + name= buf; + } - if (dict_table_t::is_temporary_name(name)) { + if (tmp_name) { /* no overflow if number < 1e13 */ - sprintf(id, "%s_ibfk_%lu", name, + idlen= sprintf(id, "%s_ibfk_%lu", name, (ulong) (*id_nr)++); + ut_ad(idlen > 0); } else { - char table_name[MAX_TABLE_NAME_LEN + 21]; + char table_name[MAX_TABLE_NAME_LEN + 22]; uint errors = 0; strncpy(table_name, name, (sizeof table_name) - 1); @@ -75,14 +91,26 @@ dict_create_add_foreign_id( } /* no overflow if number < 1e13 */ - sprintf(id, "%s_ibfk_%lu", table_name, + idlen= sprintf(id, "%s_ibfk_%lu", table_name, (ulong) (*id_nr)++); + ut_ad(idlen > 0); +// \xFF does not validate well. We don't check part that is not visible at SQL layer. if (innobase_check_identifier_length( strchr(id,'/') + 1)) { DBUG_RETURN(DB_IDENTIFIER_TOO_LONG); } } + +// ... and then append partition suffix to the end. + if (part_suffix) + { + id[idlen++]= '\xFF'; + memcpy(&id[idlen], part_suffix, part_suffix_len); + idlen+= (int) part_suffix_len; + id[idlen]= 0; + } + foreign->id = id; DBUG_PRINT("dict_create_add_foreign_id", diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index c78538a2bc64b..83c28719f38fb 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -60,17 +60,11 @@ Open a table from its database and table name, this is currently used by foreign constraint parser to get the referenced table. @return complete table name with database and table name, allocated from heap memory passed in */ -char* -dict_get_referenced_table( -/*======================*/ - const char* name, /*!< in: foreign key table name */ - const char* database_name, /*!< in: table db name */ - ulint database_name_len,/*!< in: db name length */ - const char* table_name, /*!< in: table name */ - ulint table_name_len, /*!< in: table name length */ - dict_table_t** table, /*!< out: table object or NULL */ - mem_heap_t* heap, /*!< in: heap memory */ - CHARSET_INFO* from_cs); /*!< in: table name charset */ +char *dict_get_referenced_table( + Lex_ident_db database_name, /*!< in: table db name */ + Lex_ident_table table_name, /*!< in: table name */ + dict_table_t **table, /*!< out: table object or NULL */ + mem_heap_t *heap); /*!< in/out: heap memory */ /*********************************************************************//** Frees a foreign key struct. */ void diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index d2f241f3d7330..5907a4930d53b 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1687,14 +1687,39 @@ struct dict_foreign_matches_id { bool operator()(const dict_foreign_t* foreign) const { const Lex_ident_column ident = Lex_cstring_strlen(m_id); - if (ident.streq(Lex_cstring_strlen(foreign->id))) { - return(true); +// Used by copy drop foreign key: like for inplace we must ignore partition suffix +// when finding FKs. + const char *s; + char foreign_id[MAX_FOREIGN_ID_LEN]; + const char* stripped_id; + Lex_cstring id; + + /* Similar to dict_print_info_on_foreign_key_in_create_format() */ + if ((s= strchr(foreign->id, '/'))) { + /* Strip the preceding database name from the constraint id */ + stripped_id = s + 1; + ut_ad(*stripped_id); + } else { + stripped_id = foreign->id; } - if (const char* pos = strchr(foreign->id, '/')) { - if (ident.streq(Lex_cstring_strlen(pos + 1))) { - return(true); - } + + if ((s= strchr(stripped_id, '\xFF')) && s > stripped_id) { + size_t l= size_t(s - stripped_id); + memcpy(foreign_id, stripped_id, l); + foreign_id[l]= 0; + id.str= foreign_id; + id.length= l; + } + else + { + id.str= stripped_id; + id.length= strlen(stripped_id); } + + if (ident.streq(id)) { + return(true); + } + return(false); } @@ -1906,6 +1931,9 @@ typedef enum { DICT_FRM_INCONSISTENT_KEYS = 3 /*!< Key count mismatch */ } dict_frm_t; +char* +is_partition(const char *file_name); + /** Data structure for a database table. Most fields will be zero-initialized in dict_table_t::create(). */ struct dict_table_t { @@ -1957,7 +1985,9 @@ struct dict_table_t { which denotes temporary or intermediate tables in MariaDB. */ static bool is_temporary_name(const char* name) { - return strstr(name, "/#sql"); + return strstr(name, "/#sql") || ( + is_partition(name) && + 0 == memcmp(name + strlen(name) - 5, "#TMP#", 5)); } /** @return whether instant ALTER TABLE is in effect */ diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index b5239c90c8d2a..c6f1a56007fce 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -127,6 +127,8 @@ struct table_name_t /** The start of the table basename suffix for partitioned tables */ static const char part_suffix[4]; +// Used by fixup_foreign_id() to cut out needless data in I_S, OS-dependent. + static const char subpart_suffix[5]; /** Determine the partition or subpartition name suffix. @return the partition name diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index b09dcfc9bab4b..5388b226ba5ca 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -169,6 +169,12 @@ innobase_convert_from_id( const char* from, /*!< in: identifier to convert */ ulint len); /*!< in: length of 'to', in bytes; should be at least 3 * strlen(to) + 1 */ +/******************************************************************//** +Makes all characters in a NUL-terminated UTF-8 string lower case. */ +void +innobase_casedn_str( +/*================*/ + char* a); /*!< in/out: string to put in lower case */ #ifdef WITH_WSREP ulint wsrep_innobase_mysql_sort(int mysql_type, uint charset_number, diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 878d9c9f1a267..16d300f610937 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -370,6 +370,17 @@ row_import_tablespace_for_mysql( row_prebuilt_t* prebuilt) /*!< in: prebuilt struct in MySQL */ MY_ATTRIBUTE((nonnull, warn_unused_result)); +enum rename_fk { + /** ignore FOREIGN KEY constraints */ + RENAME_IGNORE_FK= 0, + /** parse and enforce FOREIGN KEY constaints */ + RENAME_FK, + /** Rename a table as part of a native table-rebuilding DDL operation */ + RENAME_REBUILD, + /** Rename as part of ALTER TABLE...ALGORITHM=COPY */ + RENAME_ALTER_COPY +}; + /*********************************************************************//** Renames a table for MySQL. @return error code or DB_SUCCESS */ @@ -379,7 +390,7 @@ row_rename_table_for_mysql( const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx, /*!< in/out: transaction */ - bool use_fk) /*!< in: whether to parse and enforce + rename_fk fk) /*!< in: how to handle FOREIGN KEY constraints */ MY_ATTRIBUTE((nonnull, warn_unused_result)); diff --git a/storage/innobase/include/sql_funcs.h b/storage/innobase/include/sql_funcs.h new file mode 100644 index 0000000000000..79ae1c1244959 --- /dev/null +++ b/storage/innobase/include/sql_funcs.h @@ -0,0 +1,132 @@ +/***************************************************************************** + +Copyright (c) 2022, MariaDB Corporation. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/** + @file include/sql_funcs.h + + SQL procedures for InnoDB system tables operation +*/ + +/** + Rename foreign keys for rename table + + @see row_rename_table_for_mysql() +*/ +constexpr const char *rename_constraint_ids= +R"===(PROCEDURE RENAME_CONSTRAINT_IDS () IS + gen_constr_prefix CHAR; + new_db_name CHAR; + foreign_id CHAR; + foreign_id2 CHAR; + constr_name CHAR; + new_foreign_id CHAR; + old_db_name_len INT; + new_db_name_len INT; + id_len INT; + offset INT; + offset2 INT; + constr_name_len INT; + found INT; + BEGIN + found := 1; + old_db_name_len := INSTR(:old_table_name, '/') - 1; + new_db_name_len := INSTR(:new_table_name, '/') - 1; + new_db_name := SUBSTR(:new_table_name, 0, + new_db_name_len); + gen_constr_prefix := CONCAT(:old_table_name_utf8, + '_ibfk_'); + WHILE found = 1 LOOP + SELECT ID INTO foreign_id + FROM SYS_FOREIGN + WHERE FOR_NAME = :old_table_name + AND TO_BINARY(FOR_NAME) + = TO_BINARY(:old_table_name) + LOCK IN SHARE MODE; + IF (SQL % NOTFOUND) THEN + found := 0; + ELSE + UPDATE SYS_FOREIGN + SET FOR_NAME = :new_table_name + WHERE ID = foreign_id; + id_len := LENGTH(foreign_id); + foreign_id2 := foreign_id; + offset := INSTR(foreign_id, ')===" "\xFF" R"===('); + IF (SUBSTR(foreign_id, offset, 1) = ')===" "\xFF" R"===(') THEN + offset2 := offset + 1; + ELSE + offset2 := offset; + END IF; + IF (:old_is_tmp > 0 AND offset > 0) THEN + foreign_id := CONCAT(SUBSTR(foreign_id2, 0, offset - 1), + SUBSTR(foreign_id2, offset2, id_len - offset2)); + id_len := LENGTH(foreign_id); + END IF; +)===" +// CONVERT OUT: remove partition suffix +R"===( + IF (:old_is_part > 0) THEN + offset := INSTR(foreign_id, ')===" "\xFF" R"===('); + IF (offset > 0) THEN + foreign_id := CONCAT(SUBSTR(foreign_id, 0, offset - 1)); + id_len := LENGTH(foreign_id); + END IF; + END IF; +)===" +// CONVERT IN: append partition suffix +R"===( + IF (:new_is_part > 0) THEN + foreign_id := CONCAT(foreign_id, ')===" "\xFF" R"===(', :new_part); + id_len := LENGTH(foreign_id); + END IF; + IF (INSTR(foreign_id, '/') > 0) THEN + IF (INSTR(foreign_id, + gen_constr_prefix) > 0) + THEN + offset := INSTR(foreign_id, '_ibfk_') - 1; + new_foreign_id := + CONCAT(:new_table_utf8, + SUBSTR(foreign_id, offset, id_len - offset)); + ELSE + constr_name_len := id_len - old_db_name_len; + constr_name := SUBSTR(foreign_id, old_db_name_len, + constr_name_len); + IF (:new_is_tmp > 0) THEN + new_foreign_id := CONCAT(new_db_name, ')===" "/\xFF\xFF" R"===(', + SUBSTR(constr_name, 1, constr_name_len - 1)); + ELSE + new_foreign_id := CONCAT(new_db_name, constr_name); + END IF; + END IF; + UPDATE SYS_FOREIGN + SET ID = new_foreign_id + WHERE ID = foreign_id2; + UPDATE SYS_FOREIGN_COLS + SET ID = new_foreign_id + WHERE ID = foreign_id2; + END IF; + END IF; + END LOOP; +)===" +// Skip change FKs referencing this table if we just rename to backup +R"===( + IF (:rename_refs > 0) THEN + UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name + WHERE REF_NAME = :old_table_name + AND TO_BINARY(REF_NAME) = TO_BINARY(:old_table_name); + END IF; +END;)==="; diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 7e1312e3f7e4c..9a0274833c429 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -256,6 +256,10 @@ terminating '\0'. InnoDB can handle longer names internally */ the MySQL's NAME_LEN, see check_and_convert_db_name(). */ #define MAX_DATABASE_NAME_LEN MAX_TABLE_NAME_LEN +/** The maximum length of foreign key ID including trailing zero */ +/* "db/table_name\x255#P#part_name#SP#subpart_name\0" */ +#define MAX_FOREIGN_ID_LEN (MAX_TABLE_NAME_LEN * 3 + 1 + 1 + 3 + 4 + 1) + /** MAX_FULL_NAME_LEN defines the full name path including the database name and table name. In addition, 14 bytes is added for: 2 for surrounding quotes around table name diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index bf1718f6cfa14..a4a143b1f95d8 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -61,6 +61,7 @@ Created 9/17/2000 Heikki Tuuri #include "srv0mon.h" #include "srv0start.h" #include "log.h" +#include "sql_funcs.h" #include #include @@ -2520,7 +2521,7 @@ row_rename_table_for_mysql( const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx, /*!< in/out: transaction */ - bool use_fk) /*!< in: whether to parse and enforce + rename_fk fk) /*!< in: how to handle FOREIGN KEY constraints */ { dict_table_t* table = NULL; @@ -2528,8 +2529,8 @@ row_rename_table_for_mysql( mem_heap_t* heap = NULL; const char** constraints_to_drop = NULL; ulint n_constraints_to_drop = 0; - ibool old_is_tmp, new_is_tmp; pars_info_t* info = NULL; + const bool do_rename_fk = (fk == RENAME_FK || fk == RENAME_ALTER_COPY); ut_a(old_name != NULL); ut_a(new_name != NULL); @@ -2542,8 +2543,11 @@ row_rename_table_for_mysql( trx->op_info = "renaming table"; - old_is_tmp = dict_table_t::is_temporary_name(old_name); - new_is_tmp = dict_table_t::is_temporary_name(new_name); + const bool old_is_tmp = dict_table_t::is_temporary_name(old_name); + const bool new_is_tmp = dict_table_t::is_temporary_name(new_name); + const char * old_is_part = is_partition(old_name); + const char * new_is_part = is_partition(new_name); + table = dict_table_open_on_name(old_name, true, DICT_ERR_IGNORE_FK_NOKEY); @@ -2604,9 +2608,9 @@ row_rename_table_for_mysql( goto funct_exit; - } else if (use_fk && !old_is_tmp && new_is_tmp) { - /* MySQL is doing an ALTER TABLE command and it renames the - original table to a temporary table name. We want to preserve + } else if (fk == RENAME_ALTER_COPY && !old_is_tmp && new_is_tmp) { + /* ALOGRITHM=COPY ALTER TABLE is renaming the + original table to a temporary name. We want to preserve the original foreign key constraint definitions despite the name change. An exception is those constraints for which the ALTER TABLE contained DROP FOREIGN KEY .*/ @@ -2650,7 +2654,10 @@ row_rename_table_for_mysql( goto rollback_and_exit; } - if (!new_is_tmp) { + if (fk == RENAME_IGNORE_FK || do_rename_fk || !new_is_tmp) { +// (fk == RENAME_ALTER_COPY && new_is_tmp) is rename to backup for non-FK alter. +// We must not change FK refs (referencing this table) in that case. + const bool rename_refs= !(fk == RENAME_ALTER_COPY && new_is_tmp); /* Rename all constraints. */ char new_table_name[MAX_TABLE_NAME_LEN + 1]; char old_table_utf8[MAX_TABLE_NAME_LEN + 1]; @@ -2694,73 +2701,26 @@ row_rename_table_for_mysql( } pars_info_add_str_literal(info, "new_table_utf8", new_table_name); - - err = que_eval_sql( - info, - "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n" - "gen_constr_prefix CHAR;\n" - "new_db_name CHAR;\n" - "foreign_id CHAR;\n" - "new_foreign_id CHAR;\n" - "old_db_name_len INT;\n" - "old_t_name_len INT;\n" - "new_db_name_len INT;\n" - "id_len INT;\n" - "offset INT;\n" - "found INT;\n" - "BEGIN\n" - "found := 1;\n" - "old_db_name_len := INSTR(:old_table_name, '/')-1;\n" - "new_db_name_len := INSTR(:new_table_name, '/')-1;\n" - "new_db_name := SUBSTR(:new_table_name, 0,\n" - " new_db_name_len);\n" - "old_t_name_len := LENGTH(:old_table_name);\n" - "gen_constr_prefix := CONCAT(:old_table_name_utf8,\n" - " '_ibfk_');\n" - "WHILE found = 1 LOOP\n" - " SELECT ID INTO foreign_id\n" - " FROM SYS_FOREIGN\n" - " WHERE FOR_NAME = :old_table_name\n" - " AND TO_BINARY(FOR_NAME)\n" - " = TO_BINARY(:old_table_name)\n" - " LOCK IN SHARE MODE;\n" - " IF (SQL % NOTFOUND) THEN\n" - " found := 0;\n" - " ELSE\n" - " UPDATE SYS_FOREIGN\n" - " SET FOR_NAME = :new_table_name\n" - " WHERE ID = foreign_id;\n" - " id_len := LENGTH(foreign_id);\n" - " IF (INSTR(foreign_id, '/') > 0) THEN\n" - " IF (INSTR(foreign_id,\n" - " gen_constr_prefix) > 0)\n" - " THEN\n" - " offset := INSTR(foreign_id, '_ibfk_') - 1;\n" - " new_foreign_id :=\n" - " CONCAT(:new_table_utf8,\n" - " SUBSTR(foreign_id, offset,\n" - " id_len - offset));\n" - " ELSE\n" - " new_foreign_id :=\n" - " CONCAT(new_db_name,\n" - " SUBSTR(foreign_id,\n" - " old_db_name_len,\n" - " id_len - old_db_name_len));\n" - " END IF;\n" - " UPDATE SYS_FOREIGN\n" - " SET ID = new_foreign_id\n" - " WHERE ID = foreign_id;\n" - " UPDATE SYS_FOREIGN_COLS\n" - " SET ID = new_foreign_id\n" - " WHERE ID = foreign_id;\n" - " END IF;\n" - " END IF;\n" - "END LOOP;\n" - "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n" - "WHERE REF_NAME = :old_table_name\n" - " AND TO_BINARY(REF_NAME)\n" - " = TO_BINARY(:old_table_name);\n" - "END;\n", trx); + /* Old foreign ID for temporary constraint was written like this: + db_name/\xFFconstraint_name */ + pars_info_add_int4_literal(info, "old_is_tmp", + do_rename_fk && old_is_tmp); + /* New foreign ID for temporary constraint is written like this: + db_name/\xFF\xFFconstraint_name */ + pars_info_add_int4_literal(info, "new_is_tmp", + do_rename_fk && new_is_tmp); + pars_info_add_str_literal(info, "new_part", new_is_part ? new_is_part : ""); + pars_info_add_int4_literal(info, "old_is_part", old_is_part != NULL); + pars_info_add_int4_literal(info, "new_is_part", new_is_part != NULL); + pars_info_add_int4_literal(info, "rename_refs", rename_refs); + + err = que_eval_sql(info, rename_constraint_ids, trx); + /* + Higher layer should throw not ER_TABLE_EXISTS_ERROR but + ER_ERROR_ON_RENAME with explanation about foreign keys. + */ + if (err == DB_DUPLICATE_KEY) + err = DB_FOREIGN_DUPLICATE_KEY; } else if (n_constraints_to_drop > 0) { /* Drop some constraints of tmp tables. */ @@ -2821,13 +2781,14 @@ row_rename_table_for_mysql( an ALTER TABLE, not in a RENAME. */ dict_names_t fk_tables; - err = dict_load_foreigns( - new_name, nullptr, trx->id, - !old_is_tmp || trx->check_foreigns, - use_fk - ? DICT_ERR_IGNORE_NONE - : DICT_ERR_IGNORE_FK_NOKEY, - fk_tables); + if (!new_is_tmp) + err = dict_load_foreigns( + new_name, nullptr, trx->id, + !old_is_tmp || trx->check_foreigns, + fk == RENAME_FK || fk == RENAME_ALTER_COPY + ? DICT_ERR_IGNORE_NONE + : DICT_ERR_IGNORE_FK_NOKEY, + fk_tables); if (err != DB_SUCCESS) { if (old_is_tmp) { diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index 64d323d369ca0..620f8add5eb1d 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -16417,38 +16417,6 @@ void ha_mroonga::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg) DBUG_VOID_RETURN; } -bool ha_mroonga::wrapper_is_fk_defined_on_table_or_index(uint index) -{ - MRN_DBUG_ENTER_METHOD(); - bool res; - MRN_SET_WRAP_SHARE_KEY(share, table->s); - MRN_SET_WRAP_TABLE_KEY(this, table); - res = wrap_handler->is_fk_defined_on_table_or_index(index); - MRN_SET_BASE_SHARE_KEY(share, table->s); - MRN_SET_BASE_TABLE_KEY(this, table); - DBUG_RETURN(res); -} - -bool ha_mroonga::storage_is_fk_defined_on_table_or_index(uint index) -{ - MRN_DBUG_ENTER_METHOD(); - bool res = handler::is_fk_defined_on_table_or_index(index); - DBUG_RETURN(res); -} - -bool ha_mroonga::is_fk_defined_on_table_or_index(uint index) -{ - MRN_DBUG_ENTER_METHOD(); - bool res; - if (share->wrapper_mode) - { - res = wrapper_is_fk_defined_on_table_or_index(index); - } else { - res = storage_is_fk_defined_on_table_or_index(index); - } - DBUG_RETURN(res); -} - char *ha_mroonga::wrapper_get_foreign_key_create_info() { MRN_DBUG_ENTER_METHOD(); diff --git a/storage/mroonga/ha_mroonga.hpp b/storage/mroonga/ha_mroonga.hpp index 037cf492a2c2a..d95843d09c6a2 100644 --- a/storage/mroonga/ha_mroonga.hpp +++ b/storage/mroonga/ha_mroonga.hpp @@ -604,7 +604,6 @@ class ha_mroonga: public handler int index_last(uchar *buf) mrn_override; #endif void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg) mrn_override; - bool is_fk_defined_on_table_or_index(uint index) mrn_override; char *get_foreign_key_create_info() mrn_override; #ifdef MRN_HANDLER_HAVE_GET_TABLESPACE_NAME char *get_tablespace_name(THD *thd, char *name, uint name_len);