在Postgres触发函数中异常调用之前执行操作

问题描述 投票:0回答:2

这里是 Postgres 8.4。想象一下 来自 Postgres 文档的代码片段

CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
    -- Check that empname and salary are given
    IF NEW.empname IS NULL THEN
        RAISE EXCEPTION 'empname cannot be null';
    END IF;
    IF NEW.salary IS NULL THEN
        RAISE EXCEPTION '% cannot have null salary', NEW.empname;
    END IF;

    -- Who works for us when she must pay for it?
    IF NEW.salary < 0 THEN
        RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
    END IF;

    -- Remember who changed the payroll when
    NEW.last_date := current_timestamp;
    NEW.last_user := current_user;
    RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;

如果我们想做一些事情,比如登录自定义表,这些例外:

-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
    INSERT INTO my_log_table ('User didn't supplied empname')
    RAISE EXCEPTION 'empname cannot be null';
END IF;

它不会工作,因为我们在

RAISE EXCEPTION
调用之前放置的任何内容都会被回滚撤消
RAISE EXCEPTION
暗示,即我们创建的 my_log_table 行将在调用
RAISE EXCEPTION
后立即被删除。

完成这样的事情的最佳方法是什么?也许捕获我们的自定义异常?

关闭回滚@TRIGGER不是一个选项,我需要它。

postgresql exception triggers plpgsql
2个回答
10
投票

您可以捕获错误/捕获异常。

EXCEPTION
块中,您可以执行其他任何操作,例如
INSERT
进入另一个表。之后,您可以重新引发异常以传播出去,但这会将包括
INSERT
在内的整个事务回滚到日志表(除非异常被包装并捕获在外部函数中)。

可以:

或者,您可以只取消触发触发函数的行并且引发异常。交易中的其他一切都正常进行。

假设这是一个触发器

ON UPDATE
并且您有另一个具有相同结构的表来将失败的
INSERT
写入:

CREATE OR REPLACE FUNCTION emp_stamp()
  RETURNS trigger
  LANGUAGE plpgsql AS
$func$
BEGIN
   -- Check that empname and salary are given
   IF NEW.empname IS NULL THEN
      RAISE EXCEPTION 'empname cannot be null';
   END IF;

-- IF ...

   RETURN NEW;    -- regular end

EXCEPTION WHEN others THEN  -- or be more specific
   INSERT INTO log_tbl VALUES (NEW.*); -- identical table structure
   RETURN NULL;   -- cancel row
END
$func$;

请注意,

NEW
包含异常发生之前行的状态,包括同一函数中先前成功的语句。

触发:

CREATE TRIGGER emp_stamp
BEFORE INSERT OR UPDATE ON tbl
FOR EACH ROW EXECUTE FUNCTION emp_stamp();

1
投票

实际上你有两个选择。

  1. 您可以通过在 raise 上使用某种级别来登录 postgresql 日志 查看手册
  2. 将错误记录在您的应用程序上,而不是记录在数据库事务中。例如,负工资不应该在这样的触发函数中检查。或者应该在插入调用上处理异常。
© www.soinside.com 2019 - 2024. All rights reserved.