mirror of
https://github.com/postgres/postgres.git
synced 2026-01-26 09:41:40 +03:00
Fix bogus ctid requirement for dummy-root partitioned targets
ExecInitModifyTable() unconditionally required a ctid junk column even when the target was a partitioned table. This led to spurious "could not find junk ctid column" errors when all children were excluded and only the dummy root result relation remained. A partitioned table only appears in the result relations list when all leaf partitions have been pruned, leaving the dummy root as the sole entry. Assert this invariant (nrels == 1) and skip the ctid requirement. Also adjust ExecModifyTable() to tolerate invalid ri_RowIdAttNo for partitioned tables, which is safe since no rows will be processed in this case. Bug: #19099 Reported-by: Alexander Lakhin <exclusion@gmail.com> Author: Amit Langote <amitlangote09@gmail.com> Reviewed-by: Tender Wang <tndrwang@gmail.com> Reviewed-by: Kirill Reshke <reshkekirill@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/19099-e05dcfa022fe553d%40postgresql.org Backpatch-through: 14
This commit is contained in:
@@ -482,6 +482,21 @@ SELECT tableoid::regclass, * FROM p2;
|
||||
p2 | 2 | xyzzy
|
||||
(3 rows)
|
||||
|
||||
-- Test DELETE/UPDATE/MERGE 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;
|
||||
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b)
|
||||
ON false WHEN MATCHED THEN UPDATE SET b = s.b;
|
||||
SET enable_partition_pruning TO on;
|
||||
DELETE FROM pt WHERE false;
|
||||
UPDATE pt SET b = 'x' WHERE false;
|
||||
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b)
|
||||
ON false WHEN MATCHED THEN UPDATE SET b = s.b;
|
||||
DROP TABLE pt;
|
||||
-- generated column tests
|
||||
\set filename :abs_srcdir '/data/list1.csv'
|
||||
|
||||
@@ -255,6 +255,24 @@ 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/MERGE 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;
|
||||
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b)
|
||||
ON false WHEN MATCHED THEN UPDATE SET b = s.b;
|
||||
|
||||
SET enable_partition_pruning TO on;
|
||||
DELETE FROM pt WHERE false;
|
||||
UPDATE pt SET b = 'x' WHERE false;
|
||||
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b)
|
||||
ON false WHEN MATCHED THEN UPDATE SET b = s.b;
|
||||
|
||||
DROP TABLE pt;
|
||||
|
||||
-- generated column tests
|
||||
|
||||
@@ -4360,8 +4360,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);
|
||||
@@ -4874,7 +4878,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)
|
||||
|
||||
Reference in New Issue
Block a user