Skip to content

Commit 762bf7a

Browse files
committed
MDEV-22602 Disable UPDATE CASCADE for SQL constraints
CHECK constraint is checked by check_expression() which walks its items and gets into Item_field::check_vcol_func_processor() to check for conformity with foreign key list. WITHOUT OVERLAPS is checked for same conformity in mysql_prepare_create_table(). Long uniques are already impossible with InnoDB foreign keys. See ER_CANT_CREATE_TABLE in test case. 2 accompanying bugs fixed (test main.constraints failed): 1. check->name.str lived on SP execute mem_root while "check" obj itself lives on SP main mem_root. On second SP execute check->name.str had garbage data. Fixed by allocating from thd->stmt_arena->mem_root which is SP main mem_root. 2. CHECK_CONSTRAINT_IF_NOT_EXISTS value was mixed with VCOL_FIELD_REF. VCOL_FIELD_REF is assigned in check_expression() and then detected as CHECK_CONSTRAINT_IF_NOT_EXISTS in handle_if_exists_options(). Existing cases for MDEV-16932 in main.constraints cover both fixes.
1 parent 02c255d commit 762bf7a

File tree

10 files changed

+110
-24
lines changed

10 files changed

+110
-24
lines changed

mysql-test/suite/innodb/r/foreign_key.result

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,3 +766,21 @@ ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (b);
766766
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
767767
DROP TABLE t1;
768768
# End of 10.5 tests
769+
#
770+
# MDEV-22602 Disable UPDATE CASCADE for SQL constraints
771+
#
772+
# TODO: enable them after MDEV-16417 is finished
773+
create or replace table t1 (a int primary key) engine=innodb;
774+
create or replace table t2 (a int, check(a > 0), foreign key(a) references t1(a) on update cascade) engine=innodb;
775+
ERROR HY000: Function or expression 'a' cannot be used in the CHECK clause of `CONSTRAINT_1`
776+
create or replace table t1 (f1 int, f2 date, f3 date, key(f1,f3,f2)) engine=innodb;
777+
create or replace table t2 (
778+
a int, s date, e date,
779+
period for p (s, e),
780+
primary key (a, p without overlaps),
781+
foreign key (a, e, s) references t1 (f1, f3, f2) on delete cascade on update cascade) engine=innodb;
782+
ERROR HY000: Key `PRIMARY` cannot have WITHOUT OVERLAPS
783+
create or replace table t1 (a varchar(4096) unique) engine=innodb;
784+
create or replace table t2 (pk int primary key, a varchar(4096) unique, foreign key(a) references t1(a) on update cascade) engine=innodb;
785+
ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
786+
drop table t1;

mysql-test/suite/innodb/t/foreign_key.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,4 +741,27 @@ DROP TABLE t1;
741741

742742
--echo # End of 10.5 tests
743743

744+
--echo #
745+
--echo # MDEV-22602 Disable UPDATE CASCADE for SQL constraints
746+
--echo #
747+
--echo # TODO: enable them after MDEV-16417 is finished
748+
create or replace table t1 (a int primary key) engine=innodb;
749+
--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
750+
create or replace table t2 (a int, check(a > 0), foreign key(a) references t1(a) on update cascade) engine=innodb;
751+
752+
create or replace table t1 (f1 int, f2 date, f3 date, key(f1,f3,f2)) engine=innodb;
753+
--error ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS
754+
create or replace table t2 (
755+
a int, s date, e date,
756+
period for p (s, e),
757+
primary key (a, p without overlaps),
758+
foreign key (a, e, s) references t1 (f1, f3, f2) on delete cascade on update cascade) engine=innodb;
759+
760+
# FK on long unique is already disabled
761+
create or replace table t1 (a varchar(4096) unique) engine=innodb;
762+
--error ER_CANT_CREATE_TABLE
763+
create or replace table t2 (pk int primary key, a varchar(4096) unique, foreign key(a) references t1(a) on update cascade) engine=innodb;
764+
765+
drop table t1;
766+
744767
--source include/wait_until_count_sessions.inc

