@ -395,6 +395,14 @@ static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
static bool ATExecAlterConstraintInternal ( List * * wqueue , ATAlterConstraint * cmdcon , Relation conrel ,
Relation tgrel , Relation rel , HeapTuple contuple ,
bool recurse , LOCKMODE lockmode ) ;
static bool ATExecAlterConstrEnforceability ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel ,
const Oid fkrelid , const Oid pkrelid ,
HeapTuple contuple , LOCKMODE lockmode ,
Oid ReferencedParentDelTrigger ,
Oid ReferencedParentUpdTrigger ,
Oid ReferencingParentInsTrigger ,
Oid ReferencingParentUpdTrigger ) ;
static bool ATExecAlterConstrDeferrability ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel , Relation rel ,
HeapTuple contuple , bool recurse ,
@ -405,6 +413,14 @@ static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cm
static void AlterConstrTriggerDeferrability ( Oid conoid , Relation tgrel , Relation rel ,
bool deferrable , bool initdeferred ,
List * * otherrelids ) ;
static void AlterConstrEnforceabilityRecurse ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel ,
const Oid fkrelid , const Oid pkrelid ,
HeapTuple contuple , LOCKMODE lockmode ,
Oid ReferencedParentDelTrigger ,
Oid ReferencedParentUpdTrigger ,
Oid ReferencingParentInsTrigger ,
Oid ReferencingParentUpdTrigger ) ;
static void AlterConstrDeferrabilityRecurse ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel , Relation rel ,
HeapTuple contuple , bool recurse ,
@ -10610,7 +10626,7 @@ addFkConstraint(addFkConstraintSides fkside,
CONSTRAINT_FOREIGN ,
fkconstraint - > deferrable ,
fkconstraint - > initdeferred ,
true , /* Is Enforced */
fkconstraint - > is_enforced ,
fkconstraint - > initially_valid ,
parentConstr ,
RelationGetRelid ( rel ) ,
@ -10728,21 +10744,23 @@ addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period )
{
Oid deleteTriggerOid ,
updateTriggerOid ;
Oid deleteTriggerOid = InvalidOid ,
updateTriggerOid = InvalidOid ;
Assert ( CheckRelationLockedByMe ( pkrel , ShareRowExclusiveLock , true ) ) ;
Assert ( CheckRelationLockedByMe ( rel , ShareRowExclusiveLock , true ) ) ;
/*
* Create the action triggers that enforce the constraint .
* Create action triggers to enforce the constraint , or skip them if the
* constraint is NOT ENFORCED .
*/
createForeignKeyActionTriggers ( RelationGetRelid ( rel ) ,
RelationGetRelid ( pkrel ) ,
fkconstraint ,
parentConstr , indexOid ,
parentDelTrigger , parentUpdTrigger ,
& deleteTriggerOid , & updateTriggerOid ) ;
if ( fkconstraint - > is_enforced )
createForeignKeyActionTriggers ( RelationGetRelid ( rel ) ,
RelationGetRelid ( pkrel ) ,
fkconstraint ,
parentConstr , indexOid ,
parentDelTrigger , parentUpdTrigger ,
& deleteTriggerOid , & updateTriggerOid ) ;
/*
* If the referenced table is partitioned , recurse on ourselves to handle
@ -10863,8 +10881,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
Oid parentInsTrigger , Oid parentUpdTrigger ,
bool with_period )
{
Oid insertTriggerOid ,
updateTriggerOid ;
Oid insertTriggerOid = InvalidOid ,
updateTriggerOid = InvalidOid ;
Assert ( OidIsValid ( parentConstr ) ) ;
Assert ( CheckRelationLockedByMe ( rel , ShareRowExclusiveLock , true ) ) ;
@ -10876,29 +10894,32 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
errmsg ( " foreign key constraints are not supported on foreign tables " ) ) ) ;
/*
* Add the check triggers to it and , if necessary , schedule it to be
* checked in Phase 3.
* Add check triggers if the constraint is ENFORCED , and if needed ,
* schedule them 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 ( fkconstraint - > is_enforced )
createForeignKeyCheckTriggers ( RelationGetRelid ( rel ) ,
RelationGetRelid ( pkrel ) ,
fkconstraint ,
parentConstr ,
indexOid ,
parentInsTrigger , parentUpdTrigger ,
& insertTriggerOid , & updateTriggerOid ) ;
if ( rel - > rd_rel - > relkind = = RELKIND_RELATION )
{
/*
* Tell Phase 3 to check that the constraint is satisfied by existing
* rows . We can skip this during table creation , when requested
* explicitly by specifying NOT VALID in an ADD FOREIGN KEY command ,
* and when we ' re recreating a constraint following a SET DATA TYPE
* operation that did not impugn its validity .
* rows . We can skip this during table creation , when constraint is
* specified as NOT ENFORCED , or when requested explicitly by
* specifying NOT VALID in an ADD FOREIGN KEY command , and when we ' re
* recreating a constraint following a SET DATA TYPE operation that
* did not impugn its validity .
*/
if ( wqueue & & ! old_check_ok & & ! fkconstraint - > skip_validation )
if ( wqueue & & ! old_check_ok & & ! fkconstraint - > skip_validation & &
fkconstraint - > is_enforced )
{
NewConstraint * newcon ;
AlteredTableInfo * tab ;
@ -11129,8 +11150,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
AttrNumber confdelsetcols [ INDEX_MAX_KEYS ] ;
Constraint * fkconstraint ;
ObjectAddress address ;
Oid deleteTriggerOid ,
updateTriggerOid ;
Oid deleteTriggerOid = InvalidOid ,
updateTriggerOid = InvalidOid ;
tuple = SearchSysCache1 ( CONSTROID , ObjectIdGetDatum ( constrOid ) ) ;
if ( ! HeapTupleIsValid ( tuple ) )
@ -11190,8 +11211,9 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
fkconstraint - > fk_del_set_cols = NIL ;
fkconstraint - > old_conpfeqop = NIL ;
fkconstraint - > old_pktable_oid = InvalidOid ;
fkconstraint - > is_enforced = constrForm - > conenforced ;
fkconstraint - > skip_validation = false ;
fkconstraint - > initially_valid = true ;
fkconstraint - > initially_valid = constrForm - > convalidated ;
/* set up colnames that are used to generate the constraint name */
for ( int i = 0 ; i < numfks ; i + + )
@ -11219,9 +11241,10 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
* parent OIDs for similar triggers that will be created on the
* partition in addFkRecurseReferenced ( ) .
*/
GetForeignKeyActionTriggers ( trigrel , constrOid ,
constrForm - > confrelid , constrForm - > conrelid ,
& deleteTriggerOid , & updateTriggerOid ) ;
if ( constrForm - > conenforced )
GetForeignKeyActionTriggers ( trigrel , constrOid ,
constrForm - > confrelid , constrForm - > conrelid ,
& deleteTriggerOid , & updateTriggerOid ) ;
/* Add this constraint ... */
address = addFkConstraint ( addFkReferencedSide ,
@ -11354,8 +11377,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Oid indexOid ;
ObjectAddress address ;
ListCell * lc ;
Oid insertTriggerOid ,
updateTriggerOid ;
Oid insertTriggerOid = InvalidOid ,
updateTriggerOid = InvalidOid ;
bool with_period ;
tuple = SearchSysCache1 ( CONSTROID , ObjectIdGetDatum ( parentConstrOid ) ) ;
@ -11387,17 +11410,18 @@ 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 a s
* parent OIDs for similar triggers that will be created on th e
* 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 .
* Get the " check " triggers belonging to the constraint , if it i s
* ENFORCED , 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 ) ;
if ( constrForm - > conenforced )
GetForeignKeyCheckTriggers ( trigrel , constrForm - > oid ,
constrForm - > confrelid , constrForm - > conrelid ,
& insertTriggerOid , & updateTriggerOid ) ;
/*
* Before creating a new constraint , see whether any existing FKs are
@ -11450,6 +11474,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
fkconstraint - > fk_del_set_cols = NIL ;
fkconstraint - > old_conpfeqop = NIL ;
fkconstraint - > old_pktable_oid = InvalidOid ;
fkconstraint - > is_enforced = constrForm - > conenforced ;
fkconstraint - > skip_validation = false ;
fkconstraint - > initially_valid = constrForm - > convalidated ;
for ( int i = 0 ; i < numfks ; i + + )
@ -11564,6 +11589,23 @@ tryAttachPartitionForeignKey(List **wqueue,
if ( ! HeapTupleIsValid ( partcontup ) )
elog ( ERROR , " cache lookup failed for constraint %u " , fk - > conoid ) ;
partConstr = ( Form_pg_constraint ) GETSTRUCT ( partcontup ) ;
/*
* An error should be raised if the constraint enforceability is
* different . Returning false without raising an error , as we do for other
* attributes , could lead to a duplicate constraint with the same
* enforceability as the parent . While this may be acceptable , it may not
* be ideal . Therefore , it ' s better to raise an error and allow the user
* to correct the enforceability before proceeding .
*/
if ( partConstr - > conenforced ! = parentConstr - > conenforced )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_OBJECT_DEFINITION ) ,
errmsg ( " constraint \" %s \" enforceability conflicts with constraint \" %s \" on relation \" %s \" " ,
NameStr ( parentConstr - > conname ) ,
NameStr ( partConstr - > conname ) ,
RelationGetRelationName ( partition ) ) ) ) ;
if ( OidIsValid ( partConstr - > conparentid ) | |
partConstr - > condeferrable ! = parentConstr - > condeferrable | |
partConstr - > condeferred ! = parentConstr - > condeferred | |
@ -11610,8 +11652,7 @@ AttachPartitionForeignKey(List **wqueue,
bool queueValidation ;
Oid partConstrFrelid ;
Oid partConstrRelid ;
Oid insertTriggerOid ,
updateTriggerOid ;
bool parentConstrIsEnforced ;
/* Fetch the parent constraint tuple */
parentConstrTup = SearchSysCache1 ( CONSTROID ,
@ -11619,6 +11660,7 @@ AttachPartitionForeignKey(List **wqueue,
if ( ! HeapTupleIsValid ( parentConstrTup ) )
elog ( ERROR , " cache lookup failed for constraint %u " , parentConstrOid ) ;
parentConstr = ( Form_pg_constraint ) GETSTRUCT ( parentConstrTup ) ;
parentConstrIsEnforced = parentConstr - > conenforced ;
/* Fetch the child constraint tuple */
partcontup = SearchSysCache1 ( CONSTROID ,
@ -11668,17 +11710,24 @@ AttachPartitionForeignKey(List **wqueue,
/*
* Like the constraint , attach partition ' s " check " triggers to the
* corresponding parent triggers .
* corresponding parent triggers if the constraint is ENFORCED . NOT
* ENFORCED constraints do not have these triggers .
*/
GetForeignKeyCheckTriggers ( trigrel ,
partConstrOid , partConstrFrelid , partConstrRelid ,
& insertTriggerOid , & updateTriggerOid ) ;
Assert ( OidIsValid ( insertTriggerOid ) & & OidIsValid ( parentInsTrigger ) ) ;
TriggerSetParentTrigger ( trigrel , insertTriggerOid , parentInsTrigger ,
RelationGetRelid ( partition ) ) ;
Assert ( OidIsValid ( updateTriggerOid ) & & OidIsValid ( parentUpdTrigger ) ) ;
TriggerSetParentTrigger ( trigrel , updateTriggerOid , parentUpdTrigger ,
RelationGetRelid ( partition ) ) ;
if ( parentConstrIsEnforced )
{
Oid insertTriggerOid ,
updateTriggerOid ;
GetForeignKeyCheckTriggers ( trigrel ,
partConstrOid , partConstrFrelid , partConstrRelid ,
& insertTriggerOid , & updateTriggerOid ) ;
Assert ( OidIsValid ( insertTriggerOid ) & & OidIsValid ( parentInsTrigger ) ) ;
TriggerSetParentTrigger ( trigrel , insertTriggerOid , parentInsTrigger ,
RelationGetRelid ( partition ) ) ;
Assert ( OidIsValid ( updateTriggerOid ) & & OidIsValid ( parentUpdTrigger ) ) ;
TriggerSetParentTrigger ( trigrel , updateTriggerOid , parentUpdTrigger ,
RelationGetRelid ( partition ) ) ;
}
/*
* We updated this pg_constraint row above to set its parent ; validating
@ -11792,6 +11841,10 @@ RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
*
* The subroutine for tryAttachPartitionForeignKey handles the deletion of
* action triggers for the foreign key constraint .
*
* If valid confrelid and conrelid values are not provided , the respective
* trigger check will be skipped , and the trigger will be considered for
* removal .
*/
static void
DropForeignKeyConstraintTriggers ( Relation trigrel , Oid conoid , Oid confrelid ,
@ -11812,10 +11865,27 @@ DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
Form_pg_trigger trgform = ( Form_pg_trigger ) GETSTRUCT ( trigtup ) ;
ObjectAddress trigger ;
if ( trgform - > tgconstrrelid ! = conrelid )
/* Invalid if trigger is not for a referential integrity constraint */
if ( ! OidIsValid ( trgform - > tgconstrrelid ) )
continue ;
if ( trgform - > tgrelid ! = conf relid )
if ( OidIsValid ( conrelid ) & & trgform - > tgconstr relid ! = conrelid )
continue ;
if ( OidIsValid ( confrelid ) & & trgform - > tgrelid ! = confrelid )
continue ;
/* We should be droping trigger related to foreign key constraint */
Assert ( trgform - > tgfoid = = F_RI_FKEY_CHECK_INS | |
trgform - > tgfoid = = F_RI_FKEY_CHECK_UPD | |
trgform - > tgfoid = = F_RI_FKEY_CASCADE_DEL | |
trgform - > tgfoid = = F_RI_FKEY_CASCADE_UPD | |
trgform - > tgfoid = = F_RI_FKEY_RESTRICT_DEL | |
trgform - > tgfoid = = F_RI_FKEY_RESTRICT_UPD | |
trgform - > tgfoid = = F_RI_FKEY_SETNULL_DEL | |
trgform - > tgfoid = = F_RI_FKEY_SETNULL_UPD | |
trgform - > tgfoid = = F_RI_FKEY_SETDEFAULT_DEL | |
trgform - > tgfoid = = F_RI_FKEY_SETDEFAULT_UPD | |
trgform - > tgfoid = = F_RI_FKEY_NOACTION_DEL | |
trgform - > tgfoid = = F_RI_FKEY_NOACTION_UPD ) ;
/*
* The constraint is originally set up to contain this trigger as an
@ -12028,6 +12098,11 @@ ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " constraint \" %s \" of relation \" %s \" is not a foreign key constraint " ,
cmdcon - > conname , RelationGetRelationName ( rel ) ) ) ) ;
if ( cmdcon - > alterEnforceability & & currcon - > contype ! = CONSTRAINT_FOREIGN )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " cannot alter enforceability of constraint \" %s \" of relation \" %s \" " ,
cmdcon - > conname , RelationGetRelationName ( rel ) ) ) ) ;
if ( cmdcon - > alterInheritability & &
currcon - > contype ! = CONSTRAINT_NOTNULL )
ereport ( ERROR ,
@ -12107,7 +12182,7 @@ ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
/*
* A subroutine of ATExecAlterConstraint that calls the respective routines for
* altering constraint attributes .
* altering constraint ' s enforceability , deferrability or inheritability .
*/
static bool
ATExecAlterConstraintInternal ( List * * wqueue , ATAlterConstraint * cmdcon ,
@ -12115,16 +12190,35 @@ ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
HeapTuple contuple , bool recurse ,
LOCKMODE lockmode )
{
Form_pg_constraint currcon ;
bool changed = false ;
List * otherrelids = NIL ;
currcon = ( Form_pg_constraint ) GETSTRUCT ( contuple ) ;
/*
* Do the catalog work for the deferrability change , recurse if necessary .
*/
if ( cmdcon - > alterDeferrability & &
ATExecAlterConstrDeferrability ( wqueue , cmdcon , conrel , tgrel , rel ,
contuple , recurse , & otherrelids ,
lockmode ) )
* Do the catalog work for the enforceability or deferrability change ,
* recurse if necessary .
*
* Note that even if deferrability is requested to be altered along with
* enforceability , we don ' t need to explicitly update multiple entries in
* pg_trigger related to deferrability .
*
* Modifying enforceability involves either creating or dropping the
* trigger , during which the deferrability setting will be adjusted
* automatically .
*/
if ( cmdcon - > alterEnforceability & &
ATExecAlterConstrEnforceability ( wqueue , cmdcon , conrel , tgrel ,
currcon - > conrelid , currcon - > confrelid ,
contuple , lockmode , InvalidOid ,
InvalidOid , InvalidOid , InvalidOid ) )
changed = true ;
else if ( cmdcon - > alterDeferrability & &
ATExecAlterConstrDeferrability ( wqueue , cmdcon , conrel , tgrel , rel ,
contuple , recurse , & otherrelids ,
lockmode ) )
{
/*
* AlterConstrUpdateConstraintEntry already invalidated relcache for
@ -12149,6 +12243,151 @@ ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
return changed ;
}
/*
* Returns true if the constraint ' s enforceability is altered .
*
* Depending on whether the constraint is being set to ENFORCED or NOT
* ENFORCED , it creates or drops the trigger accordingly .
*
* Note that we must recurse even when trying to change a constraint to not
* enforced if it is already not enforced , in case descendant constraints
* might be enforced and need to be changed to not enforced . Conversely , we
* should do nothing if a constraint is being set to enforced and is already
* enforced , as descendant constraints cannot be different in that case .
*/
static bool
ATExecAlterConstrEnforceability ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel ,
const Oid fkrelid , const Oid pkrelid ,
HeapTuple contuple , LOCKMODE lockmode ,
Oid ReferencedParentDelTrigger ,
Oid ReferencedParentUpdTrigger ,
Oid ReferencingParentInsTrigger ,
Oid ReferencingParentUpdTrigger )
{
Form_pg_constraint currcon ;
Oid conoid ;
Relation rel ;
bool changed = false ;
/* Since this function recurses, it could be driven to stack overflow */
check_stack_depth ( ) ;
Assert ( cmdcon - > alterEnforceability ) ;
currcon = ( Form_pg_constraint ) GETSTRUCT ( contuple ) ;
conoid = currcon - > oid ;
/* Should be foreign key constraint */
Assert ( currcon - > contype = = CONSTRAINT_FOREIGN ) ;
rel = table_open ( currcon - > conrelid , lockmode ) ;
if ( currcon - > conenforced ! = cmdcon - > is_enforced )
{
AlterConstrUpdateConstraintEntry ( cmdcon , conrel , contuple ) ;
changed = true ;
}
/* Drop triggers */
if ( ! cmdcon - > is_enforced )
{
/*
* When setting a constraint to NOT ENFORCED , the constraint triggers
* need to be dropped . Therefore , we must process the child relations
* first , followed by the parent , to account for dependencies .
*/
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE | |
get_rel_relkind ( currcon - > confrelid ) = = RELKIND_PARTITIONED_TABLE )
AlterConstrEnforceabilityRecurse ( wqueue , cmdcon , conrel , tgrel ,
fkrelid , pkrelid , contuple ,
lockmode , InvalidOid , InvalidOid ,
InvalidOid , InvalidOid ) ;
/* Drop all the triggers */
DropForeignKeyConstraintTriggers ( tgrel , conoid , InvalidOid , InvalidOid ) ;
}
else if ( changed ) /* Create triggers */
{
Oid ReferencedDelTriggerOid = InvalidOid ,
ReferencedUpdTriggerOid = InvalidOid ,
ReferencingInsTriggerOid = InvalidOid ,
ReferencingUpdTriggerOid = InvalidOid ;
/* Prepare the minimal information required for trigger creation. */
Constraint * fkconstraint = makeNode ( Constraint ) ;
fkconstraint - > conname = pstrdup ( NameStr ( currcon - > conname ) ) ;
fkconstraint - > fk_matchtype = currcon - > confmatchtype ;
fkconstraint - > fk_upd_action = currcon - > confupdtype ;
fkconstraint - > fk_del_action = currcon - > confdeltype ;
/* Create referenced triggers */
if ( currcon - > conrelid = = fkrelid )
createForeignKeyActionTriggers ( currcon - > conrelid ,
currcon - > confrelid ,
fkconstraint ,
conoid ,
currcon - > conindid ,
ReferencedParentDelTrigger ,
ReferencedParentUpdTrigger ,
& ReferencedDelTriggerOid ,
& ReferencedUpdTriggerOid ) ;
/* Create referencing triggers */
if ( currcon - > confrelid = = pkrelid )
createForeignKeyCheckTriggers ( currcon - > conrelid ,
pkrelid ,
fkconstraint ,
conoid ,
currcon - > conindid ,
ReferencingParentInsTrigger ,
ReferencingParentUpdTrigger ,
& ReferencingInsTriggerOid ,
& ReferencingUpdTriggerOid ) ;
/*
* Tell Phase 3 to check that the constraint is satisfied by existing
* rows .
*/
if ( rel - > rd_rel - > relkind = = RELKIND_RELATION )
{
AlteredTableInfo * tab ;
NewConstraint * newcon ;
newcon = ( NewConstraint * ) palloc0 ( sizeof ( NewConstraint ) ) ;
newcon - > name = fkconstraint - > conname ;
newcon - > contype = CONSTR_FOREIGN ;
newcon - > refrelid = currcon - > confrelid ;
newcon - > refindid = currcon - > conindid ;
newcon - > conid = currcon - > oid ;
newcon - > qual = ( Node * ) fkconstraint ;
/* Find or create work queue entry for this table */
tab = ATGetQueueEntry ( wqueue , rel ) ;
tab - > constraints = lappend ( tab - > constraints , newcon ) ;
}
/*
* If the table at either end of the constraint is partitioned , we
* need to recurse and create triggers for each constraint that is a
* child of this one .
*/
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE | |
get_rel_relkind ( currcon - > confrelid ) = = RELKIND_PARTITIONED_TABLE )
AlterConstrEnforceabilityRecurse ( wqueue , cmdcon , conrel , tgrel ,
fkrelid , pkrelid , contuple ,
lockmode , ReferencedDelTriggerOid ,
ReferencedUpdTriggerOid ,
ReferencingInsTriggerOid ,
ReferencingUpdTriggerOid ) ;
}
table_close ( rel , NoLock ) ;
return changed ;
}
/*
* Returns true if the constraint ' s deferrability is altered .
*
@ -12353,6 +12592,55 @@ AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
systable_endscan ( tgscan ) ;
}
/*
* Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
* the specified constraint .
*
* Note that this doesn ' t handle recursion the normal way , viz . by scanning the
* list of child relations and recursing ; instead it uses the conparentid
* relationships . This may need to be reconsidered .
*
* The arguments to this function have the same meaning as the arguments to
* ATExecAlterConstrEnforceability .
*/
static void
AlterConstrEnforceabilityRecurse ( List * * wqueue , ATAlterConstraint * cmdcon ,
Relation conrel , Relation tgrel ,
const Oid fkrelid , const Oid pkrelid ,
HeapTuple contuple , LOCKMODE lockmode ,
Oid ReferencedParentDelTrigger ,
Oid ReferencedParentUpdTrigger ,
Oid ReferencingParentInsTrigger ,
Oid ReferencingParentUpdTrigger )
{
Form_pg_constraint currcon ;
Oid conoid ;
ScanKeyData pkey ;
SysScanDesc pscan ;
HeapTuple childtup ;
currcon = ( Form_pg_constraint ) GETSTRUCT ( contuple ) ;
conoid = currcon - > oid ;
ScanKeyInit ( & pkey ,
Anum_pg_constraint_conparentid ,
BTEqualStrategyNumber , F_OIDEQ ,
ObjectIdGetDatum ( conoid ) ) ;
pscan = systable_beginscan ( conrel , ConstraintParentIndexId ,
true , NULL , 1 , & pkey ) ;
while ( HeapTupleIsValid ( childtup = systable_getnext ( pscan ) ) )
ATExecAlterConstrEnforceability ( wqueue , cmdcon , conrel , tgrel , fkrelid ,
pkrelid , childtup , lockmode ,
ReferencedParentDelTrigger ,
ReferencedParentUpdTrigger ,
ReferencingParentInsTrigger ,
ReferencingParentUpdTrigger ) ;
systable_endscan ( pscan ) ;
}
/*
* Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
* the specified constraint .
@ -12413,11 +12701,25 @@ AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
HeapTuple copyTuple ;
Form_pg_constraint copy_con ;
Assert ( cmdcon - > alterDeferrability | | cmdcon - > alterInheritability ) ;
Assert ( cmdcon - > alterEnforceability | | cmdcon - > alterDeferrability | |
cmdcon - > alterInheritability ) ;
copyTuple = heap_copytuple ( contuple ) ;
copy_con = ( Form_pg_constraint ) GETSTRUCT ( copyTuple ) ;
if ( cmdcon - > alterEnforceability )
{
copy_con - > conenforced = cmdcon - > is_enforced ;
/*
* NB : The convalidated status is irrelevant when the constraint is
* set to NOT ENFORCED , but for consistency , it should still be set
* appropriately . Similarly , if the constraint is later changed to
* ENFORCED , validation will be performed during phase 3 , so it makes
* sense to mark it as valid in that case .
*/
copy_con - > convalidated = cmdcon - > is_enforced ;
}
if ( cmdcon - > alterDeferrability )
{
copy_con - > condeferrable = cmdcon - > deferrable ;
@ -17137,9 +17439,9 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
NameStr ( child_con - > conname ) , RelationGetRelationName ( child_rel ) ) ) ) ;
/*
* A non - enforced child constraint cannot be merged with an
* enforced parent constraint . However , the reverse is allowed ,
* where the child constraint is enforced .
* A NOT ENFORCED child constraint cannot be merged with an
* ENFORCED parent constraint . However , the reverse is allowed ,
* where the child constraint is ENFORCED .
*/
if ( parent_con - > conenforced & & ! child_con - > conenforced )
ereport ( ERROR ,
@ -20510,8 +20812,6 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
ForeignKeyCacheInfo * fk = lfirst ( cell ) ;
HeapTuple contup ;
Form_pg_constraint conform ;
Oid insertTriggerOid ,
updateTriggerOid ;
contup = SearchSysCache1 ( CONSTROID , ObjectIdGetDatum ( fk - > conoid ) ) ;
if ( ! HeapTupleIsValid ( contup ) )
@ -20538,17 +20838,25 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
/*
* Also , look up the partition ' s " check " triggers corresponding to the
* constraint being detached and detach them from the parent triggers .
* ENFORCED constraint being detached and detach them from the parent
* triggers . NOT ENFORCED constraints do not have these triggers ;
* therefore , this step is not needed .
*/
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 ) ) ;
if ( fk - > conenforced )
{
Oid insertTriggerOid ,
updateTriggerOid ;
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 ) ) ;
}
/*
* Lastly , create the action triggers on the referenced table , using
@ -20588,8 +20896,9 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
fkconstraint - > conname = pstrdup ( NameStr ( conform - > conname ) ) ;
fkconstraint - > deferrable = conform - > condeferrable ;
fkconstraint - > initdeferred = conform - > condeferred ;
fkconstraint - > is_enforced = conform - > conenforced ;
fkconstraint - > skip_validation = true ;
fkconstraint - > initially_valid = true ;
fkconstraint - > initially_valid = conform - > convalidated ;
/* a few irrelevant fields omitted here */
fkconstraint - > pktable = NULL ;
fkconstraint - > fk_attrs = NIL ;