From eb9e734613771aa8fe79cf4321891bead50e5700 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Sat, 3 Oct 2026 00:28:21 +0900 Subject: [PATCH v1 2/2] Fix OLD/NEW aliases EXPLAIN prints in RETURNING SubPlans set_deparse_context_plan() set the aliases for OLD and NEW in RETURNING when EXPLAIN printed a ModifyTable, but set_deparse_plan() did not. So they were not set when push_ancestor_plan() moved to the ancestor ModifyTable to print a Param of a SubPlan in its RETURNING list, and the aliases of a data-modifying CTE printed before were used. Set them in set_deparse_plan(), so that they follow the plan node in focus. Backpatch-through: 18 --- src/backend/utils/adt/ruleutils.c | 29 +++++++++++++++---------- src/test/regress/expected/returning.out | 6 ++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9b446696068..91be6dd82ec 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3839,13 +3839,6 @@ set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors) dpns->ancestors = ancestors; set_deparse_plan(dpns, plan); - /* For ModifyTable, set aliases for OLD and NEW in RETURNING */ - if (IsA(plan, ModifyTable)) - { - dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias; - dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias; - } - return dpcontext; } @@ -5148,16 +5141,30 @@ get_rtable_name(int rtindex, deparse_context *context) * of a given Plan node * * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist, - * and index_tlist fields. Caller must already have adjusted the ancestors - * list if necessary. Note that the rtable, subplans, and ctes fields do - * not need to change when shifting attention to different plan nodes in a - * single plan tree. + * and index_tlist fields, and also ret_old_alias and ret_new_alias if the + * plan is a ModifyTable (they are left unchanged for other plan types). + * Caller must already have adjusted the ancestors list if necessary. Note + * that the rtable, subplans, and ctes fields do not need to change when + * shifting attention to different plan nodes in a single plan tree. */ static void set_deparse_plan(deparse_namespace *dpns, Plan *plan) { dpns->plan = plan; + /* + * For ModifyTable, set aliases for OLD and NEW in RETURNING. This must + * happen on every shift of attention, because the aliases in dpns are + * those of the last ModifyTable visited, which need not be the ancestor + * ModifyTable whose RETURNING list a SubPlan took an expression from + * (say, when a data-modifying CTE was printed in between). + */ + if (IsA(plan, ModifyTable)) + { + dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias; + dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias; + } + /* * We special-case Append and MergeAppend to pretend that the first child * plan is the OUTER referent; we have to interpret OUTER Vars in their diff --git a/src/test/regress/expected/returning.out b/src/test/regress/expected/returning.out index 24db5074e45..251b6c96ca1 100644 --- a/src/test/regress/expected/returning.out +++ b/src/test/regress/expected/returning.out @@ -1081,13 +1081,13 @@ UPDATE returning_e1 SET c = c + 1 Output: count(*) -> Seq Scan on public.returning_e2 returning_e2_1 Output: returning_e2_1.k, returning_e2_1.c - Filter: (returning_e2_1.c = o.c) + Filter: (returning_e2_1.c = p.c) SubPlan expr_2 -> Aggregate Output: count(*) -> Seq Scan on public.returning_e2 returning_e2_2 Output: returning_e2_2.k, returning_e2_2.c - Filter: (returning_e2_2.c = n.c) + Filter: (returning_e2_2.c = q.c) (21 rows) -- likewise when a SubPlan compares values returned by the CTE @@ -1113,7 +1113,7 @@ UPDATE returning_e1 SET c = c + 1 Output: count(*) -> CTE Scan on x Output: x.k, x.oc - Filter: (x.oc = o.c) + Filter: (x.oc = p.c) (15 rows) DROP TABLE returning_e1, returning_e2; -- 2.54.0 (Apple Git-157)