Récupérer des erreurs causées par des à la base de données comme décris dans Section 43.8 peut mener à une situation indésirable où certaines opérations réussissent avant que l'une d'entre elles échoue, et après avoir récupéré cette erreur la donnée est laissé dans un état incohérent. PL/Tcl offre une solution à ce problème sous la forme de sous-transactions explicites :
Étudions une fonction qui implémente un transfert entre deux compte :
CREATE FUNCTION transfer_funds() RETURNS void AS $$ if [catch { spi_exec "UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'" spi_exec "UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'" } errormsg] { set result [format "error transferring funds: %s" $errormsg] } else { set result "funds transferred successfully" } spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')" $$ LANGUAGE pltcl;
Si le deuxième ordre UPDATE
échoue en levant une
exception, cette fonction tracera l'échec, mais le résultat du premier
UPDATE
sera néanmoins validé. Concrètement, le montant
sera débité du compte de Joe, mais ne sera pas transféré sur le compte de
Mary. C'est le cas car chaque appel à spi_exec
est
une sous-transaction séparé, et seule l'une de ces sous-transactions est
annulée.
Pour gérer un cas comme ça, vous pouvez entourer vos opération sur la base
d'une sous-transaction explicite, qui sera annulée ou validée comme un
tour. PL/Tcl fournit une commande subtransaction
pour gérer ça. Nous pouvons réécrire notre fonction ainsi :
CREATE FUNCTION transfer_funds2() RETURNS void AS $$ if [catch { subtransaction { spi_exec "UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'" spi_exec "UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'" } } errormsg] { set result [format "error transferring funds: %s" $errormsg] } else { set result "funds transferred successfully" } spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')" $$ LANGUAGE pltcl;
Veuillez noter que l'utilisation de catch
est
toujours nécessaire pour gérer ce cas. Autrement l'erreur se propagerait
jusqu'au niveau racine de la fonction, ce qui empêcherait l'insertion
voulue dans la table operations
. La commande
subtransaction
ne récupère pas les erreurs, elle
s'assure seulement que tous les ordres sur la base exécutés dans sa portée
seront annulés ensemble si une erreur survient.
L'annulation d'une sous-transaction explicite arrive lors de n'importe
quelle erreur rapportée par le code Tcl contenu, pas uniquement pour celle
en provenance de la base de données. Ainsi une exception standard Tcl
levée dans une commande subtransaction
aura également
pour effet d'annuler la sous-transaction. Toutefois, les sortie du code
Tcl contenu sans erreur (par exemple, du fait d'un
return
) ne déclencheront pas d'annulation.