Actually, I hadn't realized that the originally submitted patch had the test in postgres_fdw only, but we really want it to catch any FDW, so it needs to be somewhere more general. The best place I found to put this test is in make_modifytable ... I searched for some earlier place in the planner to do it, but couldn't find anything.
So what do people think about this? -- Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/ "La grandeza es una experiencia transitoria. Nunca es consistente. Depende en gran parte de la imaginación humana creadora de mitos" (Irulan)
>From 09fba6a6f4caf2a987aa7ee5d77dbf74372ad1d0 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Fri, 14 Oct 2022 11:19:10 +0200 Subject: [PATCH v2] Disallow MERGE cleanly for foreign partitions --- .../postgres_fdw/expected/postgres_fdw.out | 4 ++++ contrib/postgres_fdw/sql/postgres_fdw.sql | 3 +++ src/backend/optimizer/plan/createplan.c | 20 +++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 9746998751..bdeba8c291 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -8284,6 +8284,10 @@ select tableoid::regclass, * FROM remp2; (3 rows) delete from itrtest; +-- MERGE ought to fail cleanly +merge into itrtest using (select 1, 'foo') on (true) when matched then do nothing; +ERROR: cannot execute MERGE on relation "remp1" +DETAIL: This operation is not supported for foreign tables. create unique index loct1_idx on loct1 (a); -- DO NOTHING without an inference specification is supported insert into itrtest values (1, 'foo') on conflict do nothing returning *; diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 1962051e54..514df43b06 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -2207,6 +2207,9 @@ select tableoid::regclass, * FROM remp2; delete from itrtest; +-- MERGE ought to fail cleanly +merge into itrtest using (select 1, 'foo') on (true) when matched then do nothing; + create unique index loct1_idx on loct1 (a); -- DO NOTHING without an inference specification is supported diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index ab4d8e201d..ac86ce9003 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7078,12 +7078,32 @@ make_modifytable(PlannerInfo *root, Plan *subplan, RelOptInfo *resultRel = root->simple_rel_array[rti]; fdwroutine = resultRel->fdwroutine; + + /* + * MERGE is not currently supported for foreign tables and we + * already checked when the table mentioned in the query is + * foreign; but we can still get here if a partitioned table has a + * foreign table as partition. Disallow that now, to avoid an + * uglier error message later. + */ + if (operation == CMD_MERGE && fdwroutine != NULL) + { + RangeTblEntry *rte = root->simple_rte_array[rti]; + + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot execute MERGE on relation \"%s\"", + get_rel_name(rte->relid)), + errdetail_relkind_not_supported(rte->relkind)); + } + } else { RangeTblEntry *rte = planner_rt_fetch(rti, root); Assert(rte->rtekind == RTE_RELATION); + Assert(operation != CMD_MERGE); if (rte->relkind == RELKIND_FOREIGN_TABLE) fdwroutine = GetFdwRoutineByRelId(rte->relid); else -- 2.30.2