@@ -430,8 +430,8 @@ static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation
430
430
static ObjectAddress ATExecValidateConstraint(List **wqueue,
431
431
Relation rel, char *constrName,
432
432
bool recurse, bool recursing, LOCKMODE lockmode);
433
- static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel ,
434
- HeapTuple contuple, LOCKMODE lockmode);
433
+ static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel ,
434
+ Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
435
435
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
436
436
char *constrName, HeapTuple contuple,
437
437
bool recurse, bool recursing, LOCKMODE lockmode);
@@ -11858,16 +11858,19 @@ AttachPartitionForeignKey(List **wqueue,
11858
11858
if (queueValidation)
11859
11859
{
11860
11860
Relation conrel;
11861
+ Oid confrelid;
11861
11862
11862
11863
conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11863
11864
11864
11865
partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11865
11866
if (!HeapTupleIsValid(partcontup))
11866
11867
elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11867
11868
11869
+ confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11870
+
11868
11871
/* Use the same lock as for AT_ValidateConstraint */
11869
- QueueFKConstraintValidation(wqueue, conrel, partition, partcontup ,
11870
- ShareUpdateExclusiveLock);
11872
+ QueueFKConstraintValidation(wqueue, conrel, partition, confrelid ,
11873
+ partcontup, ShareUpdateExclusiveLock);
11871
11874
ReleaseSysCache(partcontup);
11872
11875
table_close(conrel, RowExclusiveLock);
11873
11876
}
@@ -12919,7 +12922,8 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12919
12922
{
12920
12923
if (con->contype == CONSTRAINT_FOREIGN)
12921
12924
{
12922
- QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12925
+ QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12926
+ tuple, lockmode);
12923
12927
}
12924
12928
else if (con->contype == CONSTRAINT_CHECK)
12925
12929
{
@@ -12952,8 +12956,8 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12952
12956
* for the specified relation and all its children.
12953
12957
*/
12954
12958
static void
12955
- QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel ,
12956
- HeapTuple contuple, LOCKMODE lockmode)
12959
+ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel ,
12960
+ Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
12957
12961
{
12958
12962
Form_pg_constraint con;
12959
12963
AlteredTableInfo *tab;
@@ -12964,7 +12968,17 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12964
12968
Assert(con->contype == CONSTRAINT_FOREIGN);
12965
12969
Assert(!con->convalidated);
12966
12970
12967
- if (rel->rd_rel->relkind == RELKIND_RELATION)
12971
+ /*
12972
+ * Add the validation to phase 3's queue; not needed for partitioned
12973
+ * tables themselves, only for their partitions.
12974
+ *
12975
+ * When the referenced table (pkrelid) is partitioned, the referencing
12976
+ * table (fkrel) has one pg_constraint row pointing to each partition
12977
+ * thereof. These rows are there only to support action triggers and no
12978
+ * table scan is needed, therefore skip this for them as well.
12979
+ */
12980
+ if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
12981
+ con->confrelid == pkrelid)
12968
12982
{
12969
12983
NewConstraint *newcon;
12970
12984
Constraint *fkconstraint;
@@ -12983,15 +12997,16 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
12983
12997
newcon->qual = (Node *) fkconstraint;
12984
12998
12985
12999
/* Find or create work queue entry for this table */
12986
- tab = ATGetQueueEntry(wqueue, rel );
13000
+ tab = ATGetQueueEntry(wqueue, fkrel );
12987
13001
tab->constraints = lappend(tab->constraints, newcon);
12988
13002
}
12989
13003
12990
13004
/*
12991
13005
* If the table at either end of the constraint is partitioned, we need to
12992
- * recurse and handle every constraint that is a child of this constraint.
13006
+ * recurse and handle every unvalidate constraint that is a child of this
13007
+ * constraint.
12993
13008
*/
12994
- if (rel ->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13009
+ if (fkrel ->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12995
13010
get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12996
13011
{
12997
13012
ScanKeyData pkey;
@@ -13023,16 +13038,24 @@ QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
13023
13038
13024
13039
childrel = table_open(childcon->conrelid, lockmode);
13025
13040
13026
- QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
13027
- lockmode);
13041
+ /*
13042
+ * NB: Note that pkrelid should be passed as-is during recursion,
13043
+ * as it is required to identify the root referenced table.
13044
+ */
13045
+ QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13046
+ childtup, lockmode);
13028
13047
table_close(childrel, NoLock);
13029
13048
}
13030
13049
13031
13050
systable_endscan(pscan);
13032
13051
}
13033
13052
13034
13053
/*
13035
- * Now update the catalog, while we have the door open.
13054
+ * Now mark the pg_constraint row as validated (even if we didn't check,
13055
+ * notably the ones for partitions on the referenced side).
13056
+ *
13057
+ * We rely on transaction abort to roll back this change if phase 3
13058
+ * ultimately finds violating rows. This is a bit ugly.
13036
13059
*/
13037
13060
copyTuple = heap_copytuple(contuple);
13038
13061
copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
0 commit comments