【问题标题】:Stack depth limit exceeded in PostgresPostgres 中超出堆栈深度限制
【发布时间】:2020-06-12 17:14:07
【问题描述】:
CREATE OR REPLACE FUNCTION verificar_pagina_inicial_final()
  RETURNS trigger AS
$BODY$
BEGIN
IF NEW.pg_inicial < NEW.pg_final THEN
 INSERT INTO artigos(id_artigo,id_editora,tipo_artigo,pg_inicial,pg_final)
VALUES(NEW.id_artigo,NEW.id_editora,NEW.tipo_artigo,NEW.pg_inicial,NEW.pg_final);
END IF;

END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER verifiar_paginas_novo_artigo
  BEFORE INSERT OR UPDATE
  ON artigos
  FOR EACH ROW
  EXECUTE PROCEDURE  verificar_pagina_inicial_final();

当我尝试插入时,它返回给我:

INSERT INTO public.artigos(id_artigo, id_editora, tipo_artigo, pg_inicial, pg_final)
VALUES (30, 3, 'teste', 1, 2);

返回:

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.
CONTEXT:  SQL statement "INSERT INTO artigos(id_artigo,id_editora,tipo_artigo,pg_inicial,pg_final)
VALUES(NEW.id_artigo,NEW.id_editora,NEW.tipo_artigo,NEW.pg_inicial,NEW.pg_final)"
PL/pgSQL function verificar_pagina_inicial_final() line 4 at SQL statement

【问题讨论】:

  • 您的触发器和过程似乎形成了无限递归。 ON INSERT 触发器调用本身在同一张表上执行 INSERT 的 SP。
  • 感谢您的回答......所以当他插入函数时,他再次调用触发器?有什么办法我可以只做一次吗?
  • 那么这个问题回答了吗?

标签: sql database postgresql triggers plpgsql


【解决方案1】:

您的触发器一遍又一遍地插入同一行。有多种方法可以防止这种情况。喜欢:

CREATE TRIGGER verifiar_paginas_novo_artigo
BEFORE INSERT OR UPDATE ON artigos
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)  -- !
EXECUTE FUNCTION verificar_pagina_inicial_final();

见:

EXECUTE FUNCTION 需要 Postgres 11。请参阅:

但是您似乎只想禁止pg_inicial &gt;= pg_final。您可以使用 CHECK 约束来做到这一点:

ALTER TABLE artigos ADD CONSTRAINT pg_final_must_be_greater_than_pg_inicial
CHECK (pg_inicial < pg_final);

CHECK 约束更简单、更快、更可靠。见:

当然,在违反时引发异常,这通常是要走的路。
为了默默地做这件事,你又回到了触发器。更简单:

CREATE OR REPLACE FUNCTION verificar_pagina_inicial_final()
  RETURNS trigger LANGUAGE plpgsql AS
$func$
BEGIN
   IF NEW.pg_inicial < NEW.pg_final THEN
      RETURN NEW;   -- proceed
   ELSE
      RETURN NULL;  -- skip insert / update
   END IF;
END
$func$;

要正常处理INSERT / UPDATEBEFORE 触发器必须RETURN NEW;RETURN NULL 取消该行。

【讨论】:

    【解决方案2】:

    由于您只是“插入”新列,因此触发器函数中的插入是完全没有必要的 - 无论如何,这就是要插入的内容。问题变成了你想要什么然后 IF NEW.pg_initial

    1. 什么都不做,在这种情况下根本没有触发器。
    2. 中止完整的插入语句。在这种情况下创建一个检查 约束并且根本没有触发器。交替“提高 触发器中的“异常”,但这不是一个好计划。
    3. 静默忽略(跳过)此特定行,否则继续 见documentation

    之前触发的行级触发器可以 返回 null 以指示触发器管理器跳过其余的 该行的操作(即,不触发后续触发器,并且 该行不会发生 INSERT/UPDATE/DELETE)。

    其中实现触发功能如下:

    create or replace 
    function verificar_pagina_inicial_final()
      returns trigger 
      language plpgsql
    as $$
    begin
       if new.pg_inicial < new.pg_final 
       then
           return new;
       else 
           return null; 
       end if; 
    end;
    $$ ;
    

    【讨论】:

      【解决方案3】:

      假设这只是插入前的测试,因为您不修改数据,也许

      IF (NEW.pg_inicial < NEW.pg_final) THEN
            RETURN NEW;
      ELSE
            RETURN NULL;
      END IF;
      

      p.s:我会做一个 DB fiddle 来检查这个语法是否正常。 这是小提琴,https://www.db-fiddle.com/f/9iDx4bmJdk5rUzPrFdevAt/2

      Edit2:Erwin Brandstetter 的回答是完整的,你应该添加一个约束而不是这个触发器。

      【讨论】:

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