Commit 95fb41b2 authored by unknown's avatar unknown

InnoDB: limit the recursion depth for ON (UPDATE|DELETE) CASCADE

(Bug #4446)


innobase/row/row0ins.c:
  row_ins_foreign_check_on_constraint(): limit recursion for UPDATE too
mysql-test/r/innodb.result:
  Add test for recursion depth limit
mysql-test/t/innodb.test:
  Add test for recursion depth limit
parent 90aa7d70
...@@ -370,6 +370,32 @@ row_ins_cascade_ancestor_updates_table( ...@@ -370,6 +370,32 @@ row_ins_cascade_ancestor_updates_table(
return(FALSE); return(FALSE);
} }
/*************************************************************************
Returns the number of ancestor UPDATE or DELETE nodes of a
cascaded update/delete node. */
static
ulint
row_ins_cascade_n_ancestors(
/*========================*/
/* out: number of ancestors */
que_node_t* node) /* in: node in a query graph */
{
que_node_t* parent;
ulint n_ancestors = 0;
parent = que_node_get_parent(node);
while (que_node_get_type(parent) == QUE_NODE_UPDATE) {
n_ancestors++;
parent = que_node_get_parent(parent);
ut_a(parent);
}
return(n_ancestors);
}
/********************************************************************** /**********************************************************************
Calculates the update vector node->cascade->update for a child table in Calculates the update vector node->cascade->update for a child table in
a cascaded update. */ a cascaded update. */
...@@ -615,6 +641,34 @@ row_ins_foreign_report_add_err( ...@@ -615,6 +641,34 @@ row_ins_foreign_report_add_err(
mutex_exit(&dict_foreign_err_mutex); mutex_exit(&dict_foreign_err_mutex);
} }
/*************************************************************************
Invalidate the query cache for the given table. */
static
void
row_ins_invalidate_query_cache(
/*===========================*/
que_thr_t* thr, /* in: query thread whose run_node
is an update node */
const char* name) /* in: table name prefixed with
database name and a '/' character */
{
char* buf;
char* ptr;
ulint len = strlen(name) + 1;
buf = mem_strdupl(name, len);
ptr = strchr(buf, '/');
ut_a(ptr);
*ptr = '\0';
/* We call a function in ha_innodb.cc */
#ifndef UNIV_HOTBACKUP
innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);
#endif
mem_free(buf);
}
/************************************************************************* /*************************************************************************
Perform referential actions or checks when a parent row is deleted or updated Perform referential actions or checks when a parent row is deleted or updated
and the constraint had an ON DELETE or ON UPDATE condition which was not and the constraint had an ON DELETE or ON UPDATE condition which was not
...@@ -650,26 +704,14 @@ row_ins_foreign_check_on_constraint( ...@@ -650,26 +704,14 @@ row_ins_foreign_check_on_constraint(
ulint n_to_update; ulint n_to_update;
ulint err; ulint err;
ulint i; ulint i;
char* ptr;
char table_name_buf[1000];
ut_a(thr && foreign && pcur && mtr); ut_a(thr && foreign && pcur && mtr);
/* Since we are going to delete or update a row, we have to invalidate /* Since we are going to delete or update a row, we have to invalidate
the MySQL query cache for table */ the MySQL query cache for table */
ut_a(ut_strlen(table->name) < 998); row_ins_invalidate_query_cache(thr, table->name);
strcpy(table_name_buf, table->name);
ptr = strchr(table_name_buf, '/');
ut_a(ptr);
*ptr = '\0';
/* We call a function in ha_innodb.cc */
#ifndef UNIV_HOTBACKUP
innobase_invalidate_query_cache(thr_get_trx(thr), table_name_buf,
ut_strlen(table->name) + 1);
#endif
node = thr->run_node; node = thr->run_node;
if (node->is_delete && 0 == (foreign->type & if (node->is_delete && 0 == (foreign->type &
...@@ -756,6 +798,16 @@ row_ins_foreign_check_on_constraint( ...@@ -756,6 +798,16 @@ row_ins_foreign_check_on_constraint(
goto nonstandard_exit_func; goto nonstandard_exit_func;
} }
if (row_ins_cascade_n_ancestors(cascade) >= 15) {
err = DB_ROW_IS_REFERENCED;
row_ins_foreign_report_err(
(char*)"Trying a too deep cascaded delete or update\n",
thr, foreign, btr_pcur_get_rec(pcur), entry);
goto nonstandard_exit_func;
}
index = btr_pcur_get_btr_cur(pcur)->index; index = btr_pcur_get_btr_cur(pcur)->index;
ut_a(index == foreign->foreign_index); ut_a(index == foreign->foreign_index);
......
...@@ -1259,3 +1259,15 @@ Cannot delete or update a parent row: a foreign key constraint fails ...@@ -1259,3 +1259,15 @@ Cannot delete or update a parent row: a foreign key constraint fails
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id; update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
Unknown table 't1' in where clause Unknown table 't1' in where clause
drop table t3,t2,t1; drop table t3,t2,t1;
create table t1(
id int primary key,
pid int,
index(pid),
foreign key(pid) references t1(id) on delete cascade) type=innodb;
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
delete from t1 where id=0;
Cannot delete or update a parent row: a foreign key constraint fails
delete from t1 where id=15;
delete from t1 where id=0;
drop table t1;
...@@ -896,3 +896,17 @@ update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id ...@@ -896,3 +896,17 @@ update t1,t2,t3 set t3.id=5, t2.id=6, t1.id=7 where t1.id =1 and t2.id = t1.id
--error 1109 --error 1109
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id; update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
drop table t3,t2,t1; drop table t3,t2,t1;
create table t1(
id int primary key,
pid int,
index(pid),
foreign key(pid) references t1(id) on delete cascade) type=innodb;
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
-- error 1217
delete from t1 where id=0;
delete from t1 where id=15;
delete from t1 where id=0;
drop table t1;
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