@ -349,6 +349,14 @@ typedef struct ForeignTruncateInfo
List * rels ;
} ForeignTruncateInfo ;
/* Partial or complete FK creation in addFkConstraint() */
typedef enum addFkConstraintSides
{
addFkReferencedSide ,
addFkReferencingSide ,
addFkBothSides ,
} addFkConstraintSides ;
/*
* Partition tables are expected to be dropped when the parent partitioned
* table gets dropped . Hence for partitioning we use AUTO dependency .
@ -508,17 +516,27 @@ static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *
Relation rel , Constraint * fkconstraint ,
bool recurse , bool recursing ,
LOCKMODE lockmode ) ;
static ObjectAddress addFkRecurseReferenced ( List * * wqueue , Constraint * fkconstraint ,
Relation rel , Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks , int16 * pkattnum , int16 * fkattnum ,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period ) ;
static void validateFkOnDeleteSetColumns ( int numfks , const int16 * fkattnums ,
int numfksetcols , const int16 * fksetcolsattnums ,
List * fksetcols ) ;
static ObjectAddress addFkConstraint ( addFkConstraintSides fkside ,
char * constraintname ,
Constraint * fkconstraint , Relation rel ,
Relation pkrel , Oid indexOid ,
Oid parentConstr ,
int numfks , int16 * pkattnum , int16 * fkattnum ,
Oid * pfeqoperators , Oid * ppeqoperators ,
Oid * ffeqoperators , int numfkdelsetcols ,
int16 * fkdelsetcols , bool is_internal ,
bool with_period ) ;
static void addFkRecurseReferenced ( Constraint * fkconstraint ,
Relation rel , Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks , int16 * pkattnum , int16 * fkattnum ,
Oid * pfeqoperators , Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period ) ;
static void addFkRecurseReferencing ( List * * wqueue , Constraint * fkconstraint ,
Relation rel , Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks , int16 * pkattnum , int16 * fkattnum ,
@ -527,7 +545,6 @@ static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
bool old_check_ok , LOCKMODE lockmode ,
Oid parentInsTrigger , Oid parentUpdTrigger ,
bool with_period ) ;
static void CloneForeignKeyConstraints ( List * * wqueue , Relation parentRel ,
Relation partitionRel ) ;
static void CloneFkReferenced ( Relation parentRel , Relation partitionRel ) ;
@ -10045,26 +10062,39 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
FindFKPeriodOpers ( opclasses [ numpks - 1 ] , & periodoperoid , & aggedperiodoperoid ) ;
}
/*
* Create all the constraint and trigger objects , recursing to partitions
* as necessary . First handle the referenced side .
*/
address = addFkRecurseReferenced ( wqueue , fkconstraint , rel , pkrel ,
indexOid ,
InvalidOid , /* no parent constraint */
numfks ,
pkattnum ,
fkattnum ,
pfeqoperators ,
ppeqoperators ,
ffeqoperators ,
numfkdelsetcols ,
fkdelsetcols ,
old_check_ok ,
InvalidOid , InvalidOid ,
with_period ) ;
/* Now handle the referencing side. */
/* First, create the constraint catalog entry itself. */
address = addFkConstraint ( addFkBothSides ,
fkconstraint - > conname , fkconstraint , rel , pkrel ,
indexOid ,
InvalidOid , /* no parent constraint */
numfks ,
pkattnum ,
fkattnum ,
pfeqoperators ,
ppeqoperators ,
ffeqoperators ,
numfkdelsetcols ,
fkdelsetcols ,
false ,
with_period ) ;
/* Next process the action triggers at the referenced side and recurse */
addFkRecurseReferenced ( fkconstraint , rel , pkrel ,
indexOid ,
address . objectId ,
numfks ,
pkattnum ,
fkattnum ,
pfeqoperators ,
ppeqoperators ,
ffeqoperators ,
numfkdelsetcols ,
fkdelsetcols ,
old_check_ok ,
InvalidOid , InvalidOid ,
with_period ) ;
/* Lastly create the check triggers at the referencing side and recurse */
addFkRecurseReferencing ( wqueue , fkconstraint , rel , pkrel ,
indexOid ,
address . objectId ,
@ -10125,47 +10155,42 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
}
/*
* addFkRecurseReferenced
* subroutine for ATAddForeignKeyConstraint ; recurses on the referenced
* side of the constraint
* addFkConstraint
* Install pg_constraint entries to implement a foreign key constraint .
* Caller must separately invoke addFkRecurseReferenced and
* addFkRecurseReferencing , as appropriate , to install pg_trigger entries
* and ( for partitioned tables ) recurse to partitions .
*
* Create pg_constraint rows for the referenced side of the constraint ,
* referencing the parent of the referencing side ; also create action triggers
* on leaf partitions . If the table is partitioned , recurse to handle each
* partition .
*
* wqueue is the ALTER TABLE work queue ; can be NULL when not running as par t
* of an ALTER TABLE sequence .
* fkconstraint is the constraint being added .
* rel is the root referencing relation .
* pkrel is the referenced relation ; might be a partition , if recursing .
* indexOid is the OID of the index ( on pkrel ) implementing this constraint .
* parentConstr is the OID of a parent constraint ; InvalidOid if this is a
* top - level constraint .
* numfks is the number of columns in the foreign key
* pkattnum is the attnum array of referenced attributes .
* fkattnum is the attnum array of referencing attributes .
* numfkdelsetcols is the number of columns in the ON DELETE SET NULL / DEFAULT
* fkside : the side of the FK ( or both ) to create . Caller should
* call addFkRecurseReferenced if this is addFkReferencedSide ,
* addFkRecurseReferencing if it ' s addFkReferencingSide , or both if it ' s
* addFkBothSides .
* constraintname : the base name for the constraint being added ,
* copied to fkconstraint - > conname if the latter is not se t
* fkconstraint : the constraint being added
* rel : the root referencing relation
* pkrel : the referenced relation ; might be a partition , if recursing
* indexOid : the OID of the index ( on pkrel ) implementing this constraint
* parentConstr : the OID of a parent constraint ; InvalidOid if this is a
* top - level constraint
* numfks : the number of columns in the foreign key
* pkattnum : the attnum array of referenced attributes
* fkattnum : the attnum array of referencing attributes
* pf / pp / ffeqoperators : OID array of operators between columns
* numfkdelsetcols : the number of columns in the ON DELETE SET NULL / DEFAULT
* ( . . . ) clause
* fkdelsetcols is the attnum array of the columns in the ON DELETE SET
* fkdelsetcols : the attnum array of the columns in the ON DELETE SET
* NULL / DEFAULT clause
* 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 .
* with_period : true if this is a temporal FK
*/
static ObjectAddress
addFkRecurseReferenced ( List * * wqueue , Constraint * fkconstraint , Relation rel ,
Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks ,
int16 * pkattnum , int16 * fkattnum , Oid * pfeqoperators ,
Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period )
addFkConstraint ( addFkConstraintSides fkside ,
char * constraintname , Constraint * fkconstraint ,
Relation rel , Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks , int16 * pkattnum ,
int16 * fkattnum , Oid * pfeqoperators , Oid * ppeqoperators ,
Oid * ffeqoperators , int numfkdelsetcols , int16 * fkdelsetcols ,
bool is_internal , bool with_period )
{
ObjectAddress address ;
Oid constrOid ;
@ -10173,8 +10198,6 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
bool conislocal ;
int16 coninhcount ;
bool connoinherit ;
Oid deleteTriggerOid ,
updateTriggerOid ;
/*
* Verify relkind for each referenced partition . At the top level , this
@ -10193,13 +10216,16 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
*/
if ( ConstraintNameIsUsed ( CONSTRAINT_RELATION ,
RelationGetRelid ( rel ) ,
fk constraint- > con name) )
constraintname ) )
conname = ChooseConstraintName ( RelationGetRelationName ( rel ) ,
ChooseForeignKeyConstraintNameAddition ( fkconstraint - > fk_attrs ) ,
" fkey " ,
RelationGetNamespace ( rel ) , NIL ) ;
else
conname = fkconstraint - > conname ;
conname = constraintname ;
if ( fkconstraint - > conname = = NULL )
fkconstraint - > conname = pstrdup ( conname ) ;
if ( OidIsValid ( parentConstr ) )
{
@ -10252,33 +10278,106 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
coninhcount , /* inhcount */
connoinherit , /* conNoInherit */
with_period , /* conPeriod */
false ) ; /* is_internal */
is_internal ) ; /* is_internal */
ObjectAddressSet ( address , ConstraintRelationId , constrOid ) ;
/*
* Mark the child constraint as part of the parent constraint ; it must not
* be dropped on its own . ( This constraint is deleted when the partition
* is detached , but a special check needs to occur that the partition
* contains no referenced values . )
* In partitioning cases , create the dependency entries for this
* constraint . ( For non - partitioned cases , relevant entries were created
* by CreateConstraintEntry . )
*
* On the referenced side , we need the constraint to have an internal
* dependency on its parent constraint ; this means that this constraint
* cannot be dropped on its own - - only through the parent constraint . It
* also means the containing partition cannot be dropped on its own , but
* it can be detached , at which point this dependency is removed ( after
* verifying that no rows are referenced via this FK . )
*
* When processing the referencing side , we link the constraint via the
* special partitioning dependencies : the parent constraint is the primary
* dependent , and the partition on which the foreign key exists is the
* secondary dependency . That way , this constraint is dropped if either
* of these objects is .
*
* Note that this is only necessary for the subsidiary pg_constraint rows
* in partitions ; the topmost row doesn ' t need any of this .
*/
if ( OidIsValid ( parentConstr ) )
{
ObjectAddress referenced ;
ObjectAddressSet ( referenced , ConstraintRelationId , parentConstr ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_INTERNAL ) ;
Assert ( fkside ! = addFkBothSides ) ;
if ( fkside = = addFkReferencedSide )
recordDependencyOn ( & address , & referenced , DEPENDENCY_INTERNAL ) ;
else
{
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_PRI ) ;
ObjectAddressSet ( referenced , RelationRelationId , RelationGetRelid ( rel ) ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_SEC ) ;
}
}
/* make new constraint visible, in case we add more */
CommandCounterIncrement ( ) ;
return address ;
}
/*
* addFkRecurseReferenced
* Recursive helper for the referenced side of foreign key creation ,
* which creates the action triggers and recurses
*
* If the referenced relation is a plain relation , create the necessary action
* triggers that implement the constraint . If the referenced relation is a
* partitioned table , then we create a pg_constraint row referencing the parent
* of the referencing side for it and recurse on this routine for each
* partition .
*
* fkconstraint : the constraint being added
* rel : the root referencing relation
* pkrel : the referenced relation ; might be a partition , if recursing
* indexOid : the OID of the index ( on pkrel ) implementing this constraint
* parentConstr : the OID of a parent constraint ; InvalidOid if this is a
* top - level constraint
* numfks : the number of columns in the foreign key
* pkattnum : the attnum array of referenced attributes
* fkattnum : the attnum array of referencing attributes
* numfkdelsetcols : the number of columns in the ON DELETE SET
* NULL / DEFAULT ( . . . ) clause
* fkdelsetcols : the attnum array of the columns in the ON DELETE SET
* NULL / DEFAULT clause
* pf / pp / ffeqoperators : OID array of operators between columns
* old_check_ok : true if this constraint replaces an existing one that
* was already validated ( thus this one doesn ' t need validation )
* parentDelTrigger and parentUpdTrigger : when recursively called on a
* partition , the OIDs of the parent action triggers for DELETE and
* UPDATE respectively .
* with_period : true if this is a temporal FK
*/
static void
addFkRecurseReferenced ( Constraint * fkconstraint , Relation rel ,
Relation pkrel , Oid indexOid , Oid parentConstr ,
int numfks ,
int16 * pkattnum , int16 * fkattnum , Oid * pfeqoperators ,
Oid * ppeqoperators , Oid * ffeqoperators ,
int numfkdelsetcols , int16 * fkdelsetcols ,
bool old_check_ok ,
Oid parentDelTrigger , Oid parentUpdTrigger ,
bool with_period )
{
Oid deleteTriggerOid ,
updateTriggerOid ;
/*
* Create the action triggers that enforce the constraint .
*/
createForeignKeyActionTriggers ( rel , RelationGetRelid ( pkrel ) ,
fkconstraint ,
constrOid , indexOid ,
parentConstr , indexOid ,
parentDelTrigger , parentUpdTrigger ,
& deleteTriggerOid , & updateTriggerOid ) ;
@ -10297,6 +10396,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
AttrMap * map ;
AttrNumber * mapped_pkattnum ;
Oid partIndexId ;
ObjectAddress address ;
partRel = table_open ( pd - > oids [ i ] , ShareRowExclusiveLock ) ;
@ -10316,13 +10416,23 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
else
mapped_pkattnum = pkattnum ;
/* do the deed */
/* Determine the index to use at this level */
partIndexId = index_get_partition ( partRel , indexOid ) ;
if ( ! OidIsValid ( partIndexId ) )
elog ( ERROR , " index for %u not found in partition %s " ,
indexOid , RelationGetRelationName ( partRel ) ) ;
addFkRecurseReferenced ( wqueue , fkconstraint , rel , partRel ,
partIndexId , constrOid , numfks ,
/* Create entry at this level ... */
address = addFkConstraint ( addFkReferencedSide ,
fkconstraint - > conname , fkconstraint , rel ,
partRel , partIndexId , parentConstr ,
numfks , mapped_pkattnum ,
fkattnum , pfeqoperators , ppeqoperators ,
ffeqoperators , numfkdelsetcols ,
fkdelsetcols , true , with_period ) ;
/* ... and recurse to our children */
addFkRecurseReferenced ( fkconstraint , rel , partRel ,
partIndexId , address . objectId , numfks ,
mapped_pkattnum , fkattnum ,
pfeqoperators , ppeqoperators , ffeqoperators ,
numfkdelsetcols , fkdelsetcols ,
@ -10339,13 +10449,12 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
}
}
}
return address ;
}
/*
* addFkRecurseReferencing
* subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
* Recursive helper for the referencing side of foreign key creation ,
* which creates the check triggers and recurses
*
* If the referencing relation is a plain relation , create the necessary check
* triggers that implement the constraint , and set up for Phase 3 constraint
@ -10357,27 +10466,28 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
* deletions . If it ' s a partitioned relation , every partition must be so
* locked .
*
* wqueue is the ALTER TABLE work queue ; can be NULL when not running as part
* of an ALTER TABLE sequence .
* fkconstraint is the constraint being added .
* rel is the referencing relation ; might be a partition , if recursing .
* pkrel is the root referenced relation .
* indexOid is the OID of the index ( on pkrel ) implementing this constraint .
* parentConstr is the OID of the parent constraint ( there is always one ) .
* numfks is the number of columns in the foreign key
* pkattnum is the attnum array of referenced attributes .
* fkattnum is the attnum array of referencing attributes .
* pf / pp / ffeqoperators are OID array of operators between columns .
* numfkdelsetcols is the number of columns in the ON DELETE SET NULL / DEFAULT
* wqueue : the ALTER TABLE work queue ; NULL when not running as part
* of an ALTER TABLE sequence .
* fkconstraint : the constraint being added
* rel : the referencing relation ; might be a partition , if recursing
* pkrel : the root referenced relation
* indexOid : the OID of the index ( on pkrel ) implementing this constraint
* parentConstr : the OID of the parent constraint ( there is always one )
* numfks : the number of columns in the foreign key
* pkattnum : the attnum array of referenced attributes
* fkattnum : the attnum array of referencing attributes
* pf / pp / ffeqoperators : OID array of operators between columns
* numfkdelsetcols : the number of columns in the ON DELETE SET NULL / DEFAULT
* ( . . . ) clause
* fkdelsetcols is the attnum array of the columns in the ON DELETE SET
* fkdelsetcols : the attnum array of the columns in the ON DELETE SET
* NULL / DEFAULT clause
* 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 .
* old_check_ok : true if this constraint replaces an existing one that
* was already validated ( thus this one doesn ' t need validation )
* lockmode : the lockmode to acquire on partitions when recursing
* parentInsTrigger and parentUpdTrigger : when being recursively called on
* a partition , the OIDs of the parent check triggers for INSERT and
* UPDATE respectively .
* with_period : true if this is a temporal FK
*/
static void
addFkRecurseReferencing ( List * * wqueue , Constraint * fkconstraint , Relation rel ,
@ -10467,10 +10577,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
AttrMap * attmap ;
AttrNumber mapped_fkattnum [ INDEX_MAX_KEYS ] ;
bool attached ;
char * conname ;
Oid constrOid ;
ObjectAddress address ,
referenced ;
ObjectAddress address ;
ListCell * cell ;
CheckAlterTableIsSafe ( partition ) ;
@ -10513,66 +10620,19 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
/*
* No luck finding a good constraint to reuse ; create our own .
*/
if ( ConstraintNameIsUsed ( CONSTRAINT_RELATION ,
RelationGetRelid ( partition ) ,
fkconstraint - > conname ) )
conname = ChooseConstraintName ( RelationGetRelationName ( partition ) ,
ChooseForeignKeyConstraintNameAddition ( fkconstraint - > fk_attrs ) ,
" fkey " ,
RelationGetNamespace ( partition ) , NIL ) ;
else
conname = fkconstraint - > conname ;
constrOid =
CreateConstraintEntry ( conname ,
RelationGetNamespace ( partition ) ,
CONSTRAINT_FOREIGN ,
fkconstraint - > deferrable ,
fkconstraint - > initdeferred ,
fkconstraint - > initially_valid ,
parentConstr ,
partitionId ,
mapped_fkattnum ,
numfks ,
numfks ,
InvalidOid ,
indexOid ,
RelationGetRelid ( pkrel ) ,
pkattnum ,
pfeqoperators ,
ppeqoperators ,
ffeqoperators ,
numfks ,
fkconstraint - > fk_upd_action ,
fkconstraint - > fk_del_action ,
fkdelsetcols ,
numfkdelsetcols ,
fkconstraint - > fk_matchtype ,
NULL ,
NULL ,
NULL ,
false , /* conIsLocal */
1 , /* conInhCount */
false , /* conNoInherit */
with_period , /* conPeriod */
false ) ;
/*
* Give this constraint partition - type dependencies on the parent
* constraint as well as the table .
*/
ObjectAddressSet ( address , ConstraintRelationId , constrOid ) ;
ObjectAddressSet ( referenced , ConstraintRelationId , parentConstr ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_PRI ) ;
ObjectAddressSet ( referenced , RelationRelationId , partitionId ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_SEC ) ;
/* Make all this visible before recursing */
CommandCounterIncrement ( ) ;
address = addFkConstraint ( addFkReferencingSide ,
fkconstraint - > conname , fkconstraint ,
partition , pkrel , indexOid , parentConstr ,
numfks , pkattnum ,
mapped_fkattnum , pfeqoperators ,
ppeqoperators , ffeqoperators ,
numfkdelsetcols , fkdelsetcols , true ,
with_period ) ;
/* call ourselves to finalize the creation and we're done */
addFkRecurseReferencing ( wqueue , fkconstraint , partition , pkrel ,
indexOid ,
constrOi d,
address . objectId ,
numfks ,
pkattnum ,
mapped_fkattnum ,
@ -10706,6 +10766,7 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
int numfkdelsetcols ;
AttrNumber confdelsetcols [ INDEX_MAX_KEYS ] ;
Constraint * fkconstraint ;
ObjectAddress address ;
Oid deleteTriggerOid ,
updateTriggerOid ;
@ -10739,7 +10800,7 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
* Because we ' re only expanding the key space at the referenced side ,
* we don ' t need to prevent any operation in the referencing table , so
* AccessShareLock suffices ( assumes that dropping the constraint
* acquires AEL ) .
* acquires Access Exclusive Lock ) .
*/
fkRel = table_open ( constrForm - > conrelid , AccessShareLock ) ;
@ -10805,12 +10866,20 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
constrForm - > confrelid , constrForm - > conrelid ,
& deleteTriggerOid , & updateTriggerOid ) ;
addFkRecurseReferenced ( NULL ,
fkconstraint ,
/* Add this constraint ... */
address = addFkConstraint ( addFkReferencedSide ,
fkconstraint - > conname , fkconstraint , fkRel ,
partitionRel , partIndexId , constrOid ,
numfks , mapped_confkey ,
conkey , conpfeqop , conppeqop , conffeqop ,
numfkdelsetcols , confdelsetcols , false ,
constrForm - > conperiod ) ;
/* ... and recurse */
addFkRecurseReferenced ( fkconstraint ,
fkRel ,
partitionRel ,
partIndexId ,
constrOid ,
address . objectI d,
numfks ,
mapped_confkey ,
conkey ,
@ -10841,8 +10910,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
* child .
*
* If wqueue is given , it is used to set up phase - 3 verification for each
* cloned constraint ; if omitted , we assume that such verification is not
* needed ( example : the partition is being created anew ) .
* cloned constraint ; omit it if such verification is not needed
* ( example : the partition is being created anew ) .
*/
static void
CloneFkReferencing ( List * * wqueue , Relation parentRel , Relation partRel )
@ -10926,9 +10995,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Constraint * fkconstraint ;
bool attached ;
Oid indexOid ;
Oid constrOid ;
ObjectAddress address ,
referenced ;
ObjectAddress address ;
ListCell * lc ;
Oid insertTriggerOid ,
updateTriggerOid ;
@ -11026,7 +11093,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
fkconstraint - > old_conpfeqop = NIL ;
fkconstraint - > old_pktable_oid = InvalidOid ;
fkconstraint - > skip_validation = false ;
fkconstraint - > initially_valid = true ;
fkconstraint - > initially_valid = constrForm - > convalidated ;
for ( int i = 0 ; i < numfks ; i + + )
{
Form_pg_attribute att ;
@ -11036,73 +11103,30 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
fkconstraint - > fk_attrs = lappend ( fkconstraint - > fk_attrs ,
makeString ( NameStr ( att - > attname ) ) ) ;
}
if ( ConstraintNameIsUsed ( CONSTRAINT_RELATION ,
RelationGetRelid ( partRel ) ,
NameStr ( constrForm - > conname ) ) )
fkconstraint - > conname =
ChooseConstraintName ( RelationGetRelationName ( partRel ) ,
ChooseForeignKeyConstraintNameAddition ( fkconstraint - > fk_attrs ) ,
" fkey " ,
RelationGetNamespace ( partRel ) , NIL ) ;
else
fkconstraint - > conname = pstrdup ( NameStr ( constrForm - > conname ) ) ;
indexOid = constrForm - > conindid ;
with_period = constrForm - > conperiod ;
constrOid =
CreateConstraintEntry ( fkconstraint - > conname ,
constrForm - > connamespace ,
CONSTRAINT_FOREIGN ,
fkconstraint - > deferrable ,
fkconstraint - > initdeferred ,
constrForm - > convalidated ,
parentConstrOid ,
RelationGetRelid ( partRel ) ,
mapped_conkey ,
numfks ,
numfks ,
InvalidOid , /* not a domain constraint */
indexOid ,
constrForm - > confrelid , /* same foreign rel */
confkey ,
conpfeqop ,
conppeqop ,
conffeqop ,
numfks ,
fkconstraint - > fk_upd_action ,
fkconstraint - > fk_del_action ,
confdelsetcols ,
numfkdelsetcols ,
fkconstraint - > fk_matchtype ,
NULL ,
NULL ,
NULL ,
false , /* conIsLocal */
1 , /* conInhCount */
false , /* conNoInherit */
with_period , /* conPeriod */
true ) ;
/* Set up partition dependencies for the new constraint */
ObjectAddressSet ( address , ConstraintRelationId , constrOid ) ;
ObjectAddressSet ( referenced , ConstraintRelationId , parentConstrOid ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_PRI ) ;
ObjectAddressSet ( referenced , RelationRelationId ,
RelationGetRelid ( partRel ) ) ;
recordDependencyOn ( & address , & referenced , DEPENDENCY_PARTITION_SEC ) ;
/* Create the pg_constraint entry at this level */
address = addFkConstraint ( addFkReferencingSide ,
NameStr ( constrForm - > conname ) , fkconstraint ,
partRel , pkrel , indexOid , parentConstrOid ,
numfks , confkey ,
mapped_conkey , conpfeqop ,
conppeqop , conffeqop ,
numfkdelsetcols , confdelsetcols ,
false , with_period ) ;
/* Done with the cloned constraint's tuple */
ReleaseSysCache ( tuple ) ;
/* Make all this visible before recursing */
CommandCounterIncrement ( ) ;
/* Create the check triggers, and recurse to partitions, if any */
addFkRecurseReferencing ( wqueue ,
fkconstraint ,
partRel ,
pkrel ,
indexOid ,
constrOi d,
address . objectId ,
numfks ,
confkey ,
mapped_conkey ,
@ -11267,6 +11291,81 @@ tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
TriggerSetParentTrigger ( trigrel , updateTriggerOid , parentUpdTrigger ,
partRelid ) ;
/*
* If the referenced table is partitioned , then the partition we ' re
* attaching now has extra pg_constraint rows and action triggers that are
* no longer needed . Remove those .
*/
if ( get_rel_relkind ( fk - > confrelid ) = = RELKIND_PARTITIONED_TABLE )
{
Relation pg_constraint = table_open ( ConstraintRelationId , RowShareLock ) ;
ObjectAddresses * objs ;
HeapTuple consttup ;
ScanKeyInit ( & key ,
Anum_pg_constraint_conrelid ,
BTEqualStrategyNumber , F_OIDEQ ,
ObjectIdGetDatum ( fk - > conrelid ) ) ;
scan = systable_beginscan ( pg_constraint ,
ConstraintRelidTypidNameIndexId ,
true , NULL , 1 , & key ) ;
objs = new_object_addresses ( ) ;
while ( ( consttup = systable_getnext ( scan ) ) ! = NULL )
{
Form_pg_constraint conform = ( Form_pg_constraint ) GETSTRUCT ( consttup ) ;
if ( conform - > conparentid ! = fk - > conoid )
continue ;
else
{
ObjectAddress addr ;
SysScanDesc scan2 ;
ScanKeyData key2 ;
int n PG_USED_FOR_ASSERTS_ONLY ;
ObjectAddressSet ( addr , ConstraintRelationId , conform - > oid ) ;
add_exact_object_address ( & addr , objs ) ;
/*
* First we must delete the dependency record that binds the
* constraint records together .
*/
n = deleteDependencyRecordsForSpecific ( ConstraintRelationId ,
conform - > oid ,
DEPENDENCY_INTERNAL ,
ConstraintRelationId ,
fk - > conoid ) ;
Assert ( n = = 1 ) ; /* actually only one is expected */
/*
* Now search for the triggers for this constraint and set
* them up for deletion too
*/
ScanKeyInit ( & key2 ,
Anum_pg_trigger_tgconstraint ,
BTEqualStrategyNumber , F_OIDEQ ,
ObjectIdGetDatum ( conform - > oid ) ) ;
scan2 = systable_beginscan ( trigrel , TriggerConstraintIndexId ,
true , NULL , 1 , & key2 ) ;
while ( ( trigtup = systable_getnext ( scan2 ) ) ! = NULL )
{
ObjectAddressSet ( addr , TriggerRelationId ,
( ( Form_pg_trigger ) GETSTRUCT ( trigtup ) ) - > oid ) ;
add_exact_object_address ( & addr , objs ) ;
}
systable_endscan ( scan2 ) ;
}
}
/* make the dependency deletions visible */
CommandCounterIncrement ( ) ;
performMultipleDeletions ( objs , DROP_RESTRICT ,
PERFORM_DELETION_INTERNAL ) ;
systable_endscan ( scan ) ;
table_close ( pg_constraint , RowShareLock ) ;
}
CommandCounterIncrement ( ) ;
return true ;
}
@ -19337,9 +19436,9 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
foreach ( cell , fks )
{
ForeignKeyCacheInfo * fk = lfirst ( cell ) ;
HeapTuple contup ;
HeapTuple contup ,
parentConTup ;
Form_pg_constraint conform ;
Constraint * fkconstraint ;
Oid insertTriggerOid ,
updateTriggerOid ;
@ -19356,7 +19455,17 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
continue ;
}
/* unset conparentid and adjust conislocal, coninhcount, etc. */
Assert ( OidIsValid ( conform - > conparentid ) ) ;
parentConTup = SearchSysCache1 ( CONSTROID ,
ObjectIdGetDatum ( conform - > conparentid ) ) ;
if ( ! HeapTupleIsValid ( parentConTup ) )
elog ( ERROR , " cache lookup failed for constraint %u " ,
conform - > conparentid ) ;
/*
* The constraint on this table must be marked no longer a child of
* the parent ' s constraint , as do its check triggers .
*/
ConstraintSetParentConstraint ( fk - > conoid , InvalidOid , InvalidOid ) ;
/*
@ -19374,35 +19483,93 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
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 .
* Lastly , create the action triggers on the referenced table , using
* addFkRecurseReferenced , which requires some elaborate setup ( so put
* it in a separate block ) . While at it , if the table is partitioned ,
* that function will recurse to create the pg_constraint rows and
* action triggers for each partition .
*
* Note there ' s no need to do addFkConstraint ( ) here , because the
* pg_constraint row already exists .
*/
fkconstraint = makeNode ( Constraint ) ;
fkconstraint - > contype = CONSTRAINT_FOREIGN ;
fkconstraint - > conname = pstrdup ( NameStr ( conform - > conname ) ) ;
fkconstraint - > deferrable = conform - > condeferrable ;
fkconstraint - > initdeferred = conform - > condeferred ;
fkconstraint - > location = - 1 ;
fkconstraint - > pktable = NULL ;
fkconstraint - > fk_attrs = NIL ;
fkconstraint - > pk_attrs = NIL ;
fkconstraint - > fk_matchtype = conform - > confmatchtype ;
fkconstraint - > fk_upd_action = conform - > confupdtype ;
fkconstraint - > fk_del_action = conform - > confdeltype ;
fkconstraint - > fk_del_set_cols = NIL ;
fkconstraint - > old_conpfeqop = NIL ;
fkconstraint - > old_pktable_oid = InvalidOid ;
fkconstraint - > skip_validation = false ;
fkconstraint - > initially_valid = true ;
{
Constraint * fkconstraint ;
int numfks ;
AttrNumber conkey [ INDEX_MAX_KEYS ] ;
AttrNumber confkey [ INDEX_MAX_KEYS ] ;
Oid conpfeqop [ INDEX_MAX_KEYS ] ;
Oid conppeqop [ INDEX_MAX_KEYS ] ;
Oid conffeqop [ INDEX_MAX_KEYS ] ;
int numfkdelsetcols ;
AttrNumber confdelsetcols [ INDEX_MAX_KEYS ] ;
AttrMap * attmap ;
Relation refdRel ;
DeconstructFkConstraintRow ( contup ,
& numfks ,
conkey ,
confkey ,
conpfeqop ,
conppeqop ,
conffeqop ,
& numfkdelsetcols ,
confdelsetcols ) ;
/* Create a synthetic node we'll use throughout */
fkconstraint = makeNode ( Constraint ) ;
fkconstraint - > contype = CONSTRAINT_FOREIGN ;
fkconstraint - > conname = pstrdup ( NameStr ( conform - > conname ) ) ;
fkconstraint - > deferrable = conform - > condeferrable ;
fkconstraint - > initdeferred = conform - > condeferred ;
fkconstraint - > skip_validation = true ;
fkconstraint - > initially_valid = true ;
/* a few irrelevant fields omitted here */
fkconstraint - > pktable = NULL ;
fkconstraint - > fk_attrs = NIL ;
fkconstraint - > pk_attrs = NIL ;
fkconstraint - > fk_matchtype = conform - > confmatchtype ;
fkconstraint - > fk_upd_action = conform - > confupdtype ;
fkconstraint - > fk_del_action = conform - > confdeltype ;
fkconstraint - > fk_del_set_cols = NIL ;
fkconstraint - > old_conpfeqop = NIL ;
fkconstraint - > old_pktable_oid = InvalidOid ;
fkconstraint - > location = - 1 ;
attmap = build_attrmap_by_name ( RelationGetDescr ( partRel ) ,
RelationGetDescr ( rel ) ,
false ) ;
for ( int i = 0 ; i < numfks ; i + + )
{
Form_pg_attribute att ;
createForeignKeyActionTriggers ( partRel , conform - > confrelid ,
fkconstraint , fk - > conoid ,
conform - > conindid ,
InvalidOid , InvalidOid ,
NULL , NULL ) ;
att = TupleDescAttr ( RelationGetDescr ( partRel ) ,
attmap - > attnums [ conkey [ i ] - 1 ] - 1 ) ;
fkconstraint - > fk_attrs = lappend ( fkconstraint - > fk_attrs ,
makeString ( NameStr ( att - > attname ) ) ) ;
}
refdRel = table_open ( fk - > confrelid , AccessShareLock ) ;
addFkRecurseReferenced ( fkconstraint , partRel ,
refdRel ,
conform - > conindid ,
fk - > conoid ,
numfks ,
confkey ,
conkey ,
conpfeqop ,
conppeqop ,
conffeqop ,
numfkdelsetcols ,
confdelsetcols ,
true ,
InvalidOid , InvalidOid ,
conform - > conperiod ) ;
table_close ( refdRel , AccessShareLock ) ;
}
ReleaseSysCache ( contup ) ;
ReleaseSysCache ( parentConTup ) ;
}
list_free_deep ( fks ) ;
if ( trigrel )