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

Postgresql 在Postgres触发器函数中执行异常调用之前的操作,postgresql,exception,triggers,plpgsql,Postgresql,Exception,Triggers,Plpgsql,这里是博士后8.4分。想象一下: 它不起作用,因为我们在引发异常调用之前放置的任何内容都会被回滚引发异常所撤消,即,我们创建的my_log_表行将在调用引发异常后立即被删除 完成这样的事情最好的方法是什么?也许能捕捉到我们的自定义异常 关闭rollback@TRIGGER不是一个选项,我需要它。实际上您有两个选项 您可以使用raise检查上的某个级别登录到postgresql日志 将错误记录在应用程序上,而不是数据库事务中。例如,不应该像这样在触发函数中检查负工资。或者应该在insert调用中处

这里是博士后8.4分。想象一下:

它不起作用,因为我们在
引发异常
调用之前放置的任何内容都会被回滚
引发异常
所撤消,即,我们创建的my_log_表行将在调用
引发异常
后立即被删除

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


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

实际上您有两个选项

  • 您可以使用raise检查上的某个级别登录到postgresql日志
  • 将错误记录在应用程序上,而不是数据库事务中。例如,不应该像这样在触发函数中检查负工资。或者应该在insert调用中处理异常
  • 您可以/捕获异常

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

    你可以:

    • 使用诸如dblink调用之类的技巧来模拟自治事务,在回滚包装事务时,该事务不会撤消。相关的:

    • RAISE
      a
      NOTICE
      WARNING
      另外,ROOLBACK也不会撤消该操作

    • RAISE
      使用您自己的文本创建一个不同的
      异常
    或者,您可以取消触发触发函数的行,而不引发异常。交易中的其他一切正常进行

    假设这是更新时的触发器
    ,并且您有另一个具有相同结构的表来写入失败的插入:

    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;

    您真正想要的是一个子事务(大致相当于Oracle的
    pragma autonomy
    。不幸的是,这仍然只是一个建议,尚未实施(Postgres 9.3)。您可以查看这篇详细介绍常见解决方法的文章。这可能会奏效,尽管我觉得在这种特殊情况下,这就像用大锤敲开螺母一样:)当您添加来自任何来源的长引用时,请同时添加指向该来源的链接。我添加了Postgres 8.4手册的链接。事实上,我试图保留的不是日志添加(这只是一个示例),而是一些不同的内容(在另一个表中插入)。我知道这一点,但在应用程序级别执行此操作会比较慢,因为我必须使用更多的DB查询来检查条件。我只是想知道postgresql是否有办法在trigger中实现这一点。我最终会使用类似的方法,我丢失了在应用程序级别捕获并显示的异常,但至少触发触发器的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;
    
    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;
    CREATE TRIGGER emp_stamp
    BEFORE INSERT OR UPDATE ON tbl
    FOR EACH ROW EXECUTE PROCEDURE emp_stamp();