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

问题描述:

Postgres 8.4在这里。试想this code snippet from Postgres doc在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是不是一种选择,我需要它。

+1

你真正想要的是一个子事务(东西大致相当于Oracle的'编译autonomous'不幸的是,这仍然只是一个提议,尚未实现(Postgres的9.3 )。你可以看看这个[post](http://raghavt.blogspot.com.au/2012/05/autonomous-transaction-in-postgresql-91.html),详细描述了一个通用的解决方法。 – Mureinik 2014-09-03 19:30:28

+0

这可以工作,虽然我感觉在这种特殊情况下会像使用大锤来破解坚果:) – jpp1jpp1 2014-09-03 20:11:47

+0

当您从*任何*来源添加长引文时,还请添加一个链接到来源。我添加了Postgres 8.4手册的链接。 – 2014-09-03 20:21:03

您可以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(); 
+0

内完成这个工作,我最终会使用类似的东西,我失去了在应用程序级别捕获并显示的异常,但至少启动触发器的plpgsql函数可以返回一个错误的价值应用程序(因为它没有检测到行更新-FOUND-) 我发现其他更复杂的解决方案不值得的麻烦 – jpp1jpp1 2014-09-04 08:38:29

实际上你有两个选择。

  1. 您可以通过使用上扬一定程度上检查manual
  2. 登录错误您的应用程序,而不是在Db的事务日志到PostgreSQL的日志。例如,不应该像这样在触发函数中检查负向工资。或者应该在插入调用上处理异常。
+0

1.事实上,我试图保留的不是log_add(它只是一个例子),而是另一种不同的东西(插入另一个表格)。 – jpp1jpp1 2014-09-03 20:06:03

+0

2.我意识到这一点,但在应用程序级别执行操作会比较慢,因为我必须使用更多数据库查询来检查条件。我只是想知道postgresql是否有办法在TRIGGERS – jpp1jpp1 2014-09-03 20:08:47