Skip to content

Commit 9b21f20

Browse files
committed
Fix detach of a partition that has a toplevel FK to a partitioned table
In common cases, foreign keys are defined on the toplevel partitioned table; but if instead one is defined on a partition and references a partitioned table, and the referencing partition is detached, we would examine the pg_constraint row on the partition being detached, and fail to realize that the sub-constraints must be left alone. This causes the ALTER TABLE DETACH process to fail with ERROR: could not find ON INSERT check triggers of foreign key constraint NNN This is similar but not quite the same as what was fixed by 53af949. This bug doesn't affect branches earlier than 15, because the detach procedure was different there, so we only backpatch down to 15. Fix by skipping such modifying constraints that are children of other constraints being detached. Author: Amul Sul <sulamul@gmail.com> Diagnosys-by: Sami Imseih <samimseih@gmail.com> Discussion: https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com
1 parent 1772d55 commit 9b21f20

File tree

3 files changed

+39
-2
lines changed

3 files changed

+39
-2
lines changed

src/backend/commands/tablecmds.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19987,6 +19987,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1998719987
HeapTuple tuple,
1998819988
newtuple;
1998919989
Relation trigrel = NULL;
19990+
List *fkoids = NIL;
1999019991

1999119992
if (concurrent)
1999219993
{
@@ -20007,6 +20008,23 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
2000720008
fks = copyObject(RelationGetFKeyList(partRel));
2000820009
if (fks != NIL)
2000920010
trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20011+
20012+
/*
20013+
* It's possible that the partition being detached has a foreign key that
20014+
* references a partitioned table. When that happens, there are multiple
20015+
* pg_constraint rows for the partition: one points to the partitioned
20016+
* table itself, while the others point to each of its partitions. Only
20017+
* the topmost one is to be considered here; the child constraints must be
20018+
* left alone, because conceptually those aren't coming from our parent
20019+
* partitioned table, but from this partition itself.
20020+
*
20021+
* We implement this by collecting all the constraint OIDs in a first scan
20022+
* of the FK array, and skipping in the loop below those constraints whose
20023+
* parents are listed here.
20024+
*/
20025+
foreach_node(ForeignKeyCacheInfo, fk, fks)
20026+
fkoids = lappend_oid(fkoids, fk->conoid);
20027+
2001020028
foreach(cell, fks)
2001120029
{
2001220030
ForeignKeyCacheInfo *fk = lfirst(cell);
@@ -20020,9 +20038,13 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
2002020038
elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
2002120039
conform = (Form_pg_constraint) GETSTRUCT(contup);
2002220040

20023-
/* consider only the inherited foreign keys */
20041+
/*
20042+
* Consider only inherited foreign keys, and only if their parents
20043+
* aren't in the list.
20044+
*/
2002420045
if (conform->contype != CONSTRAINT_FOREIGN ||
20025-
!OidIsValid(conform->conparentid))
20046+
!OidIsValid(conform->conparentid) ||
20047+
list_member_oid(fkoids, conform->conparentid))
2002620048
{
2002720049
ReleaseSysCache(contup);
2002820050
continue;

src/test/regress/expected/foreign_key.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2351,6 +2351,13 @@ UPDATE pk SET a = 4002 WHERE a = 4000;
23512351
DELETE FROM pk WHERE a = 4002;
23522352
UPDATE pk SET a = 4502 WHERE a = 4500;
23532353
DELETE FROM pk WHERE a = 4502;
2354+
-- Also, detaching a partition that has the FK itself should work
2355+
-- https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com
2356+
CREATE TABLE ffk (a int, b int REFERENCES pk) PARTITION BY list (a);
2357+
CREATE TABLE ffk1 PARTITION OF ffk FOR VALUES IN (1);
2358+
ALTER TABLE ffk1 ADD FOREIGN KEY (a) REFERENCES pk;
2359+
ALTER TABLE ffk DETACH PARTITION ffk1;
2360+
DROP TABLE ffk, ffk1;
23542361
CREATE SCHEMA fkpart4;
23552362
SET search_path TO fkpart4;
23562363
-- dropping/detaching PARTITIONs is prevented if that would break

src/test/regress/sql/foreign_key.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,14 @@ DELETE FROM pk WHERE a = 4002;
16721672
UPDATE pk SET a = 4502 WHERE a = 4500;
16731673
DELETE FROM pk WHERE a = 4502;
16741674

1675+
-- Also, detaching a partition that has the FK itself should work
1676+
-- https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com
1677+
CREATE TABLE ffk (a int, b int REFERENCES pk) PARTITION BY list (a);
1678+
CREATE TABLE ffk1 PARTITION OF ffk FOR VALUES IN (1);
1679+
ALTER TABLE ffk1 ADD FOREIGN KEY (a) REFERENCES pk;
1680+
ALTER TABLE ffk DETACH PARTITION ffk1;
1681+
DROP TABLE ffk, ffk1;
1682+
16751683
CREATE SCHEMA fkpart4;
16761684
SET search_path TO fkpart4;
16771685
-- dropping/detaching PARTITIONs is prevented if that would break

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy