web-dev-qa-db-fra.com

Transactions dans une transaction

Quel comportement PostgreSQL afficherait-il si par exemple le script ci-dessous était appelé

BEGIN;
SELECT * FROM foo;
INSERT INTO foo(name) VALUES ('bar');
BEGIN; <- The point of interest
END;

PostgreSQL rejetterait-il le second BEGIN ou un commit serait-il implicitement décidé, puis exécuter le bloc BEGINEND à la fin en tant que transaction distincte?

19
Alex

Ce dont vous auriez besoin est une soi-disant "transaction autonome" (une fonctionnalité fournie par Oracle). À ce stade, cela n'est pas encore possible dans PostgreSQL. Cependant, vous pouvez utiliser POINT DE SAUVEGARDE s:

BEGIN;
INSERT ...
SAVEPOINT a;
some error;
ROLLBACK TO SAVEPOINT a;
COMMIT;

Il ne s'agit pas d'une transaction entièrement autonome, mais elle vous permet d'obtenir "toutes les transactions" correctement. Vous pouvez l'utiliser pour réaliser ce que vous attendez des transactions autonomes.

Sinon, il n'y a pas d'autre solution raisonnable à ce stade.

14

Vous pouvez l'essayer vous-même:

ATTENTION: il y a déjà une transaction en cours

Il ne démarre aucune nouvelle (sous-) transaction car les transactions imbriquées ne sont pas implémentées dans PostgreSQL. (Vous pouvez faire de la magie dans un pl/pgsql, par exemple, qui imite ce comportement.)

Avec PostgreSQL 11, on pourrait penser que les nouvelles procédures stockées réelles et leur capacité à gérer les transactions rendraient possibles les transactions imbriquées. Cependant, selon le documentation , ce n'est pas le cas:

Dans les procédures invoquées par la commande CALL ainsi que dans les blocs de code anonymes (commande DO), il est possible de terminer les transactions à l'aide des commandes COMMIT et ROLLBACK. Une nouvelle transaction est démarrée automatiquement après la fin d'une transaction à l'aide de ces commandes, il n'y a donc pas de commande START TRANSACTION distincte.

13
dezso

PostgreSQL ne prend pas en charge les sous-transactions, mais la fonction SAVEPOINT peut répondre efficacement à votre besoin. Citant de la documentation pour couche d'accès avancé à PG via des promesses par Vitaly Tomilov sur GitHub:

PostgreSQL ne prend pas correctement en charge les transactions imbriquées, il prend uniquement en charge les restaurations partielles via points de sauvegarde dans les transactions internes. La différence entre les deux techniques est énorme, comme expliqué plus loin.

Une prise en charge appropriée des transactions imbriquées signifie que le résultat d'une sous-transaction réussie n'est pas annulé lorsque sa transaction parent est annulée. Mais avec les points de sauvegarde PostgreSQL, si vous annulez la transaction de niveau supérieur, le résultat de tous les points de sauvegarde internes est également annulé.

Les points de sauvegarde peuvent être utilisés pour des restaurations partielles à un point antérieur dans une transaction active. Par exemple, pour établir un point de sauvegarde et annuler ultérieurement les effets de toutes les commandes exécutées après sa création:

BEGIN;
    INSERT INTO table1 VALUES (1);
    SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (2);
    ROLLBACK TO SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (3);
COMMIT;

La transaction ci-dessus insérera les valeurs 1 et 3, mais pas 2. Consultez la documentation SAVEPOINT pour plus d'informations.

9
Amir Ali Akbari

Pour Postgresql 9.5 ou plus récent, vous pouvez utiliser des travailleurs d'arrière-plan dynamiques fournis par l'extension pg_background. Il crée une transaction autonome. Veuillez vous référer à la page github de l'extension. La sollution est meilleure que db_link. Il y a un guide complet sur Prise en charge des transactions autonomes dans PostgreSQL

0
shcherbak