1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix assertion after aborting internal subtransaction at transaction end

Previously, aborting an internal subtransaction during COMMIT or
PREPARE TRANSACTION could cause the following assertion failure.
This could happen, for example, when a deferred constraint trigger fired
at COMMIT and its PL/pgSQL exception block caught an error raised
while executing the trigger function.

    TRAP: failed Assert("s->blockState == TBLOCK_SUBINPROGRESS || s->blockState
    == TBLOCK_INPROGRESS || s->blockState == TBLOCK_IMPLICIT_INPROGRESS ||
    s->blockState == TBLOCK_PARALLEL_INPROGRESS || s->blockState ==
    TBLOCK_STARTED"), File: "xact.c", Line: 4851, PID: 73455

An internal subtransaction should be able to be aborted while the parent
transaction is in the COMMIT or PREPARE TRANSACTION phase. However,
RollbackAndReleaseCurrentSubTransaction()'s assertion check previously
did not allow TBLOCK_END and TBLOCK_PREPARE as parent transaction
states, causing the assertion failure.

This commit fixes the assertion check by allowing those two parent
transaction states.

Backpatch to all supported versions.

Reported-by: Fabrízio de Royes Mello <fabrizio@planetscale.com>
Author: Patrick Reynolds <piki@planetscale.com>
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Fabrízio de Royes Mello <fabrizio@planetscale.com>
Discussion: https://postgr.es/m/CABo-N97AeMbWuYTWg-3%3D2DkTR3EkvS%2BFt%3DyEaWB181STsR1mBg%40mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Fujii Masao
2026-09-17 14:28:21 +09:00
parent b8e0b45abb
commit aad2baff0e
3 changed files with 55 additions and 0 deletions

View File

@@ -4624,6 +4624,8 @@ RollbackAndReleaseCurrentSubTransaction(void)
AssertState(s->blockState == TBLOCK_SUBINPROGRESS ||
s->blockState == TBLOCK_INPROGRESS ||
s->blockState == TBLOCK_IMPLICIT_INPROGRESS ||
s->blockState == TBLOCK_END ||
s->blockState == TBLOCK_PREPARE ||
s->blockState == TBLOCK_STARTED);
}

View File

@@ -2467,6 +2467,34 @@ select * from parted;
drop table parted;
drop function parted_trigfunc();
-- Test exception handling in a deferred constraint trigger at COMMIT.
create table deferred_trigger_test (a int);
create function deferred_trigger_func() returns trigger
language plpgsql as $$
begin
perform 1 / 0;
return new;
exception when division_by_zero then
raise notice 'caught division_by_zero';
return new;
end;
$$;
create constraint trigger deferred_trigger
after insert on deferred_trigger_test
deferrable initially deferred
for each row execute function deferred_trigger_func();
begin;
insert into deferred_trigger_test values (1);
commit;
NOTICE: caught division_by_zero
select * from deferred_trigger_test;
a
---
1
(1 row)
drop table deferred_trigger_test;
drop function deferred_trigger_func();
--
-- Constraint triggers and partitioned tables
create table parted_constr_ancestor (a int, b text)

View File

@@ -1691,6 +1691,31 @@ select * from parted;
drop table parted;
drop function parted_trigfunc();
-- Test exception handling in a deferred constraint trigger at COMMIT.
create table deferred_trigger_test (a int);
create function deferred_trigger_func() returns trigger
language plpgsql as $$
begin
perform 1 / 0;
return new;
exception when division_by_zero then
raise notice 'caught division_by_zero';
return new;
end;
$$;
create constraint trigger deferred_trigger
after insert on deferred_trigger_test
deferrable initially deferred
for each row execute function deferred_trigger_func();
begin;
insert into deferred_trigger_test values (1);
commit;
select * from deferred_trigger_test;
drop table deferred_trigger_test;
drop function deferred_trigger_func();
--
-- Constraint triggers and partitioned tables
create table parted_constr_ancestor (a int, b text)