PostgreSQL:在 plpgsql 函数中回滚事务?
Posted
技术标签:
【中文标题】PostgreSQL:在 plpgsql 函数中回滚事务?【英文标题】:PostgreSQL: Rolling back a transaction within a plpgsql function? 【发布时间】:2010-03-11 02:51:16 【问题描述】:来自 MS SQL 世界,我倾向于大量使用存储过程。我目前正在编写一个使用大量 PostgreSQL plpgsql 函数的应用程序。如果我在其中的任何时候遇到异常,我想做的是回滚包含在特定函数中的所有 INSERTS/UPDATES。
我最初的印象是每个函数都包装在它自己的事务中,并且异常会自动回滚所有内容。然而,情况似乎并非如此。我想知道我是否应该将保存点与异常处理结合使用?但我并不真正了解事务和保存点之间的区别,不知道这是否是最好的方法。请问有什么建议吗?
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;
【问题讨论】:
你能发布一个函数的例子,它不会像你期望的那样回滚所有东西吗? PL/pgSQL 函数 do 在调用语句的事务上下文中执行,但 BEGIN..EXCEPTION 块可以修改该行为。没有看到一个例子,很难给出正确的建议。 编辑添加示例。谢谢。 我正在运行 8.4.2,创建了 tableA 和 B,每个都有三个 int 列,运行你的示例(在 INSERT INTO tableB 行的末尾删除了“;”)并且它被炸毁了。我检查了两张桌子,它们都是空的。我什至在两者之间添加了一些调试代码,以验证记录在失败之前就在那里,然后在失败之后就消失了。 每个函数都在它自己的事务中,当前的例子不可能在tableA中添加一条新记录并且在tableB中出现错误。这不可能发生,不可能。而且您不需要如上所述的保存点,只需进行一些适当的测试,看看事情是如何工作的。在 PostgreSQL 中,一切都与数据完整性有关,您不必担心。 马修——你是对的。我过度降低了我的代码 sn-p 的复杂性,这样做消除了明显的问题。我将继续对其进行测试以尝试定位问题。感谢您的时间和帮助。 【参考方案1】:一个函数确实代表一个交易。您不必在 BEGIN/COMMIT 中包装函数。
【讨论】:
PostgreSQL 不是这样。单个 SQL 语句,当不作为显式事务的一部分执行时(不在 BEGIN 和 COMMIT/END 之间执行),将作为单个事务执行。但是如果这样的语句调用了多个函数,那么所有的函数都在一个事务中执行。此外,当您有多个语句事务(显式 BEGIN、COMMIT)并且这些语句调用一些过程时,所有这些都将在单个事务中执行。正如其他人所说:保存点是要走的路。 @Jacek:Joshua 是对的,一个函数确实代表了它自己的事务。您不需要保存点来回滚两个插入,如果一个失败,它们都会失败。 @Frank:但那是因为外部交易。事务中同一语句或先前语句中调用的其他函数中的插入也将失败。除非您使用保存点,否则您无法在不回滚外部事务的其他结果的情况下从函数回滚插入。函数体总是在一个事务中执行,但它本身并不是一个事务。没有办法调用不启动事务的函数。【参考方案2】:你不能在函数中使用commit或rollback命令,但是你可以在一个提交的事务中使用你的函数,
开始交易;选择 do_something();提交;
这个SQL脚本只有在do_something没有异常的情况下才会提交,然后,它会回滚函数的事务。
【讨论】:
【参考方案3】:保存点可用于模拟嵌套事务。因为 postgresql 事务是一个语句序列,它们要么被应用要么被丢弃,所以保存点可以标记该序列中允许回滚的点。
由于不支持真正的嵌套事务,因此这是您最好的选择(也是一个不错的选择)。
【讨论】:
【参考方案4】:docs 这么说:
保存点是事务中的一个特殊标记,它允许在建立后执行的所有命令回滚,将事务状态恢复到保存点时的状态。
他们也举了例子。
编辑:
您需要在 BEGIN 和 COMMIT 命令中包装 transaction。
通过使用 BEGIN 和 COMMIT 命令围绕事务的 SQL 命令来设置事务
【讨论】:
我不同意文档很清楚。 “保存点”的描述听起来正是我所知道的“交易”。保存点是原子的吗? 据我了解,它们只是事务中可以回滚的标记。整个事务是原子的;在提交之前,任何其他事务都看不到任何更改。以上是关于PostgreSQL:在 plpgsql 函数中回滚事务?的主要内容,如果未能解决你的问题,请参考以下文章
Postgresql - 从 plpgsql 函数返回记录 []
在 plpgsql(PostgreSQL 的)中,可以将 CTE 保留到外循环吗?
PostgreSQL - 继续 unique_violation (plpgsql)