# 44.9.PL/Tcl中的显式子转换
从数据库访问导致的错误中恢复,如中所述第44.8节可能会导致一种不希望出现的情况,即某些操作在其中一个操作失败之前成功,而在从该错误中恢复后,数据会处于不一致的状态。PL/Tcl以显式子事务的形式提供了这个问题的解决方案。
考虑一个在两个帐户之间实现转移的函数:
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;
如果第二个使现代化
语句导致引发异常,此函数将记录失败,但第一次失败的结果使现代化
尽管如此,我们仍将继续努力。换句话说,资金将从乔的账户中提取,但不会转入玛丽的账户。这是因为高级执行官
是一个单独的子事务,其中只有一个子事务被回滚。
要处理这种情况,可以将多个数据库操作包装在一个显式子事务中,该子事务将作为一个整体成功或回滚。PL/Tcl提供了一个次转换
命令来管理这个。我们可以将函数改写为:
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;
请注意接住
为此目的仍然需要。否则,错误会传播到函数的顶层,从而阻止所需的插入操作
桌子这个次转换
命令不会捕获错误,它只确保在报告错误时,在其作用域内执行的所有数据库操作将一起回滚。
显式子事务的回滚发生在包含的Tcl代码报告的任何错误上,而不仅仅是源于数据库访问的错误。因此,在次转换
命令还将导致子事务回滚。但是,包含的Tcl代码中不存在错误(例如,由于回来
)不要导致回滚。