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);
}
......
......@@ -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
......
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