Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

Started by Shirisha Shirisha7 months ago8 messages
#1Shirisha Shirisha
shirisha.sn@broadcom.com
2 attachment(s)

Hello Hackers,

We’d like to propose a change to improve DELETE and UPDATE behavior on
partitioned tables
containing foreign partitions.

Currently, DELETE or UPDATE (D/U) on a partitioned table with foreign
partitions fails with an error
as below, if the FDW does not support the operation:

`ERROR: cannot delete from foreign table`

This failure occurs during executor initialization (`ExecInitModifyTable`),
where PostgreSQL scans
all partitions of the target table and checks whether each one supports the
requested operation.
If any foreign partition's FDW lacks support for D/U, the operation is
rejected outright, even if that
partition would not be affected by the query.

As a result, *DELETE/UPDATE operations are blocked even when they only
target non-foreign partitions*.
This means that the system errors out without considering whether foreign
partitions are actually involved in the operation.
Even if no matching rows exist in a foreign partition, the operation still
fails.

This behavior presents a usability hurdle as it forces the user to work
around this limitation by issuing D/U
statements separately on each individual child partition. This is
cumbersome and breaks the workflow of managing such tables via the root.

We are proposing a patch that would allow users to have a better workflow
by continuing to perform D/U via root partition
even in presence of foreign partitions not implementing D/U API.
*The key change is to defer the FDW check for foreign partitions from
`ExecInitModifyTable` to `ExecDelete` and `ExecUpdate`.*
This would ensure that the foreign partitions are checked only when they
are actually targeted by the operation.

However, if a D/U is issued on the root partition and it includes foreign
partitions that do not support the operation,
it will still result in an error. This is intentional because the onus of
managing data in foreign partitions lies with the user.
Only after the user removes relevant data from those foreign partitions
will such operations succeed at root level.

We also want to mention that `TRUNCATE` suffers from the same limitation
but can be taken as a next step
once D/U are handled.

The proposed patch is attached for review.

postgres=# select version();
version

----------------------------------------------------------------------------------------------------------------------
PostgreSQL 18beta1 on aarch64-apple-darwin24.5.0, compiled by Apple clang
version 17.0.0 (clang-1700.0.13.5), 64-bit
(1 row)
CREATE EXTENSION file_fdw;
CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
CREATE TABLE pt (a int, b numeric) PARTITION BY RANGE(a);
CREATE TABLE pt_part1 PARTITION OF pt FOR VALUES FROM (0) TO (10);
INSERT INTO pt SELECT 5, 0.1;
INSERT INTO pt SELECT 6, 0.2;

CREATE FOREIGN TABLE ext (a int, b numeric) SERVER file_server OPTIONS

(filename
'/Users/sshirisha/workspace/postgres/src/test/regress/data/test_data_float.csv',
format 'csv', delimiter ',');
ALTER TABLE pt ATTACH PARTITION ext FOR VALUES FROM (10) TO (20);
SELECT * FROM pt;
postgres=# SELECT * FROM pt;
a | b
----+-----
5 | 0.1
6 | 0.2
15 | 0.3
21 | 0.4
(4 rows)

*Without Patch :*

postgres=# DELETE FROM pt WHERE b = 0.2; --- delete errors out
even if foreign_table `ext` is not the target
ERROR: cannot delete from foreign table "ext"
postgres=# DELETE FROM pt;
ERROR: cannot delete from foreign table "ext"

postgres=# UPDATE pt set b = 0.5 WHERE b = 0.1; --- update errors out
even if foreign_table `ext` is not the target
ERROR: cannot update foreign table "ext"
postgres=# UPDATE pt SET b = 0.5;
ERROR: cannot update foreign table "ext"

*With Patch:*

postgres=# DELETE FROM pt WHERE b = 0.2;
DELETE 1
postgres=# DELETE FROM pt;
ERROR: cannot delete from foreign table "ext"

postgres=# UPDATE pt SET b = 0.5 WHERE b = 0.1;

UPDATE 1
postgres=# UPDATE pt SET b = 0.5;
ERROR: cannot update foreign table "ext"

Thanks and Regards,
Shirisha and Ashwin
Broadcom Inc.

--
This electronic communication and the information and any files transmitted
with it, or attached to it, are confidential and are intended solely for
the use of the individual or entity to whom it is addressed and may contain
information that is confidential, legally privileged, protected by privacy
laws, or otherwise restricted from disclosure to anyone else. If you are
not the intended recipient or the person responsible for delivering the
e-mail to the intended recipient, you are hereby notified that any use,
copying, distributing, dissemination, forwarding, printing, or copying of
this e-mail is strictly prohibited. If you received this e-mail in error,
please return the e-mail to the sender, delete it from your computer, and
destroy any printed copy of it.

Attachments:

v1-0001-Allow-DELETE-UPDATE-on-partitioned-tables-with-fo.patchapplication/octet-stream; name=v1-0001-Allow-DELETE-UPDATE-on-partitioned-tables-with-fo.patchDownload
From 430d0b104d6b5f3fa49a149442f1f331da280fca Mon Sep 17 00:00:00 2001
From: Shirisha SN <sshirisha@vmware.com>
Date: Fri, 6 Jun 2025 22:27:40 +0530
Subject: [PATCH v1 1/1] Allow DELETE/UPDATE on partitioned tables with foreign
 partitions

Currently, DELETE or UPDATE on a partitioned table with foreign partitions
fail with an error as below, if the FDW does not support the operation:

	`ERROR: cannot delete from foreign table`

This occurs because during executor initialization (ExecInitModifyTable),
PostgreSQL scans all partitions of the target table and checks whether each one
supports the requested operation. If any foreign partition's FDW lacks support
for DELETE or UPDATE, the operation is rejected outright, even if that
partition would not be affected by the query.

As a result, DELETE/UPDATE operations are blocked even when they only target
non-foreign partitions. This means the system errors out without considering
whether foreign partitions are actually involved in the operation. Even if no
matching rows exist in a foreign partition, the operation still fails.

This commit defers the FDW check for foreign partitions from
`ExecInitModifyTable` to `ExecDelete` and `ExecUpdate`. This change ensures
that foreign partitions are checked only when they are actually targeted by the
operation.

However, if a DELETE or UPDATE is issued on the root partition and it includes
foreign partitions that do not support the operation, it will still result in
an error. This is intentional because the responsibility for managing data in
foreign partitions lies with the user. Only after the user has removed relevant
data from those foreign partitions will such operations on the root partition
succeed.

** Mini repro: **
```
CREATE EXTENSION file_fdw;
CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
CREATE TABLE pt (a int, b numeric) PARTITION BY RANGE(a);
CREATE TABLE pt_part1 PARTITION OF pt FOR VALUES FROM (0) TO (10);
INSERT INTO pt SELECT 5, 0.1;
INSERT INTO pt SELECT 6, 0.2;

CREATE FOREIGN TABLE ext (a int, b numeric) SERVER file_server OPTIONS (filename '/Users/sshirisha/workspace/postgres/src/test/regress/data/test_data_float.csv', format 'csv', delimiter ',');
ALTER TABLE pt ATTACH PARTITION ext FOR VALUES FROM (10) TO (20);
postgres=# SELECT * FROM pt;
 a  |  b
----+-----
  5 | 0.1
  6 | 0.2
 15 | 0.3
 21 | 0.4
(4 rows)
```

** Before Fix: **
```
postgres=# DELETE FROM pt WHERE b = 0.2;
ERROR:  cannot delete from foreign table "ext"
postgres=# DELETE FROM pt;
ERROR:  cannot delete from foreign table "ext"

postgres=# UPDATE pt set b = 0.5 WHERE b = 0.1;
ERROR:  cannot update foreign table "ext"
postgres=# UPDATE pt SET b = 0.5;
ERROR:  cannot update foreign table "ext"
```

** After Fix: **
```
postgres=# DELETE FROM pt WHERE b = 0.2;
DELETE 1
postgres=# DELETE FROM pt;
ERROR:  cannot delete from foreign table "ext"

postgres=# UPDATE pt SET b = 0.5 WHERE b = 0.1;
UPDATE 1
postgres=# UPDATE pt SET b = 0.5;
ERROR:  cannot update foreign table "ext"
```

Co-authored-by: Ashwin Agrawal <ashwin.agrawal@broadcom.com>
---
 src/backend/executor/nodeModifyTable.c       | 66 +++++++++++++++++++-
 src/test/regress/expected/partition_info.out | 58 +++++++++++++++++
 src/test/regress/sql/partition_info.sql      | 28 +++++++++
 3 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 54da8e7995b..2a696cb0615 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1584,10 +1584,34 @@ ExecDelete(ModifyTableContext *context,
 	TupleTableSlot *slot = NULL;
 	TM_Result	result;
 	bool		saveOld;
+	FdwRoutine *fdwroutine;
 
 	if (tupleDeleted)
 		*tupleDeleted = false;
 
+	/*
+	 * For foreign partitions, raise error during DELETE if the FDW does not support it.
+	 * This check is deferred from ExecInitModifyTable to allow deletes
+	 * targeted on non-foreign partitions to proceed without error.
+	 */
+	if (resultRelationDesc->rd_rel->relkind == RELKIND_FOREIGN_TABLE &&
+			resultRelationDesc->rd_rel->relispartition)
+	{
+		fdwroutine = resultRelInfo->ri_FdwRoutine;
+		if (fdwroutine->ExecForeignDelete == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot delete from foreign table \"%s\"",
+							RelationGetRelationName(resultRelationDesc))));
+
+		if (fdwroutine->IsForeignRelUpdatable != NULL &&
+			(fdwroutine->IsForeignRelUpdatable(resultRelationDesc) & (1 << CMD_DELETE)) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					errmsg("foreign table \"%s\" does not allow deletes",
+							RelationGetRelationName(resultRelationDesc))));
+	}
+
 	/*
 	 * Prepare for the delete.  This includes BEFORE ROW triggers, so we're
 	 * done if it says we are.
@@ -2464,6 +2488,7 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 	Relation	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 	UpdateContext updateCxt = {0};
 	TM_Result	result;
+	FdwRoutine *fdwroutine;
 
 	/*
 	 * abort the operation if not running transactions
@@ -2478,6 +2503,29 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 	if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
 		return NULL;
 
+	/*
+	 * For foreign partitions, raise error during UPDATE if the FDW does not support it.
+	 * This check is deferred from ExecInitModifyTable to allow updates
+	 * targeted on non-foreign partitions to proceed without error.
+	 */
+	if (resultRelationDesc->rd_rel->relkind == RELKIND_FOREIGN_TABLE &&
+		resultRelationDesc->rd_rel->relispartition)
+	{
+		fdwroutine = resultRelInfo->ri_FdwRoutine;
+		if (fdwroutine->ExecForeignUpdate == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot update foreign table \"%s\"",
+							RelationGetRelationName(resultRelationDesc))));
+
+		if (fdwroutine->IsForeignRelUpdatable != NULL &&
+			(fdwroutine->IsForeignRelUpdatable(resultRelationDesc) & (1 << CMD_UPDATE)) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					errmsg("foreign table \"%s\" does not allow updates",
+							RelationGetRelationName(resultRelationDesc))));
+	}
+
 	/* INSTEAD OF ROW UPDATE Triggers */
 	if (resultRelInfo->ri_TrigDesc &&
 		resultRelInfo->ri_TrigDesc->trig_update_instead_row)
@@ -4783,6 +4831,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	i = 0;
 	foreach(l, resultRelations)
 	{
+		bool 		skip_rel_check = false;
 		Index		resultRelation = lfirst_int(l);
 		List	   *mergeActions = NIL;
 
@@ -4807,9 +4856,22 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			bms_is_member(i, node->fdwDirectModifyPlans);
 
 		/*
-		 * Verify result relation is a valid target for the current operation
+		 * Verify result relation is a valid target for the current operation.
+		 * Skip this verification only when:
+		 * - the relation is a foreign partition,
+		 * - the operation is DELETE or UPDATE, and
+		 * - the query involves multiple result relations
+		 *
+		 * In such cases, the validation is deferred to ExecDelete or
+		 * ExecUpdate, where the specific foreign partition is processed.
 		 */
-		CheckValidResultRel(resultRelInfo, operation, mergeActions);
+		rel = resultRelInfo->ri_RelationDesc;
+		skip_rel_check = (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE &&
+							rel->rd_rel->relispartition &&
+							(operation == CMD_DELETE || operation == CMD_UPDATE) &&
+							nrels > 1);
+		if (!skip_rel_check)
+			CheckValidResultRel(resultRelInfo, operation, mergeActions);
 
 		resultRelInfo++;
 		i++;
diff --git a/src/test/regress/expected/partition_info.out b/src/test/regress/expected/partition_info.out
index 42b6bc77cad..b7095744d77 100644
--- a/src/test/regress/expected/partition_info.out
+++ b/src/test/regress/expected/partition_info.out
@@ -349,3 +349,61 @@ SELECT pg_partition_root('ptif_li_child');
 DROP VIEW ptif_test_view;
 DROP MATERIALIZED VIEW ptif_test_matview;
 DROP TABLE ptif_li_parent, ptif_li_child;
+-- Test UPDATE/DELETE on partition table with foreign partitions
+\getenv abs_srcdir PG_ABS_SRCDIR
+CREATE EXTENSION file_fdw;
+CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
+CREATE TABLE ptif_root (a int2, b numeric) PARTITION BY range (a);
+CREATE TABLE ptif_child PARTITION OF ptif_root FOR VALUES FROM (0) TO (10);
+INSERT INTO ptif_root SELECT 5, 0.1;
+INSERT INTO ptif_root SELECT 6, 0.2;
+\set filename :abs_srcdir '/data/agg.csv'
+CREATE FOREIGN TABLE agg_csv (
+        a int2,
+        b numeric
+) SERVER file_server
+OPTIONS (format 'csv', filename :'filename', header 'false', delimiter ';', quote '@', escape '"', null '');
+ALTER TABLE ptif_root ATTACH PARTITION agg_csv FOR VALUES FROM (10) TO (20);
+SELECT * FROM ptif_root;
+  a  |    b    
+-----+---------
+   5 |     0.1
+   6 |     0.2
+  56 |     7.8
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(6 rows)
+
+DELETE FROM ptif_root WHERE b = 0.1;
+DELETE FROM ptif_root;
+ERROR:  cannot delete from foreign table "agg_csv"
+SELECT * FROM ptif_root;
+  a  |    b    
+-----+---------
+   6 |     0.2
+  56 |     7.8
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(5 rows)
+
+UPDATE ptif_root SET b = 0.6 WHERE b = 0.2;
+UPDATE ptif_root SET b = 0.10;
+ERROR:  cannot update foreign table "agg_csv"
+SELECT * FROM ptif_root;
+  a  |    b    
+-----+---------
+   6 |     0.6
+  56 |     7.8
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(5 rows)
+
+-- cleanup
+DROP EXTENSION file_fdw CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to server file_server
+drop cascades to foreign table agg_csv
+DROP TABLE ptif_root;
diff --git a/src/test/regress/sql/partition_info.sql b/src/test/regress/sql/partition_info.sql
index b5060bec7f0..e57fe821c41 100644
--- a/src/test/regress/sql/partition_info.sql
+++ b/src/test/regress/sql/partition_info.sql
@@ -127,3 +127,31 @@ SELECT pg_partition_root('ptif_li_child');
 DROP VIEW ptif_test_view;
 DROP MATERIALIZED VIEW ptif_test_matview;
 DROP TABLE ptif_li_parent, ptif_li_child;
+
+-- Test UPDATE/DELETE on partition table with foreign partitions
+\getenv abs_srcdir PG_ABS_SRCDIR
+CREATE EXTENSION file_fdw;
+CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
+
+CREATE TABLE ptif_root (a int2, b numeric) PARTITION BY range (a);
+CREATE TABLE ptif_child PARTITION OF ptif_root FOR VALUES FROM (0) TO (10);
+INSERT INTO ptif_root SELECT 5, 0.1;
+INSERT INTO ptif_root SELECT 6, 0.2;
+\set filename :abs_srcdir '/data/agg.csv'
+CREATE FOREIGN TABLE agg_csv (
+        a int2,
+        b numeric
+) SERVER file_server
+OPTIONS (format 'csv', filename :'filename', header 'false', delimiter ';', quote '@', escape '"', null '');
+ALTER TABLE ptif_root ATTACH PARTITION agg_csv FOR VALUES FROM (10) TO (20);
+SELECT * FROM ptif_root;
+DELETE FROM ptif_root WHERE b = 0.1;
+DELETE FROM ptif_root;
+SELECT * FROM ptif_root;
+UPDATE ptif_root SET b = 0.6 WHERE b = 0.2;
+UPDATE ptif_root SET b = 0.10;
+SELECT * FROM ptif_root;
+
+-- cleanup
+DROP EXTENSION file_fdw CASCADE;
+DROP TABLE ptif_root;
-- 
2.39.5 (Apple Git-154)

smime.p7sapplication/pkcs7-signature; name=smime.p7sDownload
#2Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Shirisha Shirisha (#1)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

Hi,

On Thu, Jun 12, 2025 at 1:47 PM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

We’d like to propose a change to improve DELETE and UPDATE behavior on partitioned tables
containing foreign partitions.

Currently, DELETE or UPDATE (D/U) on a partitioned table with foreign partitions fails with an error
as below, if the FDW does not support the operation:

`ERROR: cannot delete from foreign table`

This failure occurs during executor initialization (`ExecInitModifyTable`), where PostgreSQL scans
all partitions of the target table and checks whether each one supports the requested operation.
If any foreign partition's FDW lacks support for D/U, the operation is rejected outright, even if that
partition would not be affected by the query.

As a result, DELETE/UPDATE operations are blocked even when they only target non-foreign partitions.
This means that the system errors out without considering whether foreign partitions are actually involved in the operation.
Even if no matching rows exist in a foreign partition, the operation still fails.

This behavior presents a usability hurdle as it forces the user to work around this limitation by issuing D/U
statements separately on each individual child partition. This is cumbersome and breaks the workflow of managing such tables via the root.

We are proposing a patch that would allow users to have a better workflow by continuing to perform D/U via root partition
even in presence of foreign partitions not implementing D/U API.
The key change is to defer the FDW check for foreign partitions from `ExecInitModifyTable` to `ExecDelete` and `ExecUpdate`.
This would ensure that the foreign partitions are checked only when they are actually targeted by the operation.

The proposed change would make the behavior consistent with the cases
for INSERT/COPY into partitioned tables with non-insertable
foreign-table partitions, so +1 in general. (I have not looked at the
patch in detail yet.)

Best regards,
Etsuro Fujita

#3Shirisha Shirisha
shirisha.sn@broadcom.com
In reply to: Etsuro Fujita (#2)
1 attachment(s)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

Hi all,

The proposed change would make the behavior consistent with the cases

for INSERT/COPY into partitioned tables with non-insertable
foreign-table partitions, so +1 in general.

Thanks for the initial feedback on making this behavior consistent with
INSERT/COPY.

Just wanted to follow up on the patch, any additional feedback or
improvement in the patch would be very helpful.

Thanks and Regards,
Shirisha
Broadcom Inc.

On Thu, Jun 12, 2025 at 3:59 PM Etsuro Fujita <etsuro.fujita@gmail.com>
wrote:

Hi,

On Thu, Jun 12, 2025 at 1:47 PM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

We’d like to propose a change to improve DELETE and UPDATE behavior on

partitioned tables

containing foreign partitions.

Currently, DELETE or UPDATE (D/U) on a partitioned table with foreign

partitions fails with an error

as below, if the FDW does not support the operation:

`ERROR: cannot delete from foreign table`

This failure occurs during executor initialization

(`ExecInitModifyTable`), where PostgreSQL scans

all partitions of the target table and checks whether each one supports

the requested operation.

If any foreign partition's FDW lacks support for D/U, the operation is

rejected outright, even if that

partition would not be affected by the query.

As a result, DELETE/UPDATE operations are blocked even when they only

target non-foreign partitions.

This means that the system errors out without considering whether

foreign partitions are actually involved in the operation.

Even if no matching rows exist in a foreign partition, the operation

still fails.

This behavior presents a usability hurdle as it forces the user to work

around this limitation by issuing D/U

statements separately on each individual child partition. This is

cumbersome and breaks the workflow of managing such tables via the root.

We are proposing a patch that would allow users to have a better

workflow by continuing to perform D/U via root partition

even in presence of foreign partitions not implementing D/U API.
The key change is to defer the FDW check for foreign partitions from

`ExecInitModifyTable` to `ExecDelete` and `ExecUpdate`.

This would ensure that the foreign partitions are checked only when they

are actually targeted by the operation.

The proposed change would make the behavior consistent with the cases
for INSERT/COPY into partitioned tables with non-insertable
foreign-table partitions, so +1 in general. (I have not looked at the
patch in detail yet.)

Best regards,
Etsuro Fujita

--
This electronic communication and the information and any files transmitted
with it, or attached to it, are confidential and are intended solely for
the use of the individual or entity to whom it is addressed and may contain
information that is confidential, legally privileged, protected by privacy
laws, or otherwise restricted from disclosure to anyone else. If you are
not the intended recipient or the person responsible for delivering the
e-mail to the intended recipient, you are hereby notified that any use,
copying, distributing, dissemination, forwarding, printing, or copying of
this e-mail is strictly prohibited. If you received this e-mail in error,
please return the e-mail to the sender, delete it from your computer, and
destroy any printed copy of it.

Attachments:

smime.p7sapplication/pkcs7-signature; name=smime.p7sDownload
#4Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Shirisha Shirisha (#3)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

On Tue, Jul 1, 2025 at 2:24 AM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

Just wanted to follow up on the patch, any additional feedback or improvement in the patch would be very helpful.

Did you add the patch to the CommitFest App?

https://commitfest.postgresql.org

If not, I will recommend that to get more feedback from developers.

Best regards,
Etsuro Fujita

#5Shirisha Shirisha
shirisha.sn@broadcom.com
In reply to: Etsuro Fujita (#4)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

Did you add the patch to the CommitFest App?

Yes, I had attempted to register the patch earlier but had to wait for the
cool-off period to pass for my newly created account.

I’ve now successfully registered the patch on the CommitFest app
https://commitfest.postgresql.org/patch/5901

Thanks and Regards,
Shirisha
Broadcom Inc.

On Sun, Jul 6, 2025 at 2:34 PM Etsuro Fujita <etsuro.fujita@gmail.com>
wrote:

On Tue, Jul 1, 2025 at 2:24 AM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

Just wanted to follow up on the patch, any additional feedback or

improvement in the patch would be very helpful.

Did you add the patch to the CommitFest App?

https://commitfest.postgresql.org

If not, I will recommend that to get more feedback from developers.

Best regards,
Etsuro Fujita

--
This electronic communication and the information and any files transmitted
with it, or attached to it, are confidential and are intended solely for
the use of the individual or entity to whom it is addressed and may contain
information that is confidential, legally privileged, protected by privacy
laws, or otherwise restricted from disclosure to anyone else. If you are
not the intended recipient or the person responsible for delivering the
e-mail to the intended recipient, you are hereby notified that any use,
copying, distributing, dissemination, forwarding, printing, or copying of
this e-mail is strictly prohibited. If you received this e-mail in error,
please return the e-mail to the sender, delete it from your computer, and
destroy any printed copy of it.

#6Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Shirisha Shirisha (#5)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

On Mon, Jul 7, 2025 at 6:46 PM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

Did you add the patch to the CommitFest App?

Yes, I had attempted to register the patch earlier but had to wait for the cool-off period to pass for my newly created account.

I’ve now successfully registered the patch on the CommitFest app
https://commitfest.postgresql.org/patch/5901

Thanks for that. I tested/reviewed the patch quickly.

The patch does not work well with table inheritance:

create table pt (a text, b int);
insert into pt values ('AAA', 42);
create foreign table ft (a text, b int) server file_server OPTIONS
(filename 'path-to-file', format 'csv', delimiter ',');
select * from ft;
a | b
-----+----
BBB | 42
(1 row)

alter foreign table ft inherit pt;
update pt set b = b + 1000 where a = 'AAA';
ERROR: cannot update foreign table "ft"

Why doesn't the patch cover this case?

--- a/src/test/regress/sql/partition_info.sql
+++ b/src/test/regress/sql/partition_info.sql
@@ -127,3 +127,31 @@ SELECT pg_partition_root('ptif_li_child');
 DROP VIEW ptif_test_view;
 DROP MATERIALIZED VIEW ptif_test_matview;
 DROP TABLE ptif_li_parent, ptif_li_child;
+
+-- Test UPDATE/DELETE on partition table with foreign partitions
+\getenv abs_srcdir PG_ABS_SRCDIR
+CREATE EXTENSION file_fdw;
+CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;

I think the regression tests should be moved to file_fdw.

That is it. I will do the rest of the review in Commitfest PG19-2 (as
this was registered for it).

Best regards,
Etsuro Fujita

#7Shirisha Shirisha
shirisha.sn@broadcom.com
In reply to: Etsuro Fujita (#6)
2 attachment(s)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

Thanks Etsuro for the review comments.
I've addressed all of them in this updated patch:

- Rebased the patch on the latest changes
- Added support for inherited foreign tables
- Moved the regression test to file_fdw
- Added test for inherited foreign tables

Please find the updated patch attached.
CommitFest link - https://commitfest.postgresql.org/patch/5901

Thanks and Regards,
Shirisha
Broadcom Inc.

On Wed, Jul 9, 2025 at 5:06 PM Etsuro Fujita <etsuro.fujita@gmail.com>
wrote:

Show quoted text

On Mon, Jul 7, 2025 at 6:46 PM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

Did you add the patch to the CommitFest App?

Yes, I had attempted to register the patch earlier but had to wait for

the cool-off period to pass for my newly created account.

I’ve now successfully registered the patch on the CommitFest app
https://commitfest.postgresql.org/patch/5901

Thanks for that. I tested/reviewed the patch quickly.

The patch does not work well with table inheritance:

create table pt (a text, b int);
insert into pt values ('AAA', 42);
create foreign table ft (a text, b int) server file_server OPTIONS
(filename 'path-to-file', format 'csv', delimiter ',');
select * from ft;
a | b
-----+----
BBB | 42
(1 row)

alter foreign table ft inherit pt;
update pt set b = b + 1000 where a = 'AAA';
ERROR: cannot update foreign table "ft"

Why doesn't the patch cover this case?

--- a/src/test/regress/sql/partition_info.sql
+++ b/src/test/regress/sql/partition_info.sql
@@ -127,3 +127,31 @@ SELECT pg_partition_root('ptif_li_child');
DROP VIEW ptif_test_view;
DROP MATERIALIZED VIEW ptif_test_matview;
DROP TABLE ptif_li_parent, ptif_li_child;
+
+-- Test UPDATE/DELETE on partition table with foreign partitions
+\getenv abs_srcdir PG_ABS_SRCDIR
+CREATE EXTENSION file_fdw;
+CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;

I think the regression tests should be moved to file_fdw.

That is it. I will do the rest of the review in Commitfest PG19-2 (as
this was registered for it).

Best regards,
Etsuro Fujita

Attachments:

v2-0001-Allow-DELETE-UPDATE-on-tables-with-foreign-partit.patchapplication/octet-stream; name=v2-0001-Allow-DELETE-UPDATE-on-tables-with-foreign-partit.patchDownload
From b376d1e2a442066b4cae4b939fc4e7a212ae644b Mon Sep 17 00:00:00 2001
From: Shirisha SN <sshirisha@vmware.com>
Date: Fri, 6 Jun 2025 22:27:40 +0530
Subject: [PATCH v2] Allow DELETE/UPDATE on tables with foreign partitions or
 inherited foreign tables

Currently, DELETE or UPDATE on a partitioned table with foreign partitions
fail with an error as below, if the FDW does not support the operation:

	`ERROR: cannot delete from foreign table`

This occurs because during executor initialization (ExecInitModifyTable),
PostgreSQL scans all partitions of the target table and checks whether each one
supports the requested operation. If any foreign partition's FDW lacks support
for DELETE or UPDATE, the operation is rejected outright, even if that
partition would not be affected by the query.

A similar issue arises with inherited foreign tables, where DELETE/UPDATEs
targeted on non-foreign tables are also blocked.

As a result, DELETE/UPDATE operations are blocked even when they only target
non-foreign relations. This means the system errors out without considering
whether foreign or inherited foreign tables are actually involved in the
operation. Even if no matching rows exist in a foreign partition, the operation
still fails.

This commit defers the FDW check for foreign partitions and inherited foreign tables from
`ExecInitModifyTable` to `ExecDelete` and `ExecUpdate`. This change ensures
that foreign child tables are checked only when they are actually targeted by the
operation.

However, if a DELETE or UPDATE is issued on the root table and it includes
foreign child tables that do not support the operation, it will still result in
an error. This is intentional because the responsibility for managing data in
foreign tables lies with the user. Only after the user has removed relevant
data from those foreign tables will such operations on the root table
succeed.

** Mini repro for partition table with foreign partition: **
```
CREATE EXTENSION file_fdw;
CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
CREATE TABLE pt (a int, b numeric) PARTITION BY RANGE(a);
CREATE TABLE pt_part1 PARTITION OF pt FOR VALUES FROM (0) TO (10);
INSERT INTO pt SELECT 5, 0.1;
INSERT INTO pt SELECT 6, 0.2;

CREATE FOREIGN TABLE ext (a int, b numeric) SERVER file_server OPTIONS (filename 'path-to-file', format 'csv', delimiter ',');
ALTER TABLE pt ATTACH PARTITION ext FOR VALUES FROM (10) TO (20);
postgres=# SELECT * FROM pt;
 a  |  b
----+-----
  5 | 0.1
  6 | 0.2
 15 | 0.3
 21 | 0.4
(4 rows)
```

** Before Fix: **
```
postgres=# DELETE FROM pt WHERE b = 0.2;
ERROR:  cannot delete from foreign table "ext"
postgres=# DELETE FROM pt;
ERROR:  cannot delete from foreign table "ext"

postgres=# UPDATE pt set b = 0.5 WHERE b = 0.1;
ERROR:  cannot update foreign table "ext"
postgres=# UPDATE pt SET b = 0.5;
ERROR:  cannot update foreign table "ext"
```

** After Fix: **
```
postgres=# DELETE FROM pt WHERE b = 0.2;
DELETE 1
postgres=# DELETE FROM pt;
ERROR:  cannot delete from foreign table "ext"

postgres=# UPDATE pt SET b = 0.5 WHERE b = 0.1;
UPDATE 1
postgres=# UPDATE pt SET b = 0.5;
ERROR:  cannot update foreign table "ext"
```

** Mini repro for table with inherited foreign table: **
```
CREATE TABLE pt (a text, b int);
INSERT INTO pt VALUES ('AAA', 42);
CREATE FOREIGN TABLE ft (a text, b int) server file_server OPTIONS (filename 'path-to-file', format 'csv', delimiter ',');
ALTER FOREIGN TABLE ft INHERIT pt;
SELECT * FROM pt;
  a  | b
-----+----
 AAA | 42
 BBB | 42
(2 rows)
```

** Before Fix: **
```
UPDATE pt SET b = b + 1000 WHERE a = 'AAA';
ERROR:  cannot update foreign table "ft"
DELETE FROM pt WHERE a = 'AAA';
ERROR:  cannot delete from foreign table "ft"
```

** After Fix: **
```
UPDATE pt SET b = b + 1000 WHERE a = 'AAA';
UPDATE 1
DELETE FROM pt WHERE a = 'AAA';
DELETE 1
```

Co-authored-by: Ashwin Agrawal <ashwin.agrawal@broadcom.com>
---
 contrib/file_fdw/expected/file_fdw.out | 105 ++++++++++++++++++++++---
 contrib/file_fdw/sql/file_fdw.sql      |  42 +++++++++-
 src/backend/executor/nodeModifyTable.c |  67 +++++++++++++++-
 3 files changed, 199 insertions(+), 15 deletions(-)

diff --git a/contrib/file_fdw/expected/file_fdw.out b/contrib/file_fdw/expected/file_fdw.out
index 246e3d3e566..157b2851648 100644
--- a/contrib/file_fdw/expected/file_fdw.out
+++ b/contrib/file_fdw/expected/file_fdw.out
@@ -333,14 +333,18 @@ SELECT * FROM agg_csv WHERE a < 0;
 RESET constraint_exclusion;
 -- table inheritance tests
 CREATE TABLE agg (a int2, b float4);
+INSERT INTO agg SELECT 5,0.1;
+INSERT INTO agg SELECT 6,0.2;
 ALTER FOREIGN TABLE agg_csv INHERIT agg;
 SELECT tableoid::regclass, * FROM agg;
  tableoid |  a  |    b    
 ----------+-----+---------
+ agg      |   5 |     0.1
+ agg      |   6 |     0.2
  agg_csv  | 100 |  99.097
  agg_csv  |   0 | 0.09561
  agg_csv  |  42 |  324.78
-(3 rows)
+(5 rows)
 
 SELECT tableoid::regclass, * FROM agg_csv;
  tableoid |  a  |    b    
@@ -351,16 +355,51 @@ SELECT tableoid::regclass, * FROM agg_csv;
 (3 rows)
 
 SELECT tableoid::regclass, * FROM ONLY agg;
- tableoid | a | b 
-----------+---+---
-(0 rows)
+ tableoid | a |  b  
+----------+---+-----
+ agg      | 5 | 0.1
+ agg      | 6 | 0.2
+(2 rows)
 
--- updates aren't supported
-UPDATE agg SET a = 1;
+-- updates on foreign tables are not supported
+UPDATE agg SET a = 10 WHERE b = 99.097::float4;
+ERROR:  cannot update foreign table "agg_csv"
+UPDATE agg SET a = 10 WHERE a = 100;
+ERROR:  cannot update foreign table "agg_csv"
+UPDATE agg SET a = 10;
 ERROR:  cannot update foreign table "agg_csv"
+-- these updates should be allowed
+UPDATE agg SET a = 10 WHERE b = 0.1::float4;
+UPDATE agg SET a = 20 WHERE a = 10;
+SELECT tableoid::regclass, * FROM agg;
+ tableoid |  a  |    b    
+----------+-----+---------
+ agg      |   6 |     0.2
+ agg      |  20 |     0.1
+ agg_csv  | 100 |  99.097
+ agg_csv  |   0 | 0.09561
+ agg_csv  |  42 |  324.78
+(5 rows)
+
+-- deletes on foreign tables are not supported
+DELETE from agg WHERE b = 99.097::float4;
+ERROR:  cannot delete from foreign table "agg_csv"
 DELETE FROM agg WHERE a = 100;
 ERROR:  cannot delete from foreign table "agg_csv"
--- but this should be allowed
+DELETE FROM agg;
+ERROR:  cannot delete from foreign table "agg_csv"
+-- these deletes should be allowed
+DELETE FROM agg WHERE b = 0.1::float4;
+DELETE FROM agg WHERE a = 6;
+SELECT tableoid::regclass, * FROM agg;
+ tableoid |  a  |    b    
+----------+-----+---------
+ agg_csv  | 100 |  99.097
+ agg_csv  |   0 | 0.09561
+ agg_csv  |  42 |  324.78
+(3 rows)
+
+-- this should be allowed
 SELECT tableoid::regclass, * FROM agg FOR UPDATE;
  tableoid |  a  |    b    
 ----------+-----+---------
@@ -539,15 +578,63 @@ ALTER FOREIGN TABLE agg_text OPTIONS (SET format 'text');
 ERROR:  permission denied to set the "filename" option of a file_fdw foreign table
 DETAIL:  Only roles with privileges of the "pg_read_server_files" role may set this option.
 SET ROLE regress_file_fdw_superuser;
+-- Test UPDATE/DELETE on partition table with foreign partitions
+CREATE TABLE pt_root (a int2, b float4) PARTITION BY range (a);
+CREATE TABLE pt_child PARTITION OF pt_root FOR VALUES FROM (0) TO (10);
+INSERT INTO pt_root SELECT 5, 0.1;
+INSERT INTO pt_root SELECT 6, 0.2;
+ALTER TABLE pt_root ATTACH PARTITION agg_csv FOR VALUES FROM (10) TO (20);
+SELECT * FROM pt_root;
+  a  |    b    
+-----+---------
+   5 |     0.1
+   6 |     0.2
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(5 rows)
+
+-- delete on foreign tables are not supported
+DELETE FROM pt_root WHERE b = 99.097::float4;
+ERROR:  cannot delete from foreign table "agg_csv"
+DELETE FROM pt_root;
+ERROR:  cannot delete from foreign table "agg_csv"
+-- this delete should be allowed
+DELETE FROM pt_root WHERE b = 0.1::float4;
+SELECT * FROM pt_root;
+  a  |    b    
+-----+---------
+   6 |     0.2
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(4 rows)
+
+-- updates on foreign tables are not supported
+UPDATE pt_root SET b = 0.10 WHERE b = 99.097::float4;
+ERROR:  cannot update foreign table "agg_csv"
+UPDATE pt_root SET b = 0.10;
+ERROR:  cannot update foreign table "agg_csv"
+-- this update should be allowed
+UPDATE pt_root SET b = 0.6 WHERE b = 0.2::float4;
+SELECT * FROM pt_root;
+  a  |    b    
+-----+---------
+   6 |     0.6
+ 100 |  99.097
+   0 | 0.09561
+  42 |  324.78
+(4 rows)
+
+DROP TABLE pt_root;
 -- cleanup
 RESET ROLE;
 DROP EXTENSION file_fdw CASCADE;
-NOTICE:  drop cascades to 9 other objects
+NOTICE:  drop cascades to 8 other objects
 DETAIL:  drop cascades to server file_server
 drop cascades to user mapping for regress_file_fdw_superuser on server file_server
 drop cascades to user mapping for regress_no_priv_user on server file_server
 drop cascades to foreign table agg_text
-drop cascades to foreign table agg_csv
 drop cascades to foreign table agg_bad
 drop cascades to foreign table header_match
 drop cascades to foreign table header_doesnt_match
diff --git a/contrib/file_fdw/sql/file_fdw.sql b/contrib/file_fdw/sql/file_fdw.sql
index 1a397ad4bd1..dd6f0cd818c 100644
--- a/contrib/file_fdw/sql/file_fdw.sql
+++ b/contrib/file_fdw/sql/file_fdw.sql
@@ -207,14 +207,29 @@ RESET constraint_exclusion;
 
 -- table inheritance tests
 CREATE TABLE agg (a int2, b float4);
+INSERT INTO agg SELECT 5,0.1;
+INSERT INTO agg SELECT 6,0.2;
 ALTER FOREIGN TABLE agg_csv INHERIT agg;
 SELECT tableoid::regclass, * FROM agg;
 SELECT tableoid::regclass, * FROM agg_csv;
 SELECT tableoid::regclass, * FROM ONLY agg;
--- updates aren't supported
-UPDATE agg SET a = 1;
+-- updates on foreign tables are not supported
+UPDATE agg SET a = 10 WHERE b = 99.097::float4;
+UPDATE agg SET a = 10 WHERE a = 100;
+UPDATE agg SET a = 10;
+-- these updates should be allowed
+UPDATE agg SET a = 10 WHERE b = 0.1::float4;
+UPDATE agg SET a = 20 WHERE a = 10;
+SELECT tableoid::regclass, * FROM agg;
+-- deletes on foreign tables are not supported
+DELETE from agg WHERE b = 99.097::float4;
 DELETE FROM agg WHERE a = 100;
--- but this should be allowed
+DELETE FROM agg;
+-- these deletes should be allowed
+DELETE FROM agg WHERE b = 0.1::float4;
+DELETE FROM agg WHERE a = 6;
+SELECT tableoid::regclass, * FROM agg;
+-- this should be allowed
 SELECT tableoid::regclass, * FROM agg FOR UPDATE;
 ALTER FOREIGN TABLE agg_csv NO INHERIT agg;
 DROP TABLE agg;
@@ -285,6 +300,27 @@ SET ROLE regress_file_fdw_user;
 ALTER FOREIGN TABLE agg_text OPTIONS (SET format 'text');
 SET ROLE regress_file_fdw_superuser;
 
+-- Test UPDATE/DELETE on partition table with foreign partitions
+CREATE TABLE pt_root (a int2, b float4) PARTITION BY range (a);
+CREATE TABLE pt_child PARTITION OF pt_root FOR VALUES FROM (0) TO (10);
+INSERT INTO pt_root SELECT 5, 0.1;
+INSERT INTO pt_root SELECT 6, 0.2;
+ALTER TABLE pt_root ATTACH PARTITION agg_csv FOR VALUES FROM (10) TO (20);
+SELECT * FROM pt_root;
+-- delete on foreign tables are not supported
+DELETE FROM pt_root WHERE b = 99.097::float4;
+DELETE FROM pt_root;
+-- this delete should be allowed
+DELETE FROM pt_root WHERE b = 0.1::float4;
+SELECT * FROM pt_root;
+-- updates on foreign tables are not supported
+UPDATE pt_root SET b = 0.10 WHERE b = 99.097::float4;
+UPDATE pt_root SET b = 0.10;
+-- this update should be allowed
+UPDATE pt_root SET b = 0.6 WHERE b = 0.2::float4;
+SELECT * FROM pt_root;
+
+DROP TABLE pt_root;
 -- cleanup
 RESET ROLE;
 DROP EXTENSION file_fdw CASCADE;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 4c5647ac38a..26d7938488a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1585,10 +1585,34 @@ ExecDelete(ModifyTableContext *context,
 	TupleTableSlot *slot = NULL;
 	TM_Result	result;
 	bool		saveOld;
+	FdwRoutine *fdwroutine;
 
 	if (tupleDeleted)
 		*tupleDeleted = false;
 
+	/*
+	 * For foreign partitions and inherited foreign tables, raise error
+	 * during DELETE if the FDW does not support it. This check is deferred
+	 * from ExecInitModifyTable to allow deletes on non-foreign tables to
+	 * proceed without error.
+	 */
+	if (resultRelationDesc->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+	{
+		fdwroutine = resultRelInfo->ri_FdwRoutine;
+		if (fdwroutine->ExecForeignDelete == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot delete from foreign table \"%s\"",
+							RelationGetRelationName(resultRelationDesc))));
+
+		if (fdwroutine->IsForeignRelUpdatable != NULL &&
+			(fdwroutine->IsForeignRelUpdatable(resultRelationDesc) & (1 << CMD_DELETE)) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					errmsg("foreign table \"%s\" does not allow deletes",
+							RelationGetRelationName(resultRelationDesc))));
+	}
+
 	/*
 	 * Prepare for the delete.  This includes BEFORE ROW triggers, so we're
 	 * done if it says we are.
@@ -2466,6 +2490,7 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 	Relation	resultRelationDesc = resultRelInfo->ri_RelationDesc;
 	UpdateContext updateCxt = {0};
 	TM_Result	result;
+	FdwRoutine *fdwroutine;
 
 	/*
 	 * abort the operation if not running transactions
@@ -2480,6 +2505,29 @@ ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 	if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
 		return NULL;
 
+	/*
+	 * For foreign partitions and inherited foreign tables, raise error
+	 * during UPDATE if the FDW does not support it. This check is deferred
+	 * from ExecInitModifyTable to allow updates on non-foreign tables to
+	 * proceed without error.
+	 */
+	if (resultRelationDesc->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+	{
+		fdwroutine = resultRelInfo->ri_FdwRoutine;
+		if (fdwroutine->ExecForeignUpdate == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot update foreign table \"%s\"",
+							RelationGetRelationName(resultRelationDesc))));
+
+		if (fdwroutine->IsForeignRelUpdatable != NULL &&
+			(fdwroutine->IsForeignRelUpdatable(resultRelationDesc) & (1 << CMD_UPDATE)) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					errmsg("foreign table \"%s\" does not allow updates",
+							RelationGetRelationName(resultRelationDesc))));
+	}
+
 	/* INSTEAD OF ROW UPDATE Triggers */
 	if (resultRelInfo->ri_TrigDesc &&
 		resultRelInfo->ri_TrigDesc->trig_update_instead_row)
@@ -4786,6 +4834,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	i = 0;
 	foreach(l, resultRelations)
 	{
+		bool 		skip_rel_check = false;
 		Index		resultRelation = lfirst_int(l);
 		List	   *mergeActions = NIL;
 
@@ -4810,10 +4859,22 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			bms_is_member(i, node->fdwDirectModifyPlans);
 
 		/*
-		 * Verify result relation is a valid target for the current operation
+		 * Verify result relation is a valid target for the current operation.
+		 * Skip this verification only when:
+		 * - the relation is a foreign table,
+		 * - the operation is DELETE or UPDATE, and
+		 * - the query involves multiple result relations
+		 *
+		 * In such cases, the validation is deferred to ExecDelete or
+		 * ExecUpdate, where the specific foreign partition is processed.
 		 */
-		CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
-							mergeActions);
+		rel = resultRelInfo->ri_RelationDesc;
+		skip_rel_check = (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE &&
+							(operation == CMD_DELETE || operation == CMD_UPDATE) &&
+							nrels > 1);
+		if (!skip_rel_check)
+			CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
+								mergeActions);
 
 		resultRelInfo++;
 		i++;
-- 
2.39.5 (Apple Git-154)

smime.p7sapplication/pkcs7-signature; name=smime.p7sDownload
#8Etsuro Fujita
etsuro.fujita@gmail.com
In reply to: Shirisha Shirisha (#7)
Re: Proposal to allow DELETE/UPDATE on partitioned tables with unsupported foreign partitions

On Wed, Sep 17, 2025 at 1:38 AM Shirisha Shirisha
<shirisha.sn@broadcom.com> wrote:

I've addressed all of them in this updated patch:

- Rebased the patch on the latest changes
- Added support for inherited foreign tables
- Moved the regression test to file_fdw
- Added test for inherited foreign tables

Please find the updated patch attached.
CommitFest link - https://commitfest.postgresql.org/patch/5901

Will review.

Thanks for updating the patch!

Best regards,
Etsuro Fujita