aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDean Rasheed <dean.a.rasheed@gmail.com>2024-03-17 10:19:31 +0000
committerDean Rasheed <dean.a.rasheed@gmail.com>2024-03-17 10:19:31 +0000
commit34c854b93fbec6d7b6dce4ee48c5b7459364a124 (patch)
treef927d820a95324a14b9e77047f3678c8e8786c9d /src
parent56432629999736b5cdd6fa8e56545d8ac5da281f (diff)
downloadpostgresql-34c854b93fbec6d7b6dce4ee48c5b7459364a124.tar.gz
postgresql-34c854b93fbec6d7b6dce4ee48c5b7459364a124.zip
Fix EXPLAIN output for subplans in MERGE.
Given a subplan in a MERGE query, EXPLAIN would sometimes fail to properly display expressions involving Params referencing variables in other parts of the plan tree. This would affect subplans outside the topmost join plan node, for which expansion of Params would go via the top-level ModifyTable plan node. The problem was that "inner_tlist" for the ModifyTable node's deparse_namespace was set to the join node's targetlist, but "inner_plan" was set to the ModifyTable node itself, rather than the join node, leading to incorrect results when descending to the referenced variable. Fix and backpatch to v15, where MERGE was introduced. Discussion: https://postgr.es/m/CAEZATCWAv-sZuH%2BwG5xJ-%2BGt7qGNGX8wUQd3XYydMFDKgRB9nw%40mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/ruleutils.c21
-rw-r--r--src/test/regress/expected/merge.out50
-rw-r--r--src/test/regress/sql/merge.sql17
3 files changed, 79 insertions, 9 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f0b16fa23e5..f01cc2521c8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -4973,8 +4973,11 @@ set_deparse_plan(deparse_namespace *dpns, Plan *plan)
* For a WorkTableScan, locate the parent RecursiveUnion plan node and use
* that as INNER referent.
*
- * For MERGE, make the inner tlist point to the merge source tlist, which
- * is same as the targetlist that the ModifyTable's source plan provides.
+ * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
+ * INNER referent. This is the join from the target relation to the data
+ * source, and all INNER_VAR Vars in other parts of the query refer to its
+ * targetlist.
+ *
* For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
* excluded expression's tlist. (Similar to the SubqueryScan we don't want
* to reuse OUTER, it's used for RETURNING in some modify table cases,
@@ -4989,17 +4992,17 @@ set_deparse_plan(deparse_namespace *dpns, Plan *plan)
dpns->inner_plan = find_recursive_union(dpns,
(WorkTableScan *) plan);
else if (IsA(plan, ModifyTable))
- dpns->inner_plan = plan;
- else
- dpns->inner_plan = innerPlan(plan);
-
- if (IsA(plan, ModifyTable))
{
if (((ModifyTable *) plan)->operation == CMD_MERGE)
- dpns->inner_tlist = dpns->outer_tlist;
+ dpns->inner_plan = outerPlan(plan);
else
- dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
+ dpns->inner_plan = plan;
}
+ else
+ dpns->inner_plan = innerPlan(plan);
+
+ if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
+ dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
else if (dpns->inner_plan)
dpns->inner_tlist = dpns->inner_plan->targetlist;
else
diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out
index 28a69802d72..bfdf59a2750 100644
--- a/src/test/regress/expected/merge.out
+++ b/src/test/regress/expected/merge.out
@@ -1483,6 +1483,56 @@ WHEN MATCHED AND t.a < 10 THEN
DROP TABLE ex_msource, ex_mtarget;
DROP FUNCTION explain_merge(text);
+-- EXPLAIN SubPlans and InitPlans
+CREATE TABLE src (a int, b int, c int, d int);
+CREATE TABLE tgt (a int, b int, c int, d int);
+CREATE TABLE ref (ab int, cd int);
+EXPLAIN (verbose, costs off)
+MERGE INTO tgt t
+USING (SELECT *, (SELECT count(*) FROM ref r
+ WHERE r.ab = s.a + s.b
+ AND r.cd = s.c - s.d) cnt
+ FROM src s) s
+ON t.a = s.a AND t.b < s.cnt
+WHEN MATCHED AND t.c > s.cnt THEN
+ UPDATE SET (b, c) = (SELECT s.b, s.cnt);
+ QUERY PLAN
+-------------------------------------------------------------------------------------
+ Merge on public.tgt t
+ -> Hash Join
+ Output: t.ctid, s.a, s.b, s.c, s.d, s.ctid
+ Hash Cond: (t.a = s.a)
+ Join Filter: (t.b < (SubPlan 1))
+ -> Seq Scan on public.tgt t
+ Output: t.ctid, t.a, t.b
+ -> Hash
+ Output: s.a, s.b, s.c, s.d, s.ctid
+ -> Seq Scan on public.src s
+ Output: s.a, s.b, s.c, s.d, s.ctid
+ SubPlan 1
+ -> Aggregate
+ Output: count(*)
+ -> Seq Scan on public.ref r
+ Output: r.ab, r.cd
+ Filter: ((r.ab = (s.a + s.b)) AND (r.cd = (s.c - s.d)))
+ SubPlan 4
+ -> Aggregate
+ Output: count(*)
+ -> Seq Scan on public.ref r_2
+ Output: r_2.ab, r_2.cd
+ Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d)))
+ SubPlan 3 (returns $9,$10)
+ -> Result
+ Output: s.b, $8
+ InitPlan 2 (returns $8)
+ -> Aggregate
+ Output: count(*)
+ -> Seq Scan on public.ref r_1
+ Output: r_1.ab, r_1.cd
+ Filter: ((r_1.ab = (s.a + s.b)) AND (r_1.cd = (s.c - s.d)))
+(32 rows)
+
+DROP TABLE src, tgt, ref;
-- Subqueries
BEGIN;
MERGE INTO sq_target t
diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql
index 82faa7364ca..ac6d2f0e0ee 100644
--- a/src/test/regress/sql/merge.sql
+++ b/src/test/regress/sql/merge.sql
@@ -948,6 +948,23 @@ WHEN MATCHED AND t.a < 10 THEN
DROP TABLE ex_msource, ex_mtarget;
DROP FUNCTION explain_merge(text);
+-- EXPLAIN SubPlans and InitPlans
+CREATE TABLE src (a int, b int, c int, d int);
+CREATE TABLE tgt (a int, b int, c int, d int);
+CREATE TABLE ref (ab int, cd int);
+
+EXPLAIN (verbose, costs off)
+MERGE INTO tgt t
+USING (SELECT *, (SELECT count(*) FROM ref r
+ WHERE r.ab = s.a + s.b
+ AND r.cd = s.c - s.d) cnt
+ FROM src s) s
+ON t.a = s.a AND t.b < s.cnt
+WHEN MATCHED AND t.c > s.cnt THEN
+ UPDATE SET (b, c) = (SELECT s.b, s.cnt);
+
+DROP TABLE src, tgt, ref;
+
-- Subqueries
BEGIN;
MERGE INTO sq_target t