Bug #69707 Adding "_ibfk_" foreign key "Table XXX already exists"

Submitted: 10 Jul 2013 4:47 Modified: 12 Aug 2013 11:35
Reporter: Ryan Kenney Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S2 (Serious)
Version:5.5.32 Win64 OS:Windows (7)
Assigned to: CPU Architecture:Any

[10 Jul 2013 4:47] Ryan Kenney
Description:
We're seeing an error that suggests hidden name collisions in foreign keys. I saw this with MySQL 5.5.32 but not 5.5.24. The samples represent scripts that exist as part of our legacy DB upgrade tasks, and this is the first time they've failed.

I suspect this issue is related to http://bugs.mysql.com/bug.php?id=69699 and http://bugs.mysql.com/bug.php?id=69693

I ran all of the examples from the command line client to a local instance.

How to repeat:
Here's the basic order of operations:

DROP DATABASE IF EXISTS my_test_db;
CREATE DATABASE my_test_db;
USE my_test_db;
CREATE TABLE IF NOT EXISTS `door` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `window` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE `house` (
  `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `door` int(10) unsigned DEFAULT NULL,
  `window` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`key`),
  KEY `my_arbitrarily_named_constraint_ibfk_1` (`door`),
  KEY `other_arbitrarily_named_constraint_ibfk_1` (`window`),
  CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON UPDATE CASCADE,
  CONSTRAINT `house_ibfk_1` FOREIGN KEY (`window`) REFERENCES `window` (`key`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_ibfk_1`;
ALTER TABLE house ADD CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON DELETE CASCADE ON UPDATE CASCADE;

And here's the error from the last command:

ERROR 1050 (42S01): Table '.\my_test_db\house' already exists

I can eliminate the problem in a couple of ways...

===== WORKAROUND 1: Use a non-default name for the parallel constraint =====

If I setup the scenario so that the parallel constraint has a name "house_blah_ibfk_1" instead of "house_ibfk_1" (or even "house_ibfk_2"), we don't see an error.

DROP DATABASE IF EXISTS my_test_db;
CREATE DATABASE my_test_db;
USE my_test_db;
CREATE TABLE IF NOT EXISTS `door` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `window` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE `house` (
  `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `door` int(10) unsigned DEFAULT NULL,
  `window` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`key`),
  KEY `my_arbitrarily_named_constraint_ibfk_1` (`door`),
  KEY `other_arbitrarily_named_constraint_ibfk_1` (`window`),
  CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON UPDATE CASCADE,
  CONSTRAINT `house_blah_ibfk_1` FOREIGN KEY (`window`) REFERENCES `window` (`key`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_ibfk_1`;

===== WORKAROUND 2: Don't use "_ibfk_" in my arbitrarily named constraint =====

Changing my arbitrarily named constraint from "my_arbitrarily_named_constraint_ibfk_1" to "my_arbitrarily_named_constraint_blah_1" prevents the error.

DROP DATABASE IF EXISTS my_test_db;
CREATE DATABASE my_test_db;
USE my_test_db;
CREATE TABLE IF NOT EXISTS `door` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `window` (
	`key` int(10) unsigned NOT NULL AUTO_INCREMENT,
	PRIMARY KEY(`key`)
) ENGINE=INNODB;
CREATE TABLE `house` (
  `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `door` int(10) unsigned DEFAULT NULL,
  `window` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`key`),
  KEY `my_arbitrarily_named_constraint_blah_1` (`door`),
  KEY `other_arbitrarily_named_constraint_ibfk_1` (`window`),
  CONSTRAINT `my_arbitrarily_named_constraint_blah_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON UPDATE CASCADE,
  CONSTRAINT `house_ibfk_1` FOREIGN KEY (`window`) REFERENCES `window` (`key`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_blah_1`;
ALTER TABLE house ADD CONSTRAINT `my_arbitrarily_named_constraint_blah_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON DELETE CASCADE ON UPDATE CASCADE;
[10 Jul 2013 5:06] MySQL Verification Team
Hello Ryan,

Thank you for the bug report. 
Verified as described on 5.5.32 version.

Thanks,
Umesh
[10 Jul 2013 5:07] MySQL Verification Team
// 5.5.32 - affected

node1 [localhost] {msandbox} (my_test_db) > select version();
+------------+
| version()  |
+------------+
| 5.5.32-log |
+------------+
1 row in set (0.00 sec)

node1 [localhost] {msandbox} ((none)) > DROP DATABASE IF EXISTS my_test_db;
Query OK, 0 rows affected, 1 warning (0.00 sec)

node1 [localhost] {msandbox} ((none)) > CREATE DATABASE my_test_db;
Query OK, 1 row affected (0.00 sec)

node1 [localhost] {msandbox} ((none)) > USE my_test_db;
Database changed
node1 [localhost] {msandbox} (my_test_db) > CREATE TABLE IF NOT EXISTS `door` (
    -> `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    -> PRIMARY KEY(`key`)
    -> ) ENGINE=INNODB;
CREATE TABLE IF NOT EXISTS `window` (
        `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
        PRIMARY KEY(`key`)
) ENGINE=INNODB;Query OK, 0 rows affected (0.10 sec)

node1 [localhost] {msandbox} (my_test_db) > CREATE TABLE IF NOT EXISTS `window` (
    -> `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    -> PRIMARY KEY(`key`)
    -> ) ENGINE=INNODB;
Query OK, 0 rows affected (0.25 sec)

node1 [localhost] {msandbox} (my_test_db) > CREATE TABLE `house` (
    ->   `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `door` int(10) unsigned DEFAULT NULL,
    ->   `window` int(10) unsigned DEFAULT NULL,
    ->   PRIMARY KEY (`key`),
    ->   KEY `my_arbitrarily_named_constraint_ibfk_1` (`door`),
    ->   KEY `other_arbitrarily_named_constraint_ibfk_1` (`window`),
    ->   CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON UPDATE CASCADE,
    ->   CONSTRAINT `house_ibfk_1` FOREIGN KEY (`window`) REFERENCES `window` (`key`) ON DELETE SET NULL
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_ibfk_1`;
ALTER TABLE house ADD CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON DELETE CASCADE ON UPDATE CASCADE;Query OK, 0 rows affected (0.23 sec)

node1 [localhost] {msandbox} (my_test_db) > ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_ibfk_1`;
Query OK, 0 rows affected (0.23 sec)
Records: 0  Duplicates: 0  Warnings: 0

node1 [localhost] {msandbox} (my_test_db) > ALTER TABLE house ADD CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON DELETE CASCADE ON UPDATE CASCADE;
ERROR 1050 (42S01): Table './my_test_db/house' already exists
node1 [localhost] {msandbox} (my_test_db) >

// 5.6.12 not affected

mysql> select version();
+------------------+
| version()        |
+------------------+
| 5.6.12-debug-log |
+------------------+
1 row in set (0.00 sec)

mysql> DROP DATABASE IF EXISTS my_test_db;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CREATE DATABASE my_test_db;
Query OK, 1 row affected (0.00 sec)

mysql> USE my_test_db;
Database changed
mysql> CREATE TABLE IF NOT EXISTS `door` (
    -> `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    -> PRIMARY KEY(`key`)
    -> ) ENGINE=INNODB;
Query OK, 0 rows affected (0.33 sec)

mysql> CREATE TABLE IF NOT EXISTS `window` (
    -> `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    -> PRIMARY KEY(`key`)
    -> ) ENGINE=INNODB;
Query OK, 0 rows affected (0.11 sec)

mysql> CREATE TABLE `house` (
    ->   `key` int(10) unsigned NOT NULL AUTO_INCREMENT,
    ->   `door` int(10) unsigned DEFAULT NULL,
    ->   `window` int(10) unsigned DEFAULT NULL,
    ->   PRIMARY KEY (`key`),
    ->   KEY `my_arbitrarily_named_constraint_ibfk_1` (`door`),
    ->   KEY `other_arbitrarily_named_constraint_ibfk_1` (`window`),
    ->   CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON UPDATE CASCADE,
    ->   CONSTRAINT `house_ibfk_1` FOREIGN KEY (`window`) REFERENCES `window` (`key`) ON DELETE SET NULL
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.22 sec)

mysql> ALTER TABLE house DROP FOREIGN KEY `my_arbitrarily_named_constraint_ibfk_1`;
Query OK, 0 rows affected (0.15 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE house ADD CONSTRAINT `my_arbitrarily_named_constraint_ibfk_1` FOREIGN KEY (`door`) REFERENCES `door` (`key`) ON DELETE CASCADE ON UPDATE CASCADE;
Query OK, 0 rows affected (0.66 sec)
Records: 0  Duplicates: 0  Warnings: 0
[10 Jul 2013 5:22] MySQL Verification Team
Bug #69699 marked as duplicate of this one.
[11 Jul 2013 8:06] Cyril Scetbon
fixed in 5.6.12
[11 Jul 2013 17:28] Ryan Kenney
Here's the workaround I ended up applying to our software so that it can be installed on a server running 5.5.32 without incident. The gist is that I forced all foreign keys to use the standard naming convention (e.g. "<table name>_ibfk_<index>"). When you do this, 5.5.32 works just like previous versions.

It's not completely obvious from my example above, but some of the odd key naming was a result of earlier scripts doing wonky things due to an issue similar to #69693.

In the end my solution was to:
* Modify all historical scripts to use standardized foreign key names
* Add DB tasks that drop/re-add any non-standard foreign keys

Queries to the following tables let us identify the state of things, but I didn't figure out how to query these tables and then apply the fixes using pure sql, so I ended up using jdbc to solve the problem, which is fine for our system.
* INFORMATION_SCHEMA.TABLE_CONSTRAINTS
* INFORMATION_SCHEMA.KEY_COLUMN_USAGE
[12 Aug 2013 11:35] Bugs System
Added this bug number to the existing changelog entry for Bug #17076737. 

Thank you for the bug report.
[26 Jan 2017 13:57] Daniel Price
Posted by developer:
 
The changelog text was revised as follows:

"Adding a foreign key with a constraint name that included the string
_ibfk_ caused InnoDB to create a duplicate constraint with a generated
internal name. The generated internal name could also collide with an
existing user-defined constraint of the same name, causing a duplicate key
error."
[27 Jan 2017 17:19] Daniel Price
Posted by developer:
 
The following related updates were made to the MySQL reference manual:

https://dev.mysql.com/doc/refman/5.7/en/rename-table.html

RENAME TABLE changes internally generated foreign key constraint names and user-defined foreign key constraint names that contain the string “tbl_name_ibfk_” to reflect the new table name. InnoDB interprets foreign key constraint names that contain the string “tbl_name_ibfk_” as  internally generated names. 

https://dev.mysql.com/doc/refman/5.7/en/alter-table.html

ALTER TABLE tbl_name RENAME new_tbl_name changes internally generated foreign key constraint names and user-defined foreign key constraint names that contain the string “tbl_name_ibfk_” to reflect the new table name. InnoDB interprets foreign key constraint names that contain the string “tbl_name_ibfk_” as internally generated names.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.046 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.