【问题标题】:Perform action before exception call in Postgres trigger function在 Postgres 触发函数中调用异常之前执行操作
【发布时间】:2014-09-03 17:36:49
【问题描述】:

这里是 Postgres 8.4。想象一下this code snippet from Postgres doc

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 不是一个选项,我需要它。

【问题讨论】:

  • 你真正想要的是一个子事务(大致相当于 Oracle 的 pragma autonomous。不幸的是,这仍然只是一个提议,还没有实现(Postgres 9.3)。你可以检查不过,这个post 详细说明了一个常见的解决方法。
  • 这可能行得通,虽然我感觉在这种特殊情况下就像用大锤敲碎坚果:)
  • 当您从任何来源添加长引用时,请同时添加指向该来源的链接。我添加了 Postgres 8.4 手册的链接。

标签: postgresql exception triggers plpgsql


【解决方案1】:

你可以trap errors/捕获异常。

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

可以

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

假设这是一个触发器ON UPDATE,并且您有另一个具有相同结构的表可以将失败的 INSERT 写入:

CREATE OR REPLACE FUNCTION emp_stamp()
  RETURNS trigger 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$ LANGUAGE plpgsql;

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

触发器:

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

【讨论】:

  • 我最终会使用这样的东西,我丢失了在应用程序级别捕获并显示的异常,但至少触发触发器的 plpgsql 函数可以向应用程序返回错误值(因为它检测到没有更新的行-FOUND-) 我发现其他更复杂的解决方案不值得麻烦
【解决方案2】:

实际上你有两个选择。

  1. 您可以通过在 raise 上使用某个级别来登录到 postgresql 日志检查manual
  2. 在您的应用程序中记录错误,而不是在 Db 事务中。例如,不应该像这样在触发器函数中检查负工资。或者应该在插入调用时处理异常。

【讨论】:

  • 1.实际上,我要保留的不是 log_add(这只是一个示例),而是不同的东西(插入另一个表)。
  • 2.我知道这一点,但是在应用程序级别执行此操作会更慢,因为我必须使用更多数据库查询来检查条件。我只是想知道 postgresql 是否有办法在 TRIGGERS 中做到这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
  • 2014-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多