Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 34c854b

Browse files
committed
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
1 parent 5643262 commit 34c854b

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4973,8 +4973,11 @@ set_deparse_plan(deparse_namespace *dpns, Plan *plan)
49734973
* For a WorkTableScan, locate the parent RecursiveUnion plan node and use
49744974
* that as INNER referent.
49754975
*
4976-
* For MERGE, make the inner tlist point to the merge source tlist, which
4977-
* is same as the targetlist that the ModifyTable's source plan provides.
4976+
* For MERGE, pretend the ModifyTable's source plan (its outer plan) is
4977+
* INNER referent. This is the join from the target relation to the data
4978+
* source, and all INNER_VAR Vars in other parts of the query refer to its
4979+
* targetlist.
4980+
*
49784981
* For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
49794982
* excluded expression's tlist. (Similar to the SubqueryScan we don't want
49804983
* 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)
49894992
dpns->inner_plan = find_recursive_union(dpns,
49904993
(WorkTableScan *) plan);
49914994
else if (IsA(plan, ModifyTable))
4992-
dpns->inner_plan = plan;
4993-
else
4994-
dpns->inner_plan = innerPlan(plan);
4995-
4996-
if (IsA(plan, ModifyTable))
49974995
{
49984996
if (((ModifyTable *) plan)->operation == CMD_MERGE)
4999-
dpns->inner_tlist = dpns->outer_tlist;
4997+
dpns->inner_plan = outerPlan(plan);
50004998
else
5001-
dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
4999+
dpns->inner_plan = plan;
50025000
}
5001+
else
5002+
dpns->inner_plan = innerPlan(plan);
5003+
5004+
if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
5005+
dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
50035006
else if (dpns->inner_plan)
50045007
dpns->inner_tlist = dpns->inner_plan->targetlist;
50055008
else

src/test/regress/expected/merge.out

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,56 @@ WHEN MATCHED AND t.a < 10 THEN
14831483

14841484
DROP TABLE ex_msource, ex_mtarget;
14851485
DROP FUNCTION explain_merge(text);
1486+
-- EXPLAIN SubPlans and InitPlans
1487+
CREATE TABLE src (a int, b int, c int, d int);
1488+
CREATE TABLE tgt (a int, b int, c int, d int);
1489+
CREATE TABLE ref (ab int, cd int);
1490+
EXPLAIN (verbose, costs off)
1491+
MERGE INTO tgt t
1492+
USING (SELECT *, (SELECT count(*) FROM ref r
1493+
WHERE r.ab = s.a + s.b
1494+
AND r.cd = s.c - s.d) cnt
1495+
FROM src s) s
1496+
ON t.a = s.a AND t.b < s.cnt
1497+
WHEN MATCHED AND t.c > s.cnt THEN
1498+
UPDATE SET (b, c) = (SELECT s.b, s.cnt);
1499+
QUERY PLAN
1500+
-------------------------------------------------------------------------------------
1501+
Merge on public.tgt t
1502+
-> Hash Join
1503+
Output: t.ctid, s.a, s.b, s.c, s.d, s.ctid
1504+
Hash Cond: (t.a = s.a)
1505+
Join Filter: (t.b < (SubPlan 1))
1506+
-> Seq Scan on public.tgt t
1507+
Output: t.ctid, t.a, t.b
1508+
-> Hash
1509+
Output: s.a, s.b, s.c, s.d, s.ctid
1510+
-> Seq Scan on public.src s
1511+
Output: s.a, s.b, s.c, s.d, s.ctid
1512+
SubPlan 1
1513+
-> Aggregate
1514+
Output: count(*)
1515+
-> Seq Scan on public.ref r
1516+
Output: r.ab, r.cd
1517+
Filter: ((r.ab = (s.a + s.b)) AND (r.cd = (s.c - s.d)))
1518+
SubPlan 4
1519+
-> Aggregate
1520+
Output: count(*)
1521+
-> Seq Scan on public.ref r_2
1522+
Output: r_2.ab, r_2.cd
1523+
Filter: ((r_2.ab = (s.a + s.b)) AND (r_2.cd = (s.c - s.d)))
1524+
SubPlan 3 (returns $9,$10)
1525+
-> Result
1526+
Output: s.b, $8
1527+
InitPlan 2 (returns $8)
1528+
-> Aggregate
1529+
Output: count(*)
1530+
-> Seq Scan on public.ref r_1
1531+
Output: r_1.ab, r_1.cd
1532+
Filter: ((r_1.ab = (s.a + s.b)) AND (r_1.cd = (s.c - s.d)))
1533+
(32 rows)
1534+
1535+
DROP TABLE src, tgt, ref;
14861536
-- Subqueries
14871537
BEGIN;
14881538
MERGE INTO sq_target t

src/test/regress/sql/merge.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,23 @@ WHEN MATCHED AND t.a < 10 THEN
948948
DROP TABLE ex_msource, ex_mtarget;
949949
DROP FUNCTION explain_merge(text);
950950

951+
-- EXPLAIN SubPlans and InitPlans
952+
CREATE TABLE src (a int, b int, c int, d int);
953+
CREATE TABLE tgt (a int, b int, c int, d int);
954+
CREATE TABLE ref (ab int, cd int);
955+
956+
EXPLAIN (verbose, costs off)
957+
MERGE INTO tgt t
958+
USING (SELECT *, (SELECT count(*) FROM ref r
959+
WHERE r.ab = s.a + s.b
960+
AND r.cd = s.c - s.d) cnt
961+
FROM src s) s
962+
ON t.a = s.a AND t.b < s.cnt
963+
WHEN MATCHED AND t.c > s.cnt THEN
964+
UPDATE SET (b, c) = (SELECT s.b, s.cnt);
965+
966+
DROP TABLE src, tgt, ref;
967+
951968
-- Subqueries
952969
BEGIN;
953970
MERGE INTO sq_target t

0 commit comments

Comments
 (0)