From 4f0af2635be7963c3250d0469e2c159dd0338574 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Thu, 27 Aug 2026 02:41:21 +1200 Subject: [PATCH] Close relations opened specifically for AFTER triggers 39dcfda2d fixed an incorrect reuse of ResultRelInfos for AFTER triggers when the ResultRelInfo needed to have a different ri_RootResultRelInfo. That caused an issue in logical replication apply workers as finish_edata() neglects to call ExecCloseResultRelations() and instead relies on ExecCleanupTupleRouting() to close relations opened during partitioning's tuple routing. Since 39dcfda2d, because we may have done some additional table_opens() calls due to having to create an additional ResultRelInfo because of requirements to have a different ri_RootResultRelInfo, we should now be explicitly closing any relations opened on ResultRelInfos in EState's es_trig_target_relations. Since finish_edate() seems to want to avoid calling ExecCloseResultRelations(), add a new external function named ExecCloseTrigTargetRelations(). Reported-by: Hayato Kuroda (Fujitsu) Author: Hayato Kuroda (Fujitsu) Author: David Rowley Reviewed-by: Zhijie Hou (Fujitsu) Discussion: https://postgr.es/m/OS9PR01MB121491E7E05950D108AF9A6D8F5A72@OS9PR01MB12149.jpnprd01.prod.outlook.com Backpatch-through: 15 --- src/backend/executor/execMain.c | 16 ++++++++++++++++ src/backend/replication/logical/worker.c | 12 ++++++++---- src/include/executor/executor.h | 1 + src/test/subscription/t/013_partition.pl | 7 +++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index c3af96989ba..d8bbe467a70 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1642,6 +1642,22 @@ ExecCloseResultRelations(EState *estate) } } + /* + * Now close any relations that we opened for trigger target + * ResultRelInfos. + */ + ExecCloseTrigTargetRelations(estate); +} + +/* + * Close any relations that have been opened for ResultRelInfos opened + * specifically for trigger target relations. + */ +void +ExecCloseTrigTargetRelations(EState *estate) +{ + ListCell *l; + /* Close any relations that have been opened by ExecGetTriggerResultRel(). */ foreach(l, estate->es_trig_target_relations) { diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index b3cdbce8d10..52c6cc8c596 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -945,12 +945,16 @@ finish_edata(ApplyExecutionData *edata) ExecCleanupTupleRouting(edata->mtstate, edata->proute); /* - * Cleanup. It might seem that we should call ExecCloseResultRelations() - * here, but we intentionally don't. It would close the rel we added to + * Close relations opened specifically for trigger targets. It might seem + * that we should call ExecCloseResultRelations() here, but we + * intentionally don't as that would close the rel we added to * es_opened_result_relations above, which is wrong because we took no - * corresponding refcount. We rely on ExecCleanupTupleRouting() to close - * any other relations opened during execution. + * corresponding refcount. ExecCleanupTupleRouting() closes relations + * opened for tuple routing, while ExecCloseTrigTargetRelations() closes + * any relations we opened for AFTER triggers. */ + ExecCloseTrigTargetRelations(estate); + ExecResetTupleTable(estate->es_tupleTable, false); FreeExecutorState(estate); pfree(edata); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 1798e6027d4..190e8a4897a 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -703,6 +703,7 @@ extern void ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos Bitmapset *unpruned_relids); extern void ExecCloseRangeTableRelations(EState *estate); extern void ExecCloseResultRelations(EState *estate); +extern void ExecCloseTrigTargetRelations(EState *estate); static inline RangeTblEntry * exec_rt_fetch(Index rti, EState *estate) diff --git a/src/test/subscription/t/013_partition.pl b/src/test/subscription/t/013_partition.pl index 234d4f003b7..21c3a9e261b 100644 --- a/src/test/subscription/t/013_partition.pl +++ b/src/test/subscription/t/013_partition.pl @@ -897,4 +897,11 @@ $result = $node_subscriber2->safe_psql('postgres', "SELECT a, b, c FROM tab5_1 ORDER BY 1"); is($result, qq(4||1), 'updates of tab5 replicated correctly'); +# Validate we didn't neglect to cleanup any resources on either subscriber. +foreach my $node ($node_subscriber1, $node_subscriber2) +{ + ok(!$node->log_contains(qr/resource was not closed/), + 'check for resource leaks on ' . $node->name); +} + done_testing();