PostgreSQL函数是事务性的吗?


73

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;


换句话说,如果在函数执行期间发生错误,是否会回滚任何更改?如果这不是默认行为,如何使该函数具有事务性


5
你为什么不自己测试呢?只需进行回滚,您就会知道答案是什么。应该是“是”。
Frank Heikens 2012年

9
@FrankHeikens我的问题是“将在发生错误后自动回滚更改”,而不是“如果执行ROLLBACK将回滚更改”
2012年

2
@Don请注意,TRUNCATE过去曾经或曾经有一些时髦的交易行为。我不记得具体细节。搜索pgsql-general档案。
Craig Ringer

1
是的,即使是用编写的,函数也是事务性的LANGUAGE SQL
ma11hew28 2014年

据我所知,@ CraigRinger会TRUNCATE忽略所有保存点,而直接破坏您的表数据
G_V

Answers:


82

PostgreSQL 12更新PROCEDURE可以执行事务控制的顶层s的支持有限。您仍然无法使用常规的SQL调用函数来管理事务,因此,除了使用新的顶层过程时,以下内容仍然适用。


函数是调用它们的事务的一部分。如果事务回滚,它们的影响也会回滚。如果事务提交,他们的工作也会提交。BEGIN ... EXCEPT函数中的任何块都像(和在后台使用)保存点(如SAVEPOINTROLLBACK TO SAVEPOINTSQL语句)进行操作。

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

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

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

如果您在没有显式环绕事务的情况下调用函数,则规则与任何其他Pg语句完全相同:

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

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

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

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


2
非常清楚的解释。特别感谢您通过简短的实例来说明您的答案。
informatik01

我相信,从PG11开始,您现在就可以在过程内执行实际的提交了。blog.dbi-services.com/…–
gertvdijk

(有关更多信息,请参见官方文档:postgresql.org/docs/12/plpgsql-transactions.html
gertvdijk

@ craig-ringer“有限”的程序支持是什么意思?为什么受到限制?
SaneDeveloper

31

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

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

同样,如果您在执行查询时PgAdmin同样发生。

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

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

select anything_that_do_not_exists;

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

更新Sep-2018

为了阐明这个概念,我举了一个非事务功能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)(这是因为序列不是事务性的)。


4
感谢您明确的答案!阅读克雷格的答案后,我感到不确定。


1
提防非事务性事物,例如nextval;如果它们不像常规查询那样不是事务性的或不是完全事务性的,则它们也不在函数中。
克雷格·林格

8

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

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


6

在职能级别上,它不是跨国的。换句话说,函数中的每个语句都属于一个事务,这是默认的db自动提交值。默认情况下,自动提交为true。但是无论如何,您必须使用

select schemaName.functionName()

上面的语句“ select schemaName.functionName()”是单个事务,让我们将事务命名为T1,因此函数中的所有语句都属于事务T1。这样,功能就在单个事务中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.