sql/field.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10191,11 +10191,12 @@ void Column_definition::create_length_to_internal_length_newdecimal()
1019110191

1019210192

1019310193
bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
10194-
enum_vcol_info_type type)
10194+
enum_vcol_info_type type, Alter_info *alter_info)
1019510195

1019610196
{
1019710197
bool ret;
1019810198
Item::vcol_func_processor_result res;
10199+
res.alter_info= alter_info;
1019910200

1020010201
if (!vcol->name.length)
1020110202
vcol->name= *name;
@@ -10204,7 +10205,6 @@ bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
1020410205
Walk through the Item tree checking if all items are valid
1020510206
to be part of the virtual column
1020610207
*/
10207-
res.errors= 0;
1020810208
ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res);
1020910209
vcol->flags= res.errors;
1021010210

sql/field.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type)
551551
#define VCOL_AUTO_INC 16
552552
#define VCOL_IMPOSSIBLE 32
553553
#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */
554+
#define VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS 128
554555

555556
#define VCOL_NOT_STRICTLY_DETERMINISTIC \
556557
(VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC)
@@ -5719,7 +5720,7 @@ int set_field_to_null(Field *field);
57195720
int set_field_to_null_with_conversions(Field *field, bool no_conversions);
57205721
int convert_null_to_field_value_or_error(Field *field);
57215722
bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
5722-
enum_vcol_info_type type);
5723+
enum_vcol_info_type type, Alter_info *alter_info= NULL);
57235724

57245725
/*
57255726
The following are for the interface with the .frm file

sql/item.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,37 @@ bool mark_unsupported_function(const char *w1, const char *w2,
14931493
}
14941494

14951495

1496+
bool Item_field::check_vcol_func_processor(void *arg)
1497+
{
1498+
context= 0;
1499+
vcol_func_processor_result *res= (vcol_func_processor_result *) arg;
1500+
if (res && res->alter_info)
1501+
{
1502+
for (Key &k: res->alter_info->key_list)
1503+
{
1504+
if (k.type != Key::FOREIGN_KEY)
1505+
continue;
1506+
Foreign_key *fk= (Foreign_key*) &k;
1507+
if (fk->update_opt != FK_OPTION_CASCADE)
1508+
continue;
1509+
for (Key_part_spec& kp: fk->columns)
1510+
{
1511+
if (!lex_string_cmp(system_charset_info, &kp.field_name, &field_name))
1512+
{
1513+
return mark_unsupported_function(field_name.str, arg, VCOL_IMPOSSIBLE);
1514+
}
1515+
}
1516+
}
1517+
}
1518+
if (field && (field->unireg_check == Field::NEXT_NUMBER))
1519+
{
1520+
// Auto increment fields are unsupported
1521+
return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
1522+
}
1523+
return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
1524+
}
1525+
1526+
14961527
Query_fragment::Query_fragment(THD *thd, sp_head *sphead,
14971528
const char *start, const char *end)
14981529
{

sql/item.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,6 +2056,9 @@ class Item: public Value_source,
20562056
{
20572057
uint errors; /* Bits of possible errors */
20582058
const char *name; /* Not supported function */
2059+
Alter_info *alter_info;
2060+
vcol_func_processor_result() :
2061+
errors(0), name(NULL), alter_info(NULL) {}
20592062
};
20602063
struct func_processor_rename
20612064
{
@@ -3506,16 +3509,7 @@ class Item_field :public Item_ident,
35063509
bool switch_to_nullable_fields_processor(void *arg);
35073510
bool update_vcol_processor(void *arg);
35083511
bool rename_fields_processor(void *arg);
3509-
bool check_vcol_func_processor(void *arg)
3510-
{
3511-
context= 0;
3512-
if (field && (field->unireg_check == Field::NEXT_NUMBER))
3513-
{
3514-
// Auto increment fields are unsupported
3515-
return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
3516-
}
3517-
return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
3518-
}
3512+
bool check_vcol_func_processor(void *arg);
35193513
bool set_fields_as_dependent_processor(void *arg)
35203514
{
35213515
if (!(used_tables() & OUTER_REF_TABLE_BIT))
@@ -6070,7 +6064,7 @@ class Item_copy :public Item,
60706064
table_map used_tables() const { return (table_map) 1L; }
60716065
bool const_item() const { return 0; }
60726066
bool is_null() { return null_value; }
6073-
bool check_vcol_func_processor(void *arg)
6067+
bool check_vcol_func_processor(void *arg)
60746068
{
60756069
return mark_unsupported_function("copy", arg, VCOL_IMPOSSIBLE);
60766070
}

sql/sql_alter.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,6 @@ class Alter_info
9595
List<Alter_rename_key> alter_rename_key_list;
9696
// List of columns, used by both CREATE and ALTER TABLE.
9797
List<Create_field> create_list;
98-
99-
enum flags_bits
100-
{
101-
CHECK_CONSTRAINT_IF_NOT_EXISTS= 1
102-
};
10398
List<Virtual_column_info> check_constraint_list;
10499
// Type of ALTER TABLE operation.
105100
alter_table_operations flags;

sql/sql_lex.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4331,8 +4331,7 @@ struct LEX: public Query_tables_list
43314331
bool if_not_exists)
43324332
{
43334333
constr->name= name;
4334-
constr->flags= if_not_exists ?
4335-
Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
4334+
constr->flags= if_not_exists ? VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
43364335
alter_info.check_constraint_list.push_back(constr);
43374336
return false;
43384337
}

sql/sql_table.cc

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4292,9 +4292,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
42924292
key_info->algorithm == HA_KEY_ALG_LONG_HASH)
42934293

