MDEV-20480 Obsolete internal parser for FK in InnoDB

Currently InnoDB uses internal parser for adding foreign keys. Remove
internal parser and use data parsed by SQL parser (sql_yacc) for
adding foreign keys.

- create_table_info_t::create_foreign_keys() replacement for
  dict_create_foreign_constraints_low();
- Pass constraint name via Foreign_key object.

Temporary until MDEV-20865:

- Pass alter_info as part of create_info.
This commit is contained in:
Aleksey Midenkov 2019-11-20 13:18:31 +03:00
parent 20b474be5b
commit 5130f5206c
19 changed files with 826 additions and 1402 deletions

View file

@ -262,7 +262,7 @@ ALTER IGNORE TABLE t1 ADD FOREIGN KEY (a) REFERENCES t2 (b);
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
SHOW WARNINGS;
Level Code Message
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Referenced table `test`.`t2` not found in the data dictionary near 'FOREIGN KEY (a) REFERENCES t2 (b)'.
Warning 150 Alter table `test`.`t1` with foreign key (a) constraint failed. Referenced table `test`.`t2` not found in the data dictionary.
Error 1005 Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t1`
DROP TABLE t1;

View file

@ -25,7 +25,16 @@ create table t2(a int, constraint a foreign key a (a) references t1(a)) engine=i
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near ' foreign key a (a) references t1(a)) engine=innodb'.
Warning 150 Create table `test`.`t2` with foreign key `a` constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t1;
create table t1(a int unique, b int) engine=innodb;
create table t2(a int, b int, foreign key (a) references t1(a), foreign key (b) references t1(b)) engine=innodb;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
show warnings;
Level Code Message
Warning 150 Create table `test`.`t2` with foreign key (b) constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t1;
@ -42,7 +51,7 @@ alter table t2 add constraint b foreign key (b) references t2(b);
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near ' foreign key (b) references t2(b)'.
Warning 150 Alter table `test`.`t2` with foreign key `b` constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t2, t1;
@ -51,7 +60,7 @@ alter table t1 add constraint c1 foreign key (f1) references t11(f1);
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near ' foreign key (f1) references t11(f1)'.
Warning 150 Alter table `test`.`t1` with foreign key `c1` constraint failed. Referenced table `test`.`t11` not found in the data dictionary.
Error 1005 Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t1`
drop table t1;
@ -74,14 +83,14 @@ create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near 'foreign key(a) references t1(a)) engine=innodb'.
Warning 150 Create table `test`.`t2` with foreign key constraint failed. Temporary tables can't have foreign key constraints.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
alter table t1 add foreign key(b) references t1(a);
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near 'foreign key(b) references t1(a)'.
Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Temporary tables can't have foreign key constraints.
Error 1005 Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t1`
drop table t1;
@ -104,14 +113,14 @@ alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update se
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
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' near ' on update set null'.
Warning 150 Alter table `test`.`t1` with foreign key `c1` constraint failed. You have defined a SET NULL condition but column 'f1' is defined as NOT NULL.
Error 1005 Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t1`
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 "Foreign key constraint is incorrectly formed")
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' near ' on delete set null) engine=innodb'.
Warning 150 Create table `test`.`t2` with foreign key (a) constraint failed. You have defined a SET NULL condition but column 'a' is defined as NOT NULL.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t1;
@ -120,7 +129,7 @@ create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=inn
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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' near 'foreign key(a) references t1(f1)) engine=innodb'.
Warning 150 Create table `test`.`t2` with foreign key (a) constraint failed. Field type or character set for column 'a' does not mach referenced column 'f1'.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t1;

View file

