web-dev-qa-db-fra.com

Les fonctions PostgreSQL sont-elles transactionnelles?

Une fonction PostgreSQL telle que la suivante est-elle automatiquement transactionnelle?

CREATE OR REPLACE FUNCTION refresh_materialized_view(name)
  RETURNS integer AS
$BODY$
 DECLARE
     _table_name ALIAS FOR $1;
     _entry materialized_views%ROWTYPE;
     _result INT;
 BEGIN          

     EXECUTE 'TRUNCATE TABLE ' || _table_name;

     UPDATE materialized_views
     SET    last_refresh = CURRENT_TIMESTAMP
     WHERE  table_name = _table_name;

     RETURN 1;
END
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER;


En d'autres termes, si une erreur se produit pendant l'exécution de la fonction, les modifications seront-elles annulées? Si ce n'est pas le comportement par défaut, comment puis-je rendre la fonction transactionnelle?

63
Dónal

Mise à jour PostgreSQL 12 : la prise en charge des PROCEDUREs de niveau supérieur pouvant effectuer le contrôle des transactions est limitée . Vous ne pouvez toujours pas gérer les transactions dans les fonctions standard appelables SQL, donc ce qui suit reste vrai, sauf lors de l'utilisation des nouvelles procédures de niveau supérieur.


Les fonctions font partie de la transaction à partir de laquelle elles sont appelées. Leurs effets sont annulés si la transaction est annulée. Leur travail est validé si la transaction est validée. Tous les blocs BEGIN ... EXCEPT De la fonction fonctionnent comme (et sous le capot) des points de sauvegarde comme les instructions SQL SAVEPOINT et ROLLBACK TO SAVEPOINT.

La fonction réussit dans son intégralité ou échoue dans son intégralité, sauf la gestion des erreurs BEGIN ... EXCEPT. Si une erreur est déclenchée dans la fonction et n'est pas gérée, la transaction appelant la fonction est abandonnée. Les transactions abandonnées ne peuvent pas être validées et, si elles tentent de valider, COMMIT est traité comme ROLLBACK, comme pour toute autre transaction par erreur. Observer:

regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR:  division by zero
regress=# COMMIT;
ROLLBACK

Voyez comment la transaction, qui est dans l'état d'erreur en raison de la division zéro, revient sur COMMIT?

Si vous appelez une fonction sans transaction environnante explicite, les règles sont exactement les mêmes que pour toute autre instruction Pg:

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

(où COMMIT échouera si SELECT a déclenché une erreur).

PostgreSQL ne prend pas (encore) en charge les transactions autonomes dans les fonctions, où la procédure/fonction pourrait être validée/annulée indépendamment de la transaction appelante. Cela peut être simulé en utilisant une nouvelle session via dblink .

[~ # ~] mais [~ # ~] , des choses qui ne sont pas transactionnelles ou qui sont imparfaitement transactionnelles existent dans PostgreSQL. S'il a un comportement non transactionnel dans un bloc BEGIN; do stuff; COMMIT; Normal, il a également un comportement non transactionnel dans une fonction. Par exemple, nextval et setval, TRUNCATE, etc.

75
Craig Ringer

Comme ma connaissance de PostgreSQL est moins approfondie que celle de Craig Ringer, je vais essayer de donner une réponse plus courte: Oui.

Si vous exécutez une fonction qui contient une erreur, aucune des étapes n'aura d'impact sur la base de données.

De même, si vous exécutez une requête dans PgAdmin, la même chose se produit.

Par exemple, si vous exécutez dans une requête:

update your_table yt set column1 = 10 where yt.id=20;

select anything_that_do_not_exists;

La mise à jour de la ligne, id = 20 De your_table Ne sera pas enregistrée dans la base de données.

MISE À JOUR sept - 2018

Pour clarifier le concept, j'ai fait un petit exemple avec la fonction non transactionnelle nextval.

Commençons par créer une séquence:

create sequence test_sequence start 100;

Ensuite, exécutons:

update your_table yt set column1 = 10 where yt.id=20; select nextval('test_sequence'); select anything_that_do_not_exists;

Maintenant, si nous ouvrons une autre requête et exécutons

select nextval('test_sequence');

Nous obtiendrons 101 car la première valeur (100) a été utilisée dans la dernière requête (c'est parce que les séquences ne sont pas transactionnelles) bien que la mise à jour n'ait pas été validée.

26
Ignacio

Au niveau de la fonction, ce n'est pas transnational. En d'autres termes, chaque instruction de la fonction appartient à une seule transaction, qui est la valeur de validation automatique db par défaut. La validation automatique est vraie par défaut. Mais de toute façon, vous devez appeler la fonction en utilisant

select schemaName.functionName()

L'instruction ci-dessus "select schemaName.functionName ()" est une transaction unique, nommons la transaction T1, et donc toutes les instructions de la fonction appartiennent à la transaction T1. De cette façon, la fonction est en une seule transaction.

5
Robin

https://www.postgresql.org/docs/current/static/plpgsql-structure.html

Il est important de ne pas confondre l'utilisation de BEGIN/END pour regrouper des instructions dans PL/pgSQL avec les commandes SQL de même nom pour le contrôle des transactions. PLG/pgSQL BEGIN/END sont uniquement pour le regroupement; ils ne démarrent ni ne terminent une transaction. Les fonctions et procédures de déclenchement sont toujours exécutées dans une transaction établie par une requête externe - elles ne peuvent pas démarrer ou valider cette transaction, car il n'y aurait pas de contexte pour qu'elles s'exécutent. Cependant, un bloc contenant une clause EXCEPTION forme effectivement une sous-transaction qui peut être annulé sans affecter la transaction externe. Pour plus d'informations à ce sujet, voir la section 39.6.6.

4
Ivan Strelets