37

我正在寻求澄清如何确保 plpgsql 函数中的原子事务,以及为数据库的这种特定更改设置隔离级别的位置。

在下面显示的 plpgsql 函数中,我想确保删除和插入都成功。当我尝试将它们包装在单个事务中时出现错误:

ERROR:  cannot begin/end transactions in PL/pgSQL

如果另一个用户在此函数删除自定义行之后但在有机会插入自定义行之前添加了针对环境的默认行为('RAIN'、'NIGHT'、'45MPH'),则在执行下面的函数期间发生什么排?是否存在包装插入和删除的隐式事务,以便如果另一个用户更改了此函数引用的任一行,两者都会回滚?我可以为此功能设置隔离级别吗?

create function foo(v_weather varchar(10), v_timeofday varchar(10), v_speed varchar(10),
   v_behavior varchar(10))
   returns setof CUSTOMBEHAVIOR
   as $body$
   begin
      -- run-time error if either of these lines is un-commented

      -- start transaction ISOLATION LEVEL READ COMMITTED;
      -- or, alternatively, set transaction ISOLATION LEVEL READ COMMITTED;

      delete from CUSTOMBEHAVIOR 
      where weather = 'RAIN' and timeofday = 'NIGHT' and speed= '45MPH' ;

      -- if there is no default behavior insert a custom behavior

      if not exists
        (select id from DEFAULTBEHAVIOR where a = 'RAIN' and b = 'NIGHT' and c= '45MPH') then
         insert into CUSTOMBEHAVIOR
           (weather, timeofday, speed, behavior)
         values
           (v_weather, v_timeofday, v_speed, v_behavior);
      end if;

      return QUERY
      select * from CUSTOMBEHAVIOR where ...   ;

      -- commit;
   end
   $body$  LANGUAGE plpgsql;
4

2 回答 2

47

plpgsql函数在事务中自动运行。要么全部成功,要么全部失败。手册:

函数和触发器过程总是在由外部查询建立的事务中执行——它们不能启动或提交该事务,因为它们没有执行的上下文。但是,包含EXCEPTION子句的块有效地形成了一个子事务,可以回滚而不影响外部事务。有关更多信息,请参阅第 42.6.6 节。

因此,如果需要,您可以捕获理论上可能发生的异常(但可能性很小)。
手册中有关捕获错误的详细信息。

您的功能经过审查和简化:

CREATE FUNCTION foo(v_weather text
                  , v_timeofday text
                  , v_speed text
                  , v_behavior text)
  RETURNS SETOF custombehavior
  LANGUAGE plpgsql AS
$func$
BEGIN

DELETE FROM custombehavior
WHERE  weather = 'RAIN'
AND    timeofday = 'NIGHT'
AND    speed = '45MPH';

INSERT INTO custombehavior (weather, timeofday, speed, behavior)
SELECT v_weather, v_timeofday, v_speed, v_behavior
WHERE  NOT EXISTS (
   SELECT FROM defaultbehavior
   WHERE  a = 'RAIN'
   AND    b = 'NIGHT'
   AND    c = '45MPH'
   );

RETURN QUERY
SELECT * FROM custombehavior WHERE ... ;

END
$func$;

如果您确实需要开始/结束事务,如标题中所示,请查看Postgres 11 或更高版本中的 SQL过程CREATE PROCEDURE( )。看:

于 2013-01-27T17:55:46.270 回答
1

更新:在 PostgreSQL 版本 11 之后。您可以控制存储过程中的事务。

===== 版本 10 之前:

START TRANSACTION;
select foo() ;
COMMIT;

“不幸的是 Postgres 没有存储过程,所以你总是需要在调用代码中管理事务” – a_horse_with_no_name

异常块中的事务 - 如何?

于 2018-04-09T19:47:17.753 回答