aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2018-10-12 12:36:26 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2018-10-12 12:37:37 -0300
commitc7d43c4d8a5b7215ea0a32d95260188b5d3ae3f4 (patch)
treea4505c0aaae92242eb52b63fb855dcd9363412a9 /src/backend/commands/tablecmds.c
parentf1885386f6246ac7b6f8d3f0aef247988f48ee7a (diff)
downloadpostgresql-c7d43c4d8a5b7215ea0a32d95260188b5d3ae3f4.tar.gz
postgresql-c7d43c4d8a5b7215ea0a32d95260188b5d3ae3f4.zip
Correct attach/detach logic for FKs in partitions
There was no code to handle foreign key constraints on partitioned tables in the case of ALTER TABLE DETACH; and if you happened to ATTACH a partition that already had an equivalent constraint, that one was ignored and a new constraint was created. Adding this to the fact that foreign key cloning reuses the constraint name on the partition instead of generating a new name (as it probably should, to cater to SQL standard rules about constraint naming within schemas), the result was a pretty poor user experience -- the most visible failure was that just detaching a partition and re-attaching it failed with an error such as ERROR: duplicate key value violates unique constraint "pg_constraint_conrelid_contypid_conname_index" DETAIL: Key (conrelid, contypid, conname)=(26702, 0, test_result_asset_id_fkey) already exists. because it would try to create an identically-named constraint in the partition. To make matters worse, if you tried to drop the constraint in the now-independent partition, that would fail because the constraint was still seen as dependent on the constraint in its former parent partitioned table: ERROR: cannot drop inherited constraint "test_result_asset_id_fkey" of relation "test_result_cbsystem_0001_0050_monthly_2018_09" This fix attacks the problem from two angles: first, when the partition is detached, the constraint is also marked as independent, so the drop now works. Second, when the partition is re-attached, we scan existing constraints searching for one matching the FK in the parent, and if one exists, we link that one to the parent constraint. So we don't end up with a duplicate -- and better yet, we don't need to scan the referenced table to verify that the constraint holds. To implement this I made a small change to previously planner-only struct ForeignKeyCacheInfo to contain the constraint OID; also relcache now maintains the list of FKs for partitioned tables too. Backpatch to 11. Reported-by: Michael Vitale (bug #15425) Discussion: https://postgr.es/m/15425-2dbc9d2aa999f816@postgresql.org
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c23
1 files changed, 23 insertions, 0 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e10d3dbf3dd..3e112b4ef42 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -14092,6 +14092,11 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
attachrel = heap_openrv(cmd->name, AccessExclusiveLock);
/*
+ * XXX I think it'd be a good idea to grab locks on all tables referenced
+ * by FKs at this point also.
+ */
+
+ /*
* Must be owner of both parent and source table -- parent was checked by
* ATSimplePermissions call in ATPrepCmd
*/
@@ -14663,6 +14668,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
ObjectAddress address;
Oid defaultPartOid;
List *indexes;
+ List *fks;
ListCell *cell;
/*
@@ -14738,6 +14744,23 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
}
heap_close(classRel, RowExclusiveLock);
+ /* Detach foreign keys */
+ fks = copyObject(RelationGetFKeyList(partRel));
+ foreach(cell, fks)
+ {
+ ForeignKeyCacheInfo *fk = lfirst(cell);
+ HeapTuple contup;
+
+ contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
+ if (!contup)
+ elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
+
+ ConstraintSetParentConstraint(fk->conoid, InvalidOid);
+
+ ReleaseSysCache(contup);
+ }
+ list_free_deep(fks);
+
/*
* Invalidate the parent's relcache so that the partition is no longer
* included in its partition descriptor.