mirror of
https://github.com/postgres/postgres.git
synced 2025-06-17 17:02:08 +03:00
Fix creation of duplicate foreign keys on partitions
When creating a foreign key in a partitioned table, if some partitions already have equivalent constraints, we wastefully create duplicates of the constraints instead of attaching to the existing ones. That's inconsistent with the de-duplication that is applied when a table is attached as a partition. To fix, reuse the FK-cloning code instead of having a separate code path. Backpatch to Postgres 11. This is a subtle behavior change, but surely a welcome one since there's no use in having duplicate foreign keys. Discovered by Álvaro Herrera while thinking about a different problem reported by Jesper Pedersen (bug #15587). Author: Álvaro Herrera Discussion: https://postgr.es/m/201901151935.zfadrzvyof4k@alvherre.pgsql
This commit is contained in:
@ -7658,31 +7658,50 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||
if (recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
PartitionDesc partdesc;
|
||||
Relation pg_constraint;
|
||||
List *cloned = NIL;
|
||||
ListCell *cell;
|
||||
|
||||
pg_constraint = heap_open(ConstraintRelationId, RowExclusiveLock);
|
||||
partdesc = RelationGetPartitionDesc(rel);
|
||||
|
||||
for (i = 0; i < partdesc->nparts; i++)
|
||||
{
|
||||
Oid partitionId = partdesc->oids[i];
|
||||
Relation partition = heap_open(partitionId, lockmode);
|
||||
AlteredTableInfo *childtab;
|
||||
ObjectAddress childAddr;
|
||||
|
||||
CheckTableNotInUse(partition, "ALTER TABLE");
|
||||
|
||||
/* Find or create work queue entry for this table */
|
||||
childtab = ATGetQueueEntry(wqueue, partition);
|
||||
|
||||
childAddr =
|
||||
ATAddForeignKeyConstraint(wqueue, childtab, partition,
|
||||
fkconstraint, constrOid,
|
||||
recurse, true, lockmode);
|
||||
|
||||
/* Record this constraint as dependent on the parent one */
|
||||
recordDependencyOn(&childAddr, &address, DEPENDENCY_INTERNAL_AUTO);
|
||||
CloneFkReferencing(pg_constraint, rel, partition,
|
||||
list_make1_oid(constrOid),
|
||||
&cloned);
|
||||
|
||||
heap_close(partition, NoLock);
|
||||
}
|
||||
heap_close(pg_constraint, RowExclusiveLock);
|
||||
|
||||
foreach(cell, cloned)
|
||||
{
|
||||
ClonedConstraint *cc = (ClonedConstraint *) lfirst(cell);
|
||||
Relation partition = heap_open(cc->relid, lockmode);
|
||||
AlteredTableInfo *childtab;
|
||||
NewConstraint *newcon;
|
||||
|
||||
/* Find or create work queue entry for this partition */
|
||||
childtab = ATGetQueueEntry(wqueue, partition);
|
||||
|
||||
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
|
||||
newcon->name = cc->constraint->conname;
|
||||
newcon->contype = CONSTR_FOREIGN;
|
||||
newcon->refrelid = cc->refrelid;
|
||||
newcon->refindid = cc->conindid;
|
||||
newcon->conid = cc->conid;
|
||||
newcon->qual = (Node *) fkconstraint;
|
||||
|
||||
childtab->constraints = lappend(childtab->constraints, newcon);
|
||||
|
||||
heap_close(partition, lockmode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user