1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

MDEV-16060 - InnoDB: Failing assertion: ut_strcmp(index->name, key->name)

A sequel to 9180e86 and 149b754.

ALTER TABLE ... ADD FOREIGN KEY may crash if parent table is updated
concurrently.

Block FK parent table updates even earlier, before intermediate child
table is created.

Use proper charset info for my_casedn_str() and don't update original
identifiers so that lower_cast_table_names == 2 is honoured.
This commit is contained in:
Sergey Vojtovich
2019-03-29 19:08:22 +04:00
parent 3e5526b0df
commit d0b73fb8d3
5 changed files with 109 additions and 46 deletions

View File

@ -8242,6 +8242,50 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
}
}
/*
Normally, an attempt to modify an FK parent table will cause
FK children to be prelocked, so the table-being-altered cannot
be modified by a cascade FK action, because ALTER holds a lock
and prelocking will wait.
But if a new FK is being added by this very ALTER, then the target
table is not locked yet (it's a temporary table). So, we have to
lock FK parents explicitly.
*/
if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY)
{
List_iterator<Key> fk_list_it(alter_info->key_list);
while (Key *key= fk_list_it++)
{
if (key->type != Key::FOREIGN_KEY)
continue;
Foreign_key *fk= static_cast<Foreign_key*>(key);
char dbuf[NAME_LEN];
char tbuf[NAME_LEN];
const char *ref_db= fk->ref_db.str ? fk->ref_db.str : alter_ctx->new_db;
const char *ref_table= fk->ref_table.str;
MDL_request mdl_request;
if (lower_case_table_names)
{
strmake_buf(dbuf, ref_db);
my_casedn_str(system_charset_info, dbuf);
strmake_buf(tbuf, ref_table);
my_casedn_str(system_charset_info, tbuf);
ref_db= dbuf;
ref_table= tbuf;
}
mdl_request.init(MDL_key::TABLE, ref_db, ref_table, MDL_SHARED_NO_WRITE,
MDL_TRANSACTION);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
DBUG_RETURN(true);
}
}
DBUG_RETURN(false);
}
@ -9093,6 +9137,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/* Mark that we have created table in storage engine. */
no_ha_table= false;
DEBUG_SYNC(thd, "alter_table_intermediate_table_created");
if (create_info->tmp_table())
{
@ -9126,52 +9171,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
true, true);
if (!new_table)
goto err_new_table_cleanup;
/*
Normally, an attempt to modify an FK parent table will cause
FK children to be prelocked, so the table-being-altered cannot
be modified by a cascade FK action, because ALTER holds a lock
and prelocking will wait.
But if a new FK is being added by this very ALTER, then the target
table is not locked yet (it's a temporary table). So, we have to
lock FK parents explicitly.
*/
if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY)
{
List <FOREIGN_KEY_INFO> fk_list;
List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list);
FOREIGN_KEY_INFO *fk;
/* tables_opened can be > 1 only for MERGE tables */
DBUG_ASSERT(tables_opened == 1);
DBUG_ASSERT(&table_list->next_global == thd->lex->query_tables_last);
new_table->file->get_foreign_key_list(thd, &fk_list);
while ((fk= fk_list_it++))
{
MDL_request mdl_request;
if (lower_case_table_names)
{
char buf[NAME_LEN];
uint len;
strmake_buf(buf, fk->referenced_db->str);
len = my_casedn_str(files_charset_info, buf);
thd->make_lex_string(fk->referenced_db, buf, len);
strmake_buf(buf, fk->referenced_table->str);
len = my_casedn_str(files_charset_info, buf);
thd->make_lex_string(fk->referenced_table, buf, len);
}
mdl_request.init(MDL_key::TABLE,
fk->referenced_db->str, fk->referenced_table->str,
MDL_SHARED_NO_WRITE, MDL_TRANSACTION);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
goto err_new_table_cleanup;
}
}
}
/*
Note: In case of MERGE table, we do not attach children. We do not