Commit 602bb5c0 authored by Jimmy Yang's avatar Jimmy Yang

Fix Bug #53592 in plugin code, "crash replacing duplicates into table

after fast alter table added unique key". Look up MySQL index number should
go through index translation table.

rb://347, approved by Marko
parent f230391c
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;
# 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;
...@@ -6673,7 +6673,7 @@ ha_innobase::create( ...@@ -6673,7 +6673,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);
...@@ -7389,6 +7389,67 @@ ha_innobase::read_time( ...@@ -7389,6 +7389,67 @@ 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_ad(index);
ut_ad(ib_table);
ut_ad(table);
ut_ad(share);
/* 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 in the 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);
}
}
sql_print_error("Cannot find matching index number for index %s "
"in InnoDB index list.", index->name);
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. */
...@@ -7658,8 +7719,8 @@ ha_innobase::info( ...@@ -7658,8 +7719,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( ...@@ -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
......
...@@ -1645,37 +1645,6 @@ row_table_got_default_clust_index( ...@@ -1645,37 +1645,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. */
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
set -eu set -eu
TARGETDIR=../storage/innobase TARGETDIR=../storage/innodb_plugin
# link the build scripts # link the build scripts
BUILDSCRIPTS="compile-innodb compile-innodb-debug" BUILDSCRIPTS="compile-innodb compile-innodb-debug"
......
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