Skip to content

Commit 20b6051

Browse files
author
marko@hundin.mysql.fi
committed
InnoDB: limit the recursion depth for ON (UPDATE|DELETE) CASCADE
(Bug #4446)
1 parent 0614f8c commit 20b6051

File tree

3 files changed

+91
-13
lines changed

3 files changed

+91
-13
lines changed

innobase/row/row0ins.c

+65-13
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,32 @@ row_ins_cascade_ancestor_updates_table(
370370
return(FALSE);
371371
}
372372

373+
/*************************************************************************
374+
Returns the number of ancestor UPDATE or DELETE nodes of a
375+
cascaded update/delete node. */
376+
static
377+
ulint
378+
row_ins_cascade_n_ancestors(
379+
/*========================*/
380+
/* out: number of ancestors */
381+
que_node_t* node) /* in: node in a query graph */
382+
{
383+
que_node_t* parent;
384+
ulint n_ancestors = 0;
385+
386+
parent = que_node_get_parent(node);
387+
388+
while (que_node_get_type(parent) == QUE_NODE_UPDATE) {
389+
n_ancestors++;
390+
391+
parent = que_node_get_parent(parent);
392+
393+
ut_a(parent);
394+
}
395+
396+
return(n_ancestors);
397+
}
398+
373399
/**********************************************************************
374400
Calculates the update vector node->cascade->update for a child table in
375401
a cascaded update. */
@@ -615,6 +641,34 @@ row_ins_foreign_report_add_err(
615641
mutex_exit(&dict_foreign_err_mutex);
616642
}
617643

644+
/*************************************************************************
645+
Invalidate the query cache for the given table. */
646+
static
647+
void
648+
row_ins_invalidate_query_cache(
649+
/*===========================*/
650+
que_thr_t* thr, /* in: query thread whose run_node
651+
is an update node */
652+
const char* name) /* in: table name prefixed with
653+
database name and a '/' character */
654+
{
655+
char* buf;
656+
char* ptr;
657+
ulint len = strlen(name) + 1;
658+
659+
buf = mem_strdupl(name, len);
660+
661+
ptr = strchr(buf, '/');
662+
ut_a(ptr);
663+
*ptr = '\0';
664+
665+
/* We call a function in ha_innodb.cc */
666+
#ifndef UNIV_HOTBACKUP
667+
innobase_invalidate_query_cache(thr_get_trx(thr), buf, len);
668+
#endif
669+
mem_free(buf);
670+
}
671+
618672
/*************************************************************************
619673
Perform referential actions or checks when a parent row is deleted or updated
620674
and the constraint had an ON DELETE or ON UPDATE condition which was not
@@ -650,26 +704,14 @@ row_ins_foreign_check_on_constraint(
650704
ulint n_to_update;
651705
ulint err;
652706
ulint i;
653-
char* ptr;
654-
char table_name_buf[1000];
655707

656708
ut_a(thr && foreign && pcur && mtr);
657709

658710
/* Since we are going to delete or update a row, we have to invalidate
659711
the MySQL query cache for table */
660712

661-
ut_a(ut_strlen(table->name) < 998);
662-
strcpy(table_name_buf, table->name);
713+
row_ins_invalidate_query_cache(thr, table->name);
663714

664-
ptr = strchr(table_name_buf, '/');
665-
ut_a(ptr);
666-
*ptr = '\0';
667-
668-
/* We call a function in ha_innodb.cc */
669-
#ifndef UNIV_HOTBACKUP
670-
innobase_invalidate_query_cache(thr_get_trx(thr), table_name_buf,
671-
ut_strlen(table->name) + 1);
672-
#endif
673715
node = thr->run_node;
674716

675717
if (node->is_delete && 0 == (foreign->type &
@@ -756,6 +798,16 @@ row_ins_foreign_check_on_constraint(
756798
goto nonstandard_exit_func;
757799
}
758800

801+
if (row_ins_cascade_n_ancestors(cascade) >= 15) {
802+
err = DB_ROW_IS_REFERENCED;
803+
804+
row_ins_foreign_report_err(
805+
(char*)"Trying a too deep cascaded delete or update\n",
806+
thr, foreign, btr_pcur_get_rec(pcur), entry);
807+
808+
goto nonstandard_exit_func;
809+
}
810+
759811
index = btr_pcur_get_btr_cur(pcur)->index;
760812

761813
ut_a(index == foreign->foreign_index);

mysql-test/r/innodb.result

+12
Original file line numberDiff line numberDiff line change
@@ -1259,3 +1259,15 @@ Cannot delete or update a parent row: a foreign key constraint fails
12591259
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
12601260
Unknown table 't1' in where clause
12611261
drop table t3,t2,t1;
1262+
create table t1(
1263+
id int primary key,
1264+
pid int,
1265+
index(pid),
1266+
foreign key(pid) references t1(id) on delete cascade) type=innodb;
1267+
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
1268+
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
1269+
delete from t1 where id=0;
1270+
Cannot delete or update a parent row: a foreign key constraint fails
1271+
delete from t1 where id=15;
1272+
delete from t1 where id=0;
1273+
drop table t1;

mysql-test/t/innodb.test

+14
Original file line numberDiff line numberDiff line change
@@ -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
896896
--error 1109
897897
update t3 set t3.id=7 where t1.id =1 and t2.id = t1.id and t3.id = t2.id;
898898
drop table t3,t2,t1;
899+
900+
create table t1(
901+
id int primary key,
902+
pid int,
903+
index(pid),
904+
foreign key(pid) references t1(id) on delete cascade) type=innodb;
905+
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
906+
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
907+
-- error 1217
908+
delete from t1 where id=0;
909+
delete from t1 where id=15;
910+
delete from t1 where id=0;
911+
912+
drop table t1;

0 commit comments

Comments
 (0)