aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c413
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.
*/