PostgreSQL:在plpgsql函数中回滚事务?
我来自MS SQL世界,倾向于大量使用存储过程。我目前正在编写一个应用程序,它使用了很多PostgreSQL plpgsql函数。我想做的是,如果在某个特定函数中的任何一点出现异常,则回滚该函数中包含的所有插入/更新 我最初的印象是,每个函数都封装在自己的事务中,异常会自动回滚所有内容。然而,情况似乎并非如此。我想知道是否应该将保存点与异常处理结合使用?但我并不真正理解事务和保存点之间的区别,不知道这是否是最好的方法。有什么建议吗PostgreSQL:在plpgsql函数中回滚事务?,postgresql,transactions,plpgsql,Postgresql,Transactions,Plpgsql,我来自MS SQL世界,倾向于大量使用存储过程。我目前正在编写一个应用程序,它使用了很多PostgreSQL plpgsql函数。我想做的是,如果在某个特定函数中的任何一点出现异常,则回滚该函数中包含的所有插入/更新 我最初的印象是,每个函数都封装在自己的事务中,异常会自动回滚所有内容。然而,情况似乎并非如此。我想知道是否应该将保存点与异常处理结合使用?但我并不真正理解事务和保存点之间的区别,不知道这是否是最好的方法。有什么建议吗 CREATE OR REPLACE FUNCTION do_so
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;
政府说:
保存点是事务中的一个特殊标记,它允许回滚建立后执行的所有命令,从而将事务状态恢复到保存点时的状态
他们也举了一些例子
编辑:
您需要在BEGIN和COMMIT命令中包装一个
通过使用BEGIN和COMMIT命令围绕事务的SQL命令来设置事务
保存点可用于模拟嵌套事务。因为postgresql事务是一个将被应用或丢弃的语句序列,所以保存点可以在该序列中标记允许回滚到的点
由于不支持真正的嵌套事务,因此这是您的最佳选择(这是一个很好的选择)。函数表示事务。您不必在BEGIN/COMMIT中包装函数。您不能在函数中使用COMMIT或rollback命令,但可以在提交的事务中使用您的函数 开始交易;选择dou_something();承诺
此SQL脚本仅在do_something中没有异常时提交,然后它将回滚函数的事务。我不同意文档是否清晰。“保存点”的描述听起来和我所知道的“事务”完全一样。保存点是原子的吗?据我所知,它们只是事务中的标记,您可以回滚到它们。整个事务是原子的;在提交之前,任何其他事务都看不到任何更改。您能否发布一个函数的示例,该函数不会按预期回滚所有内容?PL/pgSQL函数确实在调用语句的事务上下文中执行,但BEGIN..EXCEPTION块可以修改该行为。如果看不到示例,就很难给出正确的建议。请编辑以添加示例。谢谢。我正在运行8.4.2,创建了一个表A和表B,每个表A和表B有三个int列,运行您的示例(在插入到表B行的末尾有“;”删除),然后它爆炸了。我检查了两张桌子,都是空的。我甚至在这两个函数之间添加了一些调试代码,以验证记录在失败之前是否存在,然后在失败之后是否消失。每个函数都将在它自己的事务中,在当前示例中,不可能在tableA中添加新记录并在tableB中出现错误。这不可能发生,不可能。您不需要如上所述的保存点,只需做一些支持测试,看看事情是如何工作的。在PostgreSQL中,一切都是关于数据完整性的,你不必担心。Matthew——你是对的。我过度降低了代码片段的复杂性,这样做就消除了明显的问题。我将继续测试它,试图找到问题所在。感谢您的时间和帮助。PostgreSQL并非如此。如果单个SQL语句不是作为显式事务的一部分执行(不是在BEGIN和COMMIT/END之间执行),则作为单个事务执行。但是,如果这样的语句调用多个函数,那么所有函数都在一个事务中执行。此外,当您有多个语句事务(显式开始、提交)并且这些语句调用某些过程时,所有这些都将在单个事务中执行。正如其他人所说:保存点才是出路。@Jacek:Joshua是对的,函数确实代表了它自己的事务。回滚两个插入不需要保存点,如果其中一个失败,它们都将失败。@Frank:但那是因为外部事务。在事务中的同一语句或先前语句中调用的其他函数中的插入也将失败。如果不回滚外部事务的其他结果,则无法从函数回滚插入,除非使用保存点。函数体始终在事务中执行,但它本身不是事务。没有办法调用不启动事务的函数。