首页 > 解决方案 > PL/pgSQL 函数会自行自动提交吗?

问题描述

我在 PostgreSQL 11 上有一个 PL/pgSQL 函数,如下所示:

create or replace function public.process_single_usage(usage_p "UsagePs")
  returns text
  language plpgsql
as $function$
declare
return_msg text;
   -- Variables used for business logic
begin
   -- Business logic and error handling, return_msg variable is assigned to an error message on failure 
   -- or to an empty string on success.

   -- A bunch of insert statements if no error arises.
   return return_msg;
end
$function$;

如果任何一行出现错误消息,我是否可以使用第二个函数在表的每一行上运行第一个函数并回滚每个插入,或者我是否需要使用单个函数来控制事务?

标签: postgresqlfunctiontransactionsplpgsqlcommit

解决方案


我可以使用第二个函数在表的每一行上运行第一个函数,如果任何一行结果出现错误消息,则回滚每个插入

AFUNCTION始终在单个事务中运行。但我不认为这会妨碍您尝试做的事情。带有子句的EXCEPTION可以捕获异常并回滚子事务。根据“每个插入”的确切含义,这可能对您有用。

如果“每一行”是指整个操作中的每一行(子函数的多次调用),那么您不需要做任何额外的事情。如果发生异常,默认行为是回滚所有内容。

如果“每一行”是指受子函数一次调用影响的每一行,那么请考虑关于Trapping Errors的手册:

当一个EXCEPTION子句捕捉到错误时,PL/pgSQL 函数的局部变量保持与错误发生时一样,但对块内持久数据库状态的所有更改都将回滚

大胆强调我的。有关的:

COMMIT在过程中的任何时候实际工作,您需要一个PROCEDURE, Postgres 11 引入。从相同版本开始,事务控制语句也允许在使用DOcommand执行的匿名代码块中。

基本上:

DO
$do$
DECLARE
  _rec "UsagePs";
BEGIN
   FOR _rec IN
      SELECT * FROM "UsagePs" u WHERE u.foo = 'bar' ORDER BY u.id
   LOOP
      BEGIN 
         PERFORM public.process_single_usage(_rec);
         -- COMMIT; -- ? -- requires Postgres 11
      EXCEPTION
         WHEN OTHERS THEN
            RAISE WARNING 'This error happened: %!', SQLERRM;
      END;
   END LOOP;
END
$do$;

我认为您甚至不需要或不想COMMIT进入循环。您只想回滚子事务 - 或所有内容。

请注意使用SQLERRM将任何错误消息从子函数传播出去,降级为WARNING. 手册:

特殊变量SQLERRM包含与异常相关的错误消息。

有关的:


推荐阅读