Skip to content

Commit

Permalink
MDEV-8524: Improve error messaging when there is duplicate key or for…
Browse files Browse the repository at this point in the history
…eign key names

Added better error message that will be printed when foreign key
constraint name in create table is not unique in database.
  • Loading branch information
Jan Lindström committed Jul 31, 2015
1 parent 392df76 commit e05cd97
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 2 deletions.
20 changes: 20 additions & 0 deletions mysql-test/suite/innodb/r/innodb-fk-warnings.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE t1 (
id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT test FOREIGN KEY (b) REFERENCES t1 (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE t2 (
id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT test FOREIGN KEY (b) REFERENCES t2 (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ERROR HY000: Can't create table 'test.t2' (errno: 121)
show warnings;
Level Code Message
Warning 121 InnoDB: foreign key constraint name `test/test` already exists on data dictionary. Foreign key constraint names need to be unique in database. Error in foreign key definition: CONSTRAINT `test` FOREIGN KEY (`b`) REFERENCES `test`.`t2` (`id`).
Error 1005 Can't create table 'test.t2' (errno: 121)
drop table t1;
29 changes: 29 additions & 0 deletions mysql-test/suite/innodb/t/innodb-fk-warnings.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--source include/have_innodb.inc

#
# MDEV-8524: Improve error messaging when there is duplicate key or foreign key names
#
CREATE TABLE t1 (
id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT test FOREIGN KEY (b) REFERENCES t1 (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

#
# Below create table fails because constraint name test
# is reserved for above table.
#
--error 1005
CREATE TABLE t2 (
id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT test FOREIGN KEY (b) REFERENCES t2 (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

show warnings;

drop table t1;
117 changes: 116 additions & 1 deletion storage/innobase/dict/dict0crea.c
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,91 @@ dict_create_add_foreign_field_to_dictionary(
table, foreign, trx));
}

/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
static
char*
dict_foreign_def_get(
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx) /*!< in: trx */
{
char* fk_def = mem_heap_alloc(foreign->heap, 4*1024);
const char* tbname;
char tablebuf[MAX_TABLE_NAME_LEN + 1] = "";
int i;

tbname = dict_remove_db_name(foreign->id);
innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN,
tbname, strlen(tbname), trx->mysql_thd, FALSE);

sprintf(fk_def,
(char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf);

for(i = 0; i < foreign->n_fields; i++) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->foreign_col_names[i],
strlen(foreign->foreign_col_names[i]),
trx->mysql_thd, FALSE);
strcat(fk_def, buf);
if (i < foreign->n_fields-1) {
strcat(fk_def, (char *)",");
}
}

strcat(fk_def,(char *)") REFERENCES ");

innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN,
foreign->referenced_table_name,
strlen(foreign->referenced_table_name),
trx->mysql_thd, TRUE);

strcat(fk_def, tablebuf);
strcat(fk_def, " (");

for(i = 0; i < foreign->n_fields; i++) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->referenced_col_names[i],
strlen(foreign->referenced_col_names[i]),
trx->mysql_thd, FALSE);
strcat(fk_def, buf);
if (i < foreign->n_fields-1) {
strcat(fk_def, (char *)",");
}
}
strcat(fk_def, (char *)")");

return fk_def;
}

/********************************************************************//**
Convert foreign key column names from data dictionary to SQL-layer.
*/
static
void
dict_foreign_def_get_fields(
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx, /*!< in: trx */
char** field, /*!< out: foreign column */
char** field2, /*!< out: referenced column */
int col_no) /*!< in: column number */
{
*field = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1);
*field2 = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1);

innobase_convert_name(*field, MAX_TABLE_NAME_LEN,
foreign->foreign_col_names[col_no],
strlen(foreign->foreign_col_names[col_no]),
trx->mysql_thd, FALSE);

innobase_convert_name(*field, MAX_TABLE_NAME_LEN,
foreign->referenced_col_names[col_no],
strlen(foreign->referenced_col_names[col_no]),
trx->mysql_thd, FALSE);
}

/********************************************************************//**
Add a single foreign key definition to the data dictionary tables in the
database. We also generate names to constraints that were not named by the
Expand Down Expand Up @@ -1501,6 +1586,22 @@ dict_create_add_foreign_to_dictionary(

if (error != DB_SUCCESS) {

if (error == DB_DUPLICATE_KEY) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* fk_def;

innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE);

fk_def = dict_foreign_def_get(foreign, trx);

ib_push_warning(trx, error, (const char *)"InnoDB: foreign key constraint name %s "
"already exists on data dictionary."
" Foreign key constraint names need to be unique in database."
" Error in foreign key definition: %s.",
buf, fk_def);
}

return(error);
}

Expand All @@ -1509,6 +1610,20 @@ dict_create_add_foreign_to_dictionary(
i, table, foreign, trx);

if (error != DB_SUCCESS) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* field=NULL;
char* field2=NULL;
char* fk_def;

innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE);
fk_def = dict_foreign_def_get(foreign, trx);
dict_foreign_def_get_fields(foreign, trx, &field, &field2, i);

ib_push_warning(trx, error,
(const char *)"InnoDB: Error adding foreign key constraint name %s fields %s or %s to the dictionary."
" Error in foreign key definition: %s.",
buf, i+1, fk_def);

return(error);
}
Expand Down Expand Up @@ -1593,7 +1708,7 @@ dict_create_add_foreigns_to_dictionary(
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {

error = dict_create_add_foreign_to_dictionary(&number, table,
foreign, trx);
foreign, trx);

if (error != DB_SUCCESS) {

Expand Down
26 changes: 26 additions & 0 deletions storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12304,3 +12304,29 @@ ib_warn_row_too_big(const dict_table_t* table)
" ROW_FORMAT=COMPRESSED ": ""
, prefix ? DICT_MAX_FIXED_COL_LEN : 0);
}

/********************************************************************//**
Helper function to push warnings from InnoDB internals to SQL-layer. */
extern "C" UNIV_INTERN
void
ib_push_warning(
trx_t* trx, /*!< in: trx */
ulint error, /*!< in: error code to push as warning */
const char *format,/*!< in: warning message */
...)
{
va_list args;
THD *thd = (THD *)trx->mysql_thd;
char *buf;
#define MAX_BUF_SIZE 4*1024

va_start(args, format);
buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME));
vsprintf(buf,format, args);

push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
convert_error_code_to_mysql(error, 0, thd),
buf);
my_free(buf);
va_end(args);
}
9 changes: 9 additions & 0 deletions storage/innobase/include/ha_prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,5 +326,14 @@ innobase_convert_to_filename_charset(
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes */

/********************************************************************//**
Helper function to push warnings from InnoDB internals to SQL-layer. */
UNIV_INTERN
void
ib_push_warning(
trx_t* trx, /*!< in: trx */
ulint error, /*!< in: error code to push as warning */
const char *format,/*!< in: warning message */
...);

#endif
117 changes: 116 additions & 1 deletion storage/xtradb/dict/dict0crea.c
Original file line number Diff line number Diff line change
Expand Up @@ -1626,6 +1626,91 @@ dict_create_add_foreign_field_to_dictionary(
table, foreign, trx));
}

/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
static
char*
dict_foreign_def_get(
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx) /*!< in: trx */
{
char* fk_def = mem_heap_alloc(foreign->heap, 4*1024);
const char* tbname;
char tablebuf[MAX_TABLE_NAME_LEN + 1] = "";
int i;

tbname = dict_remove_db_name(foreign->id);
innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN,
tbname, strlen(tbname), trx->mysql_thd, FALSE);

sprintf(fk_def,
(char *)"CONSTRAINT %s FOREIGN KEY (", (char *)tablebuf);

for(i = 0; i < foreign->n_fields; i++) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->foreign_col_names[i],
strlen(foreign->foreign_col_names[i]),
trx->mysql_thd, FALSE);
strcat(fk_def, buf);
if (i < foreign->n_fields-1) {
strcat(fk_def, (char *)",");
}
}

strcat(fk_def,(char *)") REFERENCES ");

innobase_convert_name(tablebuf, MAX_TABLE_NAME_LEN,
foreign->referenced_table_name,
strlen(foreign->referenced_table_name),
trx->mysql_thd, TRUE);

strcat(fk_def, tablebuf);
strcat(fk_def, " (");

for(i = 0; i < foreign->n_fields; i++) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->referenced_col_names[i],
strlen(foreign->referenced_col_names[i]),
trx->mysql_thd, FALSE);
strcat(fk_def, buf);
if (i < foreign->n_fields-1) {
strcat(fk_def, (char *)",");
}
}
strcat(fk_def, (char *)")");

return fk_def;
}

/********************************************************************//**
Convert foreign key column names from data dictionary to SQL-layer.
*/
static
void
dict_foreign_def_get_fields(
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx, /*!< in: trx */
char** field, /*!< out: foreign column */
char** field2, /*!< out: referenced column */
int col_no) /*!< in: column number */
{
*field = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1);
*field2 = mem_heap_alloc(foreign->heap, MAX_TABLE_NAME_LEN+1);

innobase_convert_name(*field, MAX_TABLE_NAME_LEN,
foreign->foreign_col_names[col_no],
strlen(foreign->foreign_col_names[col_no]),
trx->mysql_thd, FALSE);

innobase_convert_name(*field, MAX_TABLE_NAME_LEN,
foreign->referenced_col_names[col_no],
strlen(foreign->referenced_col_names[col_no]),
trx->mysql_thd, FALSE);
}

/********************************************************************//**
Add a single foreign key definition to the data dictionary tables in the
database. We also generate names to constraints that were not named by the
Expand Down Expand Up @@ -1708,6 +1793,22 @@ dict_create_add_foreign_to_dictionary(

if (error != DB_SUCCESS) {

if (error == DB_DUPLICATE_KEY) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* fk_def;

innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE);

fk_def = dict_foreign_def_get(foreign, trx);

ib_push_warning(trx, error, (const char *)"InnoDB: foreign key constraint name %s "
"already exists on data dictionary."
" Foreign key constraint names need to be unique in database."
" Error in foreign key definition: %s.",
buf, fk_def);
}

return(error);
}

Expand All @@ -1716,6 +1817,20 @@ dict_create_add_foreign_to_dictionary(
i, table, foreign, trx);

if (error != DB_SUCCESS) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* field=NULL;
char* field2=NULL;
char* fk_def;

innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
foreign->id, strlen(foreign->id), trx->mysql_thd, FALSE);
fk_def = dict_foreign_def_get(foreign, trx);
dict_foreign_def_get_fields(foreign, trx, &field, &field2, i);

ib_push_warning(trx, error,
(const char *)"InnoDB: Error adding foreign key constraint name %s fields %s or %s to the dictionary."
" Error in foreign key definition: %s.",
buf, i+1, fk_def);

return(error);
}
Expand Down Expand Up @@ -1800,7 +1915,7 @@ dict_create_add_foreigns_to_dictionary(
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {

error = dict_create_add_foreign_to_dictionary(&number, table,
foreign, trx);
foreign, trx);

if (error != DB_SUCCESS) {

Expand Down
Loading

0 comments on commit e05cd97

Please sign in to comment.