【问题标题】:Trigger raising: "ERROR: stack depth limit exceeded"触发引发:“错误:超出堆栈深度限制”
【发布时间】:2020-03-25 11:40:14
【问题描述】:

我正在尝试在插入绘画后触发触发器,然后我想将其插入In_Gallery 表或On_Loan 表,但不能同时插入两者。当我尝试制作触发功能时,我不断收到错误消息:

ERROR: stack depth limit exceeded
HINT:  Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.

我不确定这有什么问题:

    CREATE OR REPLACE FUNCTION checkOnLoan()    
    RETURNS trigger AS
$$
    DECLARE    
       countGal numeric;
    BEGIN
            SELECT COUNT(*) INTO countGal FROM IN_GALLERY WHERE P_id = new.P_id;
            IF countGal = 0 THEN    
                INSERT INTO ON_LOAN VALUES (new.Certid, new.P_id, new.Insurer);
            ELSE
                RAISE EXCEPTION 'ALREADY IN GALLERY';
            END IF;
    RETURN new;
    END;
$$

LANGUAGE 'plpgsql';

CREATE TRIGGER OnLoan
    AFTER INSERT ON ON_LOAN
    FOR EACH ROW
    EXECUTE PROCEDURE checkOnLoan();

【问题讨论】:

  • 你有一个递归调用。您的插入之一是插入到表中,它正在触发触发器,它正在插入到表中,这会触发触发器......等等。这些层中的每一层都需要堆栈空间。最终,你用完了。
  • @3Dave 您好,感谢您的帮助,我该怎么做才能摆脱它,过去 4 个小时里我的大脑一直看着这个问题。

标签: sql postgresql plpgsql database-trigger


【解决方案1】:

您在AFTER INSERT 触发器中再次INSERT,导致第二次INSERT 再次触发触发器,然后再次触发INSERTs 并重新触发触发器,依此类推。在某些时候,所有函数调用都耗尽了堆栈,您会收到错误消息。

从触发函数中删除INSERT,只删除RETURN new。返回new 将导致原始INSERT 完成。 AFTER INSERT触发器的触发器函数中不需要手动INSERT

喜欢:

CREATE OR REPLACE FUNCTION checkOnLoan()    
RETURNS trigger AS
$$
DECLARE    
    countGal numeric;
BEGIN
    SELECT COUNT(*) INTO countGal FROM IN_GALLERY WHERE P_id = new.P_id;
    IF countGal = 0 THEN    
        RETURN new;
    ELSE
        RAISE EXCEPTION 'ALREADY IN GALLERY';
    END IF;
END;
$$
LANGUAGE plpgsql;

和模拟用于其他触发功能。

【讨论】:

  • 需要注意的是RETURN new;在行级AFTER触发器中没有做任何事情。
【解决方案2】:

您的错误的直接原因是一个无限循环,就像当前接受的答案所解释的那样。但是您可能应该修复的不仅仅是这些。 BEFORE 触发器会改善这种情况...

触发功能:

CREATE OR REPLACE FUNCTION check_onloan()
  RETURNS trigger AS
$$
BEGIN
   IF EXISTS (SELECT FROM in_gallery WHERE p_id = NEW.p_id) THEN
      RAISE EXCEPTION 'p_id % already in gallery!', NEW.p_id;
   END IF;
   RETURN NEW;  -- for BEFORE trigger
END
$$  LANGUAGE plpgsql;

触发器:

CREATE TRIGGER insert_after_on_loan
BEFORE INSERT ON on_loan             -- !!!
FOR EACH ROW EXECUTE PROCEDURE check_onloan();

RETURN NEW 对于AFTER 触发器根本没有任何意义The manual:

操作后触发的行级触发器将忽略返回值,因此它们可以返回NULL

我有根据的猜测:您需要一个 BEFORE 触发器。剩下要做的就是引发异常。 在完成工作之前检查比以后回滚要便宜。为此,使用IF EXISTS ... 检查是否存在通常比计数更有效。然后你不需要定义任何变量,也不需要DECLARE 部分。

相关:

显然,在此设计中,您需要另一个表 in_gallery 的镜像触发器 - 开始时可能并不理想。

无论你怎么做,都会有剩余的比赛条件。在并发写入负载下,多个事务可以尝试几乎同时在两个表中输入相同的p_id,但在其他表中看不到p_id,然后在两个表。它有助于缩短交易时间以最小化时间范围,但原则上问题仍然存在。

一个干净的解决方案是单个表 painting 和一个 boolean 标志指示其状态。那一次只能有一个状态。细节取决于您的完整情况...

旁白:重新考虑 Postgres 中标识符的 CaMeL 大小写拼写。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多