2010-03-11 161 views
10

来自MS SQL世界,我倾向于大量使用存储过程。我目前正在编写一个应用程序,它使用了很多PostgreSQL plpgsql函数。我想要做的就是回滚特定函数中包含的所有INSERT/UPDATES,如果我在其中的任何位置发生异常。PostgreSQL:回滚plpgsql函数中的事务?

我最初的印象是,每个函数都包含在自己的事务中,并且异常会自动回滚所有内容。但是,情况似乎并非如此。我想知道是否应该将保存点与异常处理结合使用呢?但我并不真正理解事务和保存点之间的区别,以了解这是否是最佳方法。有什么建议吗?

CREATE OR REPLACE FUNCTION do_something(
     _an_input_var int 
       ) RETURNS bool AS $$ 
     DECLARE 
       _a_variable int; 
     BEGIN 
       INSERT INTO tableA (col1, col2, col3) 
         VALUES (0, 1, 2); 

       INSERT INTO tableB (col1, col2, col3) 
         VALUES (0, 1, 'whoops! not an integer'); 

       -- The exception will cause the function to bomb, but the values 
       -- inserted into "tableA" are not rolled back.  

       RETURN True; 
END; $$ LANGUAGE plpgsql; 
+0

你可以发布一个函数的例子,不会像你期望的那样回滚一切吗?在调用语句的事务上下文中执行PL/pgSQL函数* do *,但BEGIN..EXCEPTION块可以修改该行为。没有看到一个例子,很难给出适当的建议。 – 2010-03-11 18:19:40

+0

编辑添加示例。谢谢。 – jamieb 2010-03-11 18:33:52

+0

我正在运行8.4.2,创建tableA和B,每个都有三个int列,运行您的示例(在INSERT INTO tableB行结束时删除“;”)并将其轰炸。我检查了两张桌子,他们都是空的。我甚至在两者之间添加了一些调试代码来验证记录在失败之前是否存在,然后在失败之后失效。 – 2010-03-11 19:00:03

回答

13

函数确实表示一个事务。您不必在BEGIN/COMMIT中包装功能。

+7

这对于PostgreSQL来说并非如此。单个SQL语句在未作为显式事务的一部分执行时(不在BEGIN和COMMIT/END之间执行)作为单个事务执行。但是如果这样的语句调用多个函数,那么所有的函数都在一个事务中执行。另外,当你有多个语句事务(显式BEGIN,COMMIT)并且这些语句调用某些过程时,所有这些都将在单个事务中执行。正如其他人所说:保存点是要走的路。 – 2010-03-11 18:32:27

+2

@Jacek:Joshua是对的,一个函数确实代表了它自己的事务。您不需要保存点来回滚两个插入,如果一个失败,它们都会失败。 – 2010-03-11 19:08:47

+10

@Frank:但那是因为外部交易。在同一个语句或事务中的前面的语句中调用的其他函数中的插入也会失败。除非使用保存点,否则无法回滚该函数的插入而无需回滚外部事务的其他结果。 函数体总是在一个事务中执行,但它本身不是一个事务。没有办法调用不启动事务的函数。 – 2010-03-12 07:30:52

1

docs这样说:

保存点是事务,允许它的成立是为了被回滚之后执行的所有命令中一个特殊的标记,该交易状态恢复到什么当时是保存点。

他们也举例说明。

编辑:

你需要在BEGIN和COMMIT命令来包装一个transaction

事务由事务的SQL命令以BEGIN和COMMIT命令周围设置

+0

我不同意文档清晰。 “保存点”的描述听起来正是我所知道的“交易”。保存点是原子吗? – jamieb 2010-03-11 17:56:31

+0

据我所知,它们只是您可以回退的交易中的标记。整个交易是原子的;直到提交为止,任何其他交易都不会看到任何更改。 – tom 2010-03-11 23:58:27

1

保存点可以被用于模拟嵌套事务。因为postgresql事务是一系列将被应用或丢弃的语句,所以保存点可以在该序列中标记允许回滚的点。

由于不支持真正的嵌套交易,这是你最好的选择(也是最好的选择)。

+0

您是否尝试过在plpgsql函数中使用保存点? – jamieb 2010-03-11 18:10:22

4

不能使用commit或rollback命令进入功能,但你可以使用你的函数成提交的事务,

BEGIN TRANSACTION; SELECT do_something();承诺;

该SQL脚本仅在do_something中没有异常的情况下提交,然后它将回滚该函数的事务。