Commit a4b3970c authored by Eugene Kosov's avatar Eugene Kosov

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.
parent 987903b3
...@@ -2043,3 +2043,36 @@ constraint a foreign key (id) references t1 (id) ...@@ -2043,3 +2043,36 @@ constraint a foreign key (id) references t1 (id)
alter table t1 change id id2 int; alter table t1 change id id2 int;
drop table t2; drop table t2;
drop table t1; 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;
...@@ -857,3 +857,34 @@ create table t2 (input_id int primary key, id int not null, ...@@ -857,3 +857,34 @@ create table t2 (input_id int primary key, id int not null,
alter table t1 change id id2 int; alter table t1 change id id2 int;
drop table t2; drop table t2;
drop table t1; 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;
...@@ -61,10 +61,10 @@ dtype_get_at_most_n_mbchars( ...@@ -61,10 +61,10 @@ dtype_get_at_most_n_mbchars(
length is being determined */ length is being determined */
{ {
ut_a(len_is_stored(data_len)); 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) { if (mbminlen != mbmaxlen) {
ut_a(!(prefix_len % mbmaxlen)); ut_a(!(prefix_len % mbmaxlen) || !(prefix_len % 4));
return(innobase_get_at_most_n_mbchars( return(innobase_get_at_most_n_mbchars(
dtype_get_charset_coll(prtype), dtype_get_charset_coll(prtype),
prefix_len, data_len, str)); prefix_len, data_len, str));
......
...@@ -1916,7 +1916,8 @@ dict_index_add_to_cache( ...@@ -1916,7 +1916,8 @@ dict_index_add_to_cache(
/* Set the max_prefix value based on the /* Set the max_prefix value based on the
prefix_len. */ prefix_len. */
ut_ad(field->col->is_binary() 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; field->col->max_prefix = field->prefix_len;
} }
ut_ad(field->col->ord_part == 1); ut_ad(field->col->ord_part == 1);
......
...@@ -2576,6 +2576,12 @@ dict_load_indexes( ...@@ -2576,6 +2576,12 @@ dict_load_indexes(
!= DB_SUCCESS) { != DB_SUCCESS) {
goto func_exit; 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: next_rec:
btr_pcur_move_to_next_user_rec(&pcur, &mtr); btr_pcur_move_to_next_user_rec(&pcur, &mtr);
......
...@@ -11351,6 +11351,8 @@ create_index( ...@@ -11351,6 +11351,8 @@ create_index(
prefix_len = 0; prefix_len = 0;
} }
ut_ad(prefix_len % field->charset()->mbmaxlen == 0);
field_lengths[i] = key_part->length; field_lengths[i] = key_part->length;
if (!key_part->field->stored_in_db()) { if (!key_part->field->stored_in_db()) {
......
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