【问题标题】:Stack Depth Limit exceeded in PostgresQL (After Delete Trigger)PostgresQL 中超出堆栈深度限制(删除触发器后)
【发布时间】: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。

标签: postgresql


【解决方案1】:

到目前为止,你就是这样:

  1. 删除 child1。
  2. 触发删除父项。
  3. 删除 DELETE CASCADEn child1 的兄弟姐妹。
  4. 调用相同的触发器 n 次。
  5. 没有兄弟姐妹了。

没有无限循环,但仍然 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:你可以放弃任何孩子来代替同样的效果。另外,我不完全确定您的问题不是由您提到的其他触发器/约束引起的。
猜你喜欢
  • 2020-07-27
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
相关资源
最近更新 更多