Je demande des éclaircissements sur la manière d’assurer une transaction atomique dans une fonction plpgsql et sur le niveau d’isolement défini pour cette modification particulière de la base de données.
Dans la fonction plpgsql indiquée ci-dessous, je veux m'assurer que les deux suppression et insertion réussissent. Je reçois une erreur lorsque j'essaie de les envelopper dans une transaction unique:ERROR: cannot begin/end transactions in PL/pgSQL
.
Que se passe-t-il pendant 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 it a eu l'occasion d'insérer la ligne personnalisée? Existe-t-il une transaction implicite encapsulant l'insertion et la suppression afin que les deux soient annulées si un autre utilisateur a modifié l'une des lignes référencées par cette fonction? Puis-je définir 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;
Une fonction plpgsql s'exécute automatiquement dans une transaction. Tout réussit ou tout échoue. Le manuel:
Les fonctions et les procédures de déclenchement sont toujours exécutées dans un transaction établie par une requête externe - ils ne peuvent pas démarrer ou commettre cette transaction, car il n'y aurait pas de contexte pour eux execute in. Cependant, un bloc contenant une clause
EXCEPTION
forme efficacement une sous-transaction qui peut être annulée sans affectant la transaction externe. Pour plus d'informations à ce sujet, voir Section 42.6.6.
Donc, si vous en avez besoin, vous pouvez capturer une exception qui pourrait théoriquement se produire (mais est très improbable).
Détails sur les erreurs de piégeage dans le manuel.
Votre fonction revue et simplifiée:
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 transactions début/fin comme indiqué dans le titre, consultez les procédures SQL dans Postgres 11 ou ultérieur ( CREATE PROCEDURE
). En relation:
START TRANSACTION;
select foo() ;
COMMIT;
"Malheureusement, Postgres n'a pas de procédure stockée, vous devez donc toujours gérer la transaction dans le code appelant" - a_horse_with_no_name