Re: Bug in detaching a partition with a foreign key.

From: Sami Imseih <samimseih(at)gmail(dot)com>
To: Amul Sul <sulamul(at)gmail(dot)com>
Cc: PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Bug in detaching a partition with a foreign key.
Date: 2025-01-17 20:44:01
Message-ID: CAA5RZ0vk4SJ9PiD2RyG-CKOYvOFewz6QweKcp2_EegBKns=dOA@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

This is a bug indeed. I tried your patch, but it ends up in a seg fault.

I also see this was raised in another thread [0].

It can be reproduced in a slightly simplified case, using only a
single level partition.

"""
CREATE TABLE bar(id int PRIMARY KEY) PARTITION BY RANGE(id);
CREATE TABLE bar_p0 PARTITION OF bar FOR VALUES FROM (0) TO (100);

CREATE TABLE foo(id int) PARTITION BY RANGE(id);
CREATE TABLE foo_p0 PARTITION OF foo FOR VALUES FROM (0) TO (100);

ALTER TABLE foo_p0 ADD CONSTRAINT child_fk_con FOREIGN KEY (id) REFERENCES bar;

ALTER TABLE foo DETACH PARTITION foo_p0 ;
"""

Here is what I I found.

In DetachPartitionFinalize, after the child is detached from
the parent, the FK's insert and update triggers are then
removed from pg_depend with TriggerSetParentTrigger

/*
* The constraint on this table must be marked no longer a child of
* the parent's constraint, as do its check triggers.
*/
ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);

/*
* Also, look up the partition's "check" triggers corresponding to the
* constraint being detached and detach them from the parent triggers.
*/
GetForeignKeyCheckTriggers(trigrel,
fk->conoid, fk->confrelid, fk->conrelid,
&insertTriggerOid, &updateTriggerOid);
Assert(OidIsValid(insertTriggerOid));
TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
RelationGetRelid(partRel));
Assert(OidIsValid(updateTriggerOid));
TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
RelationGetRelid(partRel));

Specifically, the dependency types DEPENDENCY_PARTITION_PRI and
DEPENDENCY_PARTITION_SEC are removed from pg_depend.

deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
TriggerRelationId,
DEPENDENCY_PARTITION_PRI);
deleteDependencyRecordsForClass(TriggerRelationId, childTrigId,
RelationRelationId,
DEPENDENCY_PARTITION_SEC);

In the repro case, an FK on a partition with a reference
to a partition parent table does not create a new dependency.

postgres=# SELECT count(*) total, deptype FROM pg_depend WHERE deptype
in ('P', 'S') group by deptype;
total | deptype
-------+---------
2 | P
2 | S
(2 rows)

postgres=# ALTER TABLE foo_p0 ADD CONSTRAINT child_fk_con FOREIGN KEY
(id) REFERENCES bar;
ALTER TABLE
postgres=#
postgres=# SELECT count(*) total, deptype FROM pg_depend WHERE deptype
in ('P', 'S') group by deptype;
total | deptype
-------+---------
2 | P
2 | S
(2 rows)

We also see that the FK riggers created are associated with the parent
constraint rather than the child constraint, i.e. tgconstraint = 17387

postgres=# ALTER TABLE foo_p0 ADD CONSTRAINT child_fk_con FOREIGN KEY
(id) REFERENCES bar;
ALTER TABLE
postgres=#
postgres=# SELECT oid, tgrelid::regclass relname, tgparentid, 'insert'
trigger_type, tgconstraint FROM pg_trigger WHERE tgfoid in (1644,
1645)
postgres-# and tgtype & (1 << 2) > 0
postgres-# union all
postgres-# SELECT oid, tgrelid::regclass relname, tgparentid, 'update'
trigger_type, tgconstraint FROM pg_trigger WHERE tgfoid in (1644,
1645)
postgres-# and tgtype & (1 << 4) > 0;
oid | relname | tgparentid | trigger_type | tgconstraint
-------+---------+------------+--------------+--------------
17393 | foo_p0 | 0 | insert | 17387
17394 | foo_p0 | 0 | update | 17387
(2 rows)

postgres=# ALTER TABLE foo DETACH PARTITION foo_p0 ;
ERROR: could not find ON INSERT check triggers of foreign key constraint 17390
postgres=#
postgres=# select oid, conparentid, conrelid::regclass,
confrelid::regclass from pg_constraint where oid = 17390;
oid | conparentid | conrelid | confrelid
-------+-------------+----------+-----------
17390 | 17387 | foo_p0 | bar_p0
(1 row)

postgres=# select oid, conparentid, conrelid::regclass,
confrelid::regclass from pg_constraint where oid = 17387;
oid | conparentid | conrelid | confrelid
-------+-------------+----------+-----------
17387 | 0 | foo_p0 | bar
(1 row)

This is not the case when the constraint is created on
the parent table,
i.e. ALTER TABLE foo ADD CONSTRAINT child_fk_con FOREIGN KEY (id) REFERENCES bar

In this case we also see a dependency.

postgres=# SELECT count(*) total, deptype FROM pg_depend WHERE deptype
in ('P', 'S') group by deptype;
total | deptype
-------+---------
2 | P
2 | S
(2 rows)

postgres=# ALTER TABLE foo ADD CONSTRAINT child_fk_con FOREIGN KEY
(id) REFERENCES bar;
ALTER TABLE
postgres=# SELECT count(*) total, deptype FROM pg_depend WHERE deptype
in ('P', 'S') group by deptype;
total | deptype
-------+---------
3 | P
3 | S
(2 rows)

Also, the constraint relname in the parent constraint is that of the
parent table and the child constraint is that of the child table.

postgres=*# ALTER TABLE foo DETACH PARTITION foo_p0 ;
ALTER TABLE
postgres=*# ROLLBACK;
ROLLBACK
^
postgres=# select oid, conparentid, conrelid::regclass,
confrelid::regclass from pg_constraint where oid = 17455;
oid | conparentid | conrelid | confrelid
-------+-------------+----------+-----------
17455 | 17447 | foo_p0 | bar
(1 row)

postgres=# select oid, conparentid, conrelid::regclass,
confrelid::regclass from pg_constraint where oid = 17447;
oid | conparentid | conrelid | confrelid
-------+-------------+----------+-----------
17447 | 0 | foo | bar
(1 row

If the relation on the parent and child constraint match, that
tells us we don't have inheritance.
So, I am thinking we should add another condition for checking
if a foreign key is inherited by checking if the parent constraint
relation is different from the child constraint relation.

I am attaching an unpolished patch ( we need test coverage as well ) that
implements the above. All tests pass with this patch.

Regards,

Sami Imseih
Amazon Web Services (AWS)

[0] https://www.postgresql.org/message-id/flat/CAHewXNm5rtfQZNv2uWkiHZVJeicFFa4x7p0%3Dy-x2vAM0vorgNQ%40mail.gmail.com#2ffeca3b63339da32fab04516f066bf6

Attachment Content-Type Size
Fix-DETACH-PARTITION-with-foreign-key-referencing.patch application/octet-stream 1.8 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Sami Imseih 2025-01-17 20:52:55 Re: Psql meta-command conninfo+
Previous Message Wolfgang Walther 2025-01-17 20:20:03 Re: Fwd: Re: proposal: schema variables