diff options
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 413 |
1 files changed, 348 insertions, 65 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3631b8a9298..89bc865e287 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -483,7 +483,8 @@ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstra int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, - bool old_check_ok); + bool old_check_ok, + Oid parentDelTrigger, Oid parentUpdTrigger); static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, const int16 *fksetcolsattnums, List *fksetcols); @@ -492,7 +493,8 @@ static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, - bool old_check_ok, LOCKMODE lockmode); + bool old_check_ok, LOCKMODE lockmode, + Oid parentInsTrigger, Oid parentUpdTrigger); static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel); static void CloneFkReferenced(Relation parentRel, Relation partitionRel); @@ -500,15 +502,30 @@ static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel); static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, - Oid indexOid); + Oid indexOid, + Oid parentInsTrigger, Oid parentUpdTrigger, + Oid *insertTrigOid, Oid *updateTrigOid); static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, - Oid indexOid); + Oid indexOid, + Oid parentDelTrigger, Oid parentUpdTrigger, + Oid *deleteTrigOid, Oid *updateTrigOid); static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk, Oid partRelid, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, - Oid *conpfeqop); + Oid *conpfeqop, + Oid parentInsTrigger, + Oid parentUpdTrigger, + Relation trigrel); +static void GetForeignKeyActionTriggers(Relation trigrel, + Oid conoid, Oid confrelid, Oid conrelid, + Oid *deleteTriggerOid, + Oid *updateTriggerOid); +static void GetForeignKeyCheckTriggers(Relation trigrel, + Oid conoid, Oid confrelid, Oid conrelid, + Oid *insertTriggerOid, + Oid *updateTriggerOid); static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool recursing, @@ -9367,7 +9384,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, ffeqoperators, numfkdelsetcols, fkdelsetcols, - old_check_ok); + old_check_ok, + InvalidOid, InvalidOid); /* Now handle the referencing side. */ addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel, @@ -9382,7 +9400,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, numfkdelsetcols, fkdelsetcols, old_check_ok, - lockmode); + lockmode, + InvalidOid, InvalidOid); /* * Done. Close pk table, but keep lock until we've committed. @@ -9454,6 +9473,9 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, * pf/pp/ffeqoperators are OID array of operators between columns. * old_check_ok signals that this constraint replaces an existing one that * was already validated (thus this one doesn't need validation). + * parentDelTrigger and parentUpdTrigger, when being recursively called on + * a partition, are the OIDs of the parent action triggers for DELETE and + * UPDATE respectively. */ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, @@ -9462,7 +9484,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, - bool old_check_ok) + bool old_check_ok, + Oid parentDelTrigger, Oid parentUpdTrigger) { ObjectAddress address; Oid constrOid; @@ -9470,6 +9493,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, bool conislocal; int coninhcount; bool connoinherit; + Oid deleteTriggerOid, + updateTriggerOid; /* * Verify relkind for each referenced partition. At the top level, this @@ -9568,15 +9593,13 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, CommandCounterIncrement(); /* - * If the referenced table is a plain relation, create the action triggers - * that enforce the constraint. + * Create the action triggers that enforce the constraint. */ - if (pkrel->rd_rel->relkind == RELKIND_RELATION) - { - createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel), - fkconstraint, - constrOid, indexOid); - } + createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel), + fkconstraint, + constrOid, indexOid, + parentDelTrigger, parentUpdTrigger, + &deleteTriggerOid, &updateTriggerOid); /* * If the referenced table is partitioned, recurse on ourselves to handle @@ -9621,7 +9644,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, mapped_pkattnum, fkattnum, pfeqoperators, ppeqoperators, ffeqoperators, numfkdelsetcols, fkdelsetcols, - old_check_ok); + old_check_ok, + deleteTriggerOid, updateTriggerOid); /* Done -- clean up (but keep the lock) */ table_close(partRel, NoLock); @@ -9668,6 +9692,9 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * old_check_ok signals that this constraint replaces an existing one that * was already validated (thus this one doesn't need validation). * lockmode is the lockmode to acquire on partitions when recursing. + * parentInsTrigger and parentUpdTrigger, when being recursively called on + * a partition, are the OIDs of the parent check triggers for INSERT and + * UPDATE respectively. */ static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, @@ -9675,8 +9702,12 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, - bool old_check_ok, LOCKMODE lockmode) + bool old_check_ok, LOCKMODE lockmode, + Oid parentInsTrigger, Oid parentUpdTrigger) { + Oid insertTriggerOid, + updateTriggerOid; + AssertArg(OidIsValid(parentConstr)); if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) @@ -9685,19 +9716,21 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, errmsg("foreign key constraints are not supported on foreign tables"))); /* - * If the referencing relation is a plain table, add the check triggers to - * it and, if necessary, schedule it to be checked in Phase 3. + * Add the check triggers to it and, if necessary, schedule it to be + * checked in Phase 3. * * If the relation is partitioned, drill down to do it to its partitions. */ + createForeignKeyCheckTriggers(RelationGetRelid(rel), + RelationGetRelid(pkrel), + fkconstraint, + parentConstr, + indexOid, + parentInsTrigger, parentUpdTrigger, + &insertTriggerOid, &updateTriggerOid); + if (rel->rd_rel->relkind == RELKIND_RELATION) { - createForeignKeyCheckTriggers(RelationGetRelid(rel), - RelationGetRelid(pkrel), - fkconstraint, - parentConstr, - indexOid); - /* * Tell Phase 3 to check that the constraint is satisfied by existing * rows. We can skip this during table creation, when requested @@ -9726,6 +9759,15 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { PartitionDesc pd = RelationGetPartitionDesc(rel, true); + Relation trigrel; + + /* + * Triggers of the foreign keys will be manipulated a bunch of times + * in the loop below. To avoid repeatedly opening/closing the trigger + * catalog relation, we open it here and pass it to the subroutines + * called below. + */ + trigrel = table_open(TriggerRelationId, RowExclusiveLock); /* * Recurse to take appropriate action on each partition; either we @@ -9767,7 +9809,10 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, numfks, mapped_fkattnum, pkattnum, - pfeqoperators)) + pfeqoperators, + insertTriggerOid, + updateTriggerOid, + trigrel)) { attached = true; break; @@ -9850,10 +9895,14 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, numfkdelsetcols, fkdelsetcols, old_check_ok, - lockmode); + lockmode, + insertTriggerOid, + updateTriggerOid); table_close(partition, NoLock); } + + table_close(trigrel, RowExclusiveLock); } } @@ -9909,6 +9958,7 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) ScanKeyData key[2]; HeapTuple tuple; List *clone = NIL; + Relation trigrel; /* * Search for any constraints where this partition's parent is in the @@ -9938,6 +9988,14 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) systable_endscan(scan); table_close(pg_constraint, RowShareLock); + /* + * Triggers of the foreign keys will be manipulated a bunch of times in + * the loop below. To avoid repeatedly opening/closing the trigger + * catalog relation, we open it here and pass it to the subroutines called + * below. + */ + trigrel = table_open(TriggerRelationId, RowExclusiveLock); + attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), RelationGetDescr(parentRel)); foreach(cell, clone) @@ -9957,6 +10015,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) int numfkdelsetcols; AttrNumber confdelsetcols[INDEX_MAX_KEYS]; Constraint *fkconstraint; + Oid deleteTriggerOid, + updateTriggerOid; tuple = SearchSysCache1(CONSTROID, constrOid); if (!HeapTupleIsValid(tuple)) @@ -10025,6 +10085,16 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) if (!OidIsValid(partIndexId)) elog(ERROR, "index for %u not found in partition %s", indexOid, RelationGetRelationName(partitionRel)); + + /* + * Get the "action" triggers belonging to the constraint to pass as + * parent OIDs for similar triggers that will be created on the + * partition in addFkRecurseReferenced(). + */ + GetForeignKeyActionTriggers(trigrel, constrOid, + constrForm->confrelid, constrForm->conrelid, + &deleteTriggerOid, &updateTriggerOid); + addFkRecurseReferenced(NULL, fkconstraint, fkRel, @@ -10039,11 +10109,15 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) conffeqop, numfkdelsetcols, confdelsetcols, - true); + true, + deleteTriggerOid, + updateTriggerOid); table_close(fkRel, NoLock); ReleaseSysCache(tuple); } + + table_close(trigrel, RowExclusiveLock); } /* @@ -10066,6 +10140,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) List *partFKs; List *clone = NIL; ListCell *cell; + Relation trigrel; /* obtain a list of constraints that we need to clone */ foreach(cell, RelationGetFKeyList(parentRel)) @@ -10088,6 +10163,14 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) errmsg("foreign key constraints are not supported on foreign tables"))); /* + * Triggers of the foreign keys will be manipulated a bunch of times in + * the loop below. To avoid repeatedly opening/closing the trigger + * catalog relation, we open it here and pass it to the subroutines called + * below. + */ + trigrel = table_open(TriggerRelationId, RowExclusiveLock); + + /* * The constraint key may differ, if the columns in the partition are * different. This map is used to convert them. */ @@ -10118,6 +10201,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) ObjectAddress address, referenced; ListCell *cell; + Oid insertTriggerOid, + updateTriggerOid; tuple = SearchSysCache1(CONSTROID, parentConstrOid); if (!HeapTupleIsValid(tuple)) @@ -10148,6 +10233,19 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) mapped_conkey[i] = attmap->attnums[conkey[i] - 1]; /* + * Get the "check" triggers belonging to the constraint to pass as + * parent OIDs for similar triggers that will be created on the + * partition in addFkRecurseReferencing(). They are also passed to + * tryAttachPartitionForeignKey() below to simply assign as parents to + * the partition's existing "check" triggers, that is, if the + * corresponding constraints is deemed attachable to the parent + * constraint. + */ + GetForeignKeyCheckTriggers(trigrel, constrForm->oid, + constrForm->confrelid, constrForm->conrelid, + &insertTriggerOid, &updateTriggerOid); + + /* * Before creating a new constraint, see whether any existing FKs are * fit for the purpose. If one is, attach the parent constraint to * it, and don't clone anything. This way we avoid the expensive @@ -10165,7 +10263,10 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) numfks, mapped_conkey, confkey, - conpfeqop)) + conpfeqop, + insertTriggerOid, + updateTriggerOid, + trigrel)) { attached = true; table_close(pkrel, NoLock); @@ -10268,9 +10369,13 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) numfkdelsetcols, confdelsetcols, false, /* no old check exists */ - AccessExclusiveLock); + AccessExclusiveLock, + insertTriggerOid, + updateTriggerOid); table_close(pkrel, NoLock); } + + table_close(trigrel, RowExclusiveLock); } /* @@ -10291,16 +10396,20 @@ tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, - Oid *conpfeqop) + Oid *conpfeqop, + Oid parentInsTrigger, + Oid parentUpdTrigger, + Relation trigrel) { HeapTuple parentConstrTup; Form_pg_constraint parentConstr; HeapTuple partcontup; Form_pg_constraint partConstr; - Relation trigrel; ScanKeyData key; SysScanDesc scan; HeapTuple trigtup; + Oid insertTriggerOid, + updateTriggerOid; parentConstrTup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid)); @@ -10361,12 +10470,10 @@ tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk, * in the partition. We identify them because they have our constraint * OID, as well as being on the referenced rel. */ - trigrel = table_open(TriggerRelationId, RowExclusiveLock); ScanKeyInit(&key, Anum_pg_trigger_tgconstraint, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(fk->conoid)); - scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true, NULL, 1, &key); while ((trigtup = systable_getnext(scan)) != NULL) @@ -10399,13 +10506,136 @@ tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk, } systable_endscan(scan); - table_close(trigrel, RowExclusiveLock); ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid); + + /* + * Like the constraint, attach partition's "check" triggers to the + * corresponding parent triggers. + */ + GetForeignKeyCheckTriggers(trigrel, + fk->conoid, fk->confrelid, fk->conrelid, + &insertTriggerOid, &updateTriggerOid); + Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger)); + TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger, + partRelid); + Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger)); + TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger, + partRelid); + CommandCounterIncrement(); return true; } +/* + * GetForeignKeyActionTriggers + * Returns delete and update "action" triggers of the given relation + * belonging to the given constraint + */ +static void +GetForeignKeyActionTriggers(Relation trigrel, + Oid conoid, Oid confrelid, Oid conrelid, + Oid *deleteTriggerOid, + Oid *updateTriggerOid) +{ + ScanKeyData key; + SysScanDesc scan; + HeapTuple trigtup; + + *deleteTriggerOid = *updateTriggerOid = InvalidOid; + ScanKeyInit(&key, + Anum_pg_trigger_tgconstraint, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(conoid)); + + scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true, + NULL, 1, &key); + while ((trigtup = systable_getnext(scan)) != NULL) + { + Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup); + + if (trgform->tgconstrrelid != conrelid) + continue; + if (trgform->tgrelid != confrelid) + continue; + if (TRIGGER_FOR_DELETE(trgform->tgtype)) + { + Assert(*deleteTriggerOid == InvalidOid); + *deleteTriggerOid = trgform->oid; + } + else if (TRIGGER_FOR_UPDATE(trgform->tgtype)) + { + Assert(*updateTriggerOid == InvalidOid); + *updateTriggerOid = trgform->oid; + } + if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid)) + break; + } + + if (!OidIsValid(*deleteTriggerOid)) + elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u", + conoid); + if (!OidIsValid(*updateTriggerOid)) + elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u", + conoid); + + systable_endscan(scan); +} + +/* + * GetForeignKeyCheckTriggers + * Returns insert and update "check" triggers of the given relation + * belonging to the given constraint + */ +static void +GetForeignKeyCheckTriggers(Relation trigrel, + Oid conoid, Oid confrelid, Oid conrelid, + Oid *insertTriggerOid, + Oid *updateTriggerOid) +{ + ScanKeyData key; + SysScanDesc scan; + HeapTuple trigtup; + + *insertTriggerOid = *updateTriggerOid = InvalidOid; + ScanKeyInit(&key, + Anum_pg_trigger_tgconstraint, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(conoid)); + + scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true, + NULL, 1, &key); + while ((trigtup = systable_getnext(scan)) != NULL) + { + Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup); + + if (trgform->tgconstrrelid != confrelid) + continue; + if (trgform->tgrelid != conrelid) + continue; + if (TRIGGER_FOR_INSERT(trgform->tgtype)) + { + Assert(*insertTriggerOid == InvalidOid); + *insertTriggerOid = trgform->oid; + } + else if (TRIGGER_FOR_UPDATE(trgform->tgtype)) + { + Assert(*updateTriggerOid == InvalidOid); + *updateTriggerOid = trgform->oid; + } + if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid)) + break; + } + + if (!OidIsValid(*insertTriggerOid)) + elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u", + conoid); + if (!OidIsValid(*updateTriggerOid)) + elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u", + conoid); + + systable_endscan(scan); +} /* * ALTER TABLE ALTER CONSTRAINT @@ -11323,10 +11553,19 @@ validateForeignKeyConstraint(char *conname, ExecDropSingleTupleTableSlot(slot); } -static void +/* + * CreateFKCheckTrigger + * Creates the insert (on_insert=true) or update "check" trigger that + * implements a given foreign key + * + * Returns the OID of the so created trigger. + */ +static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, - Oid constraintOid, Oid indexOid, bool on_insert) + Oid constraintOid, Oid indexOid, Oid parentTrigOid, + bool on_insert) { + ObjectAddress trigAddress; CreateTrigStmt *fk_trigger; /* @@ -11366,23 +11605,32 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, fk_trigger->initdeferred = fkconstraint->initdeferred; fk_trigger->constrrel = NULL; - (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid, - indexOid, InvalidOid, InvalidOid, NULL, true, false); + trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, + constraintOid, indexOid, InvalidOid, + parentTrigOid, NULL, true, false); /* Make changes-so-far visible */ CommandCounterIncrement(); + + return trigAddress.objectId; } /* * createForeignKeyActionTriggers * Create the referenced-side "action" triggers that implement a foreign * key. + * + * Returns the OIDs of the so created triggers in *deleteTrigOid and + * *updateTrigOid. */ static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, - Oid constraintOid, Oid indexOid) + Oid constraintOid, Oid indexOid, + Oid parentDelTrigger, Oid parentUpdTrigger, + Oid *deleteTrigOid, Oid *updateTrigOid) { CreateTrigStmt *fk_trigger; + ObjectAddress trigAddress; /* * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON @@ -11434,9 +11682,12 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr break; } - (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel), - constraintOid, - indexOid, InvalidOid, InvalidOid, NULL, true, false); + trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, + RelationGetRelid(rel), + constraintOid, indexOid, InvalidOid, + parentDelTrigger, NULL, true, false); + if (deleteTrigOid) + *deleteTrigOid = trigAddress.objectId; /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -11491,25 +11742,35 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr break; } - (void) CreateTrigger(fk_trigger, NULL, refRelOid, RelationGetRelid(rel), - constraintOid, - indexOid, InvalidOid, InvalidOid, NULL, true, false); + trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, + RelationGetRelid(rel), + constraintOid, indexOid, InvalidOid, + parentUpdTrigger, NULL, true, false); + if (updateTrigOid) + *updateTrigOid = trigAddress.objectId; } /* * createForeignKeyCheckTriggers * Create the referencing-side "check" triggers that implement a foreign * key. + * + * Returns the OIDs of the so created triggers in *insertTrigOid and + * *updateTrigOid. */ static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, - Oid indexOid) + Oid indexOid, + Oid parentInsTrigger, Oid parentUpdTrigger, + Oid *insertTrigOid, Oid *updateTrigOid) { - CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, - indexOid, true); - CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, - indexOid, false); + *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, + constraintOid, indexOid, + parentInsTrigger, true); + *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, + constraintOid, indexOid, + parentUpdTrigger, false); } /* @@ -17859,19 +18120,10 @@ CloneRowTriggersToPartition(Relation parent, Relation partition) continue; /* - * Internal triggers require careful examination. Ideally, we don't - * clone them. However, if our parent is itself a partition, there - * might be internal triggers that must not be skipped; for example, - * triggers on our parent that are in turn clones from its parent (our - * grandparent) are marked internal, yet they are to be cloned. - * - * Note we dare not verify that the other trigger belongs to an - * ancestor relation of our parent, because that creates deadlock - * opportunities. + * Don't clone internal triggers, because the constraint cloning code + * will. */ - if (trigForm->tgisinternal && - (!parent->rd_rel->relispartition || - !OidIsValid(trigForm->tgparentid))) + if (trigForm->tgisinternal) continue; /* @@ -18173,6 +18425,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, new_repl[Natts_pg_class]; HeapTuple tuple, newtuple; + Relation trigrel = NULL; if (concurrent) { @@ -18191,12 +18444,16 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, * additional action triggers. */ fks = copyObject(RelationGetFKeyList(partRel)); + if (fks != NIL) + trigrel = table_open(TriggerRelationId, RowExclusiveLock); foreach(cell, fks) { ForeignKeyCacheInfo *fk = lfirst(cell); HeapTuple contup; Form_pg_constraint conform; Constraint *fkconstraint; + Oid insertTriggerOid, + updateTriggerOid; contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid)); if (!HeapTupleIsValid(contup)) @@ -18215,6 +18472,20 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, 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)); + + /* * Make the action triggers on the referenced relation. When this was * a partition the action triggers pointed to the parent rel (they * still do), but now we need separate ones of our own. @@ -18228,11 +18499,15 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, createForeignKeyActionTriggers(partRel, conform->confrelid, fkconstraint, fk->conoid, - conform->conindid); + conform->conindid, + InvalidOid, InvalidOid, + NULL, NULL); ReleaseSysCache(contup); } list_free_deep(fks); + if (trigrel) + table_close(trigrel, RowExclusiveLock); /* * Any sub-constraints that are in the referenced-side of a larger @@ -18458,6 +18733,14 @@ DropClonedTriggersFromPartition(Oid partitionId) continue; /* + * Ignore internal triggers that are implementation objects of foreign + * keys, because these will be detached when the foreign keys + * themselves are. + */ + if (OidIsValid(pg_trigger->tgconstrrelid)) + continue; + + /* * This is ugly, but necessary: remove the dependency markings on the * trigger so that it can be removed. */ |