art*_*hur 5 postgresql insert transaction plpgsql postgresql-9.4
我有一个相当复杂的 UDF(在一堆表中移动并创建一堆新表),其中可能会发生多次中止。在每次操作之前,我想记录操作发生的时间和查询本身。UDF 如下所示:
log function_start
log sql1
execute sql1
log sql2
execute sql2
...
log sqlN
execute sqlN
log function_end
Run Code Online (Sandbox Code Playgroud)
每条日志语句都意味着向下表中插入一条新记录:
CREATE TABLE backup_logs
(
id serial NOT NULL,
t timestamp with time zone default now(),
sql text,
CONSTRAINT backup_logs_pkey PRIMARY KEY (id)
)
Run Code Online (Sandbox Code Playgroud)
如果发生中止,我希望sql1, sql2, ... sqlN
回滚,但inserto into backup_logs
会继续存在。问题:我怎样才能实现这一目标?
当异常发生时,一切都会回滚。您想要在当前事务上下文“外部”执行选定的代码,这通常称为自治事务。目前尚未实现(从第 9.4 页开始)。Postgres TODO wiki 中有一个项目,但这是一个棘手的问题,不要屏住呼吸。
更新: Postgres 11 或更高版本允许COMMIT
in 过程。(但是提交一些更改而不是其他更改仍然很棘手。)请参阅:
现在您可以使用附加模块dblink
。它提供了在单独的连接(因此也是单独的事务)中连接到另一个数据库以在那里执行 SQL 的功能。事情已经过去了,无法挽回。通过再次连接到同一个数据库,您可以有效地实现自治事务(需要一些额外的开销)。速度相当快。
您需要为每个数据库(运行该函数的位置)安装一次 dblink:
CREATE EXTENSION dblink;
Run Code Online (Sandbox Code Playgroud)
为了方便起见,我将连接信息封装在FOREIGN SERVER
plus中USER MAPPING
:
CREATE SERVER myserver FOREIGN DATA WRAPPER dblink_fdw
OPTIONS (hostaddr '127.0.0.1', dbname 'test');
CREATE USER MAPPING FOR role_source SERVER myserver
OPTIONS (user 'role_target', password 'secret');
Run Code Online (Sandbox Code Playgroud)
连接字符串有多种选项。该示例针对名为localhost 上的数据库的用户role_source
进行连接。源和目标可以是相同的角色名称。我在这里使用密码(简单的示例),但我更喜欢无密码访问,因此我们不必保存密码(并且它不会最终出现在备份等中):role_target
test
127.0.0.1
但是,根据文档:
只有超级用户可以用来
dblink_connect
创建非密码验证的连接。如果非超级用户需要此功能,请dblink_connect_u
改用。
或者创建一个SECURITY DEFINER
函数来封装相关部分。见下文。
CREATE OR REPLACE FUNCTION f_work()
RETURNS void
LANGUAGE plpgsql VOLATILE AS
$func$
DECLARE
sql1 text := 'SELECT 1'; -- dummy code
sql2 text := 'SELECT 2';
BEGIN
PERFORM dblink_connect('myserver'); -- name of foreign server
PERFORM dblink_exec($f$INSERT INTO backup_logs(sql)
VALUES ('f_work() start')$f$); -- log start
PERFORM dblink_exec('INSERT INTO backup_logs(sql)
SELECT ' || quote_literal(sql1)); -- log sql1
EXECUTE sql1;
-- RAISE EXCEPTION 'foo'; -- unquote to test error case
PERFORM dblink_exec('INSERT INTO backup_logs(sql)
SELECT ' || quote_literal(sql2)); -- log sql2
EXECUTE sql2;
PERFORM dblink_exec($f$INSERT INTO backup_logs(sql)
VALUES ('f_work() end')$f$); -- log end
PERFORM dblink_disconnect();
END
$func$;
Run Code Online (Sandbox Code Playgroud)
请确保USER MAPPING
您正在运行此函数的角色有一个 ,或者使其成为SECURITY DEFINER
您要使用的角色所拥有的函数:
归档时间: |
|
查看次数: |
1076 次 |
最近记录: |