--source include/have_innodb.inc
--source include/have_debug.inc

CREATE TABLE parent (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;
INSERT INTO parent VALUES(1,2),(2,3);

CREATE INDEX tb ON parent(b);

INSERT INTO parent VALUES(10,20),(20,30);

CREATE TABLE child (a1 INT PRIMARY KEY, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON child(a2);

INSERT INTO child VALUES(10,20);

--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON
ALTER TABLE child ADD FOREIGN KEY(a2) REFERENCES parent(b),
ALGORITHM = INPLACE;

SET foreign_key_checks = 0;

ALTER TABLE child ADD CONSTRAINT fk_1 FOREIGN KEY (a2)
REFERENCES parent(b) ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

# duplicated foreign key name
--error ER_FK_DUP_NAME
ALTER TABLE child ADD CONSTRAINT fk_1 FOREIGN KEY (a2)
REFERENCES parent(b) ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SET foreign_key_checks = 1;

INSERT INTO child VALUES(1,2),(2,3);

--error ER_NO_REFERENCED_ROW_2
INSERT INTO child VALUES(4,4);

SELECT * FROM parent;

SET foreign_key_checks = 0;

# This would fail. No corresponding index
--error ER_FK_NO_INDEX_PARENT
ALTER TABLE child ADD CONSTRAINT fk_20 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SHOW WARNINGS;

SHOW ERRORS;

CREATE INDEX idx1 on parent(a, b);

ALTER TABLE child ADD CONSTRAINT fk_10 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;

# This should be successful, as we added the index
ALTER TABLE child ADD CONSTRAINT fk_2 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE, ADD INDEX idx1(a1,a2),
ALGORITHM = INPLACE;

ALTER TABLE child ADD CONSTRAINT fk_3 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

SET foreign_key_checks = 1;

--error ER_NO_REFERENCED_ROW_2
INSERT INTO child VALUES(5,4);

SHOW CREATE TABLE child;

DELETE FROM parent where a = 1;

SELECT * FROM child;

# Now test referenced table cannot be opened. This should work fine
# when foreign_key_checks is set to 0

SET foreign_key_checks = 0;

# This is to test the scenario we cannot open the referenced table.
# Since foreign_key_checks is set to 0, the foreign key should still
# be added.
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG = '+d,innodb_test_open_ref_fail';
ALTER TABLE child ADD CONSTRAINT fk_4 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

SELECT t2.name, t1.name FROM information_schema.innodb_sys_columns t1, information_schema.innodb_sys_tables t2 WHERE t1.table_id = t2.table_id AND t2.name LIKE "%child" ORDER BY t1.name;

SELECT NAME FROM information_schema.INNODB_SYS_TABLES;

# this should succeed, since we disabled the foreign key check
INSERT INTO child VALUES(5,4);

SET foreign_key_checks = 1;

--error ER_NO_REFERENCED_ROW_2
INSERT INTO child VALUES(6,5);

SET foreign_key_checks = 0;

# Create some table with 'funny' characters, for testing the
# error message
CREATE TABLE `#parent` (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;

CREATE INDEX tb ON `#parent`(a, b);

CREATE TABLE `#child` (a1 INT PRIMARY KEY, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON `#child`(a1, a2);

# This is to test the scenario no foreign index, alter table should fail
SET DEBUG_DBUG = '+d,innodb_test_no_foreign_idx';
--error ER_FK_NO_INDEX_CHILD,
ALTER TABLE `#child` ADD CONSTRAINT fk_40 FOREIGN KEY (a1, a2)
REFERENCES `#parent`(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SHOW ERRORS;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

# This is to test the scenario no index on referenced table,
# alter table should fail
SET DEBUG_DBUG = '+d,innodb_test_no_reference_idx';
--error ER_FK_NO_INDEX_PARENT,
ALTER TABLE child ADD CONSTRAINT fk_42 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SHOW ERRORS;

# This is to test the scenario no index on referenced table,
# alter table should fail
SET DEBUG_DBUG = '+d,innodb_test_wrong_fk_option';
--error ER_FK_INCORRECT_OPTION
ALTER TABLE child ADD CONSTRAINT fk_42 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

# This is to test the scenario cannot add fk to the system table,
# alter table should fail
SET DEBUG_DBUG = '+d,innodb_test_cannot_add_fk_system';
--error ER_FK_FAIL_ADD_SYSTEM
ALTER TABLE `#child` ADD CONSTRAINT fk_43 FOREIGN KEY (a1, a2)
REFERENCES `#parent`(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SHOW ERRORS;

DROP TABLE `#child`;
DROP TABLE `#parent`;

# Now test add multiple foreign key constrain in a single clause
SET foreign_key_checks = 0;

ALTER TABLE child ADD CONSTRAINT fk_5 FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT fk_6 FOREIGN KEY (a1, a2)
REFERENCES parent(a, b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

DROP TABLE child;
DROP TABLE parent;

# Test a case where child's foreign key index is being dropped in the
# same clause of adding the foreign key. In theory, MySQL will
# automatically create a new index to meet the index requirement
CREATE TABLE parent (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;

INSERT INTO parent VALUES(1,2),(2,3);

CREATE INDEX tb ON parent(b);

INSERT INTO parent VALUES(10,20),(20,30);

CREATE TABLE child (a1 INT PRIMARY KEY, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON child(a2);

INSERT INTO child VALUES(10,20);

SET foreign_key_checks = 0;

ALTER TABLE child DROP INDEX tb, ADD CONSTRAINT fk_4 FOREIGN KEY (a2)
REFERENCES parent(b) ON DELETE CASCADE ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SHOW CREATE TABLE child;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN;

SELECT * FROM information_schema.INNODB_SYS_FOREIGN_COLS;

SET foreign_key_checks = 1;

DROP TABLE child;

DROP TABLE parent;

# Test ADD FOREIGN KEY together with renaming columns.
CREATE TABLE parent (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;

INSERT INTO parent VALUES(1,2),(2,3);

CREATE INDEX tb ON parent(b);

INSERT INTO parent VALUES(10,20),(20,30);

CREATE TABLE child (a1 INT PRIMARY KEY, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON child(a2);

SET foreign_key_checks = 0;

--error ER_KEY_COLUMN_DOES_NOT_EXITS
ALTER TABLE child CHANGE a2 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

ALTER TABLE child CHANGE a2 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a3) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

DROP TABLE child;

DROP TABLE parent;

# Add test for add Primary key and FK on changing columns
CREATE TABLE parent (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;
INSERT INTO parent VALUES(1,2),(2,3);

CREATE INDEX tb ON parent(b);

INSERT INTO parent VALUES(10,20),(20,30);

CREATE TABLE child (a1 INT NOT NULL, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON child(a2);

SET foreign_key_checks = 0;

# Let's rebuild the table and add the FK, make the add FK failed.

SET DEBUG_DBUG = '+d,innodb_test_cannot_add_fk_system';
--error ER_FK_FAIL_ADD_SYSTEM
ALTER TABLE child ADD PRIMARY KEY idx (a3), CHANGE a1 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;
SET DEBUG_DBUG = @saved_debug_dbug;

SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN;
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS;
SELECT t2.name, t1.name FROM information_schema.innodb_sys_columns t1, information_schema.innodb_sys_tables t2 WHERE t1.table_id = t2.table_id AND t2.name LIKE "%child" ORDER BY t1.name;
SELECT NAME FROM information_schema.INNODB_SYS_TABLES;

# This should be successful. It will also check any left over
# from previous failed operation (if dictionary entries not cleaned,
# it will have dup key error.
ALTER TABLE child ADD PRIMARY KEY idx (a3), CHANGE a1 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN;
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS;
SELECT t2.name, t1.name FROM information_schema.innodb_sys_columns t1, information_schema.innodb_sys_tables t2 WHERE t1.table_id = t2.table_id AND t2.name LIKE "%child" ORDER BY t1.name;
SELECT NAME FROM information_schema.INNODB_SYS_TABLES;

SHOW CREATE TABLE child;

DROP TABLE child;

CREATE TABLE child (a1 INT NOT NULL, a2 INT) ENGINE = InnoDB;

# Now try primary index and FK
ALTER TABLE child ADD PRIMARY KEY idx (a1),
ADD CONSTRAINT fk_1 FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SELECT * from information_schema.INNODB_SYS_FOREIGN;
SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;
SELECT t2.name, t1.name FROM information_schema.innodb_sys_columns t1, information_schema.innodb_sys_tables t2 WHERE t1.table_id = t2.table_id AND t2.name LIKE "%child" ORDER BY t1.name;
SELECT NAME FROM information_schema.INNODB_SYS_TABLES;

SHOW CREATE TABLE child;

DROP TABLE child;

CREATE TABLE child (a1 INT NOT NULL, a2 INT) ENGINE = InnoDB;

ALTER TABLE child CHANGE a1 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a3) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

SELECT * from information_schema.INNODB_SYS_FOREIGN;
SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;
SELECT t2.name, t1.name FROM information_schema.innodb_sys_columns t1, information_schema.innodb_sys_tables t2 WHERE t1.table_id = t2.table_id AND t2.name LIKE "%child" ORDER BY t1.name;
SELECT NAME FROM information_schema.INNODB_SYS_TABLES;

SHOW CREATE TABLE child;

DROP TABLE child;

CREATE TABLE child (a1 INT NOT NULL, a2 INT) ENGINE = InnoDB;
# Now try all three
--error ER_FK_INCORRECT_OPTION
ALTER TABLE child ADD PRIMARY KEY idx (a3), CHANGE a1 a3 INT,
ADD CONSTRAINT fk_1 FOREIGN KEY (a3) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

DROP TABLE parent;
DROP TABLE child;

CREATE TABLE parent (a INT PRIMARY KEY, b INT NOT NULL, c INT) ENGINE = InnoDB;
INSERT INTO parent VALUES(1,2,3),(2,3,4);

CREATE INDEX tb ON parent(b);

CREATE TABLE child (a1 INT NOT NULL, a2 INT, a3 INT) ENGINE = InnoDB;
CREATE INDEX tb ON child(a2);

ALTER TABLE child
ADD CONSTRAINT fk_a FOREIGN KEY (a2) REFERENCES parent(b)
ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

ALTER TABLE child
ADD CONSTRAINT fk_b FOREIGN KEY (a1) REFERENCES parent(a),
ALGORITHM = INPLACE;

ALTER TABLE child CHANGE a2 a2_new INT, CHANGE a1 a1_new INT;

SHOW CREATE TABLE child;

SELECT * from information_schema.INNODB_SYS_FOREIGN;
SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;

# The third add FK will fail
--error ER_FK_NO_INDEX_PARENT
ALTER TABLE child
ADD CONSTRAINT fk_new_1 FOREIGN KEY (a1_new) REFERENCES parent(b),
ADD CONSTRAINT fk_new_2 FOREIGN KEY (a2_new) REFERENCES parent(a),
ADD CONSTRAINT fk_new_3 FOREIGN KEY (a3) REFERENCES parent(c),
ALGORITHM = INPLACE;

# It should still have only 2 FKs
SHOW CREATE TABLE child;

SELECT * from information_schema.INNODB_SYS_FOREIGN;

SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;

#Now let's make it successful
ALTER TABLE child
ADD CONSTRAINT fk_new_1 FOREIGN KEY (a1_new) REFERENCES parent(b),
ADD CONSTRAINT fk_new_2 FOREIGN KEY (a2_new) REFERENCES parent(a),
ADD CONSTRAINT fk_new_3 FOREIGN KEY (a3) REFERENCES parent(a),
ALGORITHM = INPLACE;

# It should still have 5 FKs
SHOW CREATE TABLE child;

SELECT * from information_schema.INNODB_SYS_FOREIGN;

SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;

DROP TABLE child;
CREATE TABLE child (a1 INT NOT NULL, a2 INT, a3 INT) ENGINE = InnoDB;
CREATE INDEX tb ON child(a2);

# Let's try this 3rd fk failure with add primary index
--error ER_FK_NO_INDEX_PARENT
ALTER TABLE child ADD PRIMARY KEY idx (a1),
ADD CONSTRAINT fk_new_1 FOREIGN KEY (a1) REFERENCES parent(b),
ADD CONSTRAINT fk_new_2 FOREIGN KEY (a2) REFERENCES parent(a),
ADD CONSTRAINT fk_new_3 FOREIGN KEY (a3) REFERENCES parent(c),
ALGORITHM = INPLACE;

# It should still have no FKs, no PRIMARY
SHOW CREATE TABLE child;

SELECT * from information_schema.INNODB_SYS_FOREIGN;

SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;

# make it successful
ALTER TABLE child ADD PRIMARY KEY idx (a1),
ADD CONSTRAINT fk_new_1 FOREIGN KEY (a1) REFERENCES parent(b),
ADD CONSTRAINT fk_new_2 FOREIGN KEY (a2) REFERENCES parent(a),
ADD CONSTRAINT fk_new_3 FOREIGN KEY (a3) REFERENCES parent(a),
ALGORITHM = INPLACE;

# It should have 3 FKs, a new PRIMARY
SHOW CREATE TABLE child;

SELECT * from information_schema.INNODB_SYS_FOREIGN;

SELECT * from information_schema.INNODB_SYS_FOREIGN_COLS;

SET foreign_key_checks = 1;

DROP TABLE child;
DROP TABLE parent;

CREATE TABLE Parent (a INT PRIMARY KEY, b INT NOT NULL) ENGINE = InnoDB;
INSERT INTO Parent VALUES(1,2),(2,3);

CREATE INDEX tb ON Parent(b);

INSERT INTO Parent VALUES(10,20),(20,30);

CREATE TABLE Child (a1 INT PRIMARY KEY, a2 INT) ENGINE = InnoDB;

CREATE INDEX tb ON Child(a2);

INSERT INTO Child VALUES(10,20);

SET foreign_key_checks = 0;

ALTER TABLE Child ADD CONSTRAINT fk_1 FOREIGN KEY (a2)
REFERENCES Parent(b) ON DELETE SET NULL ON UPDATE CASCADE,
ALGORITHM = INPLACE;

DROP TABLE Child;
DROP TABLE Parent;

# This is the test for bug 14594526 - FK: ASSERTION IN
# DICT_TABLE_CHECK_FOR_DUP_INDEXES
CREATE TABLE `t2`(a int,c int,d int) ENGINE=INNODB;
CREATE TABLE `t3`(a int,c int,d int) ENGINE=INNODB;
CREATE INDEX idx ON t3(a);

ALTER TABLE `t2` ADD CONSTRAINT `fw` FOREIGN KEY (`c`) REFERENCES t3 (a);

ALTER TABLE `t2` ADD CONSTRAINT `e` foreign key (`d`) REFERENCES t3(a);

--error ER_FK_FAIL_ADD_SYSTEM
ALTER TABLE `t3` ADD CONSTRAINT `e` foreign key (`c`) REFERENCES `t2`(`c`) ON UPDATE SET NULL;

SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN;
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS;

DROP TABLE t2;

DROP TABLE t3;
