From d40e3291c5c82717c6dd6d58735b8499fd080920 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Fri, 2 Oct 2026 23:53:56 +0900 Subject: [PATCH v1 1/2] Add tests for OLD/NEW aliases EXPLAIN prints in RETURNING SubPlans EXPLAIN VERBOSE prints a reference to the OLD or NEW of a ModifyTable in a SubPlan of its RETURNING list with the aliases of a data-modifying CTE that was printed before it. The expected output shows the behavior before the fix. The next commit fixes it and updates the output. --- src/test/regress/expected/returning.out | 64 +++++++++++++++++++++++++ src/test/regress/sql/returning.sql | 21 ++++++++ 2 files changed, 85 insertions(+) diff --git a/src/test/regress/expected/returning.out b/src/test/regress/expected/returning.out index 44d4438fb86..24db5074e45 100644 --- a/src/test/regress/expected/returning.out +++ b/src/test/regress/expected/returning.out @@ -1053,3 +1053,67 @@ MERGE INTO ret_nn USING (VALUES (2), (3)) AS src(a) ON ret_nn.a = src.a (2 rows) DROP TABLE ret_nn; +-- EXPLAIN must print the OLD/NEW aliases of the ModifyTable that owns a +-- RETURNING list also inside its SubPlans, even when a data-modifying CTE +-- has set different aliases +CREATE TABLE returning_e1 (k int, c int); +CREATE TABLE returning_e2 (k int, c int); +EXPLAIN (VERBOSE, COSTS OFF) +WITH x AS (UPDATE returning_e2 SET c = c + 1 + RETURNING WITH (OLD AS o, NEW AS n) o.k) +UPDATE returning_e1 SET c = c + 1 + RETURNING WITH (OLD AS p, NEW AS q) p.c, + (SELECT count(*) FROM returning_e2 WHERE returning_e2.c = p.c) AS m, + (SELECT count(*) FROM returning_e2 WHERE returning_e2.c = q.c) AS m2; + QUERY PLAN +----------------------------------------------------------------- + Update on public.returning_e1 + Output: p.c, (SubPlan expr_1), (SubPlan expr_2) + CTE x + -> Update on public.returning_e2 + Output: o.k + -> Seq Scan on public.returning_e2 + Output: (returning_e2.c + 1), returning_e2.ctid + -> Seq Scan on public.returning_e1 + Output: (returning_e1.c + 1), returning_e1.ctid + SubPlan expr_1 + -> Aggregate + 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) + 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) +(21 rows) + +-- likewise when a SubPlan compares values returned by the CTE +EXPLAIN (VERBOSE, COSTS OFF) +WITH x AS (UPDATE returning_e2 SET c = c + 1 + RETURNING WITH (OLD AS o, NEW AS n) n.k, o.c AS oc) +UPDATE returning_e1 SET c = c + 1 + RETURNING WITH (OLD AS p) p.c, + (SELECT count(*) FROM x WHERE x.oc = p.c) AS m; + QUERY PLAN +----------------------------------------------------------------- + Update on public.returning_e1 + Output: p.c, (SubPlan expr_1) + CTE x + -> Update on public.returning_e2 + Output: n.k, o.c + -> Seq Scan on public.returning_e2 + Output: (returning_e2.c + 1), returning_e2.ctid + -> Seq Scan on public.returning_e1 + Output: (returning_e1.c + 1), returning_e1.ctid + SubPlan expr_1 + -> Aggregate + Output: count(*) + -> CTE Scan on x + Output: x.k, x.oc + Filter: (x.oc = o.c) +(15 rows) + +DROP TABLE returning_e1, returning_e2; diff --git a/src/test/regress/sql/returning.sql b/src/test/regress/sql/returning.sql index a893f47e34d..a6a4865a025 100644 --- a/src/test/regress/sql/returning.sql +++ b/src/test/regress/sql/returning.sql @@ -444,3 +444,24 @@ MERGE INTO ret_nn USING (VALUES (2), (3)) AS src(a) ON ret_nn.a = src.a RETURNING merge_action(), old.a IS NULL, new.a IS NULL; DROP TABLE ret_nn; + +-- EXPLAIN must print the OLD/NEW aliases of the ModifyTable that owns a +-- RETURNING list also inside its SubPlans, even when a data-modifying CTE +-- has set different aliases +CREATE TABLE returning_e1 (k int, c int); +CREATE TABLE returning_e2 (k int, c int); +EXPLAIN (VERBOSE, COSTS OFF) +WITH x AS (UPDATE returning_e2 SET c = c + 1 + RETURNING WITH (OLD AS o, NEW AS n) o.k) +UPDATE returning_e1 SET c = c + 1 + RETURNING WITH (OLD AS p, NEW AS q) p.c, + (SELECT count(*) FROM returning_e2 WHERE returning_e2.c = p.c) AS m, + (SELECT count(*) FROM returning_e2 WHERE returning_e2.c = q.c) AS m2; +-- likewise when a SubPlan compares values returned by the CTE +EXPLAIN (VERBOSE, COSTS OFF) +WITH x AS (UPDATE returning_e2 SET c = c + 1 + RETURNING WITH (OLD AS o, NEW AS n) n.k, o.c AS oc) +UPDATE returning_e1 SET c = c + 1 + RETURNING WITH (OLD AS p) p.c, + (SELECT count(*) FROM x WHERE x.oc = p.c) AS m; +DROP TABLE returning_e1, returning_e2; -- 2.54.0 (Apple Git-157)