PostgreSQL ne peut pas commencer/terminer les transactions dans PL / pgSQL

je demande des éclaircissements sur la façon d'assurer une transaction atomique dans une fonction plpgsql, et où le niveau d'isolement est fixé pour ce changement particulier à la base de données.

dans la fonction plpgsql ci-dessous, je veux m'assurer que la suppression et l'insertion réussiront. J'obtiens une erreur lorsque je tente de les envelopper dans une seule transaction:

ERROR: cannot begin/end transactions in PL/pgSQL .

Ce qui se passe lors de l'exécution de la fonction ci-dessous si un autre utilisateur a ajouté un comportement par défaut pour les circonstances ('RAIN', 'NIGHT', '45MPH') après cette fonction a supprimé la ligne personnalisée mais avant il a eu une chance d'insérer la ligne personnalisée? Y a-t-il une transaction implicite enveloppant l'insertion et la suppression de sorte que les deux sont retranchés si un autre utilisateur a changé l'une des lignes référencées par cette fonction? Puis-je régler le niveau d'isolement pour cette fonction?

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;
24
demandé sur Erwin Brandstetter 2013-01-27 21:48:26

2 réponses

une fonction plpgsql s'exécute automatiquement à l'intérieur d'une transaction. Tout réussit ou tout échoue. Le manuel:

Les fonctions

et les procédures de déclenchement sont toujours exécutées à l'intérieur d'un transaction établie par une requête externe - ils ne peuvent pas démarrer ou s'engager à ce que la transaction, puisqu'il n'y aurait pas de contexte pour eux de exécuter dans. Toutefois, un bloc contenant une clause EXCEPTION forme effectivement une sous-transaction qui peut être annulée sans affectant la transaction externe. Pour en savoir plus, lisez la Section , à la section 42.6.6.

donc, si vous devez, vous pouvez attraper une exception qui pourrait théoriquement se produire (mais est très peu probable).

détails sur les erreurs de piégeage dans le manuel.

Votre fonction revus et simplifiés:

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;

si vous avez réellement besoin de début/fin des transactions comme indiqué dans le titre regardez aux procédures SQL dans Postgres 11 ou plus tard ( CREATE PROCEDURE ). Related:

34
répondu Erwin Brandstetter 2018-06-20 12:15:22
START TRANSACTION;
select foo() ;
COMMIT;

"malheureusement, Postgres n'a pas de procédures stockées, vous devez donc toujours gérer la transaction dans le code appelant "– a_horse_with_no_name

Transaction dans un bloc d'exception-comment?

0
répondu Charlie 木匠 2018-04-09 19:56:59