PostgreSQL 函数是事务性的吗?

Posted

技术标签:

【中文标题】PostgreSQL 函数是事务性的吗?【英文标题】:Are PostgreSQL functions transactional? 【发布时间】:2012-09-28 11:54:34 【问题描述】:

以下 PostgreSQL 函数是自动事务性的吗?

CREATE OR REPLACE FUNCTION refresh_materialized_view(name)
  RETURNS integer AS
$BODY$
 DECLARE
     _table_name ALIAS FOR $1;
     _entry materialized_views%ROWTYPE;
     _result INT;
 BEGIN          

     EXECUTE 'TRUNCATE TABLE ' || _table_name;

     UPDATE materialized_views
     SET    last_refresh = CURRENT_TIMESTAMP
     WHERE  table_name = _table_name;

     RETURN 1;
END
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER;

换句话说,如果函数执行过程中发生错误,是否会回滚所做的更改?如果这不是默认行为,我怎样才能使函数事务性

【问题讨论】:

@FrankHeikens 我的问题是“发生错误后更改是否会自动回滚”,而不是“如果我执行 ROLLBACK 会回滚更改” @Don 请注意,TRUNCATE 有或曾经有一些有点时髦的事务行为。我不记得细节了;搜索 pgsql-general 档案。 是的,函数是事务性的,即使写成LANGUAGE SQL @CraigRinger 据我所知,TRUNCATE 会忽略所有保存点并直接破坏您的表数据 【参考方案1】:

此外,ATOMIC 事务也包括触发器。

【讨论】:

【参考方案2】:

PostgreSQL 12 更新:there is limited support for top-level PROCEDUREs that can do transaction control。您仍然无法在常规 SQL 可调用函数中管理事务,因此除非使用新的***过程,否则以下内容仍然适用。


函数是调用它们的事务的一部分。如果事务回滚,它们的效果也会回滚。如果事务提交,他们的工作就会提交。函数中的任何BEGIN ... EXCEPT 块都像SAVEPOINTROLLBACK TO SAVEPOINT SQL 语句一样运行(并且在后台使用)保存点。

该函数要么全部成功,要么全部失败,除非BEGIN ... EXCEPT 错误处理。如果在函数中引发错误并且未处理,则调用该函数的事务将被中止。中止的事务无法提交,如果他们尝试提交 COMMIT 将被视为 ROLLBACK,与任何其他错误事务相同。观察:

regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR:  division by zero
regress=# COMMIT;
ROLLBACK

看看由于零除法而处于错误状态的事务如何在COMMIT上回滚?

如果你调用一个没有显式周围事务的函数,规则与任何其他 Pg 语句的规则完全相同:

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

(如果SELECT 引发错误,COMMIT 将失败)。

PostgreSQL (目前)不支持函数中的自治事务,其中过程/函数可以独立于调用事务提交/回滚。这可以通过dblink 使用新会话进行模拟。

但是,PostgreSQL 中存在非事务性或不完全事务性的事物。如果它在普通的BEGIN; do stuff; COMMIT; 块中具有非事务性行为,那么它在函数中也具有非事务性行为。例如nextvalsetvalTRUNCATE等。

【讨论】:

解释得很清楚。特别感谢您用简短的例子来说明您的答案。 我相信您现在可以在过程中执行实际提交,因为 PG11. blog.dbi-services.com/… (更多信息请参见官方文档:postgresql.org/docs/12/plpgsql-transactions.html @craig-ringer 您所说的对程序的“有限”支持是什么意思?为什么会受到限制?【参考方案3】:

由于我对 PostgreSQL 的了解不如 Craig Ringer 的深入,我将尝试给出一个简短的答案:是的。

如果您执行的函数有错误,则所有步骤都不会影响数据库。

此外,如果您在 PgAdmin 中执行查询,也会发生同样的情况。

例如,如果您在查询中执行:

update your_table yt set column1 = 10 where yt.id=20;

select anything_that_do_not_exists;

your_table 的行中的更新,id = 20 将不会保存在数据库中。

2018 年 9 月更新

为了阐明这个概念,我用非事务性函数 nextval 做了一个小例子。

首先,让我们创建一个序列:

create sequence test_sequence start 100;

然后,让我们执行:

update your_table yt set column1 = 10 where yt.id=20; select nextval('test_sequence'); select anything_that_do_not_exists;

现在,如果我们打开另一个查询并执行

select nextval('test_sequence');

我们将得到 101,因为在后一个查询中使用了第一个值 (100)(即因为序列不是事务性的),尽管更新没有提交。

【讨论】:

感谢您清晰明了的回答!阅读克雷格的回答后,我感到不确定。 比克雷格的答案好得多 但要注意像nextval 这样的非事务性事物;如果它们不像普通查询那样具有事务性或不完全事务性,它们也不在函数中。【参考方案4】:

https://www.postgresql.org/docs/current/static/plpgsql-structure.html

重要的是不要将在 PL/pgSQL 中用于分组语句的 BEGIN/END 与用于事务控制的类似名称的 SQL 命令混淆。 PL/pgSQL 的 BEGIN/END 仅用于分组;他们不会开始或结束交易。函数和触发器过程总是在由外部查询建立的事务中执行——它们不能启动或提交该事务,因为它们没有执行的上下文。但是,包含 EXCEPTION 子句的块有效地形成了一个子事务,可以回滚而不影响外部事务。有关详细信息,请参阅第 39.6.6 节。

【讨论】:

【参考方案5】:

在功能层面,它不是跨国的。换句话说,函数中的每条语句都属于单个事务,这是默认的 db auto commit 值。自动提交默认为真。但无论如何,您必须使用

调用该函数

select schemaName.functionName()

上面的语句'select schemaName.functionName()'是单个事务,我们把事务命名为T1,那么函数中的所有语句都属于事务T1。这样,函数就在单个事务中。

【讨论】:

以上是关于PostgreSQL 函数是事务性的吗?的主要内容,如果未能解决你的问题,请参考以下文章

请问 PostgreSQL中有 sys_guid()函数吗?或者类似的吗

PostgreSQL:在 plpgsql 函数中回滚事务?

从烧瓶中的 PostgreSQL 函数提交事务

Postgresql 学习记录,模式,分区表,触发器,事务,窗口函数,视图,建表,约束等

02-PostgreSQL 存储过程的进阶介绍(含游标错误处理自定义函数事务)

postgreSQL