Commit 802ce967 authored by Eugene Kosov's avatar Eugene Kosov Committed by Marko Mäkelä

MDEV-18041 Database corruption after renaming a prefix-indexed column

This is a regression after MDEV-13671.

The bug is related to key part prefix lengths wich are stored in SYS_FIELDS.
Storage format is not obvious and was handled incorrectly which led to data
dictionary corruption.

SYS_FIELDS.POS actually contains prefix length too in case if any key part
has prefix length.

innobase_rename_column_try(): fixed prefixes handling

Tests for prefixed indexes added too.

Closes #1063
parent b74eb5a5
......@@ -865,6 +865,27 @@ WHERE T.NAME='test/t1';
NAME
a
DROP TABLE t1;
# and an MDEV-18041 regression related to indexes prefixes
create table `test` (
`test_old` varchar(255) NOT NULL,
`other` varchar(255) NOT NULL,
PRIMARY KEY (`test_old`,`other`),
UNIQUE KEY uk (`test_old`(100), `other`)
) ENGINE=InnoDB;
select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
name pos
test_old 0
other 1
test_old 0
other 1
alter table `test` CHANGE COLUMN `test_old` `test_new` varchar(255) NOT NULL;
select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
name pos
test_new 0
other 1
test_new 0
other 1
drop table `test`;
#
# BUG 20029625 - HANDLE_FATAL_SIGNAL (SIG=11) IN
# DICT_MEM_TABLE_COL_RENAME_LOW
......
......@@ -522,6 +522,19 @@ SELECT C.NAME FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS C INNER JOIN
WHERE T.NAME='test/t1';
DROP TABLE t1;
--echo # and an MDEV-18041 regression related to indexes prefixes
create table `test` (
`test_old` varchar(255) NOT NULL,
`other` varchar(255) NOT NULL,
PRIMARY KEY (`test_old`,`other`),
UNIQUE KEY uk (`test_old`(100), `other`)
) ENGINE=InnoDB;
select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
alter table `test` CHANGE COLUMN `test_old` `test_new` varchar(255) NOT NULL;
select name, pos from information_schema.innodb_SYS_FIELDS where name in ('test_old', 'other', 'test_new');
drop table `test`;
--echo #
--echo # BUG 20029625 - HANDLE_FATAL_SIGNAL (SIG=11) IN
......
......@@ -4533,36 +4533,40 @@ innobase_rename_column_try(
index != NULL;
index = dict_table_get_next_index(index)) {
bool has_prefixes = false;
for (size_t i = 0; i < dict_index_get_n_fields(index); i++) {
if (dict_index_get_nth_field(index, i)->prefix_len) {
has_prefixes = true;
break;
}
}
for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
if (my_strcasecmp(
system_charset_info,
dict_index_get_nth_field(index, i)->name,
from)) {
const dict_field_t* field
= dict_index_get_nth_field(index, i);
if (my_strcasecmp(system_charset_info, field->name,
from)) {
continue;
}
info = pars_info_create();
int pos = i;
if (has_prefixes) {
pos = (pos << 16) + field->prefix_len;
}
pars_info_add_ull_literal(info, "indexid", index->id);
pars_info_add_int4_literal(info, "nth", i);
pars_info_add_int4_literal(info, "nth", pos);
pars_info_add_str_literal(info, "new", to);
error = que_eval_sql(
info,
"PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
"BEGIN\n"
"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
"WHERE INDEX_ID=:indexid\n"
"AND POS=:nth;\n"
/* Try again, in case there is a prefix_len
encoded in SYS_FIELDS.POS */
"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
"WHERE INDEX_ID=:indexid\n"
"AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
"END;\n",
FALSE, trx);
......
......@@ -4547,36 +4547,40 @@ innobase_rename_column_try(
index != NULL;
index = dict_table_get_next_index(index)) {
bool has_prefixes = false;
for (size_t i = 0; i < dict_index_get_n_fields(index); i++) {
if (dict_index_get_nth_field(index, i)->prefix_len) {
has_prefixes = true;
break;
}
}
for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
if (my_strcasecmp(
system_charset_info,
dict_index_get_nth_field(index, i)->name,
from)) {
const dict_field_t* field
= dict_index_get_nth_field(index, i);
if (my_strcasecmp(system_charset_info, field->name,
from)) {
continue;
}
info = pars_info_create();
int pos = i;
if (has_prefixes) {
pos = (pos << 16) + field->prefix_len;
}
pars_info_add_ull_literal(info, "indexid", index->id);
pars_info_add_int4_literal(info, "nth", i);
pars_info_add_int4_literal(info, "nth", pos);
pars_info_add_str_literal(info, "new", to);
error = que_eval_sql(
info,
"PROCEDURE RENAME_SYS_FIELDS_PROC () IS\n"
"BEGIN\n"
"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
"WHERE INDEX_ID=:indexid\n"
"AND POS=:nth;\n"
/* Try again, in case there is a prefix_len
encoded in SYS_FIELDS.POS */
"UPDATE SYS_FIELDS SET COL_NAME=:new\n"
"WHERE INDEX_ID=:indexid\n"
"AND POS>=65536*:nth AND POS<65536*(:nth+1);\n"
"END;\n",
FALSE, trx);
......
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