summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSivert Sorumgard <sivert.sorumgaard@oracle.com>2013-06-12 09:35:33 +0200
committerSivert Sorumgard <sivert.sorumgaard@oracle.com>2013-06-12 09:35:33 +0200
commitc8fc3db1b0b3a16a1f1b8863df39e10598521758 (patch)
treed7113978c83edcd98790df6a262b0777d614d208
parentb75b32291f8026d2f3362c031ecdfac6a750af65 (diff)
downloadmariadb-git-c8fc3db1b0b3a16a1f1b8863df39e10598521758.tar.gz
Bug #14227431: CHARACTER SET MISMATCH WHEN ALTERING FOREIGN KEYS
CAN LEAD TO MISSING TABLES Overview -------- If the FOREIGN_KEY_CHECKS system variable is set to 0, it is possible to break a foreign key constraint by changing the type or character set of the foreign key column, or by dropping the foreign key index (without carrying out corresponding changes on another table in the relationship). If we subsequently set FOREIGN_KEY_CHECKS to 1 and execute ALTER TABLE involving the COPY algorithm on such a table, the following happens: 1) If ALTER TABLE does not contain a RENAME clause, the attempt to install the new version of the table instead of the old one will fail due to the fact that the inconsistency will be detected. An attempt to revert the partially executed alter table operation by restoring the old table definition will fail as well due to FOREIGN_KEY_CHECKS == 1. As a result, the table being altered will be lost. 2) If ALTER TABLE contains the RENAME clause, the inconsistency will not be detected (most probably due to other bugs). But if an attempt to install the new version of the table fails (for example, due to a failure when updating triggers associated with the table), reverting the partially executed alter table by restoring the old table definition will fail too. So the table being altered might be lost as well. Suggested fix ------------- The suggested fix is to temporarily unset the option bit representing FOREIGN_KEY_CHECKS when the old table definition is restored while reverting the partially executed operation.
-rw-r--r--sql/sql_rename.cc4
-rw-r--r--sql/sql_table.cc22
-rw-r--r--sql/sql_table.h4
3 files changed, 22 insertions, 8 deletions
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index f32dc75148a..76a4b50d73a 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -293,7 +293,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
(void) mysql_rename_table(ha_resolve_by_legacy_type(thd,
table_type),
new_db, new_alias,
- ren_table->db, old_alias, 0);
+ ren_table->db, old_alias, NO_FK_CHECKS);
}
}
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index a65d2f0345b..cb03411b9ef 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4472,6 +4472,8 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
FN_TO_IS_TMP new_name is temporary.
NO_FRM_RENAME Don't rename the FRM file
but only the table in the storage engine.
+ NO_FK_CHECKS Don't check FK constraints
+ during rename.
RETURN
FALSE OK
@@ -4490,9 +4492,14 @@ mysql_rename_table(handlerton *base, const char *old_db,
char tmp_name[NAME_LEN+1];
handler *file;
int error=0;
+ ulonglong save_bits= thd->variables.option_bits;
DBUG_ENTER("mysql_rename_table");
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
old_db, old_name, new_db, new_name));
+
+ // Temporarily disable foreign key checks
+ if (flags & NO_FK_CHECKS)
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
file= (base == NULL ? 0 :
get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
@@ -4538,6 +4545,10 @@ mysql_rename_table(handlerton *base, const char *old_db,
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
else if (error)
my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
+
+ // Restore options bits to the original value
+ thd->variables.option_bits= save_bits;
+
DBUG_RETURN(error != 0);
}
@@ -6064,7 +6075,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
new_db, new_alias))
{
(void) mysql_rename_table(old_db_type, new_db, new_alias, db,
- table_name, 0);
+ table_name, NO_FK_CHECKS);
error= -1;
}
}
@@ -6787,7 +6798,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
error= 1;
(void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
(void) mysql_rename_table(old_db_type, db, old_name, db, alias,
- FN_FROM_IS_TMP);
+ FN_FROM_IS_TMP | NO_FK_CHECKS);
}
else if (new_name != table_name || new_db != db)
{
@@ -6799,7 +6810,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
error= 1;
(void) quick_rm_table(new_db_type, new_db, new_alias, 0);
(void) mysql_rename_table(old_db_type, db, old_name, db, alias,
- FN_FROM_IS_TMP);
+ FN_FROM_IS_TMP | NO_FK_CHECKS);
}
else if (Table_triggers_list::change_table_name(thd, db, alias,
table_name, new_db,
@@ -6809,7 +6820,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
error= 1;
(void) quick_rm_table(new_db_type, new_db, new_alias, 0);
(void) mysql_rename_table(old_db_type, db, old_name, db,
- alias, FN_FROM_IS_TMP);
+ alias, FN_FROM_IS_TMP | NO_FK_CHECKS);
/*
If we were performing "fast"/in-place ALTER TABLE we also need
to restore old name of table in storage engine as a separate
@@ -6818,7 +6829,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
{
(void) mysql_rename_table(save_old_db_type, new_db, new_alias,
- db, table_name, NO_FRM_RENAME);
+ db, table_name,
+ NO_FRM_RENAME | NO_FK_CHECKS);
}
}
}
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 043a8085452..6f09db12bb9 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -122,6 +122,8 @@ enum enum_explain_filename_mode
#define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP)
#define NO_FRM_RENAME (1 << 2)
#define FRM_ONLY (1 << 3)
+/** Don't check foreign key constraints while renaming table */
+#define NO_FK_CHECKS (1 << 4)
uint filename_to_tablename(const char *from, char *to, uint to_length
#ifndef DBUG_OFF