diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index 0dfefd71f21..f79bc4430c1 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -183,6 +183,14 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo) if (!bms_get_singleton_member(sjinfo->min_righthand, &innerrelid)) return false; + /* + * Never try to eliminate a left join to the query result rel. Although + * the case is syntactically impossible in standard SQL, MERGE will build + * a join tree that looks exactly like that. + */ + if (innerrelid == root->parse->resultRelation) + return false; + innerrel = find_base_rel(root, innerrelid); /* diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index bc53b2105b4..2cf1409470b 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -1771,6 +1771,41 @@ SELECT * FROM pa_target ORDER BY tid; (9 rows) ROLLBACK; +DROP TABLE pa_source; +DROP TABLE pa_target CASCADE; +-- Partitioned table with primary key +CREATE TABLE pa_target (tid integer PRIMARY KEY) PARTITION BY LIST (tid); +CREATE TABLE pa_targetp PARTITION OF pa_target DEFAULT; +CREATE TABLE pa_source (sid integer); +INSERT INTO pa_source VALUES (1), (2); +EXPLAIN (VERBOSE, COSTS OFF) +MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid + WHEN NOT MATCHED THEN INSERT VALUES (s.sid); + QUERY PLAN +------------------------------------------------------------- + Merge on public.pa_target t + Merge on public.pa_targetp t_1 + -> Hash Left Join + Output: s.sid, t_1.tableoid, t_1.ctid + Inner Unique: true + Hash Cond: (s.sid = t_1.tid) + -> Seq Scan on public.pa_source s + Output: s.sid + -> Hash + Output: t_1.tid, t_1.tableoid, t_1.ctid + -> Seq Scan on public.pa_targetp t_1 + Output: t_1.tid, t_1.tableoid, t_1.ctid +(12 rows) + +MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid + WHEN NOT MATCHED THEN INSERT VALUES (s.sid); +TABLE pa_target; + tid +----- + 1 + 2 +(2 rows) + DROP TABLE pa_source; DROP TABLE pa_target CASCADE; -- some complex joins on the source side diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index fdbcd708823..cef37e57d92 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -1116,6 +1116,26 @@ ROLLBACK; DROP TABLE pa_source; DROP TABLE pa_target CASCADE; +-- Partitioned table with primary key + +CREATE TABLE pa_target (tid integer PRIMARY KEY) PARTITION BY LIST (tid); +CREATE TABLE pa_targetp PARTITION OF pa_target DEFAULT; +CREATE TABLE pa_source (sid integer); + +INSERT INTO pa_source VALUES (1), (2); + +EXPLAIN (VERBOSE, COSTS OFF) +MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid + WHEN NOT MATCHED THEN INSERT VALUES (s.sid); + +MERGE INTO pa_target t USING pa_source s ON t.tid = s.sid + WHEN NOT MATCHED THEN INSERT VALUES (s.sid); + +TABLE pa_target; + +DROP TABLE pa_source; +DROP TABLE pa_target CASCADE; + -- some complex joins on the source side CREATE TABLE cj_target (tid integer, balance float, val text)