42944294
{
4295+
without_overlaps_err:
42954296
my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key_info->name.str);
42964297
DBUG_RETURN(true);
42974298
}
4299+
key_iterator2.rewind();
4300+
while ((key2 = key_iterator2++))
4301+
{
4302+
if (key2->type != Key::FOREIGN_KEY)
4303+
continue;
4304+
DBUG_ASSERT(key != key2);
4305+
Foreign_key *fk= (Foreign_key*) key2;
4306+
if (fk->update_opt != FK_OPTION_CASCADE)
4307+
continue;
4308+
for (Key_part_spec& kp: key->columns)
4309+
{
4310+
for (Key_part_spec& kp2: fk->columns)
4311+
{
4312+
if (!lex_string_cmp(system_charset_info, &kp.field_name,
4313+
&kp2.field_name))
4314+
{
4315+
goto without_overlaps_err;
4316+
}
4317+
}
4318+
}
4319+
}
42984320
create_info->period_info.unique_keys++;
42994321
}
43004322

@@ -4383,7 +4405,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
43834405
while ((check= c_it++))
43844406
{
43854407
if (!check->name.length || check->automatic_name)
4408+
{
4409+
if (check_expression(check, &check->name, VCOL_CHECK_TABLE, alter_info))
4410+
DBUG_RETURN(TRUE);
43864411
continue;
4412+
}
43874413

43884414
{
43894415
/* Check that there's no repeating table CHECK constraint names. */
@@ -5538,7 +5564,7 @@ static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
55385564
if (!check) // Found unique name
55395565
{
55405566
name->length= (size_t) (real_end - buff);
5541-
name->str= thd->strmake(buff, name->length);
5567+
name->str= strmake_root(thd->stmt_arena->mem_root, buff, name->length);
55425568
return (name->str == NULL);
55435569
}
55445570
}
@@ -6666,7 +6692,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info,
66666692

66676693
while ((check=it++))
66686694
{
6669-
if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
6695+
if (!(check->flags & VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
66706696
check->name.length)
66716697
continue;
66726698
check->flags= 0;

sql/table.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3574,7 +3574,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
35743574
to be part of the virtual column
35753575
*/
35763576
Item::vcol_func_processor_result res;
3577-
res.errors= 0;
35783577

35793578
int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
35803579
if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE)))

0 commit comments

Comments
 (0)