41.9. Явные подтранзакции в PL/Tcl#
41.9. Явные подтранзакции в PL/Tcl #
Восстановление после ошибок, вызванных доступом к базе данных, описанных в Раздел 41.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;
Если второй оператор UPDATE
вызывает исключение, эта функция будет регистрировать сбой, но результат первого оператора UPDATE
все равно будет зафиксирован. Другими словами, средства будут списаны с аккаунта Джо, но не будут переведены на счет Мэри. Это происходит потому, что каждый spi_exec
представляет собой отдельную подтранзакцию, и только одна из этих подтранзакций была отменена.
Для обработки таких случаев вы можете обернуть несколько операций с базой данных в явную подтранзакцию, которая будет успешно завершена или откатана в целом. PL/Tcl предоставляет команду subtransaction
для управления этим. Можно переписать нашу функцию следующим образом:
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;
Обратите внимание, что использование catch
все еще требуется для этой цели. В противном случае ошибка будет распространяться на верхний уровень функции, что препятствует желаемой вставке в таблицу operations
.
Команда subtransaction
не перехватывает ошибки, она только гарантирует, что все операции с базой данных, выполненные внутри ее области действия, будут отменены вместе при сообщении об ошибке.
Возврат явной подтранзакции происходит при любой ошибке, сообщенной
из содержащегося кода Tcl, а не только из ошибок, возникающих из базы данных.
Таким образом, обычное исключение Tcl, вызванное внутри
команды subtransaction
, также приведет к
откату подтранзакции. Однако, ненормальные выходы из
содержащегося кода Tcl (например, из-за return
) не
вызывают отката.