循环中的 Firebird 2.5 commit/rollback(使用自治事务)
Firebird 2.5 commit/rollback (using autonomous transaction) in loop
我必须在存储过程的循环中处理记录,例如:
create or alter procedure process_waiting_records
as
declare v_id type of column my_table.id;
begin
for
select
t.id
from
my_table t
where
(t.status = 'WAITING_TO_PROCESS')
order by
t.created_at
into
:v_id
do
begin
execute procedure process_one_record(:v_id);
end
end ^
问题是当 process_one_record()
的执行失败(产生任何类型的异常)时,整个修改集将从调用代码回滚。
目标是处理所有可能的记录,如果某些记录无法处理,我目前并不关心,那些失败的记录将记录在日志中 table 无论如何(使用自治事务)。
我正在考虑在带有 when any do (dummy code)
子句的自治事务块中调用 process_one_record()
存储过程。但是,我认为那是行不通的,因为那个失败的事务不会被回滚,而是被提交(参考这个话题:)。
有人可以指出正确的方向如何解决这个问题吗?
您不需要匿名交易。当存储过程抛出异常时,该存储过程的影响将自动撤消。如果存储过程包含 SUSPEND
,则只会撤消直到最后一个 SUSPEND
的效果(将其视为保存点)。对于任何其他形式的回滚,需要显式回滚事务。
另请参阅 Firebird 2.5 语言参考中的 Savepoints and PSQL。
Transaction control statements are not allowed in PSQL, as that would
break the atomicity of the statement that calls the procedure.
However, Firebird does support the raising and handling of exceptions
in PSQL, so that actions performed in stored procedures and triggers
can be selectively undone without the entire procedure failing.
Internally, automatic savepoints are used to:
- undo all actions in the
BEGIN...END
block where an exception occurs
- undo all actions performed by the procedure or trigger or, in for a selectable procedure, all actions performed since the last SUSPEND,
when execution terminates prematurely because of an uncaught error or
exception
Each PSQL exception handling block is also bounded by automatic system
savepoints.
NOTE: A BEGIN...END
block does not itself create an automatic
savepoint. A savepoint is created only in blocks that contain the WHEN
statement for handling exceptions.
在这种情况下,由于需要撤消单个 process_one_record
的效果,而不是 process_waiting_records
中的整个处理,您需要允许 [=] 抛出异常15=],但为单个过程调用捕获它。
简而言之,您需要执行以下操作:
for select
...
do
begin
execute procedure process_one_record(:v_id);
when any do
begin
-- do nothing, last call to `process_one_record` was undone
end
end
我必须在存储过程的循环中处理记录,例如:
create or alter procedure process_waiting_records
as
declare v_id type of column my_table.id;
begin
for
select
t.id
from
my_table t
where
(t.status = 'WAITING_TO_PROCESS')
order by
t.created_at
into
:v_id
do
begin
execute procedure process_one_record(:v_id);
end
end ^
问题是当 process_one_record()
的执行失败(产生任何类型的异常)时,整个修改集将从调用代码回滚。
目标是处理所有可能的记录,如果某些记录无法处理,我目前并不关心,那些失败的记录将记录在日志中 table 无论如何(使用自治事务)。
我正在考虑在带有 when any do (dummy code)
子句的自治事务块中调用 process_one_record()
存储过程。但是,我认为那是行不通的,因为那个失败的事务不会被回滚,而是被提交(参考这个话题:
有人可以指出正确的方向如何解决这个问题吗?
您不需要匿名交易。当存储过程抛出异常时,该存储过程的影响将自动撤消。如果存储过程包含 SUSPEND
,则只会撤消直到最后一个 SUSPEND
的效果(将其视为保存点)。对于任何其他形式的回滚,需要显式回滚事务。
另请参阅 Firebird 2.5 语言参考中的 Savepoints and PSQL。
Transaction control statements are not allowed in PSQL, as that would break the atomicity of the statement that calls the procedure. However, Firebird does support the raising and handling of exceptions in PSQL, so that actions performed in stored procedures and triggers can be selectively undone without the entire procedure failing.
Internally, automatic savepoints are used to:
- undo all actions in the
BEGIN...END
block where an exception occurs- undo all actions performed by the procedure or trigger or, in for a selectable procedure, all actions performed since the last SUSPEND, when execution terminates prematurely because of an uncaught error or exception
Each PSQL exception handling block is also bounded by automatic system savepoints.
NOTE: A
BEGIN...END
block does not itself create an automatic savepoint. A savepoint is created only in blocks that contain the WHEN statement for handling exceptions.
在这种情况下,由于需要撤消单个 process_one_record
的效果,而不是 process_waiting_records
中的整个处理,您需要允许 [=] 抛出异常15=],但为单个过程调用捕获它。
简而言之,您需要执行以下操作:
for select
...
do
begin
execute procedure process_one_record(:v_id);
when any do
begin
-- do nothing, last call to `process_one_record` was undone
end
end