PostgreSQL无法在PL/pgSQL中开始/结束事务

Tim*_*Tim 29 transactions runtime-error plpgsql read-committed postgresql-9.2

我正在寻求澄清如何在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;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 41

plpgsql函数自动在事务中运行.一切都成功或者都失败了.手册:

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

因此,如果需要,您可以捕获理论上可能发生的异常(但不太可能).
有关陷阱错误的详细信息,请参见手册.

您的功能已审核并简化:

CREATE FUNCTION foo(v_weather text
                  , v_timeofday text
                  , v_speed text
                  , v_behavior text)
  RETURNS SETOF custombehavior 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$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

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

  • 重点是:必须调用函数才能执行。像`SELECT foo()`。它始终在整个事务中运行。没有例外。你可以像我暗示的那样拥有 *subtransaction* .. (2认同)
  • @Tim 请注意,对函数的一次调用不会在单独的事务中运行。它将在现有事务(如果有)内运行。如果不存在,那么 SELECT 将形成一个单独的事务,在该事务中将调用您的函数。 (2认同)