Commit 13985521 authored by unknown's avatar unknown
Browse files

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 9e6f6198
Loading
Loading
Loading
Loading
+65 −13
Original line number Diff line number Diff line
@@ -370,6 +370,32 @@ row_ins_cascade_ancestor_updates_table(
	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
a cascaded update. */
@@ -615,6 +641,34 @@ row_ins_foreign_report_add_err(
	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
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		err;
	ulint		i;
	char*		ptr;
	char		table_name_buf[1000];
	
	ut_a(thr && foreign && pcur && mtr);

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

	ut_a(ut_strlen(table->name) < 998);
	strcpy(table_name_buf, table->name);
	row_ins_invalidate_query_cache(thr, 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;

	if (node->is_delete && 0 == (foreign->type &
@@ -756,6 +798,16 @@ row_ins_foreign_check_on_constraint(
		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;

	ut_a(index == foreign->foreign_index);
+12 −0
Original line number Diff line number Diff line
@@ -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;
Unknown table 't1' in where clause
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;
+14 −0
Original line number Diff line number Diff line
@@ -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
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;

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;