Явные подтранзакции в PL/Tcl
Эта страница переведена при помощи нейросети GigaChat.
Восстановление после ошибок, вызванных доступом к базе данных, описанных в разделе «Обработка ошибок в PL/Tcl», может привести к нежелательной ситуации, когда некоторые операции выполняются до того, как одна из них терпит неудачу, и после восстановления от этой ошибки данные остаются в несогласованном состоянии. 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
) не вызывают откат.