Bug #81595 FOREIGN_KEY_CHECKS disabling doesn't work
Submitted: 25 May 2016 15:10 Modified: 4 Jul 2016 4:33
Reporter: Anton Ohorodnyk Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S2 (Serious)
Version:5.7.12 OS:Linux (Ubuntu 14.04, CentOS 7)
Assigned to: CPU Architecture:Any
Tags: foreign_key, foreign_key_checks

[25 May 2016 15:10] Anton Ohorodnyk
Description:
The upgrade scheme process failed in our project, because we changing scheme without deleting foreign keys (if it's doesn't effect final tables structure).

In our project all scripts with upgrade scheme SQL located inside "FOREIGN_KEY_CHECKS=0" and "FOREIGN_KEY_CHECKS=1" setting session variables.

But in MySQL 5.7.12 we got error "[2016-05-25 17:45:20] [HY000][1553] Cannot drop index 'TEST2_FKEY2': needed in a foreign key constraint".

SQL for testing this bug is attached.

How to repeat:
DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey1` INT NOT NULL,
  `key1` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST1_FKEY1` (`fkey1`)
);

DROP TABLE IF EXISTS `test2`;
CREATE TABLE `test2` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey2` INT NOT NULL,
  `key2` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST2_FKEY2` (`fkey2`),
  CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
);

SET SQL_MODE='';
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';

alter table test2 drop key `TEST2_FKEY2`;
alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);

SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'');
SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS=0, 0, 1);

Suggested fix:
The same issue was found in https://bugs.mysql.com/bug.php?id=58547
[25 May 2016 15:46] MySQL Verification Team
C:\dbs>c:\dbs\5.6\bin\mysql -uroot --port=3560 -p --prompt="mysql 5.6 > "
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.32 Source distribution PULL: 2016-MAY-09

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql 5.6 > USE test
Database changed
mysql 5.6 > DROP TABLE IF EXISTS `test1`;
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails
mysql 5.6 > CREATE TABLE `test1` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey1` INT NOT NULL,
    ->   `key1` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST1_FKEY1` (`fkey1`)
    -> );
ERROR 1050 (42S01): Table 'test1' already exists
mysql 5.6 >
mysql 5.6 > DROP TABLE IF EXISTS `test2`;
Query OK, 0 rows affected (0.17 sec)

mysql 5.6 > CREATE TABLE `test2` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey2` INT NOT NULL,
    ->   `key2` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST2_FKEY2` (`fkey2`),
    ->   CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
    -> );
Query OK, 0 rows affected (0.45 sec)

mysql 5.6 >
mysql 5.6 > SET SQL_MODE='';
Query OK, 0 rows affected (0.00 sec)

mysql 5.6 > SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql 5.6 > SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)

mysql 5.6 >
mysql 5.6 > alter table test2 drop key `TEST2_FKEY2`;
Query OK, 0 rows affected (0.26 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql 5.6 > alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);
Query OK, 0 rows affected (0.53 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql 5.6 > exit
Bye

C:\dbs>57

C:\dbs>c:\dbs\5.7\bin\mysql -uroot -p --port=3570 --prompt="mysql 5.7 > "
Enter password: ******
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.7.14 Source distribution PULL: 2016-MAY-09

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql 5.7 > USE test
Database changed
mysql 5.7 > DROP TABLE IF EXISTS `test1`;
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails
mysql 5.7 > CREATE TABLE `test1` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey1` INT NOT NULL,
    ->   `key1` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST1_FKEY1` (`fkey1`)
    -> );
ERROR 1050 (42S01): Table 'test1' already exists
mysql 5.7 >
mysql 5.7 > DROP TABLE IF EXISTS `test2`;
Query OK, 0 rows affected (0.19 sec)

mysql 5.7 > CREATE TABLE `test2` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey2` INT NOT NULL,
    ->   `key2` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST2_FKEY2` (`fkey2`),
    ->   CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
    -> );
Query OK, 0 rows affected (0.43 sec)

mysql 5.7 >
mysql 5.7 > SET SQL_MODE='';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql 5.7 > SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql 5.7 > SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)

mysql 5.7 >
mysql 5.7 > alter table test2 drop key `TEST2_FKEY2`;
ERROR 1553 (HY000): Cannot drop index 'TEST2_FKEY2': needed in a foreign key constraint
mysql 5.7 > alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);
Query OK, 0 rows affected (0.87 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql 5.7 >
[25 May 2016 15:53] MySQL Verification Team
Thank you for the bug report.

c:\dbs\mysql-5.7.9>bin\mysql -uroot -p test
Enter password: ******
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.9 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> DROP TABLE IF EXISTS `test1`;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CREATE TABLE `test1` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey1` INT NOT NULL,
    ->   `key1` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST1_FKEY1` (`fkey1`)
    -> );
Query OK, 0 rows affected (0.33 sec)

mysql>
mysql> DROP TABLE IF EXISTS `test2`;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CREATE TABLE `test2` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey2` INT NOT NULL,
    ->   `key2` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST2_FKEY2` (`fkey2`),
    ->   CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
    -> );
Query OK, 0 rows affected (0.38 sec)

mysql>
mysql> SET SQL_MODE='';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> alter table test2 drop key `TEST2_FKEY2`;
ERROR 1553 (HY000): Cannot drop index 'TEST2_FKEY2': needed in a foreign key constraint
mysql> alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);
Query OK, 0 rows affected (0.24 sec)
Records: 0  Duplicates: 0  Warnings: 0
[20 Jun 2016 4:04] Aakanksha Verma
Posted by developer:
 
