HeapTuple tuple,
newtuple;
Relation trigrel = NULL;
+ List *fkoids = NIL;
if (concurrent)
{
fks = copyObject(RelationGetFKeyList(partRel));
if (fks != NIL)
trigrel = table_open(TriggerRelationId, RowExclusiveLock);
+
+ /*
+ * It's possible that the partition being detached has a foreign key that
+ * references a partitioned table. When that happens, there are multiple
+ * pg_constraint rows for the partition: one points to the partitioned
+ * table itself, while the others point to each of its partitions. Only
+ * the topmost one is to be considered here; the child constraints must be
+ * left alone, because conceptually those aren't coming from our parent
+ * partitioned table, but from this partition itself.
+ *
+ * We implement this by collecting all the constraint OIDs in a first scan
+ * of the FK array, and skipping in the loop below those constraints whose
+ * parents are listed here.
+ */
+ foreach_node(ForeignKeyCacheInfo, fk, fks)
+ fkoids = lappend_oid(fkoids, fk->conoid);
+
foreach(cell, fks)
{
ForeignKeyCacheInfo *fk = lfirst(cell);
elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
conform = (Form_pg_constraint) GETSTRUCT(contup);
- /* consider only the inherited foreign keys */
+ /*
+ * Consider only inherited foreign keys, and only if their parents
+ * aren't in the list.
+ */
if (conform->contype != CONSTRAINT_FOREIGN ||
- !OidIsValid(conform->conparentid))
+ !OidIsValid(conform->conparentid) ||
+ list_member_oid(fkoids, conform->conparentid))
{
ReleaseSysCache(contup);
continue;
DELETE FROM pk WHERE a = 4002;
UPDATE pk SET a = 4502 WHERE a = 4500;
DELETE FROM pk WHERE a = 4502;
+-- Also, detaching a partition that has the FK itself should work
+-- https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com
+CREATE TABLE ffk (a int, b int REFERENCES pk) PARTITION BY list (a);
+CREATE TABLE ffk1 PARTITION OF ffk FOR VALUES IN (1);
+ALTER TABLE ffk1 ADD FOREIGN KEY (a) REFERENCES pk;
+ALTER TABLE ffk DETACH PARTITION ffk1;
+DROP TABLE ffk, ffk1;
CREATE SCHEMA fkpart4;
SET search_path TO fkpart4;
-- dropping/detaching PARTITIONs is prevented if that would break
UPDATE pk SET a = 4502 WHERE a = 4500;
DELETE FROM pk WHERE a = 4502;
+-- Also, detaching a partition that has the FK itself should work
+-- https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com
+CREATE TABLE ffk (a int, b int REFERENCES pk) PARTITION BY list (a);
+CREATE TABLE ffk1 PARTITION OF ffk FOR VALUES IN (1);
+ALTER TABLE ffk1 ADD FOREIGN KEY (a) REFERENCES pk;
+ALTER TABLE ffk DETACH PARTITION ffk1;
+DROP TABLE ffk, ffk1;
+
CREATE SCHEMA fkpart4;
SET search_path TO fkpart4;
-- dropping/detaching PARTITIONs is prevented if that would break