@ -52,8 +52,7 @@ CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near ' FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE
) ENGINE=InnoDB'.
Warning 150 Create table `test`.`t2` with foreign key `fk3` constraint failed. Referenced table `test`.`t3` not found in the data dictionary.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
CREATE TABLE t2 (
@ -67,7 +66,7 @@ ALTER TABLE t2 ADD CONSTRAINT fk3 FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
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 near ' FOREIGN KEY (f3) REFERENCES t3 (id) ON DELETE CASCADE'.
Warning 150 Alter table `test`.`t2` with foreign key `fk3` constraint failed. Referenced table `test`.`t3` not found in the data dictionary.
Error 1005 Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `t2`
drop table t2;

View file

@ -15,7 +15,7 @@ CREATE TABLE t1 (
# Below create table fails because constraint name test
# is reserved for above table.
#
--error 1005
--error ER_CANT_CREATE_TABLE
CREATE TABLE t2 (
id int(11) NOT NULL PRIMARY KEY,
a int(11) NOT NULL,
@ -37,18 +37,24 @@ drop table t1;
# No index for referenced columns
#
create table t1(a int) engine=innodb;
--error 1005
--error ER_CANT_CREATE_TABLE
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 unique, b int) engine=innodb;
--error ER_CANT_CREATE_TABLE
create table t2(a int, b int, foreign key (a) references t1(a), foreign key (b) references t1(b)) engine=innodb;
show warnings;
drop table t1;
create table t1(a int not null primary key, b int) engine=innodb;
--error 1005
--error ER_CANT_CREATE_TABLE
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;
--error 1005
--error ER_CANT_CREATE_TABLE
alter table t2 add constraint b foreign key (b) references t2(b);
show warnings;
drop table t2, t1;
@ -58,7 +64,7 @@ drop table t2, t1;
#
create table t1 (f1 integer primary key) engine=innodb;
--error 1005
--error ER_CANT_CREATE_TABLE
alter table t1 add constraint c1 foreign key (f1) references t11(f1);
show warnings;
drop table t1;
@ -83,10 +89,10 @@ create temporary table t1(a int not null primary key, b int, key(b)) engine=inno
--echo 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).
--echo Error 1005 Can't create table `test`.`#sql-temporary` (errno: 150 "Foreign key constraint is incorrectly formed")
--echo Warning 1215 Cannot add foreign key constraint
--error 1005
--error ER_CANT_CREATE_TABLE
create temporary table t2(a int, foreign key(a) references t1(a)) engine=innodb;
show warnings;
--error 1005
--error ER_CANT_CREATE_TABLE
alter table t1 add foreign key(b) references t1(a);
show warnings;
drop table t1;
@ -109,10 +115,10 @@ drop table t1;
# ON UPDATE/DELETE SET NULL on NOT NULL column
#
create table t1 (f1 integer not null primary key) engine=innodb;
--error 1005
--error ER_CANT_CREATE_TABLE
alter table t1 add constraint c1 foreign key (f1) references t1(f1) on update set null;
show warnings;
--error 1005
--error ER_CANT_CREATE_TABLE
create table t2(a int not null, foreign key(a) references t1(f1) on delete set null) engine=innodb;
show warnings;
drop table t1;
@ -121,7 +127,7 @@ drop table t1;
# Incorrect types
#
create table t1 (id int not null primary key, f1 int, f2 int, key(f1)) engine=innodb;
--error 1005
--error ER_CANT_CREATE_TABLE
create table t2(a char(20), key(a), foreign key(a) references t1(f1)) engine=innodb;
show warnings;
drop table t1;

View file

@ -1524,7 +1524,7 @@ ALTER TABLE child ADD FOREIGN KEY(p) REFERENCES parent(p);
ERROR HY000: Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
show warnings;
Level Code Message
Warning 150 Alter table `test`.`child` with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns near 'FOREIGN KEY(p) REFERENCES parent(p)'.
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `child`
ALTER TABLE parent DROP INDEX idx1;
@ -1532,7 +1532,7 @@ ALTER TABLE child ADD FOREIGN KEY(p) REFERENCES parent(p);
Got one of the listed errors
show warnings;
Level Code Message
Warning 150 Alter table `test`.`child` with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns near 'FOREIGN KEY(p) REFERENCES parent(p)'.
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `child`
ALTER TABLE child DROP INDEX idx2;
@ -1540,7 +1540,7 @@ ALTER TABLE child ADD FOREIGN KEY(p) REFERENCES parent(p);
Got one of the listed errors
show warnings;
Level Code Message
Warning 150 Alter table `test`.`child` with foreign key constraint failed. There is only prefix index in the referenced table where the referenced columns appear as the first columns near 'FOREIGN KEY(p) REFERENCES parent(p)'.
Warning 150 Alter table `test`.`child` with foreign key (p) constraint failed. There is only prefix index in the referenced table where the referenced columns appear as the first columns.
Error 1005 Can't create table `test`.`child` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `child`
DROP TABLE child, parent;

View file

@ -2190,10 +2190,14 @@ struct Table_scope_and_contents_source_st:
struct HA_CREATE_INFO: public Table_scope_and_contents_source_st,
public Schema_specification_st
{
/* TODO: remove after MDEV-20865 */
Alter_info *alter_info;
void init()
{
Table_scope_and_contents_source_st::init();
Schema_specification_st::init();
alter_info= NULL;
}
bool check_conflicting_charset_declarations(CHARSET_INFO *cs);
bool add_table_option_default_charset(CHARSET_INFO *cs)

View file

@ -394,6 +394,7 @@ bool Sql_cmd_alter_table::execute(THD *thd)
*/
HA_CREATE_INFO create_info(lex->create_info);
Alter_info alter_info(lex->alter_info, thd->mem_root);
create_info.alter_info= &alter_info;
ulong priv=0;
ulong priv_needed= ALTER_ACL;
bool result;

View file

@ -189,6 +189,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root)
:Key(rhs,mem_root),
constraint_name(rhs.constraint_name),
ref_db(rhs.ref_db),
ref_table(rhs.ref_table),
ref_columns(rhs.ref_columns,mem_root),

View file

@ -393,12 +393,14 @@ class Foreign_key: public Key {
public:
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
LEX_CSTRING constraint_name;
LEX_CSTRING ref_db;
LEX_CSTRING ref_table;
List<Key_part_spec> ref_columns;
enum enum_fk_option delete_opt, update_opt;
enum fk_match_opt match_opt;
Foreign_key(const LEX_CSTRING *name_arg, List<Key_part_spec> *cols,
const LEX_CSTRING *constraint_name_arg,
const LEX_CSTRING *ref_db_arg, const LEX_CSTRING *ref_table_arg,
List<Key_part_spec> *ref_cols,
enum_fk_option delete_opt_arg, enum_fk_option update_opt_arg,
@ -406,6 +408,7 @@ public:
DDL_options ddl_options)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL,
ddl_options),
constraint_name(*constraint_name_arg),
ref_db(*ref_db_arg), ref_table(*ref_table_arg), ref_columns(*ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)

View file

@ -4154,6 +4154,7 @@ mysql_execute_command(THD *thd)
create_info.db_type= 0;
create_info.row_type= ROW_TYPE_NOT_USED;
create_info.default_table_charset= thd->variables.collation_database;
create_info.alter_info= &alter_info;
res= mysql_alter_table(thd, &first_table->db, &first_table->table_name,
&create_info, first_table, &alter_info,

View file

@ -5652,6 +5652,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
local_create_info.init(create_info->create_like_options());
local_create_info.db_type= src_table->table->s->db_type();
local_create_info.row_type= src_table->table->s->row_type;
local_create_info.alter_info= &local_alter_info;
if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
&local_alter_info, &local_alter_ctx))
goto err;
@ -10949,6 +10950,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
create_info.alter_info= &alter_info;
/* Force alter table to recreate table */
alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
@ -11228,6 +11230,13 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
goto end_with_restore_list;
}
/*
Since CREATE_INFO is not full without Alter_info, it is better to pass them
as a signle parameter. TODO: remove alter_info argument where create_info is
passed.
*/
create_info.alter_info= &alter_info;
/* Check privileges */
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;

