Trigger raising: "ERROR: stack depth limit exceeded"

Trigger raising: "ERROR: stack depth limit exceeded"

我想在插入一幅画后创建一个触发器,然后我想将它插入到 In_Gallery table 或 On_Loan table但不是两者。当我尝试制作触发器功能时,我不断收到错误消息:

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();

你在 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;

其他触发功能为模拟。

你错误的直接原因是一个无限循环,就像当前接受的答案解释的那样。但你应该修复的可能不止于此。 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();
对于 AFTER 触发器,

RETURN NEW 根本 没有任何意义。 The manual:

The return value is ignored for row-level triggers fired after an operation, and so they can return NULL.

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

相关:

  • PL/pgSQL checking if a row exists

  • Rollback Transaction on Trigger ERROR

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

无论你怎么做,都会有剩余的竞争条件。在并发写入负载下,多个事务可能会尝试几乎同时在两个 table 中输入相同的 p_id,而不会在 other[= 中看到 p_id 63=] table,然而,并在 both table 中输入它。它有助于保持交易简短以最大限度地减少时间框架,但问题仍然存在原则。

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

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

  • Are PostgreSQL column names case-sensitive?