Bug #61361 Possible of duplicated primary key by enum type
Submitted: 31 May 2011 10:59 Modified: 15 Oct 2011 17:34
Reporter: Dennis Cheung Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:5.5.9 OS:Any
Assigned to: CPU Architecture:Any

[31 May 2011 10:59] Dennis Cheung
Description:
In a table with primary key type ENUM, is expected only valid key will exists in table. However, you can insert null enum into the table.

How to repeat:
CREATE TABLE `runtime_config` (
  `dummy_id` enum('') COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`dummy_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin$$

INSERT INTO `runtime_config` VALUES('');
INSERT INTO `runtime_config` VALUES(0);

SELECT dummy_id FROM runtime_config;
[31 May 2011 11:27] Peter Laursen
A slightly elaborated test case (in my understanding it has nothing wiht NULL to do, but something weird happens.

DROP TABLE IF EXISTS `runtime_config`;

CREATE TABLE `runtime_config` (
  `dummy_id` ENUM('') COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`dummy_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `runtime_config` VALUES(''); -- reports success
INSERT INTO `runtime_config` VALUES(''); -- Error Code : 1062 - Duplicate entry '' for key 'PRIMARY'
INSERT INTO `runtime_config` VALUES(0); -- reports success
SHOW WARNINGS; -- Warning Code : 1265 - Data truncated for column 'dummy_id' at row 1
INSERT INTO `runtime_config` VALUES(0); -- Error Code : 1062 - Duplicate entry '' for key 'PRIMARY'
INSERT INTO `runtime_config` VALUES(NULL); -- Error Code : 1048 - Column 'dummy_id' cannot be null

SELECT COUNT(dummy_id) FROM runtime_config; -- 2
SELECT COUNT(dummy_id) FROM runtime_config WHERE dummy_id = ''; -- 1

SELECT HEX(dummy_id) FROM runtime_config;
+---------------+
| HEX(dummy_id) |
+---------------+
|               |
|               |
+---------------+
2 rows in set (0.00 sec)

I am mystified what the 3rd INSERT here actually inserts!

Peter
(not a MySQL person)
[31 May 2011 11:31] Peter Laursen
To me it looks like it INSERTS 'empty string' to *data* twice, but different data are UPDATED to *index*.

(I am using 5.5.13 BTW)
[31 May 2011 11:46] Peter Laursen
add to test case: 

ALTER TABLE `test`.`runtime_config` CHANGE `dummy_id` `dummy_id` ENUM('') CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,  DROP PRIMARY KEY; -- success

ALTER TABLE `test`.`runtime_config` CHANGE `dummy_id` `dummy_id` ENUM('') CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,  ADD PRIMARY KEY(`dummy_id`); -- success

SHOW WARNINGS; -- empty set

(and SELECT output is unchaged)
[31 May 2011 18:46] Peter Laursen
add: 

INSERT INTO `runtime_config` VALUES(''); -- success
INSERT INTO `runtime_config` VALUES(0); --  success with warning
INSERT INTO `runtime_config` VALUES(1); -- duplicate entry
INSERT INTO `runtime_config` VALUES(X); -- Error Code : 1054 - Unknown column 'x' in 'field list'

If "0" (without quotes) truncates (almost! :-) ) to '' why does not "X" (also without quotes) ?
[1 Jun 2011 8:56] MySQL Verification Team
Trying to figure out if the docs are explaining it: "If you insert an invalid value into an ENUM (that is, a string not present in the list of permitted values), the empty string is inserted instead as a special error value. This string can be distinguished from a “normal” empty string by the fact that this string has the numeric value 0. More about this later."

http://dev.mysql.com/doc/refman/5.6/en/enum.html

On 5.6.3 i get:

mysql> select dummy_id+0 from runtime_config where dummy_id='';
+------------+
| dummy_id+0 |
+------------+
|          1 |
+------------+
1 row in set (0.00 sec)

mysql> select dummy_id+0 from runtime_config where dummy_id=0;
+------------+
| dummy_id+0 |
+------------+
|          0 |
+------------+
1 row in set (0.00 sec)
[1 Jun 2011 9:38] Peter Laursen
Why is this implemented like that?  What is special about ENUM's as compared to other datatypes, so that it needs a 'special error value' INSERTED?

HEX() on both columns does not reveal any difference!
[1 Jun 2011 10:49] Peter Laursen
Compare:

DROP TABLE IF EXISTS tmp;
CREATE TABLE tmp (id CHAR(3) PRIMARY KEY) ENGINE=INNODB;
INSERT INTO tmp VALUES('the');
INSERT INTO tmp VALUES('there'); -- Error Code : 1062 - Duplicate entry 'the' for key 'PRIMARY'

No INSERT happens of the truncated value ("the") with a 'special error code marker'.  I do not understand why truncation when inserting ENUM's isn't handled exactly the same way as is the case for other datatypes.
[15 Oct 2011 17:34] Valeriy Kravchuk
As Shane explained, it seems internally ENUM is stored as numeric value (index of item). So, you can insert (once!) some value not present in table's definition - PRIMARY key will get value 0.

This is not a bug.
[15 Oct 2011 17:44] Peter Laursen
the implementation is still crazy! :-)
[16 Jun 2023 3:06] MySQL Verification Team
this bug makes tables impossible to dump/restore with mysqldump.

mysql> create table t(a enum('a','','b') primary key);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t values(''),('aaa');
Query OK, 2 rows affected, 1 warning (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 1

mysql> select * from t;
+---+
| a |
+---+
|   |
|   |
+---+
2 rows in set (0.00 sec)

mysql> select md5(a) from t;
+----------------------------------+
| md5(a)                           |
+----------------------------------+
| d41d8cd98f00b204e9800998ecf8427e |
| d41d8cd98f00b204e9800998ecf8427e |
+----------------------------------+
2 rows in set (0.00 sec)

mysql> select a+0 from t;
+-----+
| a+0 |
+-----+
|   0 |
|   2 |
+-----+