Commit c6207ecb authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-25803 innodb.alter_candidate_key fix

There is a case when implicit primary key may be changed when removing
NOT NULL from the part of unique key. In that case we update
modified_primary_key which is then used to not skip key sorting.

According to is_candidate_key() there is no other cases when primary
kay may be changed implicitly.
parent 63c922ae
...@@ -426,6 +426,7 @@ enum enum_alter_inplace_result { ...@@ -426,6 +426,7 @@ enum enum_alter_inplace_result {
#define HA_CREATE_TMP_ALTER 8U #define HA_CREATE_TMP_ALTER 8U
#define HA_LEX_CREATE_SEQUENCE 16U #define HA_LEX_CREATE_SEQUENCE 16U
#define HA_VERSIONED_TABLE 32U #define HA_VERSIONED_TABLE 32U
#define HA_SKIP_KEY_SORT 64U
#define HA_MAX_REC_LENGTH 65535 #define HA_MAX_REC_LENGTH 65535
......
...@@ -256,7 +256,7 @@ Alter_table_ctx::Alter_table_ctx() ...@@ -256,7 +256,7 @@ Alter_table_ctx::Alter_table_ctx()
db(null_clex_str), table_name(null_clex_str), alias(null_clex_str), db(null_clex_str), table_name(null_clex_str), alias(null_clex_str),
new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str), new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str),
fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_if_delete_row(false), fk_error_id(NULL),
fk_error_table(NULL) fk_error_table(NULL), modified_primary_key(false)
#ifdef DBUG_ASSERT_EXISTS #ifdef DBUG_ASSERT_EXISTS
, tmp_table(false) , tmp_table(false)
#endif #endif
...@@ -276,7 +276,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, ...@@ -276,7 +276,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
tables_opened(tables_opened_arg), tables_opened(tables_opened_arg),
new_db(*new_db_arg), new_name(*new_name_arg), new_db(*new_db_arg), new_name(*new_name_arg),
fk_error_if_delete_row(false), fk_error_id(NULL), fk_error_if_delete_row(false), fk_error_id(NULL),
fk_error_table(NULL) fk_error_table(NULL), modified_primary_key(false)
#ifdef DBUG_ASSERT_EXISTS #ifdef DBUG_ASSERT_EXISTS
, tmp_table(false) , tmp_table(false)
#endif #endif
......
...@@ -324,6 +324,7 @@ class Alter_table_ctx ...@@ -324,6 +324,7 @@ class Alter_table_ctx
const char *fk_error_id; const char *fk_error_id;
/** Name of table for the above error. */ /** Name of table for the above error. */
const char *fk_error_table; const char *fk_error_table;
bool modified_primary_key;
private: private:
char new_filename[FN_REFLEN + 1]; char new_filename[FN_REFLEN + 1];
......
...@@ -4215,8 +4215,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ...@@ -4215,8 +4215,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
MyISAM/Aria cannot add index inplace so we are safe to qsort key info in MyISAM/Aria cannot add index inplace so we are safe to qsort key info in
that case. And if we don't add index then we do not need qsort at all. that case. And if we don't add index then we do not need qsort at all.
*/ */
if (!(create_info->options & HA_CREATE_TMP_ALTER) || if (!(create_info->options & HA_SKIP_KEY_SORT))
alter_info->flags & ALTER_ADD_INDEX)
{ {
/* /*
Sort keys in optimized order. Sort keys in optimized order.
...@@ -8035,7 +8034,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8035,7 +8034,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
uint used_fields, dropped_sys_vers_fields= 0; uint used_fields, dropped_sys_vers_fields= 0;
KEY *key_info=table->key_info; KEY *key_info=table->key_info;
bool rc= TRUE; bool rc= TRUE;
bool modified_primary_key= FALSE;
bool vers_system_invisible= false; bool vers_system_invisible= false;
Create_field *def; Create_field *def;
Field **f_ptr,*field; Field **f_ptr,*field;
...@@ -8420,6 +8418,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8420,6 +8418,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (key_info->flags & HA_INVISIBLE_KEY) if (key_info->flags & HA_INVISIBLE_KEY)
continue; continue;
const char *key_name= key_info->name.str; const char *key_name= key_info->name.str;
const bool primary_key= table->s->primary_key == i;
const bool explicit_pk= primary_key &&
!my_strcasecmp(system_charset_info, key_name,
primary_key_name);
const bool implicit_pk= primary_key && !explicit_pk;
Alter_drop *drop; Alter_drop *drop;
drop_it.rewind(); drop_it.rewind();
while ((drop=drop_it++)) while ((drop=drop_it++))
...@@ -8433,7 +8437,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8433,7 +8437,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (table->s->tmp_table == NO_TMP_TABLE) if (table->s->tmp_table == NO_TMP_TABLE)
{ {
(void) delete_statistics_for_index(thd, table, key_info, FALSE); (void) delete_statistics_for_index(thd, table, key_info, FALSE);
if (i == table->s->primary_key) if (primary_key)
{ {
KEY *tab_key_info= table->key_info; KEY *tab_key_info= table->key_info;
for (uint j=0; j < table->s->keys; j++, tab_key_info++) for (uint j=0; j < table->s->keys; j++, tab_key_info++)
...@@ -8478,13 +8482,19 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8478,13 +8482,19 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
} }
if (!cfield) if (!cfield)
{ {
if (table->s->primary_key == i) if (primary_key)
modified_primary_key= TRUE; alter_ctx->modified_primary_key= true;
delete_index_stat= TRUE; delete_index_stat= TRUE;
if (!(kfield->flags & VERS_SYSTEM_FIELD)) if (!(kfield->flags & VERS_SYSTEM_FIELD))
dropped_key_part= key_part_name; dropped_key_part= key_part_name;
continue; // Field is removed continue; // Field is removed
} }
DBUG_ASSERT(!primary_key || kfield->flags & NOT_NULL_FLAG);
if (implicit_pk && !alter_ctx->modified_primary_key &&
!(cfield->flags & NOT_NULL_FLAG))
alter_ctx->modified_primary_key= true;
key_part_length= key_part->length; key_part_length= key_part->length;
if (cfield->field) // Not new field if (cfield->field) // Not new field
{ {
...@@ -8533,7 +8543,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8533,7 +8543,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
{ {
if (delete_index_stat) if (delete_index_stat)
(void) delete_statistics_for_index(thd, table, key_info, FALSE); (void) delete_statistics_for_index(thd, table, key_info, FALSE);
else if (modified_primary_key && else if (alter_ctx->modified_primary_key &&
key_info->user_defined_key_parts != key_info->ext_key_parts) key_info->user_defined_key_parts != key_info->ext_key_parts)
(void) delete_statistics_for_index(thd, table, key_info, TRUE); (void) delete_statistics_for_index(thd, table, key_info, TRUE);
} }
...@@ -8575,7 +8585,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, ...@@ -8575,7 +8585,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key_type= Key::SPATIAL; key_type= Key::SPATIAL;
else if (key_info->flags & HA_NOSAME) else if (key_info->flags & HA_NOSAME)
{ {
if (! my_strcasecmp(system_charset_info, key_name, primary_key_name)) if (explicit_pk)
key_type= Key::PRIMARY; key_type= Key::PRIMARY;
else else
key_type= Key::UNIQUE; key_type= Key::UNIQUE;
...@@ -9941,6 +9951,8 @@ do_continue:; ...@@ -9941,6 +9951,8 @@ do_continue:;
tmp_disable_binlog(thd); tmp_disable_binlog(thd);
create_info->options|=HA_CREATE_TMP_ALTER; create_info->options|=HA_CREATE_TMP_ALTER;
if (!(alter_info->flags & ALTER_ADD_INDEX) && !alter_ctx.modified_primary_key)
create_info->options|= HA_SKIP_KEY_SORT;
create_info->alias= alter_ctx.table_name; create_info->alias= alter_ctx.table_name;
error= create_table_impl(thd, error= create_table_impl(thd,
&alter_ctx.db, &alter_ctx.table_name, &alter_ctx.db, &alter_ctx.table_name,
......
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