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.
This commit is contained in:
parent
e05cd97b8a
commit
fa765a4525
@ -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);
|
||||
}
|
||||
|
@ -2614,6 +2614,11 @@ dict_foreign_find(
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200
|
||||
#define DB_FOREIGN_KEY_COL_NOT_NULL 201
|
||||
#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202
|
||||
#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203
|
||||
|
||||
/*********************************************************************//**
|
||||
Tries to find an index whose first fields are the columns in the array,
|
||||
in the same order and is not marked for deletion and is not the same
|
||||
@ -2631,12 +2636,21 @@ dict_foreign_find_index(
|
||||
ibool check_charsets,
|
||||
/*!< in: whether to check charsets.
|
||||
only has an effect if types_idx != NULL */
|
||||
ulint check_null)
|
||||
ulint check_null,
|
||||
/*!< in: nonzero if none of the columns must
|
||||
be declared NOT NULL */
|
||||
ulint* error, /*!< out: error code */
|
||||
ulint* err_col_no,
|
||||
/*!< out: column number where error happened */
|
||||
dict_index_t** err_index)
|
||||
/*!< out: index where error happened */
|
||||
{
|
||||
dict_index_t* index;
|
||||
|
||||
if (error) {
|
||||
*error = DB_FOREIGN_KEY_INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
index = dict_table_get_first_index(table);
|
||||
|
||||
while (index != NULL) {
|
||||
@ -2662,6 +2676,12 @@ dict_foreign_find_index(
|
||||
/* We do not accept column prefix
|
||||
indexes here */
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_IS_PREFIX_INDEX;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2673,6 +2693,11 @@ dict_foreign_find_index(
|
||||
if (check_null
|
||||
&& (field->col->prtype & DATA_NOT_NULL)) {
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_COL_NOT_NULL;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
@ -2682,6 +2707,12 @@ dict_foreign_find_index(
|
||||
i),
|
||||
check_charsets)) {
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_COLS_NOT_EQUAL;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2689,6 +2720,10 @@ dict_foreign_find_index(
|
||||
if (i == n_cols) {
|
||||
/* We found a matching index */
|
||||
|
||||
if (error) {
|
||||
*error = DB_SUCCESS;
|
||||
}
|
||||
|
||||
return(index);
|
||||
}
|
||||
}
|
||||
@ -2720,7 +2755,7 @@ dict_foreign_find_equiv_index(
|
||||
foreign->foreign_table,
|
||||
foreign->foreign_col_names, foreign->n_fields,
|
||||
foreign->foreign_index, TRUE, /* check types */
|
||||
FALSE/* allow columns to be NULL */));
|
||||
FALSE/* allow columns to be NULL */, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
@ -2883,11 +2918,15 @@ dict_foreign_add_to_cache(
|
||||
}
|
||||
|
||||
if (for_in_cache->referenced_table == NULL && ref_table) {
|
||||
ulint index_error;
|
||||
ulint err_col;
|
||||
dict_index_t *err_index=NULL;
|
||||
|
||||
index = dict_foreign_find_index(
|
||||
ref_table,
|
||||
for_in_cache->referenced_col_names,
|
||||
for_in_cache->n_fields, for_in_cache->foreign_index,
|
||||
check_charsets, FALSE);
|
||||
check_charsets, FALSE, &index_error, &err_col, &err_index);
|
||||
|
||||
if (index == NULL
|
||||
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
|
||||
@ -2919,6 +2958,9 @@ dict_foreign_add_to_cache(
|
||||
}
|
||||
|
||||
if (for_in_cache->foreign_table == NULL && for_table) {
|
||||
ulint index_error;
|
||||
ulint err_col;
|
||||
dict_index_t* err_index=NULL;
|
||||
|
||||
index = dict_foreign_find_index(
|
||||
for_table,
|
||||
@ -2927,7 +2969,8 @@ dict_foreign_add_to_cache(
|
||||
for_in_cache->referenced_index, check_charsets,
|
||||
for_in_cache->type
|
||||
& (DICT_FOREIGN_ON_DELETE_SET_NULL
|
||||
| DICT_FOREIGN_ON_UPDATE_SET_NULL));
|
||||
| DICT_FOREIGN_ON_UPDATE_SET_NULL),
|
||||
&index_error, &err_col, &err_index);
|
||||
|
||||
if (index == NULL
|
||||
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
|
||||
@ -3538,6 +3581,8 @@ static
|
||||
void
|
||||
dict_foreign_report_syntax_err(
|
||||
/*===========================*/
|
||||
const char* fmt, /*!< in: syntax err msg */
|
||||
const char* oper, /*!< in: operation */
|
||||
const char* name, /*!< in: table name */
|
||||
const char* start_of_latest_foreign,
|
||||
/*!< in: start of the foreign key clause
|
||||
@ -3548,11 +3593,101 @@ dict_foreign_report_syntax_err(
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nSyntax error close to:\n%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Push warning message to SQL-layer based on foreign key constraint
|
||||
index match error. */
|
||||
static
|
||||
void
|
||||
dict_foreign_push_index_error(
|
||||
/*==========================*/
|
||||
trx_t* trx, /*!< in: trx */
|
||||
const char* operation, /*!< in: operation create or alter
|
||||
*/
|
||||
const char* create_name, /*!< in: table name in create or
|
||||
alter table */
|
||||
const char* latest_foreign, /*!< in: start of latest foreign key
|
||||
constraint name */
|
||||
const char** columns, /*!< in: foreign key columns */
|
||||
ulint index_error, /*!< in: error code */
|
||||
ulint err_col, /*!< in: column where error happened
|
||||
*/
|
||||
dict_index_t* err_index, /*!< in: index where error happened
|
||||
*/
|
||||
dict_table_t* table, /*!< in: table */
|
||||
FILE* ef) /*!< in: output stream */
|
||||
{
|
||||
switch (index_error) {
|
||||
case DB_FOREIGN_KEY_INDEX_NOT_FOUND: {
|
||||
fprintf(ef,
|
||||
"%s table '%s' 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 %s.\n",
|
||||
operation, create_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table '%s' 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 %s.",
|
||||
operation, create_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_IS_PREFIX_INDEX: {
|
||||
fprintf(ef,
|
||||
"%s table '%s' with foreign key constraint"
|
||||
" failed. There is only prefix index in the referenced"
|
||||
" table where the referenced columns appear"
|
||||
" as the first columns. Error close to %s.\n",
|
||||
operation, create_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table '%s' with foreign key constraint"
|
||||
" failed. There is only prefix index in the referenced"
|
||||
" table where the referenced columns appear"
|
||||
" as the first columns. Error close to %s.",
|
||||
operation, create_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_COL_NOT_NULL: {
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but "
|
||||
"field %s on index is defined as NOT NULL close to %s\n",
|
||||
operation, create_name, columns[err_col], latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but "
|
||||
"field %s on index is defined as NOT NULL close to %s",
|
||||
operation, create_name, columns[err_col], latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_COLS_NOT_EQUAL: {
|
||||
dict_field_t* field;
|
||||
const char* col_name;
|
||||
field = dict_index_get_nth_field(err_index, err_col);
|
||||
|
||||
col_name = dict_table_get_col_name(
|
||||
table, dict_col_get_no(field->col));
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Field type or character set for column %s "
|
||||
"does not mach referenced column %s close to %s\n",
|
||||
operation, create_name, columns[err_col], col_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Field type or character set for column %s "
|
||||
"does not mach referenced column %s close to %s",
|
||||
operation, create_name, columns[err_col], col_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ut_error;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Scans a table create SQL string and adds to the data dictionary the foreign
|
||||
key constraints declared in the string. This function should be called after
|
||||
@ -3581,15 +3716,20 @@ dict_create_foreign_constraints_low(
|
||||
DB_CANNOT_ADD_CONSTRAINT if any foreign
|
||||
keys are found. */
|
||||
{
|
||||
dict_table_t* table;
|
||||
dict_table_t* referenced_table;
|
||||
dict_table_t* table_to_alter;
|
||||
dict_table_t* table = NULL;
|
||||
dict_table_t* referenced_table = NULL;
|
||||
dict_table_t* table_to_alter = NULL;
|
||||
dict_table_t* table_to_create = NULL;
|
||||
ulint highest_id_so_far = 0;
|
||||
dict_index_t* index;
|
||||
dict_foreign_t* foreign;
|
||||
dict_index_t* index = NULL;
|
||||
dict_foreign_t* foreign = NULL;
|
||||
const char* ptr = sql_string;
|
||||
const char* start_of_latest_foreign = sql_string;
|
||||
const char* start_of_latest_set = NULL;
|
||||
FILE* ef = dict_foreign_err_file;
|
||||
ulint index_error = DB_SUCCESS;
|
||||
dict_index_t* err_index = NULL;
|
||||
ulint err_col;
|
||||
const char* constraint_name;
|
||||
ibool success;
|
||||
ulint error;
|
||||
@ -3602,29 +3742,64 @@ dict_create_foreign_constraints_low(
|
||||
ulint n_on_updates;
|
||||
const dict_col_t*columns[500];
|
||||
const char* column_names[500];
|
||||
const char* ref_column_names[500];
|
||||
const char* referenced_table_name;
|
||||
const char* create_table_name;
|
||||
const char* orig;
|
||||
const char create_name[MAX_TABLE_NAME_LEN + 1];
|
||||
const char operation[8];
|
||||
|
||||
ut_ad(mutex_own(&(dict_sys->mutex)));
|
||||
|
||||
table = dict_table_get_low(name, DICT_ERR_IGNORE_NONE);
|
||||
/* First check if we are actually doing an ALTER TABLE, and in that
|
||||
case look for the table being altered */
|
||||
ptr = dict_accept(cs, ptr, "ALTER", &success);
|
||||
|
||||
strcpy((char *)operation, success ? "Alter " : "Create ");
|
||||
|
||||
if (!success) {
|
||||
orig = ptr;
|
||||
ptr = dict_scan_to(ptr, "CREATE");
|
||||
ptr = dict_scan_to(ptr, "TABLE");
|
||||
ptr = dict_accept(cs, ptr, "TABLE", &success);
|
||||
|
||||
if (success) {
|
||||
ptr = dict_scan_table_name(cs, ptr, &table_to_create, name,
|
||||
&success, heap, &create_table_name);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
create_table_name, strlen(create_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
ptr = orig;
|
||||
} else {
|
||||
ptr = orig;
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
name, strlen(name), trx->mysql_thd, TRUE);
|
||||
}
|
||||
|
||||
goto loop;
|
||||
}
|
||||
|
||||
if (table == NULL) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef,
|
||||
"Cannot find the table in the internal"
|
||||
" data dictionary of InnoDB.\n"
|
||||
"Create table statement:\n%s\n", sql_string);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, create_name, start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_ERROR,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.",
|
||||
operation, create_name, create_name, start_of_latest_foreign);
|
||||
return(DB_ERROR);
|
||||
}
|
||||
|
||||
/* First check if we are actually doing an ALTER TABLE, and in that
|
||||
case look for the table being altered */
|
||||
|
||||
ptr = dict_accept(cs, ptr, "ALTER", &success);
|
||||
|
||||
/* If not alter table jump to loop */
|
||||
if (!success) {
|
||||
|
||||
goto loop;
|
||||
@ -3639,13 +3814,35 @@ dict_create_foreign_constraints_low(
|
||||
|
||||
/* We are doing an ALTER TABLE: scan the table name we are altering */
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name,
|
||||
&success, heap, &referenced_table_name);
|
||||
|
||||
if (table_to_alter) {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
table_to_alter->name, strlen(table_to_alter->name),
|
||||
trx->mysql_thd, TRUE);
|
||||
} else {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
referenced_table_name, strlen(referenced_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
fprintf(stderr,
|
||||
"InnoDB: Error: could not find"
|
||||
" the table being ALTERED in:\n%s\n",
|
||||
sql_string);
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, create_name, orig);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_ERROR,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.",
|
||||
operation, create_name, create_name, orig);
|
||||
|
||||
return(DB_ERROR);
|
||||
}
|
||||
@ -3711,7 +3908,19 @@ loop:
|
||||
if so, immediately reject the command if the table is a
|
||||
temporary one. For now, this kludge will work. */
|
||||
if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s table %s with foreign key constraint"
|
||||
" failed. Temporary tables can't have foreign key constraints."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Temporary tables can't have foreign key constraints."
|
||||
" Error close to %s.",
|
||||
operation, create_name, start_of_latest_foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3747,11 +3956,21 @@ loop:
|
||||
if (!success) {
|
||||
/* MySQL allows also an index id before the '('; we
|
||||
skip it */
|
||||
orig = ptr;
|
||||
ptr = dict_skip_word(cs, ptr, &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
@ -3771,15 +3990,26 @@ loop:
|
||||
/* Scan the columns in the first list */
|
||||
col_loop1:
|
||||
ut_a(i < (sizeof column_names) / sizeof *column_names);
|
||||
orig = ptr;
|
||||
ptr = dict_scan_col(cs, ptr, &success, table, columns + i,
|
||||
heap, column_names + i);
|
||||
if (!success) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3791,11 +4021,22 @@ col_loop1:
|
||||
goto col_loop1;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, ")", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3803,27 +4044,41 @@ col_loop1:
|
||||
as the first fields and in the right order */
|
||||
|
||||
index = dict_foreign_find_index(table, column_names, i,
|
||||
NULL, TRUE, FALSE);
|
||||
NULL, TRUE, FALSE, &index_error, &err_col, &err_index);
|
||||
|
||||
if (!index) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fputs("There is no index in table ", ef);
|
||||
ut_print_name(ef, NULL, TRUE, name);
|
||||
ut_print_name(ef, NULL, TRUE, create_name);
|
||||
fprintf(ef, " where the columns appear\n"
|
||||
"as the first columns. Constraint:\n%s\n"
|
||||
"See " REFMAN "innodb-foreign-key-constraints.html\n"
|
||||
"for correct foreign key definition.\n",
|
||||
start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_CHILD_NO_INDEX);
|
||||
dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
|
||||
column_names, index_error, err_col, err_index, table, ef);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "REFERENCES", &success);
|
||||
|
||||
if (!success || !my_isspace(cs, *ptr)) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3872,24 +4127,48 @@ col_loop1:
|
||||
checking of foreign key constraints! */
|
||||
|
||||
if (!success || (!referenced_table && trx->check_foreigns)) {
|
||||
char buf[MAX_TABLE_NAME_LEN + 1] = "";
|
||||
|
||||
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
|
||||
referenced_table_name, strlen(referenced_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
|
||||
"close to %s.",
|
||||
operation, create_name, buf, start_of_latest_foreign);
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve table name close to:\n"
|
||||
"%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
|
||||
"close to %s.\n",
|
||||
operation, create_name, buf, start_of_latest_foreign);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "(", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3897,20 +4176,29 @@ col_loop1:
|
||||
i = 0;
|
||||
|
||||
col_loop2:
|
||||
orig = ptr;
|
||||
ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i,
|
||||
heap, column_names + i);
|
||||
heap, ref_column_names + i);
|
||||
i++;
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve column name close to:\n"
|
||||
"%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3920,13 +4208,23 @@ col_loop2:
|
||||
goto col_loop2;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, ")", &success);
|
||||
|
||||
if (!success || foreign->n_fields != i) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s. Too few referenced columns.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s. Too few referenced columns, you have %d when you should have %d.",
|
||||
operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3936,6 +4234,7 @@ col_loop2:
|
||||
scan_on_conditions:
|
||||
/* Loop here as long as we can find ON ... conditions */
|
||||
|
||||
start_of_latest_set = ptr;
|
||||
ptr = dict_accept(cs, ptr, "ON", &success);
|
||||
|
||||
if (!success) {
|
||||
@ -3946,13 +4245,24 @@ scan_on_conditions:
|
||||
ptr = dict_accept(cs, ptr, "DELETE", &success);
|
||||
|
||||
if (!success) {
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "UPDATE", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3984,12 +4294,22 @@ scan_on_conditions:
|
||||
ptr = dict_accept(cs, ptr, "NO", &success);
|
||||
|
||||
if (success) {
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "ACTION", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
@ -4003,42 +4323,73 @@ scan_on_conditions:
|
||||
goto scan_on_conditions;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "SET", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "NULL", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
for (j = 0; j < foreign->n_fields; j++) {
|
||||
if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype)
|
||||
& DATA_NOT_NULL) {
|
||||
const dict_col_t* col
|
||||
= dict_index_get_nth_col(foreign->foreign_index, j);
|
||||
const char* col_name = dict_table_get_col_name(foreign->foreign_index->table,
|
||||
dict_col_get_no(col));
|
||||
|
||||
/* It is not sensible to define SET NULL
|
||||
if the column is not allowed to be NULL! */
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"You have defined a SET NULL condition"
|
||||
" though some of the\n"
|
||||
"columns are defined as NOT NULL.\n",
|
||||
start_of_latest_foreign);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but column %s is defined as NOT NULL"
|
||||
" in %s close to %s.\n",
|
||||
operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but column %s is defined as NOT NULL"
|
||||
" in %s close to %s.",
|
||||
operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
}
|
||||
@ -4055,16 +4406,22 @@ try_find_index:
|
||||
if (n_on_deletes > 1 || n_on_updates > 1) {
|
||||
/* It is an error to define more than 1 action */
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"You have twice an ON DELETE clause"
|
||||
" or twice an ON UPDATE clause.\n",
|
||||
start_of_latest_foreign);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have more than one on delete or on update clause"
|
||||
" in %s close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have more than one on delete or on update clause"
|
||||
" in %s close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
dict_foreign_free(foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4074,13 +4431,13 @@ try_find_index:
|
||||
|
||||
if (referenced_table) {
|
||||
index = dict_foreign_find_index(referenced_table,
|
||||
column_names, i,
|
||||
foreign->foreign_index,
|
||||
TRUE, FALSE);
|
||||
ref_column_names, i,
|
||||
foreign->foreign_index,
|
||||
TRUE, FALSE, &index_error, &err_col, &err_index);
|
||||
if (!index) {
|
||||
dict_foreign_free(foreign);
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"Cannot find an index in the"
|
||||
" referenced table where the\n"
|
||||
@ -4098,9 +4455,13 @@ try_find_index:
|
||||
"innodb-foreign-key-constraints.html\n"
|
||||
"for correct foreign key definition.\n",
|
||||
start_of_latest_foreign);
|
||||
|
||||
dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
|
||||
column_names, index_error, err_col, err_index, referenced_table, ef);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_PARENT_NO_INDEX);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
} else {
|
||||
ut_a(trx->check_foreigns == FALSE);
|
||||
@ -4118,7 +4479,7 @@ try_find_index:
|
||||
i * sizeof(void*));
|
||||
for (i = 0; i < foreign->n_fields; i++) {
|
||||
foreign->referenced_col_names[i]
|
||||
= mem_heap_strdup(foreign->heap, column_names[i]);
|
||||
= mem_heap_strdup(foreign->heap, ref_column_names[i]);
|
||||
}
|
||||
|
||||
/* We found an ok constraint definition: add to the lists */
|
||||
@ -5251,7 +5612,8 @@ dict_table_replace_index_in_foreign_list(
|
||||
foreign->referenced_table,
|
||||
foreign->referenced_col_names,
|
||||
foreign->n_fields, index,
|
||||
/*check_charsets=*/TRUE, /*check_null=*/FALSE);
|
||||
/*check_charsets=*/TRUE, /*check_null=*/FALSE,
|
||||
NULL, NULL, NULL);
|
||||
ut_ad(new_index || !trx->check_foreigns);
|
||||
ut_ad(!new_index || new_index->table == index->table);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -2747,6 +2747,11 @@ dict_foreign_find(
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
#define DB_FOREIGN_KEY_IS_PREFIX_INDEX 200
|
||||
#define DB_FOREIGN_KEY_COL_NOT_NULL 201
|
||||
#define DB_FOREIGN_KEY_COLS_NOT_EQUAL 202
|
||||
#define DB_FOREIGN_KEY_INDEX_NOT_FOUND 203
|
||||
|
||||
/*********************************************************************//**
|
||||
Tries to find an index whose first fields are the columns in the array,
|
||||
in the same order and is not marked for deletion and is not the same
|
||||
@ -2764,12 +2769,21 @@ dict_foreign_find_index(
|
||||
ibool check_charsets,
|
||||
/*!< in: whether to check charsets.
|
||||
only has an effect if types_idx != NULL */
|
||||
ulint check_null)
|
||||
ulint check_null,
|
||||
/*!< in: nonzero if none of the columns must
|
||||
be declared NOT NULL */
|
||||
ulint* error, /*!< out: error code */
|
||||
ulint* err_col_no,
|
||||
/*!< out: column number where error happened */
|
||||
dict_index_t** err_index)
|
||||
/*!< out: index where error happened */
|
||||
{
|
||||
dict_index_t* index;
|
||||
|
||||
if (error) {
|
||||
*error = DB_FOREIGN_KEY_INDEX_NOT_FOUND;
|
||||
}
|
||||
|
||||
index = dict_table_get_first_index(table);
|
||||
|
||||
while (index != NULL) {
|
||||
@ -2795,6 +2809,12 @@ dict_foreign_find_index(
|
||||
/* We do not accept column prefix
|
||||
indexes here */
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_IS_PREFIX_INDEX;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2806,6 +2826,11 @@ dict_foreign_find_index(
|
||||
if (check_null
|
||||
&& (field->col->prtype & DATA_NOT_NULL)) {
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_COL_NOT_NULL;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
@ -2815,6 +2840,12 @@ dict_foreign_find_index(
|
||||
i),
|
||||
check_charsets)) {
|
||||
|
||||
if (error && err_col_no && err_index) {
|
||||
*error = DB_FOREIGN_KEY_COLS_NOT_EQUAL;
|
||||
*err_col_no = i;
|
||||
*err_index = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2822,6 +2853,10 @@ dict_foreign_find_index(
|
||||
if (i == n_cols) {
|
||||
/* We found a matching index */
|
||||
|
||||
if (error) {
|
||||
*error = DB_SUCCESS;
|
||||
}
|
||||
|
||||
return(index);
|
||||
}
|
||||
}
|
||||
@ -2853,7 +2888,7 @@ dict_foreign_find_equiv_index(
|
||||
foreign->foreign_table,
|
||||
foreign->foreign_col_names, foreign->n_fields,
|
||||
foreign->foreign_index, TRUE, /* check types */
|
||||
FALSE/* allow columns to be NULL */));
|
||||
FALSE/* allow columns to be NULL */, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
@ -3016,11 +3051,15 @@ dict_foreign_add_to_cache(
|
||||
}
|
||||
|
||||
if (for_in_cache->referenced_table == NULL && ref_table) {
|
||||
ulint index_error;
|
||||
ulint err_col;
|
||||
dict_index_t *err_index=NULL;
|
||||
|
||||
index = dict_foreign_find_index(
|
||||
ref_table,
|
||||
for_in_cache->referenced_col_names,
|
||||
for_in_cache->n_fields, for_in_cache->foreign_index,
|
||||
check_charsets, FALSE);
|
||||
check_charsets, FALSE, &index_error, &err_col, &err_index);
|
||||
|
||||
if (index == NULL
|
||||
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
|
||||
@ -3052,6 +3091,9 @@ dict_foreign_add_to_cache(
|
||||
}
|
||||
|
||||
if (for_in_cache->foreign_table == NULL && for_table) {
|
||||
ulint index_error;
|
||||
ulint err_col;
|
||||
dict_index_t* err_index=NULL;
|
||||
|
||||
index = dict_foreign_find_index(
|
||||
for_table,
|
||||
@ -3060,7 +3102,8 @@ dict_foreign_add_to_cache(
|
||||
for_in_cache->referenced_index, check_charsets,
|
||||
for_in_cache->type
|
||||
& (DICT_FOREIGN_ON_DELETE_SET_NULL
|
||||
| DICT_FOREIGN_ON_UPDATE_SET_NULL));
|
||||
| DICT_FOREIGN_ON_UPDATE_SET_NULL),
|
||||
&index_error, &err_col, &err_index);
|
||||
|
||||
if (index == NULL
|
||||
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
|
||||
@ -3671,6 +3714,8 @@ static
|
||||
void
|
||||
dict_foreign_report_syntax_err(
|
||||
/*===========================*/
|
||||
const char* fmt, /*!< in: syntax err msg */
|
||||
const char* oper, /*!< in: operation */
|
||||
const char* name, /*!< in: table name */
|
||||
const char* start_of_latest_foreign,
|
||||
/*!< in: start of the foreign key clause
|
||||
@ -3681,11 +3726,101 @@ dict_foreign_report_syntax_err(
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nSyntax error close to:\n%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Push warning message to SQL-layer based on foreign key constraint
|
||||
index match error. */
|
||||
static
|
||||
void
|
||||
dict_foreign_push_index_error(
|
||||
/*==========================*/
|
||||
trx_t* trx, /*!< in: trx */
|
||||
const char* operation, /*!< in: operation create or alter
|
||||
*/
|
||||
const char* create_name, /*!< in: table name in create or
|
||||
alter table */
|
||||
const char* latest_foreign, /*!< in: start of latest foreign key
|
||||
constraint name */
|
||||
const char** columns, /*!< in: foreign key columns */
|
||||
ulint index_error, /*!< in: error code */
|
||||
ulint err_col, /*!< in: column where error happened
|
||||
*/
|
||||
dict_index_t* err_index, /*!< in: index where error happened
|
||||
*/
|
||||
dict_table_t* table, /*!< in: table */
|
||||
FILE* ef) /*!< in: output stream */
|
||||
{
|
||||
switch (index_error) {
|
||||
case DB_FOREIGN_KEY_INDEX_NOT_FOUND: {
|
||||
fprintf(ef,
|
||||
"%s table '%s' 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 %s.\n",
|
||||
operation, create_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table '%s' 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 %s.",
|
||||
operation, create_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_IS_PREFIX_INDEX: {
|
||||
fprintf(ef,
|
||||
"%s table '%s' with foreign key constraint"
|
||||
" failed. There is only prefix index in the referenced"
|
||||
" table where the referenced columns appear"
|
||||
" as the first columns. Error close to %s.\n",
|
||||
operation, create_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table '%s' with foreign key constraint"
|
||||
" failed. There is only prefix index in the referenced"
|
||||
" table where the referenced columns appear"
|
||||
" as the first columns. Error close to %s.",
|
||||
operation, create_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_COL_NOT_NULL: {
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but "
|
||||
"field %s on index is defined as NOT NULL close to %s\n",
|
||||
operation, create_name, columns[err_col], latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but "
|
||||
"field %s on index is defined as NOT NULL close to %s",
|
||||
operation, create_name, columns[err_col], latest_foreign);
|
||||
break;
|
||||
}
|
||||
case DB_FOREIGN_KEY_COLS_NOT_EQUAL: {
|
||||
dict_field_t* field;
|
||||
const char* col_name;
|
||||
field = dict_index_get_nth_field(err_index, err_col);
|
||||
|
||||
col_name = dict_table_get_col_name(
|
||||
table, dict_col_get_no(field->col));
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Field type or character set for column %s "
|
||||
"does not mach referenced column %s close to %s\n",
|
||||
operation, create_name, columns[err_col], col_name, latest_foreign);
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Field type or character set for column %s "
|
||||
"does not mach referenced column %s close to %s",
|
||||
operation, create_name, columns[err_col], col_name, latest_foreign);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ut_error;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Scans a table create SQL string and adds to the data dictionary the foreign
|
||||
key constraints declared in the string. This function should be called after
|
||||
@ -3714,15 +3849,20 @@ dict_create_foreign_constraints_low(
|
||||
DB_CANNOT_ADD_CONSTRAINT if any foreign
|
||||
keys are found. */
|
||||
{
|
||||
dict_table_t* table;
|
||||
dict_table_t* referenced_table;
|
||||
dict_table_t* table_to_alter;
|
||||
dict_table_t* table = NULL;
|
||||
dict_table_t* referenced_table = NULL;
|
||||
dict_table_t* table_to_alter = NULL;
|
||||
dict_table_t* table_to_create = NULL;
|
||||
ulint highest_id_so_far = 0;
|
||||
dict_index_t* index;
|
||||
dict_foreign_t* foreign;
|
||||
dict_index_t* index = NULL;
|
||||
dict_foreign_t* foreign = NULL;
|
||||
const char* ptr = sql_string;
|
||||
const char* start_of_latest_foreign = sql_string;
|
||||
const char* start_of_latest_set = NULL;
|
||||
FILE* ef = dict_foreign_err_file;
|
||||
ulint index_error = DB_SUCCESS;
|
||||
dict_index_t* err_index = NULL;
|
||||
ulint err_col;
|
||||
const char* constraint_name;
|
||||
ibool success;
|
||||
ulint error;
|
||||
@ -3735,29 +3875,64 @@ dict_create_foreign_constraints_low(
|
||||
ulint n_on_updates;
|
||||
const dict_col_t*columns[500];
|
||||
const char* column_names[500];
|
||||
const char* ref_column_names[500];
|
||||
const char* referenced_table_name;
|
||||
const char* create_table_name;
|
||||
const char* orig;
|
||||
const char create_name[MAX_TABLE_NAME_LEN + 1];
|
||||
const char operation[8];
|
||||
|
||||
ut_ad(mutex_own(&(dict_sys->mutex)));
|
||||
|
||||
table = dict_table_get_low(name, DICT_ERR_IGNORE_NONE);
|
||||
/* First check if we are actually doing an ALTER TABLE, and in that
|
||||
case look for the table being altered */
|
||||
ptr = dict_accept(cs, ptr, "ALTER", &success);
|
||||
|
||||
strcpy((char *)operation, success ? "Alter " : "Create ");
|
||||
|
||||
if (!success) {
|
||||
orig = ptr;
|
||||
ptr = dict_scan_to(ptr, "CREATE");
|
||||
ptr = dict_scan_to(ptr, "TABLE");
|
||||
ptr = dict_accept(cs, ptr, "TABLE", &success);
|
||||
|
||||
if (success) {
|
||||
ptr = dict_scan_table_name(cs, ptr, &table_to_create, name,
|
||||
&success, heap, &create_table_name);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
create_table_name, strlen(create_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
ptr = orig;
|
||||
} else {
|
||||
ptr = orig;
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
name, strlen(name), trx->mysql_thd, TRUE);
|
||||
}
|
||||
|
||||
goto loop;
|
||||
}
|
||||
|
||||
if (table == NULL) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef,
|
||||
"Cannot find the table in the internal"
|
||||
" data dictionary of InnoDB.\n"
|
||||
"Create table statement:\n%s\n", sql_string);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, create_name, start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_ERROR,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.",
|
||||
operation, create_name, create_name, start_of_latest_foreign);
|
||||
return(DB_ERROR);
|
||||
}
|
||||
|
||||
/* First check if we are actually doing an ALTER TABLE, and in that
|
||||
case look for the table being altered */
|
||||
|
||||
ptr = dict_accept(cs, ptr, "ALTER", &success);
|
||||
|
||||
/* If not alter table jump to loop */
|
||||
if (!success) {
|
||||
|
||||
goto loop;
|
||||
@ -3772,13 +3947,35 @@ dict_create_foreign_constraints_low(
|
||||
|
||||
/* We are doing an ALTER TABLE: scan the table name we are altering */
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name,
|
||||
&success, heap, &referenced_table_name);
|
||||
|
||||
if (table_to_alter) {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
table_to_alter->name, strlen(table_to_alter->name),
|
||||
trx->mysql_thd, TRUE);
|
||||
} else {
|
||||
innobase_convert_name((char *)create_name, MAX_TABLE_NAME_LEN,
|
||||
referenced_table_name, strlen(referenced_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
fprintf(stderr,
|
||||
"InnoDB: Error: could not find"
|
||||
" the table being ALTERED in:\n%s\n",
|
||||
sql_string);
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, create_name, orig);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_ERROR,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Table %s not found from data dictionary."
|
||||
" Error close to %s.",
|
||||
operation, create_name, create_name, orig);
|
||||
|
||||
return(DB_ERROR);
|
||||
}
|
||||
@ -3844,7 +4041,19 @@ loop:
|
||||
if so, immediately reject the command if the table is a
|
||||
temporary one. For now, this kludge will work. */
|
||||
if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s table %s with foreign key constraint"
|
||||
" failed. Temporary tables can't have foreign key constraints."
|
||||
" Error close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Temporary tables can't have foreign key constraints."
|
||||
" Error close to %s.",
|
||||
operation, create_name, start_of_latest_foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3880,11 +4089,21 @@ loop:
|
||||
if (!success) {
|
||||
/* MySQL allows also an index id before the '('; we
|
||||
skip it */
|
||||
orig = ptr;
|
||||
ptr = dict_skip_word(cs, ptr, &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
@ -3904,15 +4123,26 @@ loop:
|
||||
/* Scan the columns in the first list */
|
||||
col_loop1:
|
||||
ut_a(i < (sizeof column_names) / sizeof *column_names);
|
||||
orig = ptr;
|
||||
ptr = dict_scan_col(cs, ptr, &success, table, columns + i,
|
||||
heap, column_names + i);
|
||||
if (!success) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3924,11 +4154,22 @@ col_loop1:
|
||||
goto col_loop1;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, ")", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -3936,27 +4177,41 @@ col_loop1:
|
||||
as the first fields and in the right order */
|
||||
|
||||
index = dict_foreign_find_index(table, column_names, i,
|
||||
NULL, TRUE, FALSE);
|
||||
NULL, TRUE, FALSE, &index_error, &err_col, &err_index);
|
||||
|
||||
if (!index) {
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fputs("There is no index in table ", ef);
|
||||
ut_print_name(ef, NULL, TRUE, name);
|
||||
ut_print_name(ef, NULL, TRUE, create_name);
|
||||
fprintf(ef, " where the columns appear\n"
|
||||
"as the first columns. Constraint:\n%s\n"
|
||||
"See " REFMAN "innodb-foreign-key-constraints.html\n"
|
||||
"for correct foreign key definition.\n",
|
||||
start_of_latest_foreign);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_CHILD_NO_INDEX);
|
||||
dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
|
||||
column_names, index_error, err_col, err_index, table, ef);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "REFERENCES", &success);
|
||||
|
||||
if (!success || !my_isspace(cs, *ptr)) {
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4005,24 +4260,48 @@ col_loop1:
|
||||
checking of foreign key constraints! */
|
||||
|
||||
if (!success || (!referenced_table && trx->check_foreigns)) {
|
||||
char buf[MAX_TABLE_NAME_LEN + 1] = "";
|
||||
|
||||
innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
|
||||
referenced_table_name, strlen(referenced_table_name),
|
||||
trx->mysql_thd, TRUE);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
|
||||
"close to %s.",
|
||||
operation, create_name, buf, start_of_latest_foreign);
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve table name close to:\n"
|
||||
"%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
|
||||
"close to %s.\n",
|
||||
operation, create_name, buf, start_of_latest_foreign);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "(", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4030,20 +4309,29 @@ col_loop1:
|
||||
i = 0;
|
||||
|
||||
col_loop2:
|
||||
orig = ptr;
|
||||
ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i,
|
||||
heap, column_names + i);
|
||||
heap, ref_column_names + i);
|
||||
i++;
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\nCannot resolve column name close to:\n"
|
||||
"%s\n",
|
||||
start_of_latest_foreign, ptr);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4053,13 +4341,23 @@ col_loop2:
|
||||
goto col_loop2;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, ")", &success);
|
||||
|
||||
if (!success || foreign->n_fields != i) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s. Too few referenced columns\n",
|
||||
operation, create_name, start_of_latest_foreign, orig);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s. Too few referenced columns, you have %d when you should have %d.",
|
||||
operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4069,6 +4367,7 @@ col_loop2:
|
||||
scan_on_conditions:
|
||||
/* Loop here as long as we can find ON ... conditions */
|
||||
|
||||
start_of_latest_set = ptr;
|
||||
ptr = dict_accept(cs, ptr, "ON", &success);
|
||||
|
||||
if (!success) {
|
||||
@ -4079,13 +4378,24 @@ scan_on_conditions:
|
||||
ptr = dict_accept(cs, ptr, "DELETE", &success);
|
||||
|
||||
if (!success) {
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "UPDATE", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4117,12 +4427,22 @@ scan_on_conditions:
|
||||
ptr = dict_accept(cs, ptr, "NO", &success);
|
||||
|
||||
if (success) {
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "ACTION", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(
|
||||
name, start_of_latest_foreign, ptr);
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
@ -4136,42 +4456,73 @@ scan_on_conditions:
|
||||
goto scan_on_conditions;
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "SET", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
orig = ptr;
|
||||
ptr = dict_accept(cs, ptr, "NULL", &success);
|
||||
|
||||
if (!success) {
|
||||
dict_foreign_free(foreign);
|
||||
dict_foreign_report_syntax_err(name, start_of_latest_foreign,
|
||||
ptr);
|
||||
dict_foreign_report_syntax_err(
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. Foreign key constraint parse error in %s"
|
||||
" close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
for (j = 0; j < foreign->n_fields; j++) {
|
||||
if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype)
|
||||
& DATA_NOT_NULL) {
|
||||
const dict_col_t* col
|
||||
= dict_index_get_nth_col(foreign->foreign_index, j);
|
||||
const char* col_name = dict_table_get_col_name(foreign->foreign_index->table,
|
||||
dict_col_get_no(col));
|
||||
|
||||
/* It is not sensible to define SET NULL
|
||||
if the column is not allowed to be NULL! */
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"You have defined a SET NULL condition"
|
||||
" though some of the\n"
|
||||
"columns are defined as NOT NULL.\n",
|
||||
start_of_latest_foreign);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but column %s is defined as NOT NULL"
|
||||
" in %s close to %s.\n",
|
||||
operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have defined a SET NULL condition but column %s is defined as NOT NULL"
|
||||
" in %s close to %s.",
|
||||
operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
}
|
||||
@ -4188,16 +4539,22 @@ try_find_index:
|
||||
if (n_on_deletes > 1 || n_on_updates > 1) {
|
||||
/* It is an error to define more than 1 action */
|
||||
|
||||
dict_foreign_free(foreign);
|
||||
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"You have twice an ON DELETE clause"
|
||||
" or twice an ON UPDATE clause.\n",
|
||||
start_of_latest_foreign);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have more than one on delete or on update clause"
|
||||
" in %s close to %s.\n",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
|
||||
"%s table %s with foreign key constraint"
|
||||
" failed. You have more than one on delete or on update clause"
|
||||
" in %s close to %s.",
|
||||
operation, create_name, start_of_latest_foreign, start_of_latest_set);
|
||||
dict_foreign_free(foreign);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
|
||||
@ -4207,13 +4564,13 @@ try_find_index:
|
||||
|
||||
if (referenced_table) {
|
||||
index = dict_foreign_find_index(referenced_table,
|
||||
column_names, i,
|
||||
foreign->foreign_index,
|
||||
TRUE, FALSE);
|
||||
ref_column_names, i,
|
||||
foreign->foreign_index,
|
||||
TRUE, FALSE, &index_error, &err_col, &err_index);
|
||||
if (!index) {
|
||||
dict_foreign_free(foreign);
|
||||
mutex_enter(&dict_foreign_err_mutex);
|
||||
dict_foreign_error_report_low(ef, name);
|
||||
dict_foreign_error_report_low(ef, create_name);
|
||||
fprintf(ef, "%s:\n"
|
||||
"Cannot find an index in the"
|
||||
" referenced table where the\n"
|
||||
@ -4231,9 +4588,13 @@ try_find_index:
|
||||
"innodb-foreign-key-constraints.html\n"
|
||||
"for correct foreign key definition.\n",
|
||||
start_of_latest_foreign);
|
||||
|
||||
dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
|
||||
column_names, index_error, err_col, err_index, referenced_table, ef);
|
||||
|
||||
mutex_exit(&dict_foreign_err_mutex);
|
||||
|
||||
return(DB_PARENT_NO_INDEX);
|
||||
return(DB_CANNOT_ADD_CONSTRAINT);
|
||||
}
|
||||
} else {
|
||||
ut_a(trx->check_foreigns == FALSE);
|
||||
@ -4251,7 +4612,7 @@ try_find_index:
|
||||
i * sizeof(void*));
|
||||
for (i = 0; i < foreign->n_fields; i++) {
|
||||
foreign->referenced_col_names[i]
|
||||
= mem_heap_strdup(foreign->heap, column_names[i]);
|
||||
= mem_heap_strdup(foreign->heap, ref_column_names[i]);
|
||||
}
|
||||
|
||||
/* We found an ok constraint definition: add to the lists */
|
||||
@ -5796,7 +6157,8 @@ dict_table_replace_index_in_foreign_list(
|
||||
foreign->referenced_table,
|
||||
foreign->referenced_col_names,
|
||||
foreign->n_fields, index,
|
||||
/*check_charsets=*/TRUE, /*check_null=*/FALSE);
|
||||
/*check_charsets=*/TRUE, /*check_null=*/FALSE,
|
||||
NULL, NULL, NULL);
|
||||
ut_ad(new_index || !trx->check_foreigns);
|
||||
ut_ad(!new_index || new_index->table == index->table);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user