summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Midenkov <midenok@gmail.com>2020-06-12 11:10:55 +0300
committerAleksey Midenkov <midenok@gmail.com>2020-06-12 11:12:40 +0300
commit762bf7a03b6214f091a66ca8683df341112d7d4a (patch)
treeeb86fdd1eda7b8d04dd51cde33a44c629f651528
parent02c255d1e017072858ef812b8c5c5f4e217fb6c4 (diff)
downloadmariadb-git-762bf7a03b6214f091a66ca8683df341112d7d4a.tar.gz
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.
-rw-r--r--mysql-test/suite/innodb/r/foreign_key.result18
-rw-r--r--mysql-test/suite/innodb/t/foreign_key.test23
-rw-r--r--sql/field.cc4
-rw-r--r--sql/field.h3
-rw-r--r--sql/item.cc31
-rw-r--r--sql/item.h16
-rw-r--r--sql/sql_alter.h5
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_table.cc30
-rw-r--r--sql/table.cc1
10 files changed, 110 insertions, 24 deletions
diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result
index 0f85b88483f..00563d397f3 100644
--- a/mysql-test/suite/innodb/r/foreign_key.result
+++ b/mysql-test/suite/innodb/r/foreign_key.result
@@ -766,3 +766,21 @@ ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (b);
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
DROP TABLE t1;
# End of 10.5 tests
+#
+# MDEV-22602 Disable UPDATE CASCADE for SQL constraints
+#
+# TODO: enable them after MDEV-16417 is finished
+create or replace table t1 (a int primary key) engine=innodb;
+create or replace table t2 (a int, check(a > 0), foreign key(a) references t1(a) on update cascade) engine=innodb;
+ERROR HY000: Function or expression 'a' cannot be used in the CHECK clause of `CONSTRAINT_1`
+create or replace table t1 (f1 int, f2 date, f3 date, key(f1,f3,f2)) engine=innodb;
+create or replace table t2 (
+a int, s date, e date,
+period for p (s, e),
+primary key (a, p without overlaps),
+foreign key (a, e, s) references t1 (f1, f3, f2) on delete cascade on update cascade) engine=innodb;
+ERROR HY000: Key `PRIMARY` cannot have WITHOUT OVERLAPS
+create or replace table t1 (a varchar(4096) unique) engine=innodb;
+create or replace table t2 (pk int primary key, a varchar(4096) unique, foreign key(a) references t1(a) on update cascade) engine=innodb;
+ERROR HY000: Can't create table `test`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
+drop table t1;
diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test
index c75206981af..4719384cd65 100644
--- a/mysql-test/suite/innodb/t/foreign_key.test
+++ b/mysql-test/suite/innodb/t/foreign_key.test
@@ -741,4 +741,27 @@ DROP TABLE t1;
--echo # End of 10.5 tests
+--echo #
+--echo # MDEV-22602 Disable UPDATE CASCADE for SQL constraints
+--echo #
+--echo # TODO: enable them after MDEV-16417 is finished
+create or replace table t1 (a int primary key) engine=innodb;
+--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED
+create or replace table t2 (a int, check(a > 0), foreign key(a) references t1(a) on update cascade) engine=innodb;
+
+create or replace table t1 (f1 int, f2 date, f3 date, key(f1,f3,f2)) engine=innodb;
+--error ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS
+create or replace table t2 (
+ a int, s date, e date,
+ period for p (s, e),
+ primary key (a, p without overlaps),
+ foreign key (a, e, s) references t1 (f1, f3, f2) on delete cascade on update cascade) engine=innodb;
+
+# FK on long unique is already disabled
+create or replace table t1 (a varchar(4096) unique) engine=innodb;
+--error ER_CANT_CREATE_TABLE
+create or replace table t2 (pk int primary key, a varchar(4096) unique, foreign key(a) references t1(a) on update cascade) engine=innodb;
+
+drop table t1;
+
--source include/wait_until_count_sessions.inc
diff --git a/sql/field.cc b/sql/field.cc
index 3422f54732c..d9b654945f6 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -10191,11 +10191,12 @@ void Column_definition::create_length_to_internal_length_newdecimal()
bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
- enum_vcol_info_type type)
+ enum_vcol_info_type type, Alter_info *alter_info)
{
bool ret;
Item::vcol_func_processor_result res;
+ res.alter_info= alter_info;
if (!vcol->name.length)
vcol->name= *name;
@@ -10204,7 +10205,6 @@ bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
Walk through the Item tree checking if all items are valid
to be part of the virtual column
*/
- res.errors= 0;
ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res);
vcol->flags= res.errors;
diff --git a/sql/field.h b/sql/field.h
index 748b6bc5c01..e4c5ffcc0de 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -551,6 +551,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type)
#define VCOL_AUTO_INC 16
#define VCOL_IMPOSSIBLE 32
#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */
+#define VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS 128
#define VCOL_NOT_STRICTLY_DETERMINISTIC \
(VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC)
@@ -5719,7 +5720,7 @@ int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions);
int convert_null_to_field_value_or_error(Field *field);
bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
- enum_vcol_info_type type);
+ enum_vcol_info_type type, Alter_info *alter_info= NULL);
/*
The following are for the interface with the .frm file
diff --git a/sql/item.cc b/sql/item.cc
index 5d230b4ea66..9b3690f4c3b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1493,6 +1493,37 @@ bool mark_unsupported_function(const char *w1, const char *w2,
}
+bool Item_field::check_vcol_func_processor(void *arg)
+{
+ context= 0;
+ vcol_func_processor_result *res= (vcol_func_processor_result *) arg;
+ if (res && res->alter_info)
+ {
+ for (Key &k: res->alter_info->key_list)
+ {
+ if (k.type != Key::FOREIGN_KEY)
+ continue;
+ Foreign_key *fk= (Foreign_key*) &k;
+ if (fk->update_opt != FK_OPTION_CASCADE)
+ continue;
+ for (Key_part_spec& kp: fk->columns)
+ {
+ if (!lex_string_cmp(system_charset_info, &kp.field_name, &field_name))
+ {
+ return mark_unsupported_function(field_name.str, arg, VCOL_IMPOSSIBLE);
+ }
+ }
+ }
+ }
+ if (field && (field->unireg_check == Field::NEXT_NUMBER))
+ {
+ // Auto increment fields are unsupported
+ return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
+ }
+ return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
+}
+
+
Query_fragment::Query_fragment(THD *thd, sp_head *sphead,
const char *start, const char *end)
{
diff --git a/sql/item.h b/sql/item.h
index 800945f3242..8de38978a47 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2056,6 +2056,9 @@ public:
{
uint errors; /* Bits of possible errors */
const char *name; /* Not supported function */
+ Alter_info *alter_info;
+ vcol_func_processor_result() :
+ errors(0), name(NULL), alter_info(NULL) {}
};
struct func_processor_rename
{
@@ -3506,16 +3509,7 @@ public:
bool switch_to_nullable_fields_processor(void *arg);
bool update_vcol_processor(void *arg);
bool rename_fields_processor(void *arg);
- bool check_vcol_func_processor(void *arg)
- {
- context= 0;
- if (field && (field->unireg_check == Field::NEXT_NUMBER))
- {
- // Auto increment fields are unsupported
- return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
- }
- return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
- }
+ bool check_vcol_func_processor(void *arg);
bool set_fields_as_dependent_processor(void *arg)
{
if (!(used_tables() & OUTER_REF_TABLE_BIT))
@@ -6070,7 +6064,7 @@ public:
table_map used_tables() const { return (table_map) 1L; }
bool const_item() const { return 0; }
bool is_null() { return null_value; }
- bool check_vcol_func_processor(void *arg)
+ bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function("copy", arg, VCOL_IMPOSSIBLE);
}
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
index 88873477c7c..ca343f36569 100644
--- a/sql/sql_alter.h
+++ b/sql/sql_alter.h
@@ -95,11 +95,6 @@ public:
List<Alter_rename_key> alter_rename_key_list;
// List of columns, used by both CREATE and ALTER TABLE.
List<Create_field> create_list;
-
- enum flags_bits
- {
- CHECK_CONSTRAINT_IF_NOT_EXISTS= 1
- };
List<Virtual_column_info> check_constraint_list;
// Type of ALTER TABLE operation.
alter_table_operations flags;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 49edce508ca..adfa0c08959 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -4331,8 +4331,7 @@ public:
bool if_not_exists)
{
constr->name= name;
- constr->flags= if_not_exists ?
- Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
+ constr->flags= if_not_exists ? VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
alter_info.check_constraint_list.push_back(constr);
return false;
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 61b1113f680..05bef5f4bda 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4292,9 +4292,31 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->algorithm == HA_KEY_ALG_LONG_HASH)
{
+without_overlaps_err:
my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key_info->name.str);
DBUG_RETURN(true);
}
+ key_iterator2.rewind();
+ while ((key2 = key_iterator2++))
+ {
+ if (key2->type != Key::FOREIGN_KEY)
+ continue;
+ DBUG_ASSERT(key != key2);
+ Foreign_key *fk= (Foreign_key*) key2;
+ if (fk->update_opt != FK_OPTION_CASCADE)
+ continue;
+ for (Key_part_spec& kp: key->columns)
+ {
+ for (Key_part_spec& kp2: fk->columns)
+ {
+ if (!lex_string_cmp(system_charset_info, &kp.field_name,
+ &kp2.field_name))
+ {
+ goto without_overlaps_err;
+ }
+ }
+ }
+ }
create_info->period_info.unique_keys++;
}
@@ -4383,7 +4405,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
while ((check= c_it++))
{
if (!check->name.length || check->automatic_name)
+ {
+ if (check_expression(check, &check->name, VCOL_CHECK_TABLE, alter_info))
+ DBUG_RETURN(TRUE);
continue;
+ }
{
/* 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,
if (!check) // Found unique name
{
name->length= (size_t) (real_end - buff);
- name->str= thd->strmake(buff, name->length);
+ name->str= strmake_root(thd->stmt_arena->mem_root, buff, name->length);
return (name->str == NULL);
}
}
@@ -6666,7 +6692,7 @@ remove_key:
while ((check=it++))
{
- if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
+ if (!(check->flags & VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
check->name.length)
continue;
check->flags= 0;
diff --git a/sql/table.cc b/sql/table.cc
index 8c8145c431c..bec89dbb440 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3574,7 +3574,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
to be part of the virtual column
*/
Item::vcol_func_processor_result res;
- res.errors= 0;
int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE)))