PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 17.1 » Programmation serveur » PL/Python -- Langage de procédures Python » Sous-transactions explicites

44.7. Sous-transactions explicites #

La récupération d'erreurs causées par l'accès à la base de données, comme décrite dans Section 44.6.2, peut amener à une situation indésirable où certaines opérations réussissent avant qu'une d'entre elles échoue et, après récupération de cette erreur, les données sont laissées dans un état incohérent. PL/Python propose une solution à ce problème sous la forme de sous-transactions explicites.

44.7.1. Gestionnaires de contexte de sous-transaction #

Prenez en considération une fonction qui implémente un transfert entre deux comptes :

CREATE FUNCTION transfert_fonds() RETURNS void AS $$
try:
    plpy.execute("UPDATE comptes SET balance = balance - 100 WHERE nom = 'joe'")
    plpy.execute("UPDATE comptes SET balance = balance + 100 WHERE nom = 'mary'")
except plpy.SPIError as e:
    result = "erreur lors du transfert de fond : %s" % e.args
else:
    result = "fonds transféré correctement"
plan = plpy.prepare("INSERT INTO operations (resultat) VALUES ($1)", ["text"])
plpy.execute(plan, [result])
$$ LANGUAGE plpython3u;
    

Si la deuxième instruction UPDATE se termine avec la levée d'une exception, cette fonction renverra l'erreur mais le résultat du premier UPDATE sera validé malgré tout. Autrement dit, les fonds auront été débités du compte de Joe mais ils n'auront pas été crédités sur le compte de Mary.

Pour éviter ce type de problèmes, vous pouvez intégrer vos appels à plpy.execute dans une sous-transaction explicite. Le module plpy fournit un objet d'aide à la gestion des sous-transactions explicites qui sont créées avec la fonction plpy.subtransaction(). Les objets créés par cette fonction implémentent l' interface de gestion du contexte. Nous pouvons réécrire notre fonction en utilisant les sous-transactions explicites :

CREATE FUNCTION transfert_fonds2() RETURNS void AS $$
try:
    with plpy.subtransaction():
        plpy.execute("UPDATE comptes SET balance = balance - 100 WHERE nom = 'joe'")
        plpy.execute("UPDATE comptes SET balance = balance + 100 WHERE nom = 'mary'")
except plpy.SPIError as e:
    result = "erreur lors du transfert de fond : %s" % e.args
else:
    result = "fonds transféré correctement"
plan = plpy.prepare("INSERT INTO operations (resultat) VALUES ($1)", ["text"])
plpy.execute(plan, [result])
$$ LANGUAGE plpython3u;
    

Notez que l'utilisation de try/catch est toujours requis. Sinon, l'exception se propagerait en haut de la pile Python et causerait l'annulation de la fonction entière avec une erreur PostgreSQL, pour que la table operations ne contienne aucune des lignes insérées. Le gestionnaire de contexte des sous-transactions ne récupère pas les erreurs, il assure seulement que toutes les opérations de bases de données exécutées dans son cadre seront validées ou annulées de façon atomique. Une annulation d'un bloc de sous-transaction survient à la sortie de tout type d'exception, pas seulement celles causées par des erreurs venant de l'accès à la base de données. Une exception standard Python levée dans un bloc de sous-transaction explicite causerait aussi l'annulation de la sous-transaction.