PostgresQL 中超出堆栈深度限制(删除触发器后)
Posted
技术标签:
【中文标题】PostgresQL 中超出堆栈深度限制(删除触发器后)【英文标题】:Stack Depth Limit exceeded in PostgresQL (After Delete Trigger) 【发布时间】:2014-08-23 18:33:06 【问题描述】:CREATE TABLE parent (
parent_id VARCHAR(255) PRIMARY KEY
);
CREATE TABLE child (
parent_id VARCHAR(255) REFERENCES parent ON DELETE CASCADE,
child_id VARCHAR(255) PRIMARY KEY
);
CREATE OR REPLACE FUNCTION delete_parent()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM parent WHERE parent_id = OLD.parent_id;
RETURN NULL;
END; $$ LANGUAGE 'plpgsql';
CREATE TRIGGER delete_parent AFTER DELETE
ON child
FOR EACH ROW
EXECUTE PROCEDURE delete_parent();
错误:
stack depth limit exceeded
提示:'在确保平台的堆栈深度限制足够之后,增加配置参数“max_stack_depth”(当前为 6144kB)。'
背景:
一个parent
可以有多个children
该架构设计为,如果parent
被删除,它的所有子记录也会被删除。
如果删除了child
,触发器将删除parent
,然后级联删除与该parent
相关的所有其他children
这已经工作了几个月,今天突然我们开始收到这个错误。
我找不到可能存在无限递归的情况,我正在考虑将堆栈深度限制加倍以查看会发生什么。
注意:实际的架构比这更复杂,并且有更多的相关表具有 CASCADE 删除约束。但这是唯一的触发因素。
更新:所以我将 max_stack_depth 限制加倍,现在可以了。我认为这不是一个好的解决方案,我仍然不确定如何防止这种情况在未来发生。
【问题讨论】:
通过 child->parent FK 的级联删除将导致如果删除父级,则所有子级都将被删除。你的触发器正好相反。所以一旦一个孩子被删除,它的父母和它的兄弟姐妹就会被删除。这是你的意图吗? 没错。如果孩子被删除,请删除父母及其所有 silbings 顺便说一句:你为什么使用 varchar(255) 字段作为 PK 和 FK ? 它实际上不是 VARCHAR(255)。服务器现在着火了,所以我只是快速编写了一个人为的示例,而不是复制真实的模式。它是一个 UUID。 【参考方案1】:到目前为止,你就是这样:
-
删除 child1。
触发删除父项。
删除
DELETE CASCADE
的 n
child1 的兄弟姐妹。
调用相同的触发器 n
次。
没有兄弟姐妹了。
没有无限循环,但仍然 n
次调用触发器。这可以解释为什么超出了堆栈深度限制,但您可以通过增加限制来修复它。更大的n
可能会再次发生同样的情况。
或者,将您的触发器替换为:
CREATE OR REPLACE FUNCTION delete_family()
RETURNS TRIGGER AS
$func$
BEGIN
DELETE FROM child WHERE parent_id = OLD.parent_id;
DELETE FROM parent WHERE parent_id = OLD.parent_id; -- done after 1st call
RETURN NULL;
END
$func$ LANGUAGE plpgsql; -- don't quote the language name!
CREATE TRIGGER delete_family
AFTER DELETE ON child
FOR EACH ROW EXECUTE PROCEDURE delete_family();
并将 FK 约束替换为 没有 ON DELETE CASCADE
的版本。代码示例:
现在,对于DELETE
全家人,你不能像以前那样删除父级(现在被 FK 禁止)。取而代之的是 DELETE
任何孩子。
也应该更快。
【讨论】:
嗨@erwin 感谢您的解决方案。似乎唯一改变的是添加了DELETE FROM child WHERE parent_id = OLD.parent_id;
为什么会减少调用次数?
此外,当父级被删除时,它会级联到子级,并且子级删除触发尝试再次删除父级(此时不再存在)
@samol:抱歉,我忘了添加基本细节:从 FK 约束中删除 ON DELETE CASCADE
。
但我需要 on delete 级联触发器,因为有时我删除父级,我需要删除级联
@samol:你可以放弃任何孩子来代替同样的效果。另外,我不完全确定您的问题不是由您提到的其他触发器/约束引起的。以上是关于PostgresQL 中超出堆栈深度限制(删除触发器后)的主要内容,如果未能解决你的问题,请参考以下文章