mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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
This commit is contained in:
@@ -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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
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
|
|||||||
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
|
|||||||
--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;
|
||||||
|
Reference in New Issue
Block a user