过程是否在 PostgreSQL 的事务中运行?

问题描述 投票:0回答:4

如果过程在中间失败,那么从 SP 开头开始的更改是否会隐式回滚,或者我们是否必须编写任何显式代码来确保 SP 仅在事务中运行?

database postgresql stored-procedures transactions relational-database
4个回答
45
投票

该术语经常被错误地用来指代函数

CREATE FUNCTION
),它提供了与其他 RDBMS 提供的“存储过程”相同的功能(甚至更多)。

ISO/IEC 标准中定义的

真正的存储过程 (

CREATE PROCEDURE
) 是在 Postgres 11 中引入的。主要区别(除其他外)是事务处理。参见:

它们中的任何一个总是在事务上下文中运行手册

PostgreSQL 实际上将每条 SQL 语句都视为正在执行 在一笔交易内。如果您不发出

BEGIN
命令,则每个 个别陈述有一个隐含的
BEGIN
并且(如果成功)
COMMIT
包裹在它周围。

功能

...不能

COMMIT
。它们是原子 - 它们成功或失败完全

嗯,PL/pgSQL 中有错误处理。 说明书:

默认情况下,PL/pgSQL 函数中发生的任何错误都会中止 函数和相关事务的执行。你可以 使用

BEGIN
块捕获错误并从中恢复
EXCEPTION
条款。

请勿将 PL/pgSQL

BEGIN
(代码块的开始)与 SQL
BEGIN
(事务的开始)混淆。

流程

...允许

COMMIT
- 立即启动新事务。 手册中对此有详细说明:

新交易以默认交易特征开始 例如事务隔离级别。如果交易是 在循环中提交,可能需要启动新事务 自动具有与前一个相同的特征。这 命令

COMMIT AND CHAIN
ROLLBACK AND CHAIN
可以完成此操作。

例外情况

诸如

VACUUM
CREATE DATABASE
CREATE INDEX CONCURRENTLY
之类的命令不在事务上下文中运行,并且在其中也根本不允许。

有些事情永远无法回滚,包括:

  • 数据写入日志文件

  • 对序列进行的更改。 说明书:

    重要:某些 PostgreSQL 数据类型和函数有特殊规则 关于交易行为。特别是,对 序列(以及使用

    serial
    声明的列的计数器) 对所有其他交易立即可见并且不会滚动 如果进行更改的事务中止,则返回。

  • 准备好的语句(旧的sqlfiddle演示)

  • 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
$$;

出于所有实际目的,在

BEGIN
END
块之间编写的代码在单个事务中执行。因此,如果块中的任何语句失败,则所有先前的语句将自动回滚。您不需要显式编写任何回滚代码。

但是,在某些特殊情况下,人们可以对何时启动/提交/回滚事务进行细粒度控制。请参阅:https://www.postgresql.org/docs/current/plpgsql-transactions.html了解详细信息。


2
投票

摘自Postgresql官方文档:

在 CALL 命令调用的过程中以及匿名代码中 块(DO命令),可以使用 命令提交和回滚。新的交易开始 使用这些命令结束事务后自动执行,因此 没有单独的 START TRANSACTION 命令。 (请注意 BEGIN 和 END 在 PL/pgSQL 中有不同的含义。)

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


0
投票

过程在事务中运行,因此如果出现错误,事务将回滚。

例如,您创建

my_proc()
程序,将
5
设置为
my.var
那么可能会导致
division by zero
错误,如下所示:

CREATE PROCEDURE my_proc(INOUT value INTEGER) AS $$
BEGIN
SET my.var = 5; -- Here
SELECT 1/value INTO value;
END;
$$ LANGUAGE plpgsql;

首先将

2
设置为
my.var
,然后调用
my_proc(1)
,则
5
成功设置为
my.var
,如下所示:

postgres=# SET my.var = 2;
SET
postgres=# CALL my_proc(1);
 my_func
---------
       1
(1 row)
postgres=# SELECT current_setting('my.var');
 current_setting
-----------------
 5
(1 row)

现在,你将

2
设置为
my.var
,然后调用
my_proc(0)
,然后
division by zero 
发生错误,那么
my.var
仍然是
2
,如下所示,因为
my_proc()
过程已回滚:

postgres=# SET my.var = 2;
SET
postgres=# CALL my_proc(0);
ERROR:  division by zero
...
postgres=# SELECT current_setting('my.var');
 current_setting
-----------------
 2
(1 row)
© www.soinside.com 2019 - 2024. All rights reserved.