我有一个相当复杂的 UDF(在一堆表中移动并创建一堆新表),其中可能会发生多次中止。在每次操作之前,我想记录操作发生的时间和查询本身。UDF 如下所示:
log function_start
log sql1
execute sql1
log sql2
execute sql2
...
log sqlN
execute sqlN
log function_end
每条日志语句都意味着在下表中插入一条新记录:
CREATE TABLE backup_logs
(
id serial NOT NULL,
t timestamp with time zone default now(),
sql text,
CONSTRAINT backup_logs_pkey PRIMARY KEY (id)
)
如果中止,我想sql1, sql2, ... sqlN
回滚,但inserto into backup_logs
要坚持。问题:我怎样才能做到这一点?
当发生异常时,一切都会回滚。您希望在当前事务上下文“外部”执行选定的代码,这通常称为自主事务。目前尚未实施(截至第 9.4 页)。Postgres TODO wiki 中有一项,但这是一件棘手的事情,请不要屏住呼吸。
现在您可以使用附加模块
dblink
。它提供了在单独的连接中连接到另一个数据库(因此也是单独的事务)以在那里执行 SQL 的功能。已经完成的已经完成,无法回滚。通过另一次连接到同一个数据库,您可以有效地实现自主事务(有一些额外的开销)。它的速度相当快。概念验证
准备工作
您需要为每个数据库(运行函数的位置)安装一次 dblink:
为了方便起见,我将连接信息封装在一个
FOREIGN SERVER
加号中USER MAPPING
:连接字符串有多种选择。该示例适用于名为localhost 上的数据库的用户
role_source
连接。源和目标可以是相同的角色名称。我在这里使用密码(简单示例),但出于此目的我更喜欢无密码访问,因此我们不必保存 PW(并且它不会以备份等结束):role_target
test
127.0.0.1
但是,根据文档:
或者创建一个
SECURITY DEFINER
函数来封装相关部分。见下文。主功能
确保为
USER MAPPING
运行此函数的角色设置一个 ,或者使其成为SECURITY DEFINER
您要使用的角色所拥有的函数: