mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Port bug fix #53592 from mysql-5.1-innodb to mysql-trunk-innodb.
This commit is contained in:
43
mysql-test/suite/innodb/r/innodb_bug53592.result
Normal file
43
mysql-test/suite/innodb/r/innodb_bug53592.result
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
set old_alter_table=0;
|
||||||
|
create table bug53592(a int) engine=innodb row_format=compact;
|
||||||
|
alter table bug53592 add column b text charset utf8;
|
||||||
|
alter table bug53592 add column c blob not null;
|
||||||
|
create index bug53592_b on bug53592(b(81));
|
||||||
|
create unique index bug53592_c on bug53592(c(1));
|
||||||
|
replace into bug53592 values (),();
|
||||||
|
Warnings:
|
||||||
|
Warning 1364 Field 'c' doesn't have a default value
|
||||||
|
check table bug53592;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.bug53592 check status OK
|
||||||
|
drop table bug53592;
|
||||||
|
set old_alter_table=1;
|
||||||
|
create table bug53592(a int) engine=innodb row_format=compact;
|
||||||
|
alter table bug53592 add column b text charset utf8;
|
||||||
|
alter table bug53592 add column c blob not null;
|
||||||
|
create index bug53592_b on bug53592(b(81));
|
||||||
|
create unique index bug53592_c on bug53592(c(1));
|
||||||
|
replace into bug53592 values (),();
|
||||||
|
Warnings:
|
||||||
|
Warning 1364 Field 'c' doesn't have a default value
|
||||||
|
check table bug53592;
|
||||||
|
Table Op Msg_type Msg_text
|
||||||
|
test.bug53592 check status OK
|
||||||
|
drop table bug53592;
|
||||||
|
CREATE TABLE bug53592_1(
|
||||||
|
col1 int, col2 int,
|
||||||
|
PRIMARY KEY (col1, col2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
CREATE TABLE bug53592_2 (
|
||||||
|
col int PRIMARY KEY,
|
||||||
|
FOREIGN KEY (col) REFERENCES bug53592_1 (col1)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
INSERT INTO bug53592_1 VALUES (1, 2);
|
||||||
|
INSERT INTO bug53592_1 VALUES (3, 4);
|
||||||
|
INSERT INTO bug53592_2 VALUES (1);
|
||||||
|
INSERT INTO bug53592_2 VALUES (3);
|
||||||
|
UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
|
||||||
|
ERROR 23000: Upholding foreign key constraints for table 'bug53592_1', entry '3-2', key 1 would lead to a duplicate entry
|
||||||
|
drop table bug53592_2;
|
||||||
|
drop table bug53592_1;
|
82
mysql-test/suite/innodb/t/innodb_bug53592.test
Normal file
82
mysql-test/suite/innodb/t/innodb_bug53592.test
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Testcase for Bug #53592 - "crash replacing duplicates into
|
||||||
|
# table after fast alter table added unique key". The fix is to make
|
||||||
|
# sure index number lookup should go through "index translation table".
|
||||||
|
|
||||||
|
--source include/have_innodb.inc
|
||||||
|
|
||||||
|
# Use FIC for index creation
|
||||||
|
set old_alter_table=0;
|
||||||
|
|
||||||
|
create table bug53592(a int) engine=innodb row_format=compact;
|
||||||
|
|
||||||
|
alter table bug53592 add column b text charset utf8;
|
||||||
|
|
||||||
|
alter table bug53592 add column c blob not null;
|
||||||
|
|
||||||
|
# Create a non-unique nonclustered index
|
||||||
|
create index bug53592_b on bug53592(b(81));
|
||||||
|
|
||||||
|
# Create a unique index, this unique index should have smaller
|
||||||
|
# index number than bug53592_b, since unique index ranks higher
|
||||||
|
# than regular index does
|
||||||
|
create unique index bug53592_c on bug53592(c(1));
|
||||||
|
|
||||||
|
# This will trigger a dup key error and will require fetching
|
||||||
|
# the index number through a index structure for the error reporting.
|
||||||
|
# To get the correct index number, the code should go through index
|
||||||
|
# translation table. Otherwise, it will get the wrong index
|
||||||
|
# number and later trigger a server crash.
|
||||||
|
replace into bug53592 values (),();
|
||||||
|
|
||||||
|
check table bug53592;
|
||||||
|
|
||||||
|
drop table bug53592;
|
||||||
|
|
||||||
|
# Running the same set of test when "old_alter_table" is turned on
|
||||||
|
set old_alter_table=1;
|
||||||
|
|
||||||
|
create table bug53592(a int) engine=innodb row_format=compact;
|
||||||
|
|
||||||
|
alter table bug53592 add column b text charset utf8;
|
||||||
|
|
||||||
|
alter table bug53592 add column c blob not null;
|
||||||
|
|
||||||
|
# Create a non-unique nonclustered index
|
||||||
|
create index bug53592_b on bug53592(b(81));
|
||||||
|
|
||||||
|
# Create a unique index
|
||||||
|
create unique index bug53592_c on bug53592(c(1));
|
||||||
|
|
||||||
|
# This will trigger a dup key error and will require fetching
|
||||||
|
# the index number through a index structure for the error reporting.
|
||||||
|
# To get the correct index number, the code should go through index
|
||||||
|
# translation table. Otherwise, it will get the wrong index
|
||||||
|
# number and later trigger a server crash.
|
||||||
|
replace into bug53592 values (),();
|
||||||
|
|
||||||
|
check table bug53592;
|
||||||
|
drop table bug53592;
|
||||||
|
|
||||||
|
# Test a dup key reported by foreign key constriant.
|
||||||
|
CREATE TABLE bug53592_1(
|
||||||
|
col1 int, col2 int,
|
||||||
|
PRIMARY KEY (col1, col2)
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE bug53592_2 (
|
||||||
|
col int PRIMARY KEY,
|
||||||
|
FOREIGN KEY (col) REFERENCES bug53592_1 (col1)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
INSERT INTO bug53592_1 VALUES (1, 2);
|
||||||
|
INSERT INTO bug53592_1 VALUES (3, 4);
|
||||||
|
|
||||||
|
INSERT INTO bug53592_2 VALUES (1);
|
||||||
|
INSERT INTO bug53592_2 VALUES (3);
|
||||||
|
|
||||||
|
--error ER_FOREIGN_DUPLICATE_KEY
|
||||||
|
UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
|
||||||
|
|
||||||
|
drop table bug53592_2;
|
||||||
|
drop table bug53592_1;
|
@ -6866,7 +6866,7 @@ ha_innobase::create(
|
|||||||
(int) form->s->primary_key :
|
(int) form->s->primary_key :
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
/* Our function row_get_mysql_key_number_for_index assumes
|
/* Our function innobase_get_mysql_key_number_for_index assumes
|
||||||
the primary key is always number 0, if it exists */
|
the primary key is always number 0, if it exists */
|
||||||
|
|
||||||
ut_a(primary_key_no == -1 || primary_key_no == 0);
|
ut_a(primary_key_no == -1 || primary_key_no == 0);
|
||||||
@ -7582,6 +7582,84 @@ ha_innobase::read_time(
|
|||||||
return(ranges + (double) rows / (double) total_rows * time_for_scan);
|
return(ranges + (double) rows / (double) total_rows * time_for_scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************************************************************//**
|
||||||
|
Calculates the key number used inside MySQL for an Innobase index. We will
|
||||||
|
first check the "index translation table" for a match of the index to get
|
||||||
|
the index number. If there does not exist an "index translation table",
|
||||||
|
or not able to find the index in the translation table, then we will fall back
|
||||||
|
to the traditional way of looping through dict_index_t list to find a
|
||||||
|
match. In this case, we have to take into account if we generated a
|
||||||
|
default clustered index for the table
|
||||||
|
@return the key number used inside MySQL */
|
||||||
|
static
|
||||||
|
unsigned int
|
||||||
|
innobase_get_mysql_key_number_for_index(
|
||||||
|
/*====================================*/
|
||||||
|
INNOBASE_SHARE* share, /*!< in: share structure for index
|
||||||
|
translation table. */
|
||||||
|
const TABLE* table, /*!< in: table in MySQL data
|
||||||
|
dictionary */
|
||||||
|
dict_table_t* ib_table,/*!< in: table in Innodb data
|
||||||
|
dictionary */
|
||||||
|
const dict_index_t* index) /*!< in: index */
|
||||||
|
{
|
||||||
|
const dict_index_t* ind;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
ut_a(index);
|
||||||
|
|
||||||
|
/* If index does not belong to the table of share structure. Search
|
||||||
|
index->table instead */
|
||||||
|
if (index->table != ib_table
|
||||||
|
&& innobase_strcasecmp(index->table->name, share->table_name)) {
|
||||||
|
i = 0;
|
||||||
|
ind = dict_table_get_first_index(index->table);
|
||||||
|
|
||||||
|
while (index != ind) {
|
||||||
|
ind = dict_table_get_next_index(ind);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row_table_got_default_clust_index(index->table)) {
|
||||||
|
ut_a(i > 0);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If index translation table exists, we will first check
|
||||||
|
the index through index translation table for a match. */
|
||||||
|
if (share->idx_trans_tbl.index_mapping) {
|
||||||
|
for (i = 0; i < share->idx_trans_tbl.index_count; i++) {
|
||||||
|
if (share->idx_trans_tbl.index_mapping[i] == index) {
|
||||||
|
return(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print an error message if we cannot find the index
|
||||||
|
** in the "index translation table". */
|
||||||
|
sql_print_error("Cannot find index %s in InnoDB index "
|
||||||
|
"translation table.", index->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we do not have an "index translation table", or not able
|
||||||
|
to find the index in the translation table, we'll directly find
|
||||||
|
matching index with information from mysql TABLE structure and
|
||||||
|
InnoDB dict_index_t list */
|
||||||
|
for (i = 0; i < table->s->keys; i++) {
|
||||||
|
ind = dict_table_get_index_on_name(
|
||||||
|
ib_table, table->key_info[i].name);
|
||||||
|
|
||||||
|
if (index == ind) {
|
||||||
|
return(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ut_error;
|
||||||
|
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
/*********************************************************************//**
|
/*********************************************************************//**
|
||||||
Returns statistics information of the table to the MySQL interpreter,
|
Returns statistics information of the table to the MySQL interpreter,
|
||||||
in various fields of the handle object. */
|
in various fields of the handle object. */
|
||||||
@ -7851,8 +7929,8 @@ ha_innobase::info(
|
|||||||
err_index = trx_get_error_info(prebuilt->trx);
|
err_index = trx_get_error_info(prebuilt->trx);
|
||||||
|
|
||||||
if (err_index) {
|
if (err_index) {
|
||||||
errkey = (unsigned int)
|
errkey = innobase_get_mysql_key_number_for_index(
|
||||||
row_get_mysql_key_number_for_index(err_index);
|
share, table, ib_table, err_index);
|
||||||
} else {
|
} else {
|
||||||
errkey = (unsigned int) prebuilt->trx->error_key_num;
|
errkey = (unsigned int) prebuilt->trx->error_key_num;
|
||||||
}
|
}
|
||||||
|
@ -253,15 +253,6 @@ row_table_got_default_clust_index(
|
|||||||
/*==============================*/
|
/*==============================*/
|
||||||
const dict_table_t* table); /*!< in: table */
|
const dict_table_t* table); /*!< in: table */
|
||||||
/*********************************************************************//**
|
/*********************************************************************//**
|
||||||
Calculates the key number used inside MySQL for an Innobase index. We have
|
|
||||||
to take into account if we generated a default clustered index for the table
|
|
||||||
@return the key number used inside MySQL */
|
|
||||||
UNIV_INTERN
|
|
||||||
ulint
|
|
||||||
row_get_mysql_key_number_for_index(
|
|
||||||
/*===============================*/
|
|
||||||
const dict_index_t* index); /*!< in: index */
|
|
||||||
/*********************************************************************//**
|
|
||||||
Does an update or delete of a row for MySQL.
|
Does an update or delete of a row for MySQL.
|
||||||
@return error code or DB_SUCCESS */
|
@return error code or DB_SUCCESS */
|
||||||
UNIV_INTERN
|
UNIV_INTERN
|
||||||
|
@ -1647,37 +1647,6 @@ row_table_got_default_clust_index(
|
|||||||
return(dict_index_get_nth_col(clust_index, 0)->mtype == DATA_SYS);
|
return(dict_index_get_nth_col(clust_index, 0)->mtype == DATA_SYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************//**
|
|
||||||
Calculates the key number used inside MySQL for an Innobase index. We have
|
|
||||||
to take into account if we generated a default clustered index for the table
|
|
||||||
@return the key number used inside MySQL */
|
|
||||||
UNIV_INTERN
|
|
||||||
ulint
|
|
||||||
row_get_mysql_key_number_for_index(
|
|
||||||
/*===============================*/
|
|
||||||
const dict_index_t* index) /*!< in: index */
|
|
||||||
{
|
|
||||||
const dict_index_t* ind;
|
|
||||||
ulint i;
|
|
||||||
|
|
||||||
ut_a(index);
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
ind = dict_table_get_first_index(index->table);
|
|
||||||
|
|
||||||
while (index != ind) {
|
|
||||||
ind = dict_table_get_next_index(ind);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row_table_got_default_clust_index(index->table)) {
|
|
||||||
ut_a(i > 0);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************//**
|
/*********************************************************************//**
|
||||||
Locks the data dictionary in shared mode from modifications, for performing
|
Locks the data dictionary in shared mode from modifications, for performing
|
||||||
foreign key check, rollback, or other operation invisible to MySQL. */
|
foreign key check, rollback, or other operation invisible to MySQL. */
|
||||||
|
Reference in New Issue
Block a user