View file

@ -5779,6 +5779,7 @@ key_def:
Key *key= (new (thd->mem_root)
Foreign_key($5.str ? &$5 : &$1,
&lex->last_key->columns,
$1.str ? &$1 : &$5,
&$10->db,
&$10->table,
&lex->ref_list,

View file

@ -5778,6 +5778,7 @@ key_def:
Key *key= (new (thd->mem_root)
Foreign_key($5.str ? &$5 : &$1,
&lex->last_key->columns,
$1.str ? &$1 : &$5,
&$10->db,
&$10->table,
&lex->ref_list,

File diff suppressed because it is too large Load diff

View file

@ -78,6 +78,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "btr0defragment.h"
#include "dict0crea.h"
#include "dict0dict.h"
#include "dict0priv.h"
#include "dict0stats.h"
#include "dict0stats_bg.h"
#include "fil0fil.h"
@ -112,6 +113,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "trx0trx.h"
#include "fil0pagecompress.h"
#include "ut0mem.h"
#include "ut0mutex.h"
#include "row0ext.h"
#define thd_get_trx_isolation(X) ((enum_tx_isolation)thd_tx_isolation(X))
@ -12079,6 +12081,579 @@ int create_table_info_t::prepare_create_table(const char* name, bool strict)
DBUG_RETURN(parse_table_name(name));
}
/** Push warning message to SQL-layer based on foreign key constraint index
match error.
@param[in] trx Current transaction
@param[in] operation Operation ("Create" or "Alter")
@param[in] create_name Table name as specified in SQL
@param[in] columns Foreign key column names array
@param[in] index_error Index error code
@param[in] err_col Column where error happened
@param[in] err_index Index where error happened
@param[in] table Table object */
static void
foreign_push_index_error(trx_t* trx, const char* operation,
const char* create_name, const char* fk_text,
const char** columns, fkerr_t index_error,
ulint err_col, dict_index_t* err_index,
dict_table_t* table)
{
switch (index_error) {
case FK_SUCCESS:
break;
case FK_INDEX_NOT_FOUND:
ib_foreign_warn(trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s with foreign key %s constraint"
" failed. There is no index in the referenced"
" table where the referenced columns appear"
" as the first columns.",
operation, create_name, fk_text);
return;
case FK_IS_PREFIX_INDEX:
ib_foreign_warn(
trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s with foreign key %s constraint"
" failed. There is only prefix index in the referenced"
" table where the referenced columns appear"
" as the first columns.",
operation, create_name, fk_text);
return;
case FK_COL_NOT_NULL:
ib_foreign_warn(
trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s with foreign key %s constraint"
" failed. You have defined a SET NULL condition but "
"column '%s' on index is defined as NOT NULL.",
operation, create_name, fk_text, columns[err_col]);
return;
case FK_COLS_NOT_EQUAL:
dict_field_t* field;
const char* col_name;
field = dict_index_get_nth_field(err_index, err_col);
col_name = field->col->is_virtual()
? "(null)"
: dict_table_get_col_name(
table, dict_col_get_no(field->col));
ib_foreign_warn(
trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s with foreign key %s constraint"
" failed. Field type or character set for column '%s' "
"does not mach referenced column '%s'.",
operation, create_name, fk_text, columns[err_col],
col_name);
return;
}
DBUG_ASSERT(!"unknown error");
}
/** Find column or virtual column in table by its name.
@param[in] table Table where column is searched
@param[in] name Name to search for
@retval true if found
@retval false if not found */
static bool
find_col(dict_table_t* table, const char** name)
{
ulint i;
for (i = 0; i < dict_table_get_n_cols(table); i++) {
const char* col_name = dict_table_get_col_name(table, i);
if (0 == innobase_strcasecmp(col_name, *name)) {
/* Found */
strcpy((char*)*name, col_name);
return true;
}
}
for (i = 0; i < dict_table_get_n_v_cols(table); i++) {
const char* col_name = dict_table_get_v_col_name(table, i);
if (0 == innobase_strcasecmp(col_name, *name)) {
/* Found */
strcpy((char*)*name, col_name);
return true;
}
}
return false;
}
/** Foreign key printer for error messages. Prints FK name if it exists or
key part list in the form (col1, col2, col3, ...) */
class key_text
{
static const size_t MAX_TEXT = 48;
char buf[MAX_TEXT + 1];
public:
key_text(Key* key)
{
char* ptr = buf;
if (key->name.str) {
size_t len = std::min(key->name.length, MAX_TEXT - 2);
*(ptr++) = '`';
memcpy(ptr, key->name.str, len);
ptr += len;
*(ptr++) = '`';
*ptr = '\0';
return;
}
*(ptr++) = '(';
List_iterator_fast<Key_part_spec> it(key->columns);
while (Key_part_spec* k = it++) {
/* 3 is etc continuation ("...");
2 is comma separator (", ") in case of next exists;
1 is terminating ')' */
if ((size_t)(ptr - buf) < MAX_TEXT
- (it.peek() ? 3 + 2 + 1 : 3 + 1)
- k->field_name.length) {
memcpy(ptr, k->field_name.str,
k->field_name.length);
ptr += k->field_name.length;
if (it.peek()) {
*(ptr++) = ',';
*(ptr++) = ' ';
}
} else {
ut_ad((size_t)(ptr - buf) < MAX_TEXT - 4);
memcpy(ptr, "...", 3);
ptr += 3;
break;
}
}
*(ptr++) = ')';
*ptr = '\0';
}
const char* str() { return buf; }
};
/** Create InnoDB foreign keys from MySQL alter_info. Collect all
dict_foreign_t items into local_fk_set and then add into system table.
@return DB_SUCCESS or specific error code */
dberr_t
create_table_info_t::create_foreign_keys()
{
dict_foreign_set local_fk_set;
dict_foreign_set_free local_fk_set_free(local_fk_set);
dberr_t error;
ulint number = 1;
static const unsigned MAX_COLS_PER_FK = 500;
const char* column_names[MAX_COLS_PER_FK];
const char* ref_column_names[MAX_COLS_PER_FK];
char create_name[MAX_TABLE_NAME_LEN + 1];
dict_index_t* index = NULL;
fkerr_t index_error = FK_SUCCESS;
dict_index_t* err_index = NULL;
ulint err_col;
const bool tmp_table = m_flags2 & DICT_TF2_TEMPORARY;
const CHARSET_INFO* cs = innobase_get_charset(m_thd);
const char* operation = "Create ";
const char* name = m_table_name;
enum_sql_command sqlcom = enum_sql_command(thd_sql_command(m_thd));
if (sqlcom == SQLCOM_ALTER_TABLE) {
dict_table_t* table_to_alter;
mem_heap_t* heap = mem_heap_create(10000);
ulint highest_id_so_far;
char* n = dict_get_referenced_table(
name, LEX_STRING_WITH_LEN(m_form->s->db),
LEX_STRING_WITH_LEN(m_form->s->table_name),
&table_to_alter, heap, cs);
/* Starting from 4.0.18 and 4.1.2, we generate foreign key id's
in the format databasename/tablename_ibfk_[number], where
[number] is local to the table; look for the highest [number]
for table_to_alter, so that we can assign to new constraints
higher numbers. */
/* If we are altering a temporary table, the table name after
ALTER TABLE does not correspond to the internal table name, and
table_to_alter is NULL. TODO: should we fix this somehow? */
if (table_to_alter) {
n = table_to_alter->name.m_name;
highest_id_so_far = dict_table_get_highest_foreign_id(
table_to_alter);
} else {
highest_id_so_far = 0;
}
char* bufend = innobase_convert_name(
create_name, MAX_TABLE_NAME_LEN, n, strlen(n), m_thd);
create_name[bufend - create_name] = '\0';
number = highest_id_so_far + 1;
mem_heap_free(heap);
operation = "Alter ";
} else {
char* bufend = innobase_convert_name(create_name,
MAX_TABLE_NAME_LEN, name,
strlen(name), m_thd);
create_name[bufend - create_name] = '\0';
}
Alter_info* alter_info = m_create_info->alter_info;
ut_ad(alter_info);
List_iterator_fast<Key> key_it(alter_info->key_list);
dict_table_t* table = dict_table_get_low(name);
if (!table) {
ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT, create_name,
"%s table %s foreign key constraint"
" failed. Table not found.",
operation, create_name);
return (DB_CANNOT_ADD_CONSTRAINT);
}
while (Key* key = key_it++) {
if (key->type != Key::FOREIGN_KEY)
continue;
if (tmp_table) {
ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table `%s`.`%s` with foreign key "
"constraint failed. "
"Temporary tables can't have "
"foreign key constraints.",
operation, m_form->s->db.str,
m_form->s->table_name.str);
return (DB_CANNOT_ADD_CONSTRAINT);
}
Foreign_key* fk = static_cast<Foreign_key*>(key);
Key_part_spec* col;
bool success;
dict_foreign_t* foreign = dict_mem_foreign_create();
if (!foreign) {
return (DB_OUT_OF_MEMORY);
}
List_iterator_fast<Key_part_spec> col_it(fk->columns);
unsigned i = 0, j = 0;
while ((col = col_it++)) {
column_names[i] = mem_heap_strdupl(
foreign->heap, col->field_name.str,
col->field_name.length);
success = find_col(table, column_names + i);
if (!success) {
key_text k(fk);
ib_foreign_warn(
m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table %s foreign key %s constraint"
" failed. Column %s was not found.",
operation, create_name, k.str(),
column_names[i]);
return (DB_CANNOT_ADD_CONSTRAINT);
}
++i;
if (i >= MAX_COLS_PER_FK) {
key_text k(fk);
ib_foreign_warn(
m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table %s foreign key %s constraint"
" failed. Too many columns: %u (%u "
"allowed).",
operation, create_name, k.str(), i,
MAX_COLS_PER_FK);
return (DB_CANNOT_ADD_CONSTRAINT);
}
}
index = dict_foreign_find_index(
table, NULL, column_names, i, NULL, TRUE, FALSE,
&index_error, &err_col, &err_index);
if (!index) {
key_text k(fk);
foreign_push_index_error(m_trx, operation, create_name,
k.str(), column_names,
index_error, err_col,
err_index, table);
return (DB_CANNOT_ADD_CONSTRAINT);
}
if (fk->constraint_name.str) {
ulint db_len;
/* Catenate 'databasename/' to the constraint name
specified by the user: we conceive the constraint as
belonging to the same MySQL 'database' as the table
itself. We store the name to foreign->id. */
db_len = dict_get_db_name_len(table->name.m_name);
foreign->id = static_cast<char*>(mem_heap_alloc(
foreign->heap,
db_len + fk->constraint_name.length + 2));
memcpy(foreign->id, table->name.m_name, db_len);
foreign->id[db_len] = '/';
strcpy(foreign->id + db_len + 1,
fk->constraint_name.str);
}
if (foreign->id == NULL) {
error = dict_create_add_foreign_id(
&number, table->name.m_name, foreign);
if (error != DB_SUCCESS) {
dict_foreign_free(foreign);
return (error);
}
}
std::pair<dict_foreign_set::iterator, bool> ret
= local_fk_set.insert(foreign);
if (!ret.second) {
/* A duplicate foreign key name has been found */
dict_foreign_free(foreign);
return (DB_CANNOT_ADD_CONSTRAINT);
}
foreign->foreign_table = table;
foreign->foreign_table_name
= mem_heap_strdup(foreign->heap, table->name.m_name);
if (!foreign->foreign_table_name) {
return (DB_OUT_OF_MEMORY);
}
dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
foreign->foreign_index = index;
foreign->n_fields = (unsigned int)i;
foreign->foreign_col_names = static_cast<const char**>(
mem_heap_alloc(foreign->heap, i * sizeof(void*)));
if (!foreign->foreign_col_names) {
return (DB_OUT_OF_MEMORY);
}
memcpy(foreign->foreign_col_names, column_names,
i * sizeof(void*));
foreign->referenced_table_name = dict_get_referenced_table(
name, LEX_STRING_WITH_LEN(fk->ref_db),
LEX_STRING_WITH_LEN(fk->ref_table),
&foreign->referenced_table, foreign->heap, cs);
if (!foreign->referenced_table_name) {
return (DB_OUT_OF_MEMORY);
}
if (!foreign->referenced_table && m_trx->check_foreigns) {
char buf[MAX_TABLE_NAME_LEN + 1] = "";
char* bufend;
bufend = innobase_convert_name(
buf, MAX_TABLE_NAME_LEN,
foreign->referenced_table_name,
strlen(foreign->referenced_table_name), m_thd);
buf[bufend - buf] = '\0';
key_text k(fk);
ib_foreign_warn(m_trx, DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table %s with foreign key %s "
"constraint failed. Referenced table "
"%s not found in the data dictionary.",
operation, create_name, k.str(), buf);
return (DB_CANNOT_ADD_CONSTRAINT);
}
/* Don't allow foreign keys on partitioned tables yet. */
if (foreign->referenced_table
&& dict_table_is_partition(foreign->referenced_table)) {
/* How could one make a referenced table to be a
* partition? */
ut_ad(0);
my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
return (DB_CANNOT_ADD_CONSTRAINT);
}
col_it.init(fk->ref_columns);
while ((col = col_it++)) {
ref_column_names[j] = mem_heap_strdupl(
foreign->heap, col->field_name.str,
col->field_name.length);
if (foreign->referenced_table) {
success = find_col(foreign->referenced_table,
ref_column_names + j);
if (!success) {
key_text k(fk);
ib_foreign_warn(
m_trx,
DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table %s foreign key %s "
"constraint failed. "
"Column %s was not found.",
operation, create_name,
k.str(), ref_column_names[j]);
return (DB_CANNOT_ADD_CONSTRAINT);
}
}
++j;
}
/* See ER_WRONG_FK_DEF in mysql_prepare_create_table() */
ut_ad(i == j);
/* Try to find an index which contains the columns as the first
fields and in the right order, and the types are the same as in
foreign->foreign_index */
if (foreign->referenced_table) {
index = dict_foreign_find_index(
foreign->referenced_table, NULL,
ref_column_names, i, foreign->foreign_index,
TRUE, FALSE, &index_error, &err_col,
&err_index);
if (!index) {
key_text k(fk);
foreign_push_index_error(
m_trx, operation, create_name, k.str(),
column_names, index_error, err_col,
err_index, foreign->referenced_table);
return (DB_CANNOT_ADD_CONSTRAINT);
}
} else {
ut_a(m_trx->check_foreigns == FALSE);
index = NULL;
}
foreign->referenced_index = index;
dict_mem_referenced_table_name_lookup_set(foreign, TRUE);
foreign->referenced_col_names = static_cast<const char**>(
mem_heap_alloc(foreign->heap, i * sizeof(void*)));
if (!foreign->referenced_col_names) {
return (DB_OUT_OF_MEMORY);
}
memcpy(foreign->referenced_col_names, ref_column_names,
i * sizeof(void*));
if (fk->delete_opt == FK_OPTION_SET_NULL
|| fk->update_opt == FK_OPTION_SET_NULL) {
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! */
key_text k(fk);
ib_foreign_warn(
m_trx,
DB_CANNOT_ADD_CONSTRAINT,
create_name,
"%s table %s with foreign key "
"%s constraint failed. You have"
" defined a SET NULL condition "
"but column '%s' is defined as "
"NOT NULL.",
operation, create_name,
k.str(), col_name);
return (DB_CANNOT_ADD_CONSTRAINT);
}
}
}
switch (fk->delete_opt) {
case FK_OPTION_UNDEF:
case FK_OPTION_RESTRICT:
break;
case FK_OPTION_CASCADE:
foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
break;
case FK_OPTION_NO_ACTION:
foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
break;
case FK_OPTION_SET_DEFAULT:
// TODO: MDEV-10393 Foreign keys SET DEFAULT action
break;
default:
ut_ad(0);
break;
}
switch (fk->update_opt) {
case FK_OPTION_UNDEF:
case FK_OPTION_RESTRICT:
break;
case FK_OPTION_CASCADE:
foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
break;
case FK_OPTION_SET_NULL:
foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
break;
case FK_OPTION_NO_ACTION:
foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
break;
case FK_OPTION_SET_DEFAULT:
// TODO: MDEV-10393 Foreign keys SET DEFAULT action
break;
default:
ut_ad(0);
break;
}
}
if (dict_foreigns_has_s_base_col(local_fk_set, table)) {
return (DB_NO_FK_ON_S_BASE_COL);
}
/**********************************************************/
/* The following call adds the foreign key constraints
to the data dictionary system tables on disk */
m_trx->op_info = "adding foreign keys";
trx_start_if_not_started_xa(m_trx, true);
trx_set_dict_operation(m_trx, TRX_DICT_OP_TABLE);
error = dict_create_add_foreigns_to_dictionary(local_fk_set, table,
m_trx);
if (error == DB_SUCCESS) {
table->foreign_set.insert(local_fk_set.begin(),
local_fk_set.end());
std::for_each(local_fk_set.begin(), local_fk_set.end(),
dict_foreign_add_to_referenced_table());
local_fk_set.clear();
dict_mem_table_fill_foreign_vcol_set(table);
}
return (error);
}
/** Create the internal innodb table.
@param create_fk whether to add FOREIGN KEY constraints */
int create_table_info_t::create_table(bool create_fk)
@ -12198,64 +12773,58 @@ int create_table_info_t::create_table(bool create_fk)
dict_table_get_all_fts_indexes(m_table, fts->indexes);
}
size_t stmt_len;
if (const char* stmt = innobase_get_stmt_unsafe(m_thd, &stmt_len)) {
dberr_t err = create_fk
? dict_create_foreign_constraints(
m_trx, stmt, stmt_len, m_table_name,
m_flags2 & DICT_TF2_TEMPORARY)
: DB_SUCCESS;
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
dict_names_t fk_tables;
err = dict_load_foreigns(m_table_name, NULL,
false, true,
DICT_ERR_IGNORE_NONE,
fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_load_table(fk_tables.front(),
DICT_ERR_IGNORE_NONE);
fk_tables.pop_front();
}
}
dberr_t err = create_fk ? create_foreign_keys() : DB_SUCCESS;
switch (err) {
case DB_PARENT_NO_INDEX:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. There is no index in the referenced"
" table where the referenced columns appear"
" as the first columns.\n", m_table_name);
break;
case DB_CHILD_NO_INDEX:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. There is no index in the referencing"
" table where referencing columns appear"
" as the first columns.\n", m_table_name);
break;
case DB_NO_FK_ON_S_BASE_COL:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. Cannot add foreign key constraint"
" placed on the base column of stored"
" column. \n",
m_table_name);
default:
break;
if (err == DB_SUCCESS) {
/* Check that also referencing constraints are ok */
dict_names_t fk_tables;
err = dict_load_foreigns(m_table_name, NULL,
false, true,
DICT_ERR_IGNORE_NONE,
fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_load_table(fk_tables.front(),
DICT_ERR_IGNORE_NONE);
fk_tables.pop_front();
}
}
if (err != DB_SUCCESS) {
DBUG_RETURN(convert_error_code_to_mysql(
err, m_flags, NULL));
}
switch (err) {
case DB_PARENT_NO_INDEX:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. There is no index in the referenced"
" table where the referenced columns appear"
" as the first columns.\n", m_table_name);
break;
case DB_CHILD_NO_INDEX:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. There is no index in the referencing"
" table where referencing columns appear"
" as the first columns.\n", m_table_name);
break;
case DB_NO_FK_ON_S_BASE_COL:
push_warning_printf(
m_thd, Sql_condition::WARN_LEVEL_WARN,
HA_ERR_CANNOT_ADD_FOREIGN,
"Create table '%s' with foreign key constraint"
" failed. Cannot add foreign key constraint"
" placed on the base column of stored"
" column. \n",
m_table_name);
default:
break;
}
if (err != DB_SUCCESS) {
DBUG_RETURN(convert_error_code_to_mysql(
err, m_flags, NULL));
}
if (!row_size_is_acceptable(*m_table)) {
@ -14787,8 +15356,8 @@ static
FOREIGN_KEY_INFO*
get_foreign_key_info(
/*=================*/
THD* thd, /*!< in: user thread handle */
dict_foreign_t* foreign)/*!< in: foreign key constraint */
THD* thd, /*!< in: user thread handle */
dict_foreign_t* foreign)/*!< in: foreign key constraint */
{
FOREIGN_KEY_INFO f_key_info;
FOREIGN_KEY_INFO* pf_key_info;
@ -14801,8 +15370,8 @@ get_foreign_key_info(
LEX_CSTRING* name = NULL;
if (dict_table_t::is_temporary_name(foreign->foreign_table_name)) {
return NULL;
}
return NULL;
}
ptr = dict_remove_db_name(foreign->id);
f_key_info.foreign_id = thd_make_lex_string(
@ -14893,7 +15462,7 @@ get_foreign_key_info(
<< foreign->referenced_table_name
<< " not found for foreign table "
<< foreign->foreign_table_name;
}
}
} else {
dict_table_close(ref_table, TRUE, FALSE);
@ -21201,6 +21770,8 @@ static void innodb_remember_check_sysvar_funcs()
check_sysvar_int = MYSQL_SYSVAR_NAME(flush_log_at_timeout).check;
}
static const size_t MAX_BUF_SIZE = 4 * 1024;
/********************************************************************//**
Helper function to push warnings from InnoDB internals to SQL-layer. */
UNIV_INTERN
@ -21215,7 +21786,6 @@ ib_push_warning(
THD *thd = (THD *)trx->mysql_thd;
va_list args;
char *buf;
#define MAX_BUF_SIZE 4*1024
va_start(args, format);
buf = (char *)my_malloc(MAX_BUF_SIZE, MYF(MY_WME));
@ -21242,7 +21812,6 @@ ib_push_warning(
va_list args;
THD *thd = (THD *)ithd;
char *buf;
#define MAX_BUF_SIZE 4*1024
if (ithd == NULL) {
thd = current_thd;
@ -21261,6 +21830,52 @@ ib_push_warning(
}
}
/** Helper function to push warnings from InnoDB internals to SQL-layer.
@param[in] trx
@param[in] error Error code to push as warning
@param[in] table_name Table name
@param[in] format Warning message
@param[in] ... Message arguments */
UNIV_INTERN
void
ib_foreign_warn(trx_t* trx, /*!< in: trx */
dberr_t error, /*!< in: error code to push as warning */
const char* table_name,
const char* format, /*!< in: warning message */
...)
{
va_list args;
char* buf;
static FILE* ef = dict_foreign_err_file;
static const size_t MAX_BUF_SIZE = 4 * 1024;
buf = (char*)my_malloc(MAX_BUF_SIZE, MYF(MY_WME));
if (!buf) {
return;
}
va_start(args, format);
vsprintf(buf, format, args);
va_end(args);
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
ut_print_timestamp(ef);
fprintf(ef, " Error in foreign key constraint of table %s:\n",
table_name);
fputs(buf, ef);
mutex_exit(&dict_foreign_err_mutex);
if (trx && trx->mysql_thd) {
THD* thd = (THD*)trx->mysql_thd;
push_warning_printf(
thd, Sql_condition::WARN_LEVEL_WARN,
uint(convert_error_code_to_mysql(error, 0, thd)), buf);
}
my_free(buf);
}
/********************************************************************//**
Helper function to push frm mismatch error to error log and
if needed to sql-layer. */

View file

@ -643,6 +643,9 @@ public:
/** Set m_tablespace_type. */
void set_tablespace_type(bool table_being_altered_is_file_per_table);
/** Create InnoDB foreign keys from MySQL alter_info. */
dberr_t create_foreign_keys();
/** Create the internal innodb table.
@param create_fk whether to add FOREIGN KEY constraints */
int create_table(bool create_fk = true);

View file

@ -2863,6 +2863,7 @@ innobase_get_foreign_key_info(
char* referenced_table_name = NULL;
ulint num_fk = 0;
Alter_info* alter_info = ha_alter_info->alter_info;
const CHARSET_INFO* cs = innobase_get_charset(trx->mysql_thd);
DBUG_ENTER("innobase_get_foreign_key_info");
@ -2880,12 +2881,6 @@ innobase_get_foreign_key_info(
ulint num_col = 0;
ulint referenced_num_col = 0;
bool correct_option;
char* db_namep = NULL;
char* tbl_namep = NULL;
ulint db_name_len = 0;
ulint tbl_name_len = 0;
char db_name[MAX_DATABASE_NAME_LEN];
char tbl_name[MAX_TABLE_NAME_LEN];
Foreign_key* fk_key = static_cast<Foreign_key*>(&key);
@ -2933,45 +2928,14 @@ innobase_get_foreign_key_info(
add_fk[num_fk] = dict_mem_foreign_create();
#ifndef _WIN32
if (fk_key->ref_db.str) {
tablename_to_filename(fk_key->ref_db.str, db_name,
MAX_DATABASE_NAME_LEN);
db_namep = db_name;
db_name_len = strlen(db_name);
}
if (fk_key->ref_table.str) {
tablename_to_filename(fk_key->ref_table.str, tbl_name,
MAX_TABLE_NAME_LEN);
tbl_namep = tbl_name;
tbl_name_len = strlen(tbl_name);
}
#else
ut_ad(fk_key->ref_table.str);
tablename_to_filename(fk_key->ref_table.str, tbl_name,
MAX_TABLE_NAME_LEN);
innobase_casedn_str(tbl_name);
tbl_name_len = strlen(tbl_name);
tbl_namep = &tbl_name[0];
if (fk_key->ref_db.str != NULL) {
tablename_to_filename(fk_key->ref_db.str, db_name,
MAX_DATABASE_NAME_LEN);
innobase_casedn_str(db_name);
db_name_len = strlen(db_name);
db_namep = &db_name[0];
}
#endif
mutex_enter(&dict_sys.mutex);
referenced_table_name = dict_get_referenced_table(
table->name.m_name,
db_namep,
db_name_len,
tbl_namep,
tbl_name_len,
LEX_STRING_WITH_LEN(fk_key->ref_db),
LEX_STRING_WITH_LEN(fk_key->ref_table),
&referenced_table,
add_fk[num_fk]->heap);
add_fk[num_fk]->heap, cs);
/* Test the case when referenced_table failed to
open, if trx->check_foreigns is not set, we should
@ -2982,7 +2946,7 @@ innobase_get_foreign_key_info(
if (!referenced_table && trx->check_foreigns) {
mutex_exit(&dict_sys.mutex);
my_error(ER_FK_CANNOT_OPEN_PARENT,
MYF(0), tbl_namep);
MYF(0), fk_key->ref_table.str);
goto err_exit;
}
@ -3017,7 +2981,7 @@ innobase_get_foreign_key_info(
my_error(ER_FK_NO_INDEX_PARENT, MYF(0),
fk_key->name.str
? fk_key->name.str : "",
tbl_namep);
fk_key->ref_table.str);
goto err_exit;
}
} else {
@ -3029,7 +2993,8 @@ innobase_get_foreign_key_info(
/* Not possible to add a foreign key without a
referenced column */
mutex_exit(&dict_sys.mutex);
my_error(ER_CANNOT_ADD_FOREIGN, MYF(0), tbl_namep);
my_error(ER_CANNOT_ADD_FOREIGN, MYF(0),
fk_key->ref_table.str);
goto err_exit;
}

View file

@ -62,7 +62,8 @@ dict_get_referenced_table(
const char* table_name, /*!< in: table name */
ulint table_name_len, /*!< in: table name length */
dict_table_t** table, /*!< out: table object or NULL */
mem_heap_t* heap); /*!< in: heap memory */
mem_heap_t* heap, /*!< in: heap memory */
CHARSET_INFO* from_cs); /*!< in: table name charset */
/*********************************************************************//**
Frees a foreign key struct. */
void
@ -79,6 +80,21 @@ dict_table_get_highest_foreign_id(
/*==============================*/
dict_table_t* table); /*!< in: table in the dictionary
memory cache */
/** Check whether the dict_table_t is a partition.
A partitioned table on the SQL level is composed of InnoDB tables,
where each InnoDB table is a [sub]partition including its secondary indexes
which belongs to the partition.
@param[in] table Table to check.
@return true if the dict_table_t is a partition else false. */
UNIV_INLINE
bool
dict_table_is_partition(const dict_table_t* table)
{
/* Check both P and p on all platforms in case it was moved to/from
WIN. */
return (strstr(table->name.m_name, "#p#")
|| strstr(table->name.m_name, "#P#"));
}
/********************************************************************//**
Return the end of table name where we have removed dbname and '/'.
@return table name */
@ -421,34 +437,6 @@ dict_foreign_replace_index(
to use table->col_names */
const dict_index_t* index) /*!< in: index to be replaced */
MY_ATTRIBUTE((nonnull(1,3), warn_unused_result));
/** 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 the indexes for a table have been created.
Each foreign key constraint must be accompanied with indexes in
bot participating tables. The indexes are allowed to contain more
fields than mentioned in the constraint.
@param[in] trx transaction
@param[in] sql_string table create statement where
foreign keys are declared like:
FOREIGN KEY (a, b) REFERENCES table2(c, d),
table2 can be written also with the database
name before it: test.table2; the default
database id the database of parameter name
@param[in] sql_length length of sql_string
@param[in] name table full name in normalized form
@param[in] reject_fks if TRUE, fail with error code
DB_CANNOT_ADD_CONSTRAINT if any
foreign keys are found.
@return error code or DB_SUCCESS */
dberr_t
dict_create_foreign_constraints(
trx_t* trx,
const char* sql_string,
size_t sql_length,
const char* name,
ibool reject_fks)
MY_ATTRIBUTE((warn_unused_result));
/**********************************************************************//**
Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement.
@return DB_SUCCESS or DB_CANNOT_DROP_CONSTRAINT if syntax error or the

View file

@ -518,6 +518,17 @@ ib_push_warning(
const char *format,/*!< in: warning message */
...);
/********************************************************************//**
Helper function to push warnings from InnoDB internals to SQL-layer. */
UNIV_INTERN
void
ib_foreign_warn(
trx_t* trx, /*!< in: trx */
dberr_t error, /*!< in: error code to push as warning */
const char *table_name,
const char *format,/*!< in: warning message */
...);
/*****************************************************************//**
Normalizes a table name string. A normalized name consists of the
database name catenated to '/' and table name. An example: