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 PROCEDURE
s that can do transaction control。您仍然无法在常规 SQL 可调用函数中管理事务,因此除非使用新的***过程,否则以下内容仍然适用。
函数是调用它们的事务的一部分。如果事务回滚,它们的效果也会回滚。如果事务提交,他们的工作就会提交。函数中的任何BEGIN ... EXCEPT
块都像SAVEPOINT
和ROLLBACK 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;
块中具有非事务性行为,那么它在函数中也具有非事务性行为。例如nextval
和setval
、TRUNCATE
等。
【讨论】:
解释得很清楚。特别感谢您用简短的例子来说明您的答案。 我相信您现在可以在过程中执行实际提交,因为 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 学习记录,模式,分区表,触发器,事务,窗口函数,视图,建表,约束等