This is regarding BPS  bug : http://clustra.no.oracle.com/orabugs/bug.php?id=23347100, its basically a regression due to the following commit by Thirunarayanan B:
Author: Thirunarayanan B <thirunarayanan.balathandayuth@oracle.com>
Date: Mon May 5 16:47:13 2014 +0530

Bug #17449901 TABLE DISAPPEARS WHEN ALTERING WITH
FOREIGN KEY CHECKS OFF

Analysis:
By removing the FK index with out removing the foreign key constraint,
we are moving the table into inconsistent state. FK index lookup happens
during load table process. So it fails to open the table when FK index
is missing.

Fix:
Don't allow to drop an index if it is needed for an FK constraint,
even if foreign_key_checks is disabled.

Approved by Jimmy (rb-5274)
The Fix by Thirunarayanan B is a documented behavior added to mysql-5.7.5 which is
" Don't allow to drop an index if it is needed for an FK constraint even if foreign_key_checks is disabled."

So I would like to suggest the reporter to try this workaround test script for his upgrade scheme
and i.e

DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey1` INT NOT NULL,
  `key1` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST1_FKEY1` (`fkey1`)
);

DROP TABLE IF EXISTS `test2`;
CREATE TABLE `test2` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey2` INT NOT NULL,
  `key2` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST2_FKEY2` (`fkey2`),
  CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1`
(`fkey1`) ON DELETE CASCADE
);

SET SQL_MODE='';
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';

alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);
SHOW CREATE TABLE test2;
SHOW CREATE TABLE test1;

SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'');
SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS=0, 0, 1);
DROP TABLE test2;
DROP TABLE test1;
[20 Jun 2016 4:04] Aakanksha Verma
Posted by developer:
 
This is regarding BPS  bug : http://clustra.no.oracle.com/orabugs/bug.php?id=23347100, its basically a regression due to the following commit by Thirunarayanan B:
Author: Thirunarayanan B <thirunarayanan.balathandayuth@oracle.com>
Date: Mon May 5 16:47:13 2014 +0530

Bug #17449901 TABLE DISAPPEARS WHEN ALTERING WITH
FOREIGN KEY CHECKS OFF

Analysis:
By removing the FK index with out removing the foreign key constraint,
we are moving the table into inconsistent state. FK index lookup happens
during load table process. So it fails to open the table when FK index
is missing.

Fix:
Don't allow to drop an index if it is needed for an FK constraint,
even if foreign_key_checks is disabled.

Approved by Jimmy (rb-5274)
The Fix by Thirunarayanan B is a documented behavior added to mysql-5.7.5 which is
" Don't allow to drop an index if it is needed for an FK constraint even if foreign_key_checks is disabled."

So I would like to suggest the reporter to try this workaround test script for his upgrade scheme
and i.e

DROP TABLE IF EXISTS `test1`;
CREATE TABLE `test1` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey1` INT NOT NULL,
  `key1` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST1_FKEY1` (`fkey1`)
);

DROP TABLE IF EXISTS `test2`;
CREATE TABLE `test2` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `fkey2` INT NOT NULL,
  `key2` INT NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST2_FKEY2` (`fkey2`),
  CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1`
(`fkey1`) ON DELETE CASCADE
);

SET SQL_MODE='';
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';

alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`);
SHOW CREATE TABLE test2;
SHOW CREATE TABLE test1;

SET SQL_MODE=IFNULL(@OLD_SQL_MODE,'');
SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS=0, 0, 1);
DROP TABLE test2;
DROP TABLE test1;
[24 Jun 2016 7:47] Aakanksha Verma
Verified that bug#81595 is not a regression. It caused by
Fix of bug#70260 (external bug of bug#17449901)
Fix of bug#70260 is a documented behaviour 
"As of 5.7.5, dropping an index required by a foreign key constraint is not permitted, even when foreign_key_checks=0. The foreign key constraint must be removed before dropping the index"

So Would like to suggest to try this new workaround script and i.e
mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> use test;
Database changed
mysql> CREATE TABLE `test1` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey1` INT NOT NULL,
    ->   `key1` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST1_FKEY1` (`fkey1`)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> CREATE TABLE `test2` (
    ->   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   `fkey2` INT NOT NULL,
    ->   `key2` INT NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `TEST2_FKEY2` (`fkey2`),
    ->   CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1`
    -> (`fkey1`) ON DELETE CASCADE
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> alter table test2 ADD INDEX `TEST2_FKEY2_KEY2` (`fkey2`, `key2`), algorithm=inplace;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test2;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                                            |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test2 | CREATE TABLE `test2` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `fkey2` int(11) NOT NULL,
  `key2` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST2_FKEY2` (`fkey2`),
  KEY `TEST2_FKEY2_KEY2` (`fkey2`,`key2`),
  CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> alter table test2 drop key `TEST2_FKEY2`;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test2;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                             |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test2 | CREATE TABLE `test2` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `fkey2` int(11) NOT NULL,
  `key2` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `TEST2_FKEY2_KEY2` (`fkey2`,`key2`),
  CONSTRAINT `TEST12_FKEY12` FOREIGN KEY (`fkey2`) REFERENCES `test1` (`fkey1`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

**NOTE It will achieve the same whatever user wants to achieve..