Commit daabc5cc authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-20729 Fix REFERENCES constraint in column definition

Add support of referential constraints directly in column defininions:

create table t1 (id1 int primary key);
create table t2 (id2 int references t1(id1));

Referenced field name can be omitted if equal to foreign field name:

create table t1 (id int primary key);
create table t2 (id int references t1);

Until 10.5 this syntax was understood by the parser but was silently
ignored.

In case of generated columns this syntax is disabled at parser level
by ER_PARSE_ERROR. Note that separate FOREIGN KEY clause for generated
columns is disabled at storage engine level.
parent 5130f520
Subproject commit 980f2dbea6586091333057bb2994b18747466942
Subproject commit 261a5c435581c8d6c6341afac95bcc5c96d1435c
drop table if exists t1,t2;
create table t1 (
a int not null references t2,
b int not null references t2 (c),
b int not null constraint t2_c references t2 (c),
primary key (a,b),
foreign key (a) references t3 match full,
foreign key (a) references t3 match partial,
......
......@@ -10,7 +10,7 @@ drop table if exists t1,t2;
create table t1 (
a int not null references t2,
b int not null references t2 (c),
b int not null constraint t2_c references t2 (c),
primary key (a,b),
foreign key (a) references t3 match full,
foreign key (a) references t3 match partial,
......
......@@ -109,6 +109,7 @@ def mysql help_category mysql name
def mysql help_category mysql PRIMARY
def mysql help_keyword mysql name
def mysql help_keyword mysql PRIMARY
def mysql help_relation mysql help_topic_id
def mysql help_relation mysql PRIMARY
def mysql help_relation mysql PRIMARY
def mysql help_topic mysql name
......
......@@ -30,6 +30,7 @@ def mysql help_category 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_category 0 mysql PRIMARY 1 help_category_id A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 1 mysql help_topic_id 1 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 2 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_topic 0 mysql name 1 name A #CARD# NULL NULL BTREE
......
......@@ -30,6 +30,7 @@ def mysql help_category 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_category 0 mysql PRIMARY 1 help_category_id A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 1 mysql help_topic_id 1 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 2 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_topic 0 mysql name 1 name A #CARD# NULL NULL BTREE
......@@ -110,6 +111,7 @@ def mysql help_category 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_category 0 mysql PRIMARY 1 help_category_id A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql name 1 name A #CARD# NULL NULL BTREE
def mysql help_keyword 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 1 mysql help_topic_id 1 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 1 help_keyword_id A #CARD# NULL NULL BTREE
def mysql help_relation 0 mysql PRIMARY 2 help_topic_id A #CARD# NULL NULL BTREE
def mysql help_topic 0 mysql name 1 name A #CARD# NULL NULL BTREE
......
......@@ -134,12 +134,16 @@ drop table t1;
create table t2 (a int);
--error ER_PARSE_ERROR
create table t1 (a int, b int generated always as (a % 2) stored references t2(a));
create table t1 (a int, b int generated always as (a % 2) stored);
show create table t1;
drop table t1;
create table t1 (a int, b int generated always as (a % 2) virtual);
--error 1064
--error ER_PARSE_ERROR
alter table t1 modify b int generated always as (a % 2) stored references t2(a);
--error ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN
alter table t1 modify b int generated always as (a % 2) stored;
show create table t1;
drop table t1;
drop table t2;
......
......@@ -154,6 +154,8 @@ a b
drop table t1;
create table t2 (a int);
create table t1 (a int, b int generated always as (a % 2) stored references t2(a));
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ')' at line 1
create table t1 (a int, b int generated always as (a % 2) stored);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......@@ -164,6 +166,8 @@ drop table t1;
create table t1 (a int, b int generated always as (a % 2) virtual);
alter table t1 modify b int generated always as (a % 2) stored references t2(a);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'references t2(a)' at line 1
alter table t1 modify b int generated always as (a % 2) stored;
ERROR HY000: This is not yet supported for generated columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......
......@@ -154,6 +154,8 @@ a b
drop table t1;
create table t2 (a int);
create table t1 (a int, b int generated always as (a % 2) stored references t2(a));
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ')' at line 1
create table t1 (a int, b int generated always as (a % 2) stored);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......@@ -164,6 +166,8 @@ drop table t1;
create table t1 (a int, b int generated always as (a % 2) virtual);
alter table t1 modify b int generated always as (a % 2) stored references t2(a);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'references t2(a)' at line 1
alter table t1 modify b int generated always as (a % 2) stored;
ERROR HY000: This is not yet supported for generated columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......
......@@ -522,3 +522,47 @@ test.t1 check status OK
DROP TABLE t1;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
# End of 10.2 tests
#
# MDEV-20729 Fix REFERENCES constraint in column definition
#
set default_storage_engine= innodb;
create table t1 (x int primary key, y int unique);
create table t2 (x int references t1(x), y int constraint fk references t1(y));
show create table t2;
Table Create Table
t2 CREATE TABLE `t2` (
`x` int(11) DEFAULT NULL,
`y` int(11) DEFAULT NULL,
KEY `x` (`x`),
KEY `fk` (`y`),
CONSTRAINT `fk` FOREIGN KEY (`y`) REFERENCES `t1` (`y`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`x`) REFERENCES `t1` (`x`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
create table t3 (z int);
alter table t3 add x int references t1(x), add y int constraint fk2 references t1(y);
show create table t3;
Table Create Table
t3 CREATE TABLE `t3` (
`z` int(11) DEFAULT NULL,
`x` int(11) DEFAULT NULL,
`y` int(11) DEFAULT NULL,
KEY `x` (`x`),
KEY `fk2` (`y`),
CONSTRAINT `fk2` FOREIGN KEY (`y`) REFERENCES `t1` (`y`),
CONSTRAINT `t3_ibfk_1` FOREIGN KEY (`x`) REFERENCES `t1` (`x`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop tables t3, t2, t1;
create table t1 (id int primary key);
create table t2 (id2 int references t1);
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
create table t2 (id int references t1);
show create table t2;
Table Create Table
t2 CREATE TABLE `t2` (
`id` int(11) DEFAULT NULL,
KEY `id` (`id`),
CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop tables t2, t1;
set default_storage_engine= default;
# End of 10.5 tests
......@@ -498,4 +498,27 @@ SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
--echo # End of 10.2 tests
--echo #
--echo # MDEV-20729 Fix REFERENCES constraint in column definition
--echo #
set default_storage_engine= innodb;
create table t1 (x int primary key, y int unique);
create table t2 (x int references t1(x), y int constraint fk references t1(y));
show create table t2;
create table t3 (z int);
alter table t3 add x int references t1(x), add y int constraint fk2 references t1(y);
show create table t3;
drop tables t3, t2, t1;
create table t1 (id int primary key);
--error ER_CANT_CREATE_TABLE
create table t2 (id2 int references t1);
create table t2 (id int references t1);
show create table t2;
drop tables t2, t1;
set default_storage_engine= default;
--echo # End of 10.5 tests
--source include/wait_until_count_sessions.inc
......@@ -103,11 +103,11 @@ drop table t1;
create table t2 (a int);
create table t1 (a int, b int as (a % 2) persistent references t2(a));
create table t1 (a int, b int as (a % 2) persistent);
show create table t1;
drop table t1;
create table t1 (a int, b int as (a % 2));
--error ER_PARSE_ERROR
alter table t1 modify b int as (a % 2) persistent references t2(a);
--error ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN
alter table t1 modify b int as (a % 2) persistent;
show create table t1;
drop table t1;
......@@ -126,7 +126,7 @@ a b
2 0
drop table t1;
create table t2 (a int);
create table t1 (a int, b int as (a % 2) persistent references t2(a));
create table t1 (a int, b int as (a % 2) persistent);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......@@ -135,8 +135,8 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
create table t1 (a int, b int as (a % 2));
alter table t1 modify b int as (a % 2) persistent references t2(a);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'references t2(a)' at line 1
alter table t1 modify b int as (a % 2) persistent;
ERROR HY000: This is not yet supported for generated columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......
......@@ -126,7 +126,7 @@ a b
2 0
drop table t1;
create table t2 (a int);
create table t1 (a int, b int as (a % 2) persistent references t2(a));
create table t1 (a int, b int as (a % 2) persistent);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......@@ -135,8 +135,8 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
create table t1 (a int, b int as (a % 2));
alter table t1 modify b int as (a % 2) persistent references t2(a);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'references t2(a)' at line 1
alter table t1 modify b int as (a % 2) persistent;
ERROR HY000: This is not yet supported for generated columns
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
......
......@@ -429,3 +429,41 @@ insert into t2 values (1), (1);
# DELETE from foreign table is allowed
delete from t2;
drop tables t2, t1;
#
# MDEV-20729 Fix REFERENCES constraint in column definition
#
create or replace table t1(
id int
);
# system fields can't be foreign keys:
create or replace table t2(
x int,
sys_start SYS_DATATYPE as row start references t1(id),
sys_end SYS_DATATYPE as row end,
period for system_time(sys_start, sys_end)
) engine innodb with system versioning;
Got one of the listed errors
create or replace table t2(
x int,
sys_start SYS_DATATYPE as row start,
sys_end SYS_DATATYPE as row end references t1(id),
period for system_time(sys_start, sys_end)
) engine innodb with system versioning;
Got one of the listed errors
create or replace table t2(
x int,
sys_start SYS_DATATYPE as row start,
sys_end SYS_DATATYPE as row end,
period for system_time(sys_start, sys_end),
foreign key (sys_start) references t1(id)
) engine innodb with system versioning;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
create or replace table t2(
x int,
sys_start SYS_DATATYPE as row start,
sys_end SYS_DATATYPE as row end,
period for system_time(sys_start, sys_end),
foreign key (sys_end) references t1(id)
) engine innodb with system versioning;
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
drop table t1;
......@@ -458,4 +458,47 @@ insert into t2 values (1), (1);
delete from t2;
drop tables t2, t1;
--echo #
--echo # MDEV-20729 Fix REFERENCES constraint in column definition
--echo #
create or replace table t1(
id int
);
--echo # system fields can't be foreign keys:
--replace_result $sys_datatype_expl SYS_DATATYPE
--error ER_PARSE_ERROR,ER_PARSE_ERROR
eval create or replace table t2(
x int,
sys_start $sys_datatype_expl as row start references t1(id),
sys_end $sys_datatype_expl as row end,
period for system_time(sys_start, sys_end)
) engine innodb with system versioning;
--replace_result $sys_datatype_expl SYS_DATATYPE
--error ER_PARSE_ERROR,ER_PARSE_ERROR
eval create or replace table t2(
x int,
sys_start $sys_datatype_expl as row start,
sys_end $sys_datatype_expl as row end references t1(id),
period for system_time(sys_start, sys_end)
) engine innodb with system versioning;
--replace_result $sys_datatype_expl SYS_DATATYPE
--error ER_CANT_CREATE_TABLE
eval create or replace table t2(
x int,
sys_start $sys_datatype_expl as row start,
sys_end $sys_datatype_expl as row end,
period for system_time(sys_start, sys_end),
foreign key (sys_start) references t1(id)
) engine innodb with system versioning;
--replace_result $sys_datatype_expl SYS_DATATYPE
--error ER_CANT_CREATE_TABLE
eval create or replace table t2(
x int,
sys_start $sys_datatype_expl as row start,
sys_end $sys_datatype_expl as row end,
period for system_time(sys_start, sys_end),
foreign key (sys_end) references t1(id)
) engine innodb with system versioning;
drop table t1;
--source suite/versioning/common_finish.inc
......@@ -11168,3 +11168,71 @@ sp_condition_value *LEX::stmt_signal_value(const Lex_ident_sys_st &ident)
}
return cond;
}
bool LEX::add_table_foreign_key(const LEX_CSTRING *name,
const LEX_CSTRING *constraint_name,
Table_ident *ref_table_name,
DDL_options ddl_options)
{
Key *key= new (thd->mem_root) Foreign_key(name,
&last_key->columns,
constraint_name,
&ref_table_name->db,
&ref_table_name->table,
&ref_list,
fk_delete_opt,
fk_update_opt,
fk_match_option,
ddl_options);
if (unlikely(key == NULL))
return true;
/*
handle_if_exists_options() expects the two keys in this order:
the Foreign_key, followed by its auto-generated Key.
*/
alter_info.key_list.push_back(key, thd->mem_root);
alter_info.key_list.push_back(last_key, thd->mem_root);
option_list= NULL;
/* Only used for ALTER TABLE. Ignored otherwise. */
alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
return false;
}
bool LEX::add_column_foreign_key(const LEX_CSTRING *name,
const LEX_CSTRING *constraint_name,
Table_ident *ref_table_name,
DDL_options ddl_options)
{
if (last_field->vcol_info || last_field->vers_sys_field())
{
thd->parse_error();
return true;
}
if (unlikely(!(last_key= (new (thd->mem_root)
Key(Key::MULTIPLE, constraint_name,
HA_KEY_ALG_UNDEF, true, ddl_options)))))
return true;
Key_part_spec *key= new (thd->mem_root) Key_part_spec(name, 0);
if (unlikely(key == NULL))
return true;
last_key->columns.push_back(key, thd->mem_root);
if (ref_list.is_empty())
{
ref_list.push_back(key, thd->mem_root);
}
if (unlikely(add_table_foreign_key(constraint_name, constraint_name,
ref_table_name, ddl_options)))
return true;
option_list= NULL;
/* Only used for ALTER TABLE. Ignored otherwise. */
alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
return false;
}
......@@ -4613,6 +4613,15 @@ struct LEX: public Query_tables_list
const LEX_CSTRING &name);
void mark_first_table_as_inserting();
bool add_table_foreign_key(const LEX_CSTRING *name,
const LEX_CSTRING *constraint_name,
Table_ident *table_name,
DDL_options ddl_options);
bool add_column_foreign_key(const LEX_CSTRING *name,
const LEX_CSTRING *constraint_name,
Table_ident *ref_table_name,
DDL_options ddl_options);
};
......
......@@ -5713,8 +5713,13 @@ field_list_item:
column_def:
field_spec
{ $$= $1; }
| field_spec references
{ $$= $1; }
| field_spec opt_constraint references
{
if (unlikely(Lex->add_column_foreign_key(&($1->field_name), &$2,
$3, DDL_options())))
MYSQL_YYABORT;
$$= $1;
}
;
key_def:
......@@ -5775,30 +5780,9 @@ key_def:
}
'(' key_list ')' references
{
LEX *lex=Lex;
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,
lex->fk_delete_opt,
lex->fk_update_opt,
lex->fk_match_option,
$4));
if (unlikely(key == NULL))
if (unlikely(Lex->add_table_foreign_key($5.str ? &$5 : &$1,
$1.str ? &$1 : &$5, $10, $4)))
MYSQL_YYABORT;
/*
handle_if_exists_options() expectes the two keys in this order:
the Foreign_key, followed by its auto-generated Key.
*/
lex->alter_info.key_list.push_back(key, thd->mem_root);
lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root);
lex->option_list= NULL;
/* Only used for ALTER TABLE. Ignored otherwise. */
lex->alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
}
;
......
......@@ -5712,8 +5712,13 @@ field_list_item:
column_def:
field_spec
{ $$= $1; }
| field_spec references
{ $$= $1; }
| field_spec opt_constraint references
{
if (unlikely(Lex->add_column_foreign_key(&($1->field_name), &$2,
$3, DDL_options())))
MYSQL_YYABORT;
$$= $1;
}
;
key_def:
......@@ -5774,30 +5779,9 @@ key_def:
}
'(' key_list ')' references
{
LEX *lex=Lex;
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,
lex->fk_delete_opt,
lex->fk_update_opt,
lex->fk_match_option,
$4));
if (unlikely(key == NULL))
if (unlikely(Lex->add_table_foreign_key($5.str ? &$5 : &$1,
$1.str ? &$1 : &$5, $10, $4)))
MYSQL_YYABORT;
/*
handle_if_exists_options() expectes the two keys in this order:
the Foreign_key, followed by its auto-generated Key.
*/
lex->alter_info.key_list.push_back(key, thd->mem_root);
lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root);
lex->option_list= NULL;
/* Only used for ALTER TABLE. Ignored otherwise. */
lex->alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
}
;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment