mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
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:
parent
20b474be5b
commit
5130f5206c
19 changed files with 826 additions and 1402 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue