@ -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 ) )
@ -10087,6 +10162,14 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
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 ) )
@ -10147,6 +10232,19 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
for ( int i = 0 ; i < numfks ; i + + )
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
@ -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 ) )
@ -18214,6 +18471,20 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
/* unset conparentid and adjust conislocal, coninhcount, etc. */
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
@ -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
@ -18457,6 +18732,14 @@ DropClonedTriggersFromPartition(Oid partitionId)
if ( ! OidIsValid ( pg_trigger - > tgparentid ) )
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 .