85

像下面这样的 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;


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

4

5 回答 5

96

PostgreSQL 12 update: there is limited support for top-level PROCEDUREs that can do transaction control. You still cannot manage transactions in regular SQL-callable functions, so the below remains true except when using the new top-level procedures.


Functions are part of the transaction they're called from. Their effects are rolled back if the transaction rolls back. Their work commits if the transaction commits. Any BEGIN ... EXCEPT blocks within the function operate like (and under the hood use) savepoints like the SAVEPOINT and ROLLBACK TO SAVEPOINT SQL statements.

The function either succeeds in its entirety or fails in its entirety, barring BEGIN ... EXCEPT error handling. If an error is raised within the function and not handled, the transaction calling the function is aborted. Aborted transactions cannot commit, and if they try to commit the COMMIT is treated as ROLLBACK, same as for any other transaction in error. Observe:

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

See how the transaction, which is in the error state due to the zero division, rolls back on COMMIT?

If you call a function without an explicit surounding transaction the rules are exactly the same as for any other Pg statement:

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

(where COMMIT will fail if the SELECT raised an error).

PostgreSQL does not (yet) support autonomous transactions in functions, where the procedure/function could commit/rollback independently of the calling transaction. This can be simulated using a new session via dblink.

BUT, things that aren't transactional or are imperfectly transactional exist in PostgreSQL. If it has non-transactional behaviour in a normal BEGIN; do stuff; COMMIT; block, it has non-transactional behaviour in a function too. For example, nextval and setval, TRUNCATE, etc.

于 2012-10-08T09:19:19.120 回答
38

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

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

PgAdmin此外,如果您在同样的情况下执行查询。

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

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

select anything_that_do_not_exists;

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

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)(即因为序列不是事务性的),尽管更新没有提交。

于 2015-07-09T13:10:15.010 回答
12

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

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

于 2016-10-31T10:59:31.783 回答
7

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

select schemaName.functionName()

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

于 2016-03-02T19:59:30.300 回答
0

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

于 2021-04-12T18:51:54.650 回答