diff options
author | Aleksey Midenkov <midenok@gmail.com> | 2019-11-20 13:18:31 +0300 |
---|---|---|
committer | Aleksey Midenkov <midenok@gmail.com> | 2019-11-20 13:18:31 +0300 |
commit | 5130f5206c150ba1e8a723aae63884ff64408012 (patch) | |
tree | 43304b46565bdc6dfbdef24b5f2afcab10e66a37 | |
parent | 20b474be5b75929727c693f91448f12257d1b6d4 (diff) | |
download | mariadb-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.result | 2 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/innodb-fk-warnings.result | 25 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/innodb-fk.result | 5 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/innodb-fk-warnings.test | 26 | ||||
-rw-r--r-- | mysql-test/suite/innodb_gis/r/point_basic.result | 6 | ||||
-rw-r--r-- | sql/handler.h | 4 | ||||
-rw-r--r-- | sql/sql_alter.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.cc | 1 | ||||
-rw-r--r-- | sql/sql_class.h | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 1 | ||||
-rw-r--r-- | sql/sql_table.cc | 9 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 1 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 1 | ||||
-rw-r--r-- | storage/innobase/dict/dict0dict.cc | 1293 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 737 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.h | 3 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 51 | ||||
-rw-r--r-- | storage/innobase/include/dict0dict.h | 46 | ||||
-rw-r--r-- | storage/innobase/include/ha_prototypes.h | 11 |
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: |