From d10c42b42541deed899dd1d1e04b69475339196c Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Thu, 7 Oct 2021 15:04:56 +0300 Subject: [PATCH] MDEV-20131 Assertion `!pk->has_virtual()' failed Assertion `!pk->has_virtual()' failed in dict_index_build_internal_clust while creating PRIMARY key longer than possible to store in the page. This happened because the key was wrongly deduced as Long UNIQUE supported, however PRIMARY KEY cannot be of that type. The main reason is that only 8 bytes are used to store the hash, see HA_HASH_FIELD_LENGTH. This is also why HA_NOSAME flag is removed (and caused the assertion in turn) in open_table_from_share: if (key_info->algorithm == HA_KEY_ALG_LONG_HASH) { key_part_end++; key_info->flags&= ~HA_NOSAME; } To make it unique, the additional check is done by check_duplicate_long_entries call from ha_write_row, and similar one from ha_update_row. PRIMARY key is already forbidden, which is checked by the first test in main.long_unique, however is_hash_field_needed was wrongly deduced to true in mysql_prepare_create_table in this particular case. FIX: * Improve the check for Key::PRIMARY type * Simplify is_hash_field_needed deduction for a more neat reading --- mysql-test/main/long_unique_innodb.opt | 1 + mysql-test/main/long_unique_innodb.result | 3 +++ mysql-test/main/long_unique_innodb.test | 5 +++++ sql/sql_table.cc | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 mysql-test/main/long_unique_innodb.opt diff --git a/mysql-test/main/long_unique_innodb.opt b/mysql-test/main/long_unique_innodb.opt new file mode 100644 index 00000000000..058a129cdc2 --- /dev/null +++ b/mysql-test/main/long_unique_innodb.opt @@ -0,0 +1 @@ +--innodb-page-size=8K diff --git a/mysql-test/main/long_unique_innodb.result b/mysql-test/main/long_unique_innodb.result index 135bb0808cc..96e5fac7310 100644 --- a/mysql-test/main/long_unique_innodb.result +++ b/mysql-test/main/long_unique_innodb.result @@ -131,3 +131,6 @@ connection default; drop table t1; disconnect con1; disconnect con2; +# MDEV-20131 Assertion `!pk->has_virtual()' failed +create table t1 (a text, primary key(a(1871))) engine=innodb; +ERROR 42000: Specified key was too long; max key length is 1536 bytes diff --git a/mysql-test/main/long_unique_innodb.test b/mysql-test/main/long_unique_innodb.test index aac68cd2271..dd2d9f94de3 100644 --- a/mysql-test/main/long_unique_innodb.test +++ b/mysql-test/main/long_unique_innodb.test @@ -138,3 +138,8 @@ connection default; drop table t1; disconnect con1; disconnect con2; + +--echo # MDEV-20131 Assertion `!pk->has_virtual()' failed + +--error ER_TOO_LONG_KEY +create table t1 (a text, primary key(a(1871))) engine=innodb; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 12e09e7bcb2..f64abe72bac 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3968,7 +3968,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!column->length) { if (key->type == Key::UNIQUE) - is_hash_field_needed= true; + is_hash_field_needed= true; // for case "a BLOB UNIQUE" else if (key->type == Key::MULTIPLE) column->length= file->max_key_length() + 1; else @@ -4064,8 +4064,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, /* Align key length to multibyte char boundary */ key_part_length-= key_part_length % sql_field->charset->mbmaxlen; } - else - is_hash_field_needed= true; } } // Catch invalid use of partial keys @@ -4111,11 +4109,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } else { - if (key->type == Key::UNIQUE) - { - is_hash_field_needed= true; - } - else + if (key->type != Key::UNIQUE) { key_part_length= MY_MIN(max_key_length, file->max_key_part_length()); my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); @@ -4123,6 +4117,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } } + + if (key->type == Key::UNIQUE + && key_part_length > MY_MIN(max_key_length, + file->max_key_part_length())) + { + is_hash_field_needed= true; + } + /* We can not store key_part_length more then 2^16 - 1 in frm */ if (is_hash_field_needed && column->length > UINT_MAX16) {