Commit 2ff76f67 authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-5406 add index to an innodb table with a uniqueness violation crashes mysqld

After table->file->add_index() in mysql_alter_table() the table in the engine
has the intermediate temporary structure, it's neither the original nor the
final table structure (it'll be final after successful table->file->drop_index()
call). So, when add_index() fails with a unique key violation, we cannot simply
get the failed key number and easily map it to the key name and key structure via
table->key_info[key_no].

For now we'll create this "intermediate temporary structure", emulating InnoDB
internal rules.

This bug and the fix will go away in 10.0 that uses completely different online
alter table code.

mysql-test/t/alter_table_trans.test:
  mdev:5406
parent 0cdf1573
...@@ -4,3 +4,15 @@ ALTER TABLE t1 RENAME TO t2, DISABLE KEYS; ...@@ -4,3 +4,15 @@ ALTER TABLE t1 RENAME TO t2, DISABLE KEYS;
Warnings: Warnings:
Note 1031 Table storage engine for 't1' doesn't have this option Note 1031 Table storage engine for 't1' doesn't have this option
DROP TABLE t2; DROP TABLE t2;
CREATE TABLE t1 (
col4 text NOT NULL,
col2 int(11) NOT NULL DEFAULT '0',
col3 int(11) DEFAULT NULL,
extra int(11) DEFAULT NULL,
KEY idx (col4(10))
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert t1 values (repeat('1', 8193),3,1,1);
insert t1 values (repeat('3', 8193),3,1,1);
ALTER TABLE t1 ADD PRIMARY KEY (col4(10)) , ADD UNIQUE KEY uidx (col3);
ERROR 23000: Duplicate entry '1' for key 'uidx'
DROP TABLE t1;
...@@ -13,3 +13,23 @@ drop table if exists t1,t2; ...@@ -13,3 +13,23 @@ drop table if exists t1,t2;
CREATE TABLE t1 (a INT, INDEX(a)) engine=innodb; CREATE TABLE t1 (a INT, INDEX(a)) engine=innodb;
ALTER TABLE t1 RENAME TO t2, DISABLE KEYS; ALTER TABLE t1 RENAME TO t2, DISABLE KEYS;
DROP TABLE t2; DROP TABLE t2;
#
# MDEV-5406 add index to an innodb table with a uniqueness violation crashes mysqld
#
CREATE TABLE t1 (
col4 text NOT NULL,
col2 int(11) NOT NULL DEFAULT '0',
col3 int(11) DEFAULT NULL,
extra int(11) DEFAULT NULL,
KEY idx (col4(10))
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert t1 values (repeat('1', 8193),3,1,1);
insert t1 values (repeat('3', 8193),3,1,1);
--error ER_DUP_ENTRY
ALTER TABLE t1 ADD PRIMARY KEY (col4(10)) , ADD UNIQUE KEY uidx (col3);
DROP TABLE t1;
...@@ -6893,14 +6893,35 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6893,14 +6893,35 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/* Only report error if handler has not already reported an error */ /* Only report error if handler has not already reported an error */
if (!thd->is_error()) if (!thd->is_error())
{ {
/* /*
Exchange the key_info for the error message. If we exchange HACK HACK HACK
key number by key name in the message later, we need correct info. Prepare the list of keys for an error message.
*/ It must match what the engine does internally in ::add_index().
KEY *save_key_info= table->key_info; Here we emulate what innobase_create_key_def() does.
table->key_info= key_info; Luckily, in 10.0 this will go away.
table->file->print_error(error, MYF(0)); */
table->key_info= save_key_info; KEY *save_key_info= table->key_info;
uint add_cnt= index_add_count, old_cnt= table->s->keys;
KEY *merged= (KEY*)thd->alloc((old_cnt + add_cnt) * sizeof(KEY));
#define is_PK(K) (!my_strcasecmp(system_charset_info, (K)->name, "PRIMARY"))
if (is_PK(key_info))
{
merged[0]= key_info[0];
if (is_PK(table->key_info))
{
old_cnt--;
table->key_info++;
}
memcpy(merged + 1, table->key_info, old_cnt * sizeof(KEY));
memcpy(merged + old_cnt + 1, key_info + 1, (add_cnt - 1) * sizeof(KEY));
}
else
merged= key_info;
table->key_info= merged;
table->file->print_error(error, MYF(0));
table->key_info= save_key_info;
} }
goto err_new_table_cleanup; goto err_new_table_cleanup;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment