Восстановление после ошибок, возникших при доступе к базе данных, как описано в Раздел 5.7.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 "ошибка при переводе средств: %s" $errormsg]
} else {
set result "перевод средств выполнен успешно"
}
spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')"
$$ LANGUAGE pltcl;
Если вторая UPDATE инструкция приведет к возникновению исключения, эта функция зафиксирует ошибку в журнале, но результат выполнения первой UPDATE будет
тем не менее зафиксирован. Другими словами, денежные средства будут списаны со счета Джо, но не будут переведены на счет Мэри. Это происходит потому, что каждая spi_exec
инструкция выполняется как отдельная подтранзакция, и только одна из этих подтранзакций была откачена.
Для обработки таких случаев можно обернуть несколько операций базы данных в явную подтранзакцию, которая будет зафиксирована или откачена как единое целое. 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 "ошибка при переводе средств: %s" $errormsg]
} else {
set result "перевод средств выполнен успешно"
}
spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')"
$$ LANGUAGE pltcl;
Обратите внимание, что использование catch по-прежнему требуется для этой
цели. В противном случае ошибка распространится на верхний уровень функции, что предотвратит выполнение вставки в таблицу operations .
Данная команда подтранзакция не перехватывает ошибки; она лишь гарантирует, что все операции с базой данных, выполненные в её области действия, будут откачены совместно при возникновении ошибки.
Откат явной подтранзакции происходит при любой ошибке, инициируемой кодом Tcl, а не только при ошибках, возникающих при доступе к базе данных. Таким образом, стандартное исключение Tcl, возникшее внутри
команды подтранзакция также приведет к откату подтранзакции. Однако штатные завершения вложенного кода Tcl (например, вследствие return) не
приводят к откату.