diff --git a/contrib/file_fdw/input/file_fdw.source b/contrib/file_fdw/input/file_fdw.source index d8101d49840..7b977cfbe97 100644 --- a/contrib/file_fdw/input/file_fdw.source +++ b/contrib/file_fdw/input/file_fdw.source @@ -189,6 +189,20 @@ UPDATE pt set a = 1 where a = 2; -- ERROR SELECT tableoid::regclass, * FROM pt; SELECT tableoid::regclass, * FROM p1; SELECT tableoid::regclass, * FROM p2; + +-- Test DELETE/UPDATE on a partitioned table when all partitions +-- are excluded and only the dummy root result relation remains. The +-- operation is a no-op but should not fail regardless of whether the +-- foreign child was processed (pruning off) or not (pruning on). +DROP TABLE p2; +SET enable_partition_pruning TO off; +DELETE FROM pt WHERE false; +UPDATE pt SET b = 'x' WHERE false; + +SET enable_partition_pruning TO on; +DELETE FROM pt WHERE false; +UPDATE pt SET b = 'x' WHERE false; + DROP TABLE pt; -- generated column tests diff --git a/contrib/file_fdw/output/file_fdw.source b/contrib/file_fdw/output/file_fdw.source index dfcc833d80b..45f0e0322ca 100644 --- a/contrib/file_fdw/output/file_fdw.source +++ b/contrib/file_fdw/output/file_fdw.source @@ -379,6 +379,17 @@ SELECT tableoid::regclass, * FROM p2; p2 | 2 | xyzzy (3 rows) +-- Test DELETE/UPDATE on a partitioned table when all partitions +-- are excluded and only the dummy root result relation remains. The +-- operation is a no-op but should not fail regardless of whether the +-- foreign child was processed (pruning off) or not (pruning on). +DROP TABLE p2; +SET enable_partition_pruning TO off; +DELETE FROM pt WHERE false; +UPDATE pt SET b = 'x' WHERE false; +SET enable_partition_pruning TO on; +DELETE FROM pt WHERE false; +UPDATE pt SET b = 'x' WHERE false; DROP TABLE pt; -- generated column tests CREATE FOREIGN TABLE gft1 (a int, b text, c text GENERATED ALWAYS AS ('foo') STORED) SERVER file_server diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index d52482ae191..70f5500ef27 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2663,8 +2663,12 @@ ExecModifyTable(PlanState *pstate) relkind == RELKIND_MATVIEW || relkind == RELKIND_PARTITIONED_TABLE) { - /* ri_RowIdAttNo refers to a ctid attribute */ - Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo)); + /* + * ri_RowIdAttNo refers to a ctid attribute. See the comment + * in ExecInitModifyTable(). + */ + Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo) || + relkind == RELKIND_PARTITIONED_TABLE); datum = ExecGetJunkAttribute(slot, resultRelInfo->ri_RowIdAttNo, &isNull); @@ -3025,7 +3029,16 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { resultRelInfo->ri_RowIdAttNo = ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid"); - if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo)) + + /* + * For heap relations, a ctid junk attribute must be present. + * Partitioned tables should only appear here when all leaf + * partitions were pruned, in which case no rows can be + * produced and ctid is not needed. + */ + if (relkind == RELKIND_PARTITIONED_TABLE) + Assert(nrels == 1); + else if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo)) elog(ERROR, "could not find junk ctid column"); } else if (relkind == RELKIND_FOREIGN_TABLE)