Skip to content

Commit

Permalink
Bug#16208542 DROP INDEX ON A FOREIGN KEY COLUMN LEADS TO MISSING TABLE
Browse files Browse the repository at this point in the history
== Analysis ==
The bug is caused by the reason that dict_load_foreigns() will return error 
when it couldn't find a equivalent fk index, and dict_load_table() who get 
the error will return NULL to indicate there is something wrong with the 
table. This happens no matter which value(1/0) is set to FOREIGN_KEY_CHECKS.

== Solution ==
Allow user to open the table with missing fk indexes when 
FOREIGN_KEY_CHECKS=0. When the table is opened, user has to recreate 
the missing indexes to fulfill the fk constraints. After that the table 
can be open in a normal way.

A new enum type in dict_err_ignore_t called DICT_ERR_IGNORE_FK_NOKEY 
is defined, with which dict_load_foreigns() will always load all the 
fk constraints and fk indexes ignoring those missing. User can decide 
which indexes should be created according to the table definition with 
all the fk constraints.

An error message can be found when open a table with missing fk indexes. 
I think using ib_logf in dict_load_table() to print the error message is 
an easy way.

rb#2308 is approved by Marko
  • Loading branch information
bin.x.su@oracle.com committed Apr 18, 2013
1 parent 4c4a98e commit b21b995
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 55 deletions.
21 changes: 14 additions & 7 deletions storage/innobase/dict/dict0dict.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3218,11 +3218,16 @@ UNIV_INTERN
dberr_t
dict_foreign_add_to_cache(
/*======================*/
dict_foreign_t* foreign, /*!< in, own: foreign key constraint */
const char** col_names, /*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets) /*!< in: whether to check charset
compatibility */
dict_foreign_t* foreign,
/*!< in, own: foreign key constraint */
const char** col_names,
/*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets,
/*!< in: whether to check charset
compatibility */
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
{
dict_table_t* for_table;
dict_table_t* ref_table;
Expand Down Expand Up @@ -3262,7 +3267,8 @@ dict_foreign_add_to_cache(
for_in_cache->n_fields, for_in_cache->foreign_index,
check_charsets, false);

if (index == NULL) {
if (index == NULL
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
dict_foreign_error_report(
ef, for_in_cache,
"there is no index in referenced table"
Expand Down Expand Up @@ -3297,7 +3303,8 @@ dict_foreign_add_to_cache(
& (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL));

if (index == NULL) {
if (index == NULL
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
dict_foreign_error_report(
ef, for_in_cache,
"there is no index in the table"
Expand Down
43 changes: 28 additions & 15 deletions storage/innobase/dict/dict0load.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 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 the Free Software
Expand Down Expand Up @@ -2411,9 +2411,16 @@ dict_load_table(
if (!cached || table->ibd_file_missing) {
/* Don't attempt to load the indexes from disk. */
} else if (err == DB_SUCCESS) {
err = dict_load_foreigns(table->name, NULL, true, true);
err = dict_load_foreigns(table->name, NULL, true, true,
ignore_err);

if (err != DB_SUCCESS) {
ib_logf(IB_LOG_LEVEL_WARN,
"Load table '%s' failed, the table has missing "
"foreign key indexes. Turn off "
"'foreign_key_checks' and try again.",
table->name);

dict_table_remove_from_cache(table);
table = NULL;
} else {
Expand Down Expand Up @@ -2714,18 +2721,21 @@ static __attribute__((nonnull(1), warn_unused_result))
dberr_t
dict_load_foreign(
/*==============*/
const char* id, /*!< in: foreign constraint id, must be
const char* id,
/*!< in: foreign constraint id, must be
'\0'-terminated */
const char** col_names,
const char** col_names,
/*!< in: column names, or NULL
to use foreign->foreign_table->col_names */
bool check_recursive,
bool check_recursive,
/*!< in: whether to record the foreign table
parent count to avoid unlimited recursive
load of chained foreign tables */
bool check_charsets)
bool check_charsets,
/*!< in: whether to check charset
compatibility */
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
{
dict_foreign_t* foreign;
dict_table_t* sys_foreign;
Expand Down Expand Up @@ -2894,7 +2904,8 @@ dict_load_foreign(
a new foreign key constraint but loading one from the data
dictionary. */

return(dict_foreign_add_to_cache(foreign, col_names, check_charsets));
return(dict_foreign_add_to_cache(foreign, col_names, check_charsets,
ignore_err));
}

/***********************************************************************//**
Expand All @@ -2908,13 +2919,15 @@ UNIV_INTERN
dberr_t
dict_load_foreigns(
/*===============*/
const char* table_name, /*!< in: table name */
const char** col_names, /*!< in: column names, or NULL to use
table->col_names */
bool check_recursive,/*!< in: Whether to check recursive
load of tables chained by FK */
bool check_charsets) /*!< in: whether to check charset
compatibility */
const char* table_name, /*!< in: table name */
const char** col_names, /*!< in: column names, or NULL
to use table->col_names */
bool check_recursive,/*!< in: Whether to check
recursive load of tables
chained by FK */
bool check_charsets, /*!< in: whether to check
charset compatibility */
dict_err_ignore_t ignore_err) /*!< in: error to be ignored */
{
ulint tuple_buf[(DTUPLE_EST_ALLOC(1) + sizeof(ulint) - 1)
/ sizeof(ulint)];
Expand Down Expand Up @@ -3026,7 +3039,7 @@ dict_load_foreigns(
/* Load the foreign constraint definition to the dictionary cache */

err = dict_load_foreign(fk_id, col_names,
check_recursive, check_charsets);
check_recursive, check_charsets, ignore_err);

if (err != DB_SUCCESS) {
btr_pcur_close(&pcur);
Expand Down
35 changes: 21 additions & 14 deletions storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4602,17 +4602,18 @@ UNIV_INTERN
int
ha_innobase::open(
/*==============*/
const char* name, /*!< in: table name */
int mode, /*!< in: not used */
uint test_if_locked) /*!< in: not used */
{
dict_table_t* ib_table;
char norm_name[FN_REFLEN];
THD* thd;
ulint retries = 0;
char* is_part = NULL;
ibool par_case_name_set = FALSE;
char par_case_name[FN_REFLEN];
const char* name, /*!< in: table name */
int mode, /*!< in: not used */
uint test_if_locked) /*!< in: not used */
{
dict_table_t* ib_table;
char norm_name[FN_REFLEN];
THD* thd;
ulint retries = 0;
char* is_part = NULL;
ibool par_case_name_set = FALSE;
char par_case_name[FN_REFLEN];
dict_err_ignore_t ignore_err = DICT_ERR_IGNORE_NONE;

DBUG_ENTER("ha_innobase::open");

Expand Down Expand Up @@ -4652,10 +4653,16 @@ ha_innobase::open(
is_part = strstr(norm_name, "#P#");
#endif /* __WIN__ */

/* Check whether FOREIGN_KEY_CHECKS is set to 0. If so, the table
can be opened even if some FK indexes are missing. If not, the table
can't be opened in the same situation */
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
ignore_err = DICT_ERR_IGNORE_FK_NOKEY;
}

retry:
/* Get pointer to a table object in InnoDB dictionary cache */
ib_table = dict_table_open_on_name(norm_name, FALSE, TRUE,
DICT_ERR_IGNORE_NONE);
ib_table = dict_table_open_on_name(norm_name, FALSE, TRUE, ignore_err);

if (ib_table
&& ((!DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID)
Expand Down Expand Up @@ -4721,7 +4728,7 @@ ha_innobase::open(

ib_table = dict_table_open_on_name(
par_case_name, FALSE, TRUE,
DICT_ERR_IGNORE_NONE);
ignore_err);
}

if (!ib_table) {
Expand Down
3 changes: 2 additions & 1 deletion storage/innobase/handler/handler0alter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4678,7 +4678,8 @@ innobase_update_foreign_cache(
and prevent the table from being evicted from the data
dictionary cache (work around the lack of WL#6049). */
DBUG_RETURN(dict_load_foreigns(user_table->name,
ctx->col_names, false, true));
ctx->col_names, false, true,
DICT_ERR_IGNORE_NONE));
}

/** Commit the changes made during prepare_inplace_alter_table()
Expand Down
17 changes: 11 additions & 6 deletions storage/innobase/include/dict0dict.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
Expand Down Expand Up @@ -408,11 +408,16 @@ UNIV_INTERN
dberr_t
dict_foreign_add_to_cache(
/*======================*/
dict_foreign_t* foreign, /*!< in, own: foreign key constraint */
const char** col_names, /*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets) /*!< in: whether to check charset
compatibility */
dict_foreign_t* foreign,
/*!< in, own: foreign key constraint */
const char** col_names,
/*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets,
/*!< in: whether to check charset
compatibility */
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
__attribute__((nonnull(1), warn_unused_result));
/*********************************************************************//**
Check if the index is referenced by a foreign key, if TRUE return the
Expand Down
18 changes: 10 additions & 8 deletions storage/innobase/include/dict0load.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 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 the Free Software
Expand Down Expand Up @@ -220,13 +220,15 @@ UNIV_INTERN
dberr_t
dict_load_foreigns(
/*===============*/
const char* table_name, /*!< in: table name */
const char** col_names, /*!< in: column names, or NULL to use
table->col_names */
bool check_recursive,/*!< in: Whether to check recursive
load of tables chained by FK */
bool check_charsets) /*!< in: whether to check charset
compatibility */
const char* table_name, /*!< in: table name */
const char** col_names, /*!< in: column names, or NULL
to use table->col_names */
bool check_recursive,/*!< in: Whether to check
recursive load of tables
chained by FK */
bool check_charsets, /*!< in: whether to check
charset compatibility */
dict_err_ignore_t ignore_err) /*!< in: error to be ignored */
__attribute__((nonnull(1), warn_unused_result));
/********************************************************************//**
Prints to the standard output information on all tables found in the data
Expand Down
4 changes: 3 additions & 1 deletion storage/innobase/include/dict0types.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 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 the Free Software
Expand Down Expand Up @@ -57,6 +57,8 @@ enum dict_err_ignore_t {
DICT_ERR_IGNORE_INDEX_ROOT = 1, /*!< ignore error if index root
page is FIL_NULL or incorrect value */
DICT_ERR_IGNORE_CORRUPT = 2, /*!< skip corrupted indexes */
DICT_ERR_IGNORE_FK_NOKEY = 4, /*!< ignore error if any foreign
key is missing */
DICT_ERR_IGNORE_ALL = 0xFFFF /*!< ignore all errors */
};

Expand Down
2 changes: 1 addition & 1 deletion storage/innobase/include/handler0alter.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2005, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2005, 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 the Free Software
Expand Down
6 changes: 4 additions & 2 deletions storage/innobase/row/row0mysql.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2521,7 +2521,8 @@ row_table_add_foreign_constraints(

if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
err = dict_load_foreigns(name, NULL, false, true);
err = dict_load_foreigns(name, NULL, false, true,
DICT_ERR_IGNORE_NONE);
}

if (err != DB_SUCCESS) {
Expand Down Expand Up @@ -4986,7 +4987,8 @@ row_rename_table_for_mysql(

err = dict_load_foreigns(
new_name, NULL,
false, !old_is_tmp || trx->check_foreigns);
false, !old_is_tmp || trx->check_foreigns,
DICT_ERR_IGNORE_NONE);

if (err != DB_SUCCESS) {
ut_print_timestamp(stderr);
Expand Down

0 comments on commit b21b995

Please sign in to comment.