]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Avoid ABI break in ModifyTableState from the FDW pruning fix
authorAmit Langote <amitlan@postgresql.org>
Thu, 25 Jun 2026 03:12:59 +0000 (12:12 +0900)
committerAmit Langote <amitlan@postgresql.org>
Thu, 25 Jun 2026 03:15:02 +0000 (12:15 +0900)
Commit 1ef917e3a6 fixed the re-indexing of ModifyTable's FDW arrays
when initial runtime pruning removes result relations, but it did so
by adding a new mt_fdwPrivLists field to ModifyTableState.  Although
the field was placed at the end of the struct to keep the offsets of
existing fields stable, it still enlarges sizeof(ModifyTableState),
which the ABI compliance check flags on the buildfarm (e.g. crake).

The field existed only so that show_modifytable_info() could recover the
re-indexed fdw_private after executor startup; the executor-side fix in
ExecInitModifyTable() that actually prevents the crash does not depend on
it.  Remove the field and have show_modifytable_info() instead look up
each kept relation's fdw_private from the original, pre-pruning
node->fdwPrivLists, which is parallel to node->resultRelations and left
intact by pruning.  When nothing was pruned the lookup is a direct index;
otherwise it matches on the range table index.

This is applied to REL_18 only; master keeps the mt_fdwPrivLists field
and is unaffected, so the two diverge slightly here.

Reported on the buildfarm (member crake).

Per a suggestion from Tom Lane.

Reviewed-by: Etsuro Fujita <etsuro.fujita@gmail.com>
Discussion: https://postgr.es/m/CA+HiwqEhe7-v5Q0-oOoW3RaO4voYcGK-JfinbYEWXwutDGSOtQ@mail.gmail.com

src/backend/commands/explain.c
src/backend/executor/nodeModifyTable.c
src/include/nodes/execnodes.h

index 6d2624e75b87fb335ebcf2323bde346823f60f0c..15ef33048636d6145f2125217f8fb6f01379764c 100644 (file)
@@ -4524,6 +4524,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
        const char *operation;
        const char *foperation;
        bool            labeltargets;
+       bool            nopruning;
        int                     j;
        List       *idxNames = NIL;
        ListCell   *lst;
@@ -4571,6 +4572,16 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
        if (labeltargets)
                ExplainOpenGroup("Target Tables", "Target Tables", false, es);
 
+       /*
+        * node->fdwPrivLists is parallel to node->resultRelations, in the
+        * original pre-pruning order.  If no result relations were pruned, the
+        * entries in mtstate->resultRelInfo[] are in that same order and can be
+        * matched to fdwPrivLists positionally; otherwise we have to look each
+        * one up by range table index below.  This test is loop-invariant, so
+        * compute it once here.
+        */
+       nopruning = (list_length(node->resultRelations) == mtstate->mt_nrels);
+
        for (j = 0; j < mtstate->mt_nrels; j++)
        {
                ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
@@ -4609,7 +4620,30 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
                        fdwroutine != NULL &&
                        fdwroutine->ExplainForeignModify != NULL)
                {
-                       List       *fdw_private = (List *) list_nth(mtstate->mt_fdwPrivLists, j);
+                       List       *fdw_private;
+
+                       /*
+                        * Find this relation's fdw_private: index fdwPrivLists directly
+                        * when nothing was pruned, else match by range table index.
+                        */
+                       if (nopruning)
+                               fdw_private = (List *) list_nth(node->fdwPrivLists, j);
+                       else
+                       {
+                               Index           rti = resultRelInfo->ri_RangeTableIndex;
+                               ListCell   *lc1;
+                               ListCell   *lc2;
+
+                               fdw_private = NIL;
+                               forboth(lc1, node->resultRelations, lc2, node->fdwPrivLists)
+                               {
+                                       if (lfirst_int(lc1) == (int) rti)
+                                       {
+                                               fdw_private = (List *) lfirst(lc2);
+                                               break;
+                                       }
+                               }
+                       }
 
                        fdwroutine->ExplainForeignModify(mtstate,
                                                                                         resultRelInfo,
index cac30666663bf0dd35226108818173bbecb56c28..7c1d0e9588e20d9b2e278c38139d4d513da6966f 100644 (file)
@@ -4770,7 +4770,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
        mtstate->mt_updateColnosLists = updateColnosLists;
        mtstate->mt_mergeActionLists = mergeActionLists;
        mtstate->mt_mergeJoinConditions = mergeJoinConditions;
-       mtstate->mt_fdwPrivLists = fdwPrivLists;
 
        /*----------
         * Resolve the target relation. This is the same as:
index 6677a03caab2c7819095f2e669562f95a85a224b..409e172bfb669f5bd13adf5e8985798e402ad451 100644 (file)
@@ -1453,15 +1453,13 @@ typedef struct ModifyTableState
        double          mt_merge_deleted;
 
        /*
-        * Lists of valid updateColnosLists, mergeActionLists,
-        * mergeJoinConditions, and fdwPrivLists.  These contain only entries for
-        * unpruned relations, filtered from the corresponding lists in
-        * ModifyTable.
+        * Lists of valid updateColnosLists, mergeActionLists, and
+        * mergeJoinConditions.  These contain only entries for unpruned
+        * relations, filtered from the corresponding lists in ModifyTable.
         */
        List       *mt_updateColnosLists;
        List       *mt_mergeActionLists;
        List       *mt_mergeJoinConditions;
-       List       *mt_fdwPrivLists;
 } ModifyTableState;
 
 /* ----------------