mirror of
https://github.com/postgres/postgres.git
synced 2026-02-10 22:33:50 -05: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:
parent
3f56de3aad
commit
fab386f748
3 changed files with 49 additions and 3 deletions
|
|
@ -415,6 +415,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'
|
||||
|
|
|
|||
|
|
@ -226,6 +226,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
|
||||
|
|
|
|||
|
|
@ -3916,8 +3916,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);
|
||||
|
|
@ -4307,7 +4311,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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue