diff --git a/mysql-test/suite/innodb/r/innodb-fk-warnings.result b/mysql-test/suite/innodb/r/innodb-fk-warnings.result index c64cb3024b5..542fc972880 100644 --- a/mysql-test/suite/innodb/r/innodb-fk-warnings.result +++ b/mysql-test/suite/innodb/r/innodb-fk-warnings.result @@ -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; diff --git a/mysql-test/suite/innodb/r/innodb-fk.result b/mysql-test/suite/innodb/r/innodb-fk.result index 3c557534dbd..2b53bc21b31 100644 --- a/mysql-test/suite/innodb/r/innodb-fk.result +++ b/mysql-test/suite/innodb/r/innodb-fk.result @@ -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; diff --git a/mysql-test/suite/innodb/t/innodb-fk-warnings.test b/mysql-test/suite/innodb/t/innodb-fk-warnings.test index e12a0ecf820..fe92f1345f4 100644 --- a/mysql-test/suite/innodb/t/innodb-fk-warnings.test +++ b/mysql-test/suite/innodb/t/innodb-fk-warnings.test @@ -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; diff --git a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c index 7a52aff3ec3..87fbd462697 100644 --- a/storage/innobase/dict/dict0crea.c +++ b/storage/innobase/dict/dict0crea.c @@ -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); } diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index cbb17fc5a6f..4211d864d7c 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -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); diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index 9faf580b0cc..a58bcb181aa 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -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 diff --git a/storage/xtradb/dict/dict0crea.c b/storage/xtradb/dict/dict0crea.c index 71a00ca8d0f..64a6d706557 100644 --- a/storage/xtradb/dict/dict0crea.c +++ b/storage/xtradb/dict/dict0crea.c @@ -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); } diff --git a/storage/xtradb/dict/dict0dict.c b/storage/xtradb/dict/dict0dict.c index bd0fdf6e9b9..754ddfa752d 100644 --- a/storage/xtradb/dict/dict0dict.c +++ b/storage/xtradb/dict/dict0dict.c @@ -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); diff --git a/storage/xtradb/include/dict0crea.h b/storage/xtradb/include/dict0crea.h index 762ab54a353..c67910f0d55 100644 --- a/storage/xtradb/include/dict0crea.h +++ b/storage/xtradb/include/dict0crea.h @@ -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