MDEV-25951 MariaDB crash after ALTER TABLE convert to utf8mb4

Bug happens when partially indexed CHAR or VARCHAR field in converted from
utf8mb3 to utf8mb4.

Fixing by relaxing assertions. For some time dict_index_t and dict_table_t
are becoming not synchronized. Namely, dict_index_t has a new prefix_len which
is a multiple of a user-provided length and charset->mbmaxlen. But
the table still have and old mbmaxlen and assertion fails. This happens only
during utf8mb3 -> utf8mb4 conversions and the magic number 4 comes from
utf8mb_4_.

At the end of ALTER TABLE (innobase_rename_or_enlarge_columns_cache())
dict_index_t and dict_table_t became synchronized
again and will stay so at all times. For, example, they will be synchronized
on table load and newly added assertion proves that.
This commit is contained in:
Eugene Kosov 2021-09-06 15:48:40 +06:00
commit a4b3970c6e
6 changed files with 76 additions and 3 deletions

View file

@ -2043,3 +2043,36 @@ constraint a foreign key (id) references t1 (id)
alter table t1 change id id2 int;
drop table t2;
drop table t1;
#
# MDEV-25951 MariaDB crash after ALTER TABLE convert to utf8mb4
#
CREATE TABLE t1 (id INT PRIMARY KEY, a VARCHAR(32), KEY (a(7))) ENGINE=INNODB DEFAULT CHARSET=UTF8;
INSERT INTO t1 VALUES (1, 'a1'), (2, 'a1');
ALTER TABLE t1
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci,
ADD UNIQUE INDEX test_key (a);
ERROR 23000: Duplicate entry 'a1' for key 'test_key'
ALTER TABLE t1 CONVERT TO CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_520_CI;
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
SELECT * FROM t1;
id a
1 a1
2 a1
DROP TABLE t1;
CREATE TABLE t1 (id INT PRIMARY KEY, a CHAR(32), KEY (a(7))) ENGINE=INNODB DEFAULT CHARSET=UTF8;
INSERT INTO t1 VALUES (1, 'a1'), (2, 'a1');
ALTER TABLE t1
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci,
ADD UNIQUE INDEX test_key (a);
ERROR 23000: Duplicate entry 'a1' for key 'test_key'
ALTER TABLE t1 CONVERT TO CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_520_CI;
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check status OK
SELECT * FROM t1;
id a
1 a1
2 a1
DROP TABLE t1;

View file

@ -857,3 +857,34 @@ create table t2 (input_id int primary key, id int not null,
alter table t1 change id id2 int;
drop table t2;
drop table t1;
--echo #
--echo # MDEV-25951 MariaDB crash after ALTER TABLE convert to utf8mb4
--echo #
CREATE TABLE t1 (id INT PRIMARY KEY, a VARCHAR(32), KEY (a(7))) ENGINE=INNODB DEFAULT CHARSET=UTF8;
INSERT INTO t1 VALUES (1, 'a1'), (2, 'a1');
--error ER_DUP_ENTRY
ALTER TABLE t1
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci,
ADD UNIQUE INDEX test_key (a);
ALTER TABLE t1 CONVERT TO CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_520_CI;
CHECK TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
CREATE TABLE t1 (id INT PRIMARY KEY, a CHAR(32), KEY (a(7))) ENGINE=INNODB DEFAULT CHARSET=UTF8;
INSERT INTO t1 VALUES (1, 'a1'), (2, 'a1');
--error ER_DUP_ENTRY
ALTER TABLE t1
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci,
ADD UNIQUE INDEX test_key (a);
ALTER TABLE t1 CONVERT TO CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_520_CI;
CHECK TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;

View file

@ -61,10 +61,10 @@ dtype_get_at_most_n_mbchars(
length is being determined */
{
ut_a(len_is_stored(data_len));
ut_ad(!mbmaxlen || !(prefix_len % mbmaxlen));
ut_ad(!mbmaxlen || !(prefix_len % mbmaxlen) || !(prefix_len % 4));
if (mbminlen != mbmaxlen) {
ut_a(!(prefix_len % mbmaxlen));
ut_a(!(prefix_len % mbmaxlen) || !(prefix_len % 4));
return(innobase_get_at_most_n_mbchars(
dtype_get_charset_coll(prtype),
prefix_len, data_len, str));

View file

@ -1916,7 +1916,8 @@ dict_index_add_to_cache(
/* Set the max_prefix value based on the
prefix_len. */
ut_ad(field->col->is_binary()
|| field->prefix_len % field->col->mbmaxlen == 0);
|| field->prefix_len % field->col->mbmaxlen == 0
|| field->prefix_len % 4 == 0);
field->col->max_prefix = field->prefix_len;
}
ut_ad(field->col->ord_part == 1);

View file

@ -2576,6 +2576,12 @@ corrupted:
!= DB_SUCCESS) {
goto func_exit;
}
for (uint i = 0; i < index->n_fields; i++) {
dict_field_t &f = index->fields[i];
ut_ad(f.col->mbmaxlen == 0
|| f.prefix_len % f.col->mbmaxlen == 0);
}
}
next_rec:
btr_pcur_move_to_next_user_rec(&pcur, &mtr);

View file

@ -11351,6 +11351,8 @@ create_index(
prefix_len = 0;
}
ut_ad(prefix_len % field->charset()->mbmaxlen == 0);
field_lengths[i] = key_part->length;
if (!key_part->field->stored_in_db()) {