summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Midenkov <midenok@gmail.com>2019-11-20 13:18:31 +0300
committerAleksey Midenkov <midenok@gmail.com>2019-11-20 13:18:31 +0300
commit5130f5206c150ba1e8a723aae63884ff64408012 (patch)
tree43304b46565bdc6dfbdef24b5f2afcab10e66a37
parent20b474be5b75929727c693f91448f12257d1b6d4 (diff)
downloadmariadb-git-5130f5206c150ba1e8a723aae63884ff64408012.tar.gz
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.
-rw-r--r--mysql-test/suite/innodb/r/foreign_key.result2
-rw-r--r--mysql-test/suite/innodb/r/innodb-fk-warnings.result25
-rw-r--r--mysql-test/suite/innodb/r/innodb-fk.result5
-rw-r--r--mysql-test/suite/innodb/t/innodb-fk-warnings.test26
-rw-r--r--mysql-test/suite/innodb_gis/r/point_basic.result6
-rw-r--r--sql/handler.h4
-rw-r--r--sql/sql_alter.cc1
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h3
-rw-r--r--sql/sql_parse.cc1
-rw-r--r--sql/sql_table.cc9
-rw-r--r--sql/sql_yacc.yy1
-rw-r--r--sql/sql_yacc_ora.yy1
-rw-r--r--storage/innobase/dict/dict0dict.cc1293
-rw-r--r--storage/innobase/handler/ha_innodb.cc737
-rw-r--r--storage/innobase/handler/ha_innodb.h3
-rw-r--r--storage/innobase/handler/handler0alter.cc51
-rw-r--r--storage/innobase/include/dict0dict.h46
-rw-r--r--storage/innobase/include/ha_prototypes.h11
19 files changed, 825 insertions, 1401 deletions
diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result
index 288b6bb835d..869a66a0e1b 100644
--- a/mysql-test/suite/innodb/r/foreign_key.result
+++ b/mysql-test/suite/innodb/r/foreign_key.result
@@ -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;
diff --git a/mysql-test/suite/innodb/r/innodb-fk-warnings.result b/mysql-test/suite/innodb/r/innodb-fk-warnings.result
index 21e7c23d249..99b16dc1e9c 100644
--- a/mysql-test/suite/innodb/r/innodb-fk-warnings.result
+++ b/mysql-test/suite/innodb/r/innodb-fk-warnings.result
@@ -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;
diff --git a/mysql-test/suite/innodb/r/innodb-fk.result b/mysql-test/suite/innodb/r/innodb-fk.result
index a7b667b3839..aa4e8c15bbe 100644
--- a/mysql-test/suite/innodb/r/innodb-fk.result
+++ b/mysql-test/suite/innodb/r/innodb-fk.result
@@ -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;
diff --git a/mysql-test/suite/innodb/t/innodb-fk-warnings.test b/mysql-test/suite/innodb/t/innodb-fk-warnings.test
index 55284179692..8a0ed5815b8 100644
--- a/mysql-test/suite/innodb/t/innodb-fk-warnings.test
+++ b/mysql-test/suite/innodb/t/innodb-fk-warnings.test
@@ -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;
diff --git a/mysql-test/suite/innodb_gis/r/point_basic.result b/mysql-test/suite/innodb_gis/r/point_basic.result
index f24ddfeb761..eb22fa92741 100644
--- a/mysql-test/suite/innodb_gis/r/point_basic.result
+++ b/mysql-test/suite/innodb_gis/r/point_basic.result
@@ -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;
diff --git a/sql/handler.h b/sql/handler.h
index 65dd31933cc..6c9b8f4edcb 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -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)
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 75d792523c7..bc4c903d05d 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -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;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 79fd8c173bc..d75cac56bff 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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),
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 39b7f4dffa3..8fc33523896 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index beda00592e1..b787f99e94a 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1520cc7c705..b484d9502d5 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -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;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 59c22eff31c..448e2b4c152 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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,
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 8d55ce76362..f066e33e6e0 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -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,
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 862698d75cc..1f434a882d5 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -2531,23 +2531,6 @@ dict_index_build_internal_fts(
}
/*====================== FOREIGN KEY PROCESSING ========================*/
-/** 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#"));
-}
-
/*********************************************************************//**
Checks if a table is referenced by foreign keys.
@return TRUE if table is referenced by a foreign key */
@@ -3084,106 +3067,70 @@ convert_id:
}
/*********************************************************************//**
-Tries to scan a column name.
-@return scanned to */
-static
-const char*
-dict_scan_col(
-/*==========*/
- CHARSET_INFO* cs, /*!< in: the character set of ptr */
- const char* ptr, /*!< in: scanned to */
- ibool* success,/*!< out: TRUE if success */
- dict_table_t* table, /*!< in: table in which the column is */
- const dict_col_t** column, /*!< out: pointer to column if success */
- mem_heap_t* heap, /*!< in: heap where to allocate */
- const char** name) /*!< out,own: the column name;
- NULL if no name was scannable */
-{
- ulint i;
-
- *success = FALSE;
-
- ptr = dict_scan_id(cs, ptr, heap, name, FALSE, TRUE);
-
- if (*name == NULL) {
-
- return(ptr); /* Syntax error */
- }
-
- if (table == NULL) {
- *success = TRUE;
- *column = NULL;
- } else {
- 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 */
-
- *success = TRUE;
- *column = dict_table_get_nth_col(table, i);
- strcpy((char*) *name, col_name);
-
- break;
- }
- }
-
- 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 */
- dict_v_col_t * vcol;
- *success = TRUE;
- vcol = dict_table_get_nth_v_col(table, i);
- *column = &vcol->m_col;
- strcpy((char*) *name, col_name);
-
- break;
- }
- }
- }
-
- return(ptr);
-}
-
-/*********************************************************************//**
Open a table from its database and table name, this is currently used by
foreign constraint parser to get the referenced table.
@return complete table name with database and table name, allocated from
heap memory passed in */
char*
dict_get_referenced_table(
-/*======================*/
- const char* name, /*!< in: foreign key table name */
- const char* database_name, /*!< in: table db name */
- ulint database_name_len, /*!< in: db name length */
- 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/out: heap memory */
+ const char* name, /*!< in: foreign key table name */
+ const char* database_name, /*!< in: table db name */
+ ulint database_name_len, /*!< in: db name length */
+ 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/out: heap memory */
+ CHARSET_INFO* from_cs) /*!< in: table name charset */
{
char* ref;
- const char* db_name;
+ char db_name[MAX_DATABASE_NAME_LEN];
+ char tbl_name[MAX_TABLE_NAME_LEN];
+ CHARSET_INFO* to_cs = &my_charset_filename;
+ uint errors;
+ ut_ad(database_name || name);
+ ut_ad(table_name);
+
+ if (!strncmp(table_name, srv_mysql50_table_name_prefix,
+ sizeof(srv_mysql50_table_name_prefix) - 1)) {
+ /* This is a pre-5.1 table name
+ containing chars other than [A-Za-z0-9].
+ Discard the prefix and use raw UTF-8 encoding. */
+ table_name += sizeof(srv_mysql50_table_name_prefix) - 1;
+ table_name_len -= sizeof(srv_mysql50_table_name_prefix) - 1;
+
+ to_cs = system_charset_info;
+ }
+
+ table_name_len = strconvert(from_cs, table_name, table_name_len, to_cs,
+ tbl_name, MAX_TABLE_NAME_LEN, &errors);
+ table_name = tbl_name;
+
+ if (database_name) {
+ to_cs = &my_charset_filename;
+ if (!strncmp(database_name, srv_mysql50_table_name_prefix,
+ sizeof(srv_mysql50_table_name_prefix) - 1)) {
+ database_name
+ += sizeof(srv_mysql50_table_name_prefix) - 1;
+ database_name_len
+ -= sizeof(srv_mysql50_table_name_prefix) - 1;
+ to_cs = system_charset_info;
+ }
- if (!database_name) {
+ database_name_len = strconvert(
+ from_cs, database_name, database_name_len, to_cs,
+ db_name, MAX_DATABASE_NAME_LEN, &errors);
+ database_name = db_name;
+ } else {
/* Use the database name of the foreign key table */
- db_name = name;
+ database_name = name;
database_name_len = dict_get_db_name_len(name);
- } else {
- db_name = database_name;
}
/* Copy database_name, '/', table_name, '\0' */
- ref = static_cast<char*>(
- mem_heap_alloc(heap, database_name_len + table_name_len + 2));
-
- memcpy(ref, db_name, database_name_len);
+ ref = static_cast<char*>(mem_heap_alloc(
+ heap, database_name_len + table_name_len + 2));
+ memcpy(ref, database_name, database_name_len);
ref[database_name_len] = '/';
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
@@ -3193,7 +3140,7 @@ dict_get_referenced_table(
if (innobase_get_lower_case_table_names() == 2) {
innobase_casedn_str(ref);
*table = dict_table_get_low(ref);
- memcpy(ref, db_name, database_name_len);
+ memcpy(ref, database_name, database_name_len);
ref[database_name_len] = '/';
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1);
@@ -3210,105 +3157,6 @@ dict_get_referenced_table(
return(ref);
}
-/*********************************************************************//**
-Scans a table name from an SQL string.
-@return scanned to */
-static
-const char*
-dict_scan_table_name(
-/*=================*/
- CHARSET_INFO* cs, /*!< in: the character set of ptr */
- const char* ptr, /*!< in: scanned to */
- dict_table_t** table, /*!< out: table object or NULL */
- const char* name, /*!< in: foreign key table name */
- ibool* success,/*!< out: TRUE if ok name found */
- mem_heap_t* heap, /*!< in: heap where to allocate the id */
- const char** ref_name)/*!< out,own: the table name;
- NULL if no name was scannable */
-{
- const char* database_name = NULL;
- ulint database_name_len = 0;
- const char* table_name = NULL;
- const char* scan_name;
-
- *success = FALSE;
- *table = NULL;
-
- ptr = dict_scan_id(cs, ptr, heap, &scan_name, TRUE, FALSE);
-
- if (scan_name == NULL) {
-
- return(ptr); /* Syntax error */
- }
-
- if (*ptr == '.') {
- /* We scanned the database name; scan also the table name */
-
- ptr++;
-
- database_name = scan_name;
- database_name_len = strlen(database_name);
-
- ptr = dict_scan_id(cs, ptr, heap, &table_name, TRUE, FALSE);
-
- if (table_name == NULL) {
-
- return(ptr); /* Syntax error */
- }
- } else {
- /* To be able to read table dumps made with InnoDB-4.0.17 or
- earlier, we must allow the dot separator between the database
- name and the table name also to appear within a quoted
- identifier! InnoDB used to print a constraint as:
- ... REFERENCES `databasename.tablename` ...
- starting from 4.0.18 it is
- ... REFERENCES `databasename`.`tablename` ... */
- const char* s;
-
- for (s = scan_name; *s; s++) {
- if (*s == '.') {
- database_name = scan_name;
- database_name_len = ulint(s - scan_name);
- scan_name = ++s;
- break;/* to do: multiple dots? */
- }
- }
-
- table_name = scan_name;
- }
-
- *ref_name = dict_get_referenced_table(
- name, database_name, database_name_len,
- table_name, strlen(table_name), table, heap);
-
- *success = TRUE;
- return(ptr);
-}
-
-/*********************************************************************//**
-Skips one id. The id is allowed to contain also '.'.
-@return scanned to */
-static
-const char*
-dict_skip_word(
-/*===========*/
- CHARSET_INFO* cs, /*!< in: the character set of ptr */
- const char* ptr, /*!< in: scanned to */
- ibool* success)/*!< out: TRUE if success, FALSE if just spaces
- left in string or a syntax error */
-{
- const char* start;
-
- *success = FALSE;
-
- ptr = dict_scan_id(cs, ptr, NULL, &start, FALSE, TRUE);
-
- if (start) {
- *success = TRUE;
- }
-
- return(ptr);
-}
/*********************************************************************//**
Removes MySQL comments from an SQL string. A comment is either
@@ -3484,1047 +3332,6 @@ dict_table_get_highest_foreign_id(
DBUG_RETURN(biggest_id);
}
-/*********************************************************************//**
-Reports a simple foreign key create clause syntax error. */
-static
-void
-dict_foreign_report_syntax_err(
-/*===========================*/
- const char* fmt, /*!< in: syntax err msg */
- const char* oper, /*!< in: operation */
- const char* name, /*!< in: table name */
- const char* start_of_latest_foreign,
- /*!< in: start of the foreign key clause
- in the SQL string */
- const char* ptr) /*!< in: place of the syntax error */
-{
- ut_ad(!srv_read_only_mode);
-
- FILE* ef = dict_foreign_err_file;
-
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, name);
- fprintf(ef, fmt, oper, name, start_of_latest_foreign, ptr);
- mutex_exit(&dict_foreign_err_mutex);
-}
-
-/*********************************************************************//**
-Push warning message to SQL-layer based on foreign key constraint
-index match error. */
-static
-void
-dict_foreign_push_index_error(
-/*==========================*/
- trx_t* trx, /*!< in: trx */
- const char* operation, /*!< in: operation create or alter
- */
- const char* create_name, /*!< in: table name in create or
- alter table */
- const char* latest_foreign, /*!< in: start of latest foreign key
- constraint name */
- const char** columns, /*!< in: foreign key columns */
- fkerr_t index_error, /*!< in: error code */
- ulint err_col, /*!< in: column where error happened
- */
- dict_index_t* err_index, /*!< in: index where error happened
- */
- dict_table_t* table, /*!< in: table */
- FILE* ef) /*!< in: output stream */
-{
- switch (index_error) {
- case FK_SUCCESS:
- break;
- case FK_INDEX_NOT_FOUND:
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. There is no index in the referenced"
- " table where the referenced columns appear"
- " as the first columns near '%s'.\n",
- operation, create_name, latest_foreign);
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. There is no index in the referenced"
- " table where the referenced columns appear"
- " as the first columns near '%s'.",
- operation, create_name, latest_foreign);
- return;
- case FK_IS_PREFIX_INDEX:
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. There is only prefix index in the referenced"
- " table where the referenced columns appear"
- " as the first columns near '%s'.\n",
- operation, create_name, latest_foreign);
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. There is only prefix index in the referenced"
- " table where the referenced columns appear"
- " as the first columns near '%s'.",
- operation, create_name, latest_foreign);
- return;
- case FK_COL_NOT_NULL:
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. You have defined a SET NULL condition but "
- "column '%s' on index is defined as NOT NULL near '%s'.\n",
- operation, create_name, columns[err_col], latest_foreign);
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. You have defined a SET NULL condition but "
- "column '%s' on index is defined as NOT NULL near '%s'.",
- operation, create_name, columns[err_col], latest_foreign);
- 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));
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. Field type or character set for column '%s' "
- "does not mach referenced column '%s' near '%s'.\n",
- operation, create_name, columns[err_col], col_name, latest_foreign);
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Field type or character set for column '%s' "
- "does not mach referenced column '%s' near '%s'.",
- operation, create_name, columns[err_col], col_name, latest_foreign);
- return;
- }
- DBUG_ASSERT(!"unknown error");
-}
-
-/*********************************************************************//**
-Scans a table create SQL string and adds to the data dictionary the foreign key
-constraints declared in the string. This function should be called after 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.
-@return error code or DB_SUCCESS */
-static
-dberr_t
-dict_create_foreign_constraints_low(
- trx_t* trx,
- mem_heap_t* heap,
- CHARSET_INFO* cs,
- const char* sql_string,
- const char* name,
- ibool reject_fks)
-{
- dict_table_t* table = NULL;
- dict_table_t* referenced_table = NULL;
- dict_table_t* table_to_alter = NULL;
- dict_table_t* table_to_create = NULL;
- ulint highest_id_so_far = 0;
- ulint number = 1;
- dict_index_t* index = NULL;
- dict_foreign_t* foreign = NULL;
- const char* ptr = sql_string;
- const char* start_of_latest_foreign = sql_string;
- const char* start_of_latest_set = NULL;
- FILE* ef = dict_foreign_err_file;
- fkerr_t index_error = FK_SUCCESS;
- dict_index_t* err_index = NULL;
- ulint err_col;
- const char* constraint_name;
- ibool success;
- dberr_t error;
- const char* ptr1;
- const char* ptr2;
- ulint i;
- ulint j;
- ibool is_on_delete;
- ulint n_on_deletes;
- ulint n_on_updates;
- const dict_col_t*columns[500];
- const char* column_names[500];
- const char* ref_column_names[500];
- const char* referenced_table_name;
- dict_foreign_set local_fk_set;
- dict_foreign_set_free local_fk_set_free(local_fk_set);
- const char* create_table_name;
- const char* orig;
- char create_name[MAX_TABLE_NAME_LEN + 1];
-
- ut_ad(!srv_read_only_mode);
- ut_ad(mutex_own(&dict_sys.mutex));
-
- table = dict_table_get_low(name);
- /* First check if we are actually doing an ALTER TABLE, and in that
- case look for the table being altered */
- orig = ptr;
- ptr = dict_accept(cs, ptr, "ALTER", &success);
-
- const char* const operation = success ? "Alter " : "Create ";
-
- if (!success) {
- orig = ptr;
- ptr = dict_scan_to(ptr, "CREATE");
- ptr = dict_scan_to(ptr, "TABLE");
- ptr = dict_accept(cs, ptr, "TABLE", &success);
- create_table_name = NULL;
-
- if (success) {
- ptr = dict_scan_table_name(cs, ptr, &table_to_create, name,
- &success, heap, &create_table_name);
- }
-
- ptr = orig;
- const char* n = create_table_name ? create_table_name : name;
- char *bufend = innobase_convert_name(create_name, MAX_TABLE_NAME_LEN,
- n, strlen(n), trx->mysql_thd);
- create_name[bufend-create_name] = '\0';
- } else {
- strncpy(create_name, name, sizeof create_name);
- create_name[(sizeof create_name) - 1] = '\0';
- }
-
- if (table == NULL) {
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef, "%s table %s with foreign key constraint"
- " failed. Table %s not found from data dictionary."
- " Error close to %s.\n",
- operation, create_name, create_name, start_of_latest_foreign);
- mutex_exit(&dict_foreign_err_mutex);
- ib_push_warning(trx, DB_ERROR,
- "%s table %s with foreign key constraint"
- " failed. Table %s not found from data dictionary."
- " Error close to %s.",
- operation, create_name, create_name, start_of_latest_foreign);
-
- return(DB_ERROR);
- }
-
- /* If not alter table jump to loop */
- if (!success) {
-
- goto loop;
- }
-
- orig = ptr;
- for (;;) {
- ptr = dict_accept(cs, ptr, "TABLE", &success);
- if (success) {
- break;
- }
- ptr = dict_accept(cs, ptr, "ONLINE", &success);
- if (success) {
- continue;
- }
- ptr = dict_accept(cs, ptr, "IGNORE", &success);
- if (!success) {
- goto loop;
- }
- }
-
- /* We are doing an ALTER TABLE: scan the table name we are altering */
-
- orig = ptr;
- ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name,
- &success, heap, &referenced_table_name);
-
- {
- const char* n = table_to_alter
- ? table_to_alter->name.m_name : referenced_table_name;
- char* bufend = innobase_convert_name(
- create_name, MAX_TABLE_NAME_LEN, n, strlen(n),
- trx->mysql_thd);
- create_name[bufend-create_name]='\0';
- }
-
- if (!success) {
- ib::error() << "Could not find the table " << create_name << " being" << operation << " near to "
- << orig;
-
- ib_push_warning(trx, DB_ERROR,
- "%s table %s with foreign key constraint"
- " failed. Table %s not found from data dictionary."
- " Error close to %s.",
- operation, create_name, create_name, orig);
-
- return(DB_ERROR);
- }
-
- /* 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 == NULL) {
- highest_id_so_far = 0;
- } else {
- highest_id_so_far = dict_table_get_highest_foreign_id(
- table_to_alter);
- }
-
- number = highest_id_so_far + 1;
- /* Scan for foreign key declarations in a loop */
-loop:
- /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */
-
- ptr1 = dict_scan_to(ptr, "CONSTRAINT");
- ptr2 = dict_scan_to(ptr, "FOREIGN");
-
- constraint_name = NULL;
-
- if (ptr1 < ptr2) {
- /* The user may have specified a constraint name. Pick it so
- that we can store 'databasename/constraintname' as the id of
- of the constraint to system tables. */
- ptr = ptr1;
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "CONSTRAINT", &success);
-
- ut_a(success);
-
- if (!my_isspace(cs, *ptr) && *ptr != '"' && *ptr != '`') {
- goto loop;
- }
-
- while (my_isspace(cs, *ptr)) {
- ptr++;
- }
-
- /* read constraint name unless got "CONSTRAINT FOREIGN" */
- if (ptr != ptr2) {
- ptr = dict_scan_id(cs, ptr, heap,
- &constraint_name, FALSE, FALSE);
- }
- } else {
- ptr = ptr2;
- }
-
- if (*ptr == '\0') {
- /* The proper way to reject foreign keys for temporary
- tables would be to split the lexing and syntactical
- analysis of foreign key clauses from the actual adding
- of them, so that ha_innodb.cc could first parse the SQL
- command, determine if there are any foreign keys, and
- if so, immediately reject the command if the table is a
- temporary one. For now, this kludge will work. */
- if (reject_fks && !local_fk_set.empty()) {
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef, "%s table %s with foreign key constraint"
- " failed. Temporary tables can't have foreign key constraints."
- " Error close to %s.\n",
- operation, create_name, start_of_latest_foreign);
- mutex_exit(&dict_foreign_err_mutex);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Temporary tables can't have foreign key constraints."
- " Error close to %s.",
- operation, create_name, start_of_latest_foreign);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- 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 */
- trx->op_info = "adding foreign keys";
-
- trx_start_if_not_started_xa(trx, true);
-
- trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
-
- error = dict_create_add_foreigns_to_dictionary(
- local_fk_set, table, 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);
- }
-
- start_of_latest_foreign = ptr;
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "FOREIGN", &success);
-
- if (!success) {
- goto loop;
- }
-
- if (!my_isspace(cs, *ptr)) {
- goto loop;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "KEY", &success);
-
- if (!success) {
- goto loop;
- }
-
- if (my_isspace(cs, *ptr)) {
- ptr1 = dict_accept(cs, ptr, "IF", &success);
-
- if (success) {
- if (!my_isspace(cs, *ptr1)) {
- goto loop;
- }
- ptr1 = dict_accept(cs, ptr1, "NOT", &success);
- if (!success) {
- goto loop;
- }
- ptr1 = dict_accept(cs, ptr1, "EXISTS", &success);
- if (!success) {
- goto loop;
- }
- ptr = ptr1;
- }
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "(", &success);
-
- if (!success) {
- if (constraint_name) {
- /* MySQL allows also an index id before the '('; we
- skip it */
- ptr = dict_skip_word(cs, ptr, &success);
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
- } else {
- while (my_isspace(cs, *ptr)) {
- ptr++;
- }
-
- ptr = dict_scan_id(cs, ptr, heap,
- &constraint_name, FALSE, FALSE);
- }
-
- ptr = dict_accept(cs, ptr, "(", &success);
-
- if (!success) {
- /* We do not flag a syntax error here because in an
- ALTER TABLE we may also have DROP FOREIGN KEY abc */
-
- goto loop;
- }
- }
-
- i = 0;
-
- /* Scan the columns in the first list */
-col_loop1:
- ut_a(i < (sizeof column_names) / sizeof *column_names);
- orig = ptr;
- ptr = dict_scan_col(cs, ptr, &success, table, columns + i,
- heap, column_names + i);
- if (!success) {
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- mutex_exit(&dict_foreign_err_mutex);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- i++;
-
- ptr = dict_accept(cs, ptr, ",", &success);
-
- if (success) {
- goto col_loop1;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, ")", &success);
-
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* Try to find an index which contains the columns
- as the first fields and in the right order. There is
- no need to check column type match (on types_idx), since
- the referenced table can be NULL if foreign_key_checks is
- set to 0 */
-
- index = dict_foreign_find_index(
- table, NULL, column_names, i,
- NULL, TRUE, FALSE, &index_error, &err_col, &err_index);
-
- if (!index) {
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fputs("There is no index in table ", ef);
- ut_print_name(ef, NULL, create_name);
- fprintf(ef, " where the columns appear\n"
- "as the first columns. Constraint:\n%s\n%s",
- start_of_latest_foreign,
- FOREIGN_KEY_CONSTRAINTS_MSG);
- dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
- column_names, index_error, err_col, err_index, table, ef);
-
- mutex_exit(&dict_foreign_err_mutex);
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "REFERENCES", &success);
-
- if (!success || !my_isspace(cs, *ptr)) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* Don't allow foreign keys on partitioned tables yet. */
- ptr1 = dict_scan_to(ptr, "PARTITION");
- if (ptr1) {
- ptr1 = dict_accept(cs, ptr1, "PARTITION", &success);
- if (success && my_isspace(cs, *ptr1)) {
- ptr2 = dict_accept(cs, ptr1, "BY", &success);
- if (success) {
- my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0));
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
- }
- }
- if (dict_table_is_partition(table)) {
- my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0));
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* Let us create a constraint struct */
-
- foreign = dict_mem_foreign_create();
-
- if (constraint_name) {
- 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 + strlen(constraint_name) + 2));
-
- memcpy(foreign->id, table->name.m_name, db_len);
- foreign->id[db_len] = '/';
- strcpy(foreign->id + db_len + 1, constraint_name);
- }
-
- 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);
- 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*)));
-
- for (i = 0; i < foreign->n_fields; i++) {
- foreign->foreign_col_names[i] = mem_heap_strdup(
- foreign->heap, column_names[i]);
- }
-
- ptr = dict_scan_table_name(cs, ptr, &referenced_table, name,
- &success, heap, &referenced_table_name);
-
- /* Note that referenced_table can be NULL if the user has suppressed
- checking of foreign key constraints! */
-
- if (!success || (!referenced_table && trx->check_foreigns)) {
- char buf[MAX_TABLE_NAME_LEN + 1] = "";
- char* bufend;
-
- bufend = innobase_convert_name(buf, MAX_TABLE_NAME_LEN,
- referenced_table_name, strlen(referenced_table_name),
- trx->mysql_thd);
- buf[bufend - buf] = '\0';
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
- "near '%s'.",
- operation, create_name, buf, start_of_latest_foreign);
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef,
- "%s table %s with foreign key constraint failed. Referenced table %s not found in the data dictionary "
- "near '%s'.\n",
- operation, create_name, buf, start_of_latest_foreign);
-
- mutex_exit(&dict_foreign_err_mutex);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* Don't allow foreign keys on partitioned tables yet. */
- if (referenced_table && dict_table_is_partition(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);
- }
-
- ptr = dict_accept(cs, ptr, "(", &success);
-
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* Scan the columns in the second list */
- i = 0;
-
-col_loop2:
- orig = ptr;
- ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i,
- heap, ref_column_names + i);
- i++;
-
- if (!success) {
-
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, orig);
- mutex_exit(&dict_foreign_err_mutex);
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, orig);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, ",", &success);
-
- if (success) {
- goto col_loop2;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, ")", &success);
-
- if (!success || foreign->n_fields != i) {
-
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s' near '%s'. Referencing column count does not match referenced column count.\n",
- operation, create_name, start_of_latest_foreign, orig);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s' near '%s'. Referencing column count %d does not match referenced column count %d.\n",
- operation, create_name, start_of_latest_foreign, orig, i, foreign->n_fields);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- n_on_deletes = 0;
- n_on_updates = 0;
-
-scan_on_conditions:
- /* Loop here as long as we can find ON ... conditions */
-
- start_of_latest_set = ptr;
- ptr = dict_accept(cs, ptr, "ON", &success);
-
- if (!success) {
-
- goto try_find_index;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "DELETE", &success);
-
- if (!success) {
- orig = ptr;
- ptr = dict_accept(cs, ptr, "UPDATE", &success);
-
- if (!success) {
-
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- is_on_delete = FALSE;
- n_on_updates++;
- } else {
- is_on_delete = TRUE;
- n_on_deletes++;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "RESTRICT", &success);
-
- if (success) {
- goto scan_on_conditions;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "CASCADE", &success);
-
- if (success) {
- if (is_on_delete) {
- foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE;
- } else {
- foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
- }
-
- goto scan_on_conditions;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "NO", &success);
-
- if (success) {
- orig = ptr;
- ptr = dict_accept(cs, ptr, "ACTION", &success);
-
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- if (is_on_delete) {
- foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION;
- } else {
- foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
- }
-
- goto scan_on_conditions;
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "SET", &success);
-
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- orig = ptr;
- ptr = dict_accept(cs, ptr, "NULL", &success);
-
- if (!success) {
- dict_foreign_report_syntax_err(
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.\n",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. Parse error in '%s'"
- " near '%s'.",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- for (j = 0; j < foreign->n_fields; j++) {
- if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype)
- & DATA_NOT_NULL) {
- const dict_col_t* col
- = dict_index_get_nth_col(foreign->foreign_index, j);
- const char* col_name = dict_table_get_col_name(foreign->foreign_index->table,
- dict_col_get_no(col));
-
- /* It is not sensible to define SET NULL
- if the column is not allowed to be NULL! */
-
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. You have defined a SET NULL condition but column '%s' is defined as NOT NULL"
- " in '%s' near '%s'.\n",
- operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
- mutex_exit(&dict_foreign_err_mutex);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. You have defined a SET NULL condition but column '%s' is defined as NOT NULL"
- " in '%s' near '%s'.",
- operation, create_name, col_name, start_of_latest_foreign, start_of_latest_set);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
- }
-
- if (is_on_delete) {
- foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL;
- } else {
- foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
- }
-
- goto scan_on_conditions;
-
-try_find_index:
- if (n_on_deletes > 1 || n_on_updates > 1) {
- /* It is an error to define more than 1 action */
-
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef,
- "%s table %s with foreign key constraint"
- " failed. You have more than one on delete or on update clause"
- " in '%s' near '%s'.\n",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
- mutex_exit(&dict_foreign_err_mutex);
-
- ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT,
- "%s table %s with foreign key constraint"
- " failed. You have more than one on delete or on update clause"
- " in '%s' near '%s'.",
- operation, create_name, start_of_latest_foreign, start_of_latest_set);
-
- dict_foreign_free(foreign);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
-
- /* 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 (referenced_table) {
- index = dict_foreign_find_index(referenced_table, NULL,
- ref_column_names, i,
- foreign->foreign_index,
- TRUE, FALSE, &index_error, &err_col, &err_index);
-
- if (!index) {
- mutex_enter(&dict_foreign_err_mutex);
- dict_foreign_error_report_low(ef, create_name);
- fprintf(ef, "%s:\n"
- "Cannot find an index in the"
- " referenced table where the\n"
- "referenced columns appear as the"
- " first columns, or column types\n"
- "in the table and the referenced table"
- " do not match for constraint.\n"
- "Note that the internal storage type of"
- " ENUM and SET changed in\n"
- "tables created with >= InnoDB-4.1.12,"
- " and such columns in old tables\n"
- "cannot be referenced by such columns"
- " in new tables.\n%s\n",
- start_of_latest_foreign,
- FOREIGN_KEY_CONSTRAINTS_MSG);
-
- dict_foreign_push_index_error(trx, operation, create_name, start_of_latest_foreign,
- column_names, index_error, err_col, err_index, referenced_table, ef);
-
- mutex_exit(&dict_foreign_err_mutex);
-
- return(DB_CANNOT_ADD_CONSTRAINT);
- }
- } else {
- ut_a(trx->check_foreigns == FALSE);
- index = NULL;
- }
-
- foreign->referenced_index = index;
- foreign->referenced_table = referenced_table;
-
- foreign->referenced_table_name = mem_heap_strdup(
- foreign->heap, referenced_table_name);
- 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*)));
-
- for (i = 0; i < foreign->n_fields; i++) {
- foreign->referenced_col_names[i]
- = mem_heap_strdup(foreign->heap, ref_column_names[i]);
- }
-
- goto loop;
-}
-
-/** 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)
-{
- char* str;
- dberr_t err;
- mem_heap_t* heap;
-
- ut_a(trx);
- ut_a(trx->mysql_thd);
-
- str = dict_strip_comments(sql_string, sql_length);
- heap = mem_heap_create(10000);
-
- err = dict_create_foreign_constraints_low(
- trx, heap, innobase_get_charset(trx->mysql_thd),
- str, name, reject_fks);
-
- mem_heap_free(heap);
- ut_free(str);
-
- return(err);
-}
-
/**********************************************************************//**
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
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 265422c28bc..1f2b818bc12 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -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;
+
+ 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();
}
+ }
- 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;
+ 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;
- }
+ 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 (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. */
diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h
index e00003c30a6..56ef1fcb0c4 100644
--- a/storage/innobase/handler/ha_innodb.h
+++ b/storage/innobase/handler/ha_innodb.h
@@ -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);
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 3aa1fcc5292..4f58aecd101 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -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;
}
diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h
index a2666e7cfbc..158171dd3f3 100644
--- a/storage/innobase/include/dict0dict.h
+++ b/storage/innobase/include/dict0dict.h
@@ -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
diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h
index cd7f5355818..443dce51ff5 100644
--- a/storage/innobase/include/ha_prototypes.h
+++ b/storage/innobase/include/ha_prototypes.h
@@ -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: