Commit fa765a45 authored by Jan Lindström's avatar Jan Lindström

MDEV-6697: Improve foreign keys warnings/errors

There is several different ways to incorrectly define
foreign key constraint. In many cases earlier MariaDB
versions the error messages produced by these cases
are not very clear and helpful. This patch improves
the warning messages produced by foreign key parsing.
parent e05cd97b
......@@ -10,11 +10,96 @@ id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT mytest FOREIGN KEY (c) REFERENCES t1(id),
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`).
Warning 121 Create or Alter table `test`.`t2` with foreign key constraint failed. Foreign key constraint `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;
create table t1(a int) engine=innodb;
create table t2(a int, constraint a foreign key a (a) references t1(a)) engine=innodb;
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key a (a) references t1(a)) engine=innodb.
Error 1005 Can't create table 'test.t2' (errno: 150)
drop table t1;
create table t1(a int not null primary key, b int) engine=innodb;
create table t2(a int, b int, constraint a foreign key a (a) references t1(a),
constraint a foreign key a (a) references t1(b)) engine=innodb;
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key a (a) references t1(b)) engine=innodb.
Error 1005 Can't create table 'test.t2' (errno: 150)
create table t2(a int, b int, constraint a foreign key a (a) references t1(a)) engine=innodb;
alter table t2 add constraint b foreign key (b) references t2(b);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table '`test`.`t2`' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns. Error close to foreign key (b) references t2(b).
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t2, t1;
create table t1 (f1 integer primary key) engine=innodb;
alter table t1 add constraint c1 foreign key (f1) references t11(f1);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Referenced table `test`.`t11` not found in the data dictionary close to foreign key (f1) references t11(f1).
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t1;
create temporary table t1(a int not null primary key, b int, key(b)) engine=innodb;
create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb;
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table `mysqld.1`.`t2` with foreign key constraint failed. Referenced table `mysqld.1`.`t1` not found in the data dictionary close to foreign key(a) references t1(a)) engine=innodb.
Error 1005 Can't create table 'test.t2' (errno: 150)
alter table t1 add foreign key(b) references t1(a);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `mysqld.1`.`t1` with foreign key constraint failed. Referenced table `mysqld.1`.`t1` not found in the data dictionary close to foreign key(b) references t1(a).
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t1;
create table t1(a int not null primary key, b int, key(b)) engine=innodb;
alter table t1 add foreign key(a,b) references t1(a);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Foreign key constraint parse error in foreign key(a,b) references t1(a) close to ). Too few referenced columns, you have 1 when you should have 2.
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t1;
create table t1(a int not null primary key, b int, key(b)) engine=innodb;
alter table t1 add foreign key(a) references t1(a,b);
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Foreign key constraint parse error in foreign key(a) references t1(a,b) close to ). Too few referenced columns, you have 2 when you should have 1.
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t1;
create table t1 (f1 integer not null primary key) engine=innodb;
alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update set null;
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. You have defined a SET NULL condition but column f1 is defined as NOT NULL in foreign key (f1) references t1(f1) on update set null close to on update set null.
Error 1005 Can't create table '#sql-temporary' (errno: 150)
create table t2(a int not null, foreign key(a) references t1(f1) on delete set null) engine=innodb;
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table `test`.`t2` with foreign key constraint failed. You have defined a SET NULL condition but column a is defined as NOT NULL in foreign key(a) references t1(f1) on delete set null) engine=innodb close to on delete set null) engine=innodb.
Error 1005 Can't create table 'test.t2' (errno: 150)
drop table t1;
create table t1 (id int not null primary key, f1 int, f2 int, key(f1)) engine=innodb;
create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=innodb;
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table `test`.`t2` with foreign key constraint failed. Field type or character set for column a does not mach referenced column f1 close to foreign key(a) references t1(f1)) engine=innodb
Error 1005 Can't create table 'test.t2' (errno: 150)
drop table t1;
......@@ -50,6 +50,8 @@ CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE
ERROR HY000: Can't create table 'test.t2' (errno: 150)
show warnings;
Level Code Message
Warning 150 Create table `test`.`t2` with foreign key constraint failed. Referenced table `test`.`t3` not found in the data dictionary close to FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE
) ENGINE=InnoDB.
Error 1005 Can't create table 'test.t2' (errno: 150)
CREATE TABLE t2 (
id int(11) NOT NULL AUTO_INCREMENT,
......@@ -62,6 +64,7 @@ ALTER TABLE t2 ADD CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE
ERROR HY000: Can't create table '#sql-temporary' (errno: 150)
show warnings;
Level Code Message
Warning 150 Alter table `test`.`t2` with foreign key constraint failed. Referenced table `test`.`t3` not found in the data dictionary close to FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE.
Error 1005 Can't create table '#sql-temporary' (errno: 150)
drop table t2;
drop table t1;
......@@ -21,9 +21,110 @@ CREATE TABLE t2 (
a int(11) NOT NULL,
b int(11) NOT NULL,
c int not null,
CONSTRAINT mytest FOREIGN KEY (c) REFERENCES t1(id),
CONSTRAINT test FOREIGN KEY (b) REFERENCES t2 (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
show warnings;
drop table t1;
#
# MDEV-6697: Improve foreign keys warnings/errors
#
#
# No index for referenced columns
#
create table t1(a int) engine=innodb;
--error 1005
create table t2(a int, constraint a foreign key a (a) references t1(a)) engine=innodb;
show warnings;
drop table t1;
create table t1(a int not null primary key, b int) engine=innodb;
--error 1005
create table t2(a int, b int, constraint a foreign key a (a) references t1(a),
constraint a foreign key a (a) references t1(b)) engine=innodb;
show warnings;
create table t2(a int, b int, constraint a foreign key a (a) references t1(a)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t2 add constraint b foreign key (b) references t2(b);
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t2, t1;
#
# Referenced table does not exists
#
create table t1 (f1 integer primary key) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t1 add constraint c1 foreign key (f1) references t11(f1);
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
#
# Foreign key on temporal tables
#
create temporary table t1(a int not null primary key, b int, key(b)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t1 add foreign key(b) references t1(a);
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
#
# Column numbers do not match
#
create table t1(a int not null primary key, b int, key(b)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t1 add foreign key(a,b) references t1(a);
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
create table t1(a int not null primary key, b int, key(b)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t1 add foreign key(a) references t1(a,b);
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
#
# ON UPDATE/DELETE SET NULL on NOT NULL column
#
create table t1 (f1 integer not null primary key) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update set null;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
create table t2(a int not null, foreign key(a) references t1(f1) on delete set null) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
#
# Incorrect types
#
create table t1 (id int not null primary key, f1 int, f2 int, key(f1)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
--error 1005
create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=innodb;
--replace_regex /'[^']*test.#sql-[0-9a-f_]*'/'#sql-temporary'/
show warnings;
drop table t1;
......@@ -1422,9 +1422,10 @@ dict_create_add_foreign_field_to_dictionary(
/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
static
UNIV_INTERN
char*
dict_foreign_def_get(
/*=================*/
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx) /*!< in: trx */
{
......@@ -1484,6 +1485,7 @@ 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 */
......@@ -1588,18 +1590,25 @@ dict_create_add_foreign_to_dictionary(
if (error == DB_DUPLICATE_KEY) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char tablename[MAX_TABLE_NAME_LEN + 1] = "";
char* fk_def;
innobase_convert_name(tablename, MAX_TABLE_NAME_LEN,
table->name, strlen(table->name),
trx->mysql_thd, TRUE);
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."
ib_push_warning(trx, error,
"Create or Alter table %s with foreign key constraint"
" failed. Foreign key constraint %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);
tablename, buf, fk_def);
}
return(error);
......@@ -1611,19 +1620,25 @@ dict_create_add_foreign_to_dictionary(
if (error != DB_SUCCESS) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char tablename[MAX_TABLE_NAME_LEN + 1] = "";
char* field=NULL;
char* field2=NULL;
char* fk_def;
innobase_convert_name(tablename, MAX_TABLE_NAME_LEN,
table->name, strlen(table->name),
trx->mysql_thd, TRUE);
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."
"Create or Alter table %s with foreign key constraint"
" failed. 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);
tablename, buf, i+1, fk_def);
return(error);
}
......
This diff is collapsed.
......@@ -106,6 +106,17 @@ UNIV_INTERN
ulint
dict_create_or_check_foreign_constraint_tables(void);
/*================================================*/
/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
UNIV_INTERN
char*
dict_foreign_def_get(
/*=================*/
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx); /*!< in: trx */
/********************************************************************//**
Adds foreign key definitions to data dictionary tables in the database. We
look at table->foreign_list, and also generate names to constraints that were
......
......@@ -1629,9 +1629,10 @@ dict_create_add_foreign_field_to_dictionary(
/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
static
UNIV_INTERN
char*
dict_foreign_def_get(
/*=================*/
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx) /*!< in: trx */
{
......@@ -1691,6 +1692,7 @@ 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 */
......@@ -1795,18 +1797,25 @@ dict_create_add_foreign_to_dictionary(
if (error == DB_DUPLICATE_KEY) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char tablename[MAX_TABLE_NAME_LEN + 1] = "";
char* fk_def;
innobase_convert_name(tablename, MAX_TABLE_NAME_LEN,
table->name, strlen(table->name),
trx->mysql_thd, TRUE);
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."
ib_push_warning(trx, error,
"Create or Alter table %s with foreign key constraint"
" failed. Foreign key constraint %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);
tablename, buf, fk_def);
}
return(error);
......@@ -1818,19 +1827,25 @@ dict_create_add_foreign_to_dictionary(
if (error != DB_SUCCESS) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char tablename[MAX_TABLE_NAME_LEN + 1] = "";
char* field=NULL;
char* field2=NULL;
char* fk_def;
innobase_convert_name(tablename, MAX_TABLE_NAME_LEN,
table->name, strlen(table->name),
trx->mysql_thd, TRUE);
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."
"Create or Alter table %s with foreign key constraint"
" failed. 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);
tablename, buf, i+1, fk_def);
return(error);
}
......
This diff is collapsed.
......@@ -121,6 +121,17 @@ UNIV_INTERN
ulint
dict_create_or_check_foreign_constraint_tables(void);
/*================================================*/
/********************************************************************//**
Construct foreign key constraint defintion from data dictionary information.
*/
UNIV_INTERN
char*
dict_foreign_def_get(
/*=================*/
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx); /*!< in: trx */
/********************************************************************//**
Adds foreign key definitions to data dictionary tables in the database. We
look at table->foreign_list, and also generate names to constraints that were
......
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