Commit 1522ee29 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-18016: Assertion failure on ALTER TABLE after foreign_key_checks=0

ha_innobase::commit_inplace_alter_table(): Do not crash if
innobase_update_foreign_cache() returns an error. It can return
an error on ALTER TABLE if an inconsistent FOREIGN KEY constraint
was created earlier when SET foreign_key_checks=0 was in effect.
Instead, report a warning to the client that constraints cannot
be loaded.
parent 6699cac0
...@@ -70,3 +70,15 @@ CREATE TABLE t2 (b INT PRIMARY KEY, FOREIGN KEY fk1 (b) REFERENCES t1 (a)) ...@@ -70,3 +70,15 @@ CREATE TABLE t2 (b INT PRIMARY KEY, FOREIGN KEY fk1 (b) REFERENCES t1 (a))
ENGINE=InnoDB; ENGINE=InnoDB;
ALTER TABLE t2 DROP FOREIGN KEY fk1, DROP FOREIGN KEY fk1; ALTER TABLE t2 DROP FOREIGN KEY fk1, DROP FOREIGN KEY fk1;
DROP TABLE t2, t1; DROP TABLE t2, t1;
CREATE TABLE t1 (f VARCHAR(256)) ENGINE=InnoDB;
SET SESSION FOREIGN_KEY_CHECKS = OFF;
ALTER TABLE t1 ADD FOREIGN KEY (f) REFERENCES non_existing_table (x);
SET SESSION FOREIGN_KEY_CHECKS = ON;
ALTER TABLE t1 ADD FULLTEXT INDEX ft1 (f);
Warnings:
Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID
Warning 1088 failed to load FOREIGN KEY constraints
ALTER TABLE t1 ADD FULLTEXT INDEX ft2 (f);
Warnings:
Warning 1088 failed to load FOREIGN KEY constraints
DROP TABLE t1;
...@@ -96,3 +96,11 @@ CREATE TABLE t2 (b INT PRIMARY KEY, FOREIGN KEY fk1 (b) REFERENCES t1 (a)) ...@@ -96,3 +96,11 @@ CREATE TABLE t2 (b INT PRIMARY KEY, FOREIGN KEY fk1 (b) REFERENCES t1 (a))
ENGINE=InnoDB; ENGINE=InnoDB;
ALTER TABLE t2 DROP FOREIGN KEY fk1, DROP FOREIGN KEY fk1; ALTER TABLE t2 DROP FOREIGN KEY fk1, DROP FOREIGN KEY fk1;
DROP TABLE t2, t1; DROP TABLE t2, t1;
CREATE TABLE t1 (f VARCHAR(256)) ENGINE=InnoDB;
SET SESSION FOREIGN_KEY_CHECKS = OFF;
ALTER TABLE t1 ADD FOREIGN KEY (f) REFERENCES non_existing_table (x);
SET SESSION FOREIGN_KEY_CHECKS = ON;
ALTER TABLE t1 ADD FULLTEXT INDEX ft1 (f);
ALTER TABLE t1 ADD FULLTEXT INDEX ft2 (f);
DROP TABLE t1;
...@@ -5638,7 +5638,6 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5638,7 +5638,6 @@ ha_innobase::commit_inplace_alter_table(
Alter_inplace_info* ha_alter_info, Alter_inplace_info* ha_alter_info,
bool commit) bool commit)
{ {
dberr_t error;
ha_innobase_inplace_ctx* ctx0 ha_innobase_inplace_ctx* ctx0
= static_cast<ha_innobase_inplace_ctx*> = static_cast<ha_innobase_inplace_ctx*>
(ha_alter_info->handler_ctx); (ha_alter_info->handler_ctx);
...@@ -5705,7 +5704,7 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5705,7 +5704,7 @@ ha_innobase::commit_inplace_alter_table(
transactions collected during crash recovery could be transactions collected during crash recovery could be
holding InnoDB locks only, not MySQL locks. */ holding InnoDB locks only, not MySQL locks. */
error = row_merge_lock_table( dberr_t error = row_merge_lock_table(
prebuilt->trx, ctx->old_table, LOCK_X); prebuilt->trx, ctx->old_table, LOCK_X);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
...@@ -5890,9 +5889,9 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5890,9 +5889,9 @@ ha_innobase::commit_inplace_alter_table(
file operations that will be performed in file operations that will be performed in
commit_cache_rebuild(), and if none, generate commit_cache_rebuild(), and if none, generate
the redo log for these operations. */ the redo log for these operations. */
error = fil_mtr_rename_log(ctx->old_table, dberr_t error = fil_mtr_rename_log(
ctx->new_table, ctx->old_table, ctx->new_table, ctx->tmp_name,
ctx->tmp_name, &mtr); &mtr);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
/* Out of memory or a problem will occur /* Out of memory or a problem will occur
when renaming files. */ when renaming files. */
...@@ -6017,39 +6016,30 @@ ha_innobase::commit_inplace_alter_table( ...@@ -6017,39 +6016,30 @@ ha_innobase::commit_inplace_alter_table(
/* Rename the tablespace files. */ /* Rename the tablespace files. */
commit_cache_rebuild(ctx); commit_cache_rebuild(ctx);
error = innobase_update_foreign_cache(ctx, user_thd); if (innobase_update_foreign_cache(ctx, user_thd)
if (error != DB_SUCCESS) { != DB_SUCCESS
goto foreign_fail; && prebuilt->trx->check_foreigns) {
foreign_fail:
push_warning_printf(
user_thd,
Sql_condition::WARN_LEVEL_WARN,
ER_ALTER_INFO,
"failed to load FOREIGN KEY"
" constraints");
} }
} else { } else {
error = innobase_update_foreign_cache(ctx, user_thd); bool fk_fail = innobase_update_foreign_cache(
ctx, user_thd) != DB_SUCCESS;
if (error != DB_SUCCESS) { if (!commit_cache_norebuild(ctx, table, trx)) {
foreign_fail: fk_fail = true;
/* The data dictionary cache ut_ad(!prebuilt->trx->check_foreigns);
should be corrupted now. The }
best solution should be to
kill and restart the server,
but the *.frm file has not
been replaced yet. */
my_error(ER_CANNOT_ADD_FOREIGN,
MYF(0));
sql_print_error(
"InnoDB: dict_load_foreigns()"
" returned %u for %s",
(unsigned) error,
thd_query_string(user_thd)
->str);
ut_ad(0);
} else {
if (!commit_cache_norebuild(
ctx, table, trx)) {
ut_a(!prebuilt->trx->check_foreigns);
}
innobase_rename_columns_cache( innobase_rename_columns_cache(ha_alter_info, table,
ha_alter_info, table, ctx->new_table);
ctx->new_table); if (fk_fail && prebuilt->trx->check_foreigns) {
goto foreign_fail;
} }
} }
DBUG_INJECT_CRASH("ib_commit_inplace_crash", DBUG_INJECT_CRASH("ib_commit_inplace_crash",
......
...@@ -5654,7 +5654,6 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5654,7 +5654,6 @@ ha_innobase::commit_inplace_alter_table(
Alter_inplace_info* ha_alter_info, Alter_inplace_info* ha_alter_info,
bool commit) bool commit)
{ {
dberr_t error;
ha_innobase_inplace_ctx* ctx0 ha_innobase_inplace_ctx* ctx0
= static_cast<ha_innobase_inplace_ctx*> = static_cast<ha_innobase_inplace_ctx*>
(ha_alter_info->handler_ctx); (ha_alter_info->handler_ctx);
...@@ -5721,7 +5720,7 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5721,7 +5720,7 @@ ha_innobase::commit_inplace_alter_table(
transactions collected during crash recovery could be transactions collected during crash recovery could be
holding InnoDB locks only, not MySQL locks. */ holding InnoDB locks only, not MySQL locks. */
error = row_merge_lock_table( dberr_t error = row_merge_lock_table(
prebuilt->trx, ctx->old_table, LOCK_X); prebuilt->trx, ctx->old_table, LOCK_X);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
...@@ -5906,9 +5905,9 @@ ha_innobase::commit_inplace_alter_table( ...@@ -5906,9 +5905,9 @@ ha_innobase::commit_inplace_alter_table(
file operations that will be performed in file operations that will be performed in
commit_cache_rebuild(), and if none, generate commit_cache_rebuild(), and if none, generate
the redo log for these operations. */ the redo log for these operations. */
error = fil_mtr_rename_log(ctx->old_table, dberr_t error = fil_mtr_rename_log(
ctx->new_table, ctx->old_table, ctx->new_table, ctx->tmp_name,
ctx->tmp_name, &mtr); &mtr);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
/* Out of memory or a problem will occur /* Out of memory or a problem will occur
when renaming files. */ when renaming files. */
...@@ -6033,39 +6032,30 @@ ha_innobase::commit_inplace_alter_table( ...@@ -6033,39 +6032,30 @@ ha_innobase::commit_inplace_alter_table(
/* Rename the tablespace files. */ /* Rename the tablespace files. */
commit_cache_rebuild(ctx); commit_cache_rebuild(ctx);
error = innobase_update_foreign_cache(ctx, user_thd); if (innobase_update_foreign_cache(ctx, user_thd)
if (error != DB_SUCCESS) { != DB_SUCCESS
goto foreign_fail; && prebuilt->trx->check_foreigns) {
foreign_fail:
push_warning_printf(
user_thd,
Sql_condition::WARN_LEVEL_WARN,
ER_ALTER_INFO,
"failed to load FOREIGN KEY"
" constraints");
} }
} else { } else {
error = innobase_update_foreign_cache(ctx, user_thd); bool fk_fail = innobase_update_foreign_cache(
ctx, user_thd) != DB_SUCCESS;
if (error != DB_SUCCESS) { if (!commit_cache_norebuild(ctx, table, trx)) {
foreign_fail: fk_fail = true;
/* The data dictionary cache ut_ad(!prebuilt->trx->check_foreigns);
should be corrupted now. The }
best solution should be to
kill and restart the server,
but the *.frm file has not
been replaced yet. */
my_error(ER_CANNOT_ADD_FOREIGN,
MYF(0));
sql_print_error(
"InnoDB: dict_load_foreigns()"
" returned %u for %s",
(unsigned) error,
thd_query_string(user_thd)
->str);
ut_ad(0);
} else {
if (!commit_cache_norebuild(
ctx, table, trx)) {
ut_a(!prebuilt->trx->check_foreigns);
}
innobase_rename_columns_cache( innobase_rename_columns_cache(ha_alter_info, table,
ha_alter_info, table, ctx->new_table);
ctx->new_table); if (fk_fail && prebuilt->trx->check_foreigns) {
goto foreign_fail;
} }
} }
DBUG_INJECT_CRASH("ib_commit_inplace_crash", DBUG_INJECT_CRASH("ib_commit_inplace_crash",
......
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