Postgres中的存储过程是否在数据库事务中运行?

24

如果存储过程在中间失败了,那么从存储过程开始到失败点的更改会隐式回滚吗?还是我们需要编写任何显式代码来确保存储过程只在数据库事务中运行?


可能是重复的问题:将PostgreSQL存储过程作为一个事务执行 - Mitch Wheat
2
所有的内容都在手册中有详细介绍,包括在PostgreSQL中实际上没有存储过程这一事实。 - Richard Huxton
3个回答

45
严格来说,在11版本之前,Postgres并没有像ISO/IEC标准中定义的存储过程。这个术语经常被错误地用来指代函数,它们提供了与其他关系型数据库管理系统提供的“存储过程”相同(甚至更多)的功能,唯一的区别在于事务处理。 真正的存储过程最终在Postgres 11中引入:

函数在Postgres中是原子性的它们始终在事务上下文中运行,并且要么完全成功,要么完全失败。 因此,在函数内部无法开始或提交事务。而不在事务上下文中运行的命令,如VACUUMCREATE DATABASECREATE INDEX CONCURRENTLY是不允许的。

关于PL/pgSQL的手册:

函数和触发器过程总是在外部查询建立的事务中执行 - 它们不能启动或提交该事务,因为它们没有执行的上下文。然而,包含EXCEPTION子句的块实际上形成了一个可以回滚而不影响外部事务的子事务。

错误处理:

默认情况下,PL/pgSQL函数中发生的任何错误都会中止函数的执行,甚至中止周围事务的执行。您可以使用带有BEGIN块和EXCEPTION子句来捕获错误并从中恢复。
这里有一些例外情况,包括但不限于:
  • 数据写入日志文件

  • 对序列进行的更改

    重要提示:某些 PostgreSQL 数据类型和函数在事务行为方面有特殊规则。特别是,对序列的更改(因此也包括使用 serial 声明的列的计数器)立即对所有其他事务可见,并且如果进行更改的事务中止,则不会回滚。

  • 预编译语句
    SQL Fiddle 演示

  • dblink 调用(或类似操作)


5
如果您正在使用类似如下的 Postgres 14 存储过程:
CREATE OR REPLACE PROCEDURE test_error(schema_name text)
LANGUAGE plpgsql
AS
$$
declare
<declare any vars that you need>
BEGIN
<do your thing>
END
$$;

实际上,在BEGINEND块中编写的代码将在一个事务中执行。因此,如果块中的任何语句失败,所有先前的语句都将自动回滚。您不需要显式编写任何回滚代码。
然而,有些特殊情况下,你可以对何时开始/提交/回滚事务进行细粒度控制。详见:https://www.postgresql.org/docs/current/plpgsql-transactions.html

2

来自Postgresql的官方文件:

在由CALL命令调用的过程以及匿名代码块(DO命令)中,可以使用COMMIT和ROLLBACK命令结束事务。在使用这些命令结束事务后,会自动启动一个新的事务,因此没有单独的START TRANSACTION命令。(请注意,PL/pgSQL中BEGIN和END具有不同的含义。)

https://www.postgresql.org/docs/11/plpgsql-transactions.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接