On Wed May 20, 2026 at 9:37 AM -03, Ayush Tiwari wrote:
> I reproduced the crash on master. The plan EXPLAIN under
> force_generic_plan shows runtime pruning is in effect:
>
> Update on pt
> Foreign Update on pt_p2 pt_2
> -> Append
> Subplans Removed: 1
> -> Foreign Update on pt_p2 pt_2
>
> The SEGV happens inside postgresBeginForeignModify() because
> ExecInitModifyTable() builds re-indexed "kept" copies of several
> parallel per-result-relation lists after dropping pruned relations -
> withCheckOptionLists, returningLists, updateColnosLists,
> mergeActionLists and mergeJoinConditions, however two members were
> missed:
>
> - node->fdwPrivLists, read with list_nth(node->fdwPrivLists, i) when
> BeginForeignModify() is called, and
> - node->fdwDirectModifyPlans, checked with bms_is_member(i, ...) when
> setting ri_usesFdwDirectModify.
>
> Both were still indexed against the original (pre-pruning) positions
> while the surrounding loop's "i" is now the kept position. When the
> foreign partition's kept-index no longer matched its original index,
> BeginForeignModify() got the wrong fdw_private and crashed.
>
> Attached patch builds re-indexed kept copies for these two arrays in
> the same loop as the other parallel lists, and uses them at the two
> call sites.
>
A good catch. However there is one issue that remains here, in show_modifytable_info still is using the old index here fdw_private = (List *) list_nth(node->fdwPrivLists, j) i.e. the one before pruning.
In fact I found a scenario where it is causing crash, try this
create table fdw_part_update2 (a int not null, b int) partition by list (a);
create table fdw_part_update2_p1 partition of fdw_part_update2 for values in (1);
create table fdw_part_update2_remote (a int not null, b int);
create foreign table fdw_part_update2_p2 partition of fdw_part_update2
for values in (2)
server loopback options (table_name 'fdw_part_update2_remote');
insert into fdw_part_update2_p1 values (1, 10);
insert into fdw_part_update2_remote values (2, 20);
set plan_cache_mode = force_generic_plan;
prepare fdw_part_upd2(int) as
update fdw_part_update2 set b = b + random()::int * 0 + 1 where a = $1
returning tableoid::regclass, a, b;
execute fdw_part_upd2(2);
explain (analyze, verbose, costs off, timing off, summary off)
execute fdw_part_upd2(2);
Please find the attached file for the patch to fix this. This patch applies over the earlier patch (given by Ayush) in this thread.
Hi, thanks for the patch. This issue started on version 18 by commit
cbc127917e0.
The patch fixes the issue and it make sense to me. One a minor comment
is that I think pg_indent is needed on nodeModifyTable.c
--
Matheus Alcantara
EDB: https://www.enterprisedb.com