关于事务:PostgreSQL函数是事务性的吗?

Are PostgreSQL functions transactional?

PostgreSQL函数如下面的自动事务是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;

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


PostgreSQL 12更新:对可以进行事务控制的顶级PROCEDURE的支持有限。您仍然无法管理常规SQL可调用函数中的事务,因此除非使用新的顶级过程,否则以下情况仍然适用。

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

除了BEGIN ... EXCEPT错误处理之外,该函数要么完全成功要么完全失败。如果在函数内引发错误而未处理,则中止调用该函数的事务。中止的事务无法提交,如果它们尝试提交COMMIT被视为ROLLBACK,则与错误中的任何其他事务相同。注意:

1
2
3
4
5
6
regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR:  division BY zero
regress=# COMMIT;
ROLLBACK

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

如果在没有明确的外围事务的情况下调用函数,则规则与任何其他Pg语句完全相同:

1
2
3
BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

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

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

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


由于我对PostgreSQL的了解不如Craig Ringer那么深,我会尝试给出一个较短的答案:是的。

如果执行其中包含错误的函数,则任何步骤都不会影响数据库。

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

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

1
2
3
UPDATE your_table yt SET column1 = 10 WHERE yt.id=20;

SELECT anything_that_do_not_exists;

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

更新于2008年9月至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)在后一个查询中使用(这是因为序列不是事务性的),尽管未提交更新。


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

select schemaName.functionName()

上面的语句'select schemaName.functionName()'是单个事务,让我们将事务命名为T1,因此函数中的所有语句都属于事务T1。通过这种方式,该功能处于单个事务中。


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

It is important not to confuse the use of BEGIN/END for grouping statements in PL/pgSQL with the similarly-named SQL commands for transaction control. PL/pgSQL's BEGIN/END are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction established by an outer query — they cannot start or commit that transaction, since there would be no context for them to execute in. However, a block containing an EXCEPTION clause effectively forms a subtransaction that can be rolled back without affecting the outer transaction. For more about that see Section 39.6.6.