@ -480,10 +480,9 @@ static void RemoveInheritance(Relation child_rel, Relation parent_rel);
static ObjectAddress ATExecAttachPartition ( List * * wqueue , Relation rel ,
PartitionCmd * cmd ) ;
static void AttachPartitionEnsureIndexes ( Relation rel , Relation attachrel ) ;
static void ValidatePartitionConstraints ( List * * wqueue , Relation scanrel ,
List * scanrel_children ,
List * partConstraint ,
bool validate_default ) ;
static void QueuePartitionConstraintValidation ( List * * wqueue , Relation scanrel ,
List * partConstraint ,
bool validate_default ) ;
static void CloneRowTriggersToPartition ( Relation parent , Relation partition ) ;
static ObjectAddress ATExecDetachPartition ( Relation rel , RangeVar * name ) ;
static ObjectAddress ATExecAttachPartitionIdx ( List * * wqueue , Relation rel ,
@ -13939,29 +13938,23 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
}
/*
* ValidatePartitionConstraints
* QueuePartitionConstraintValidation
*
* Check whether all rows in the given table obey the given partition
* constraint ; if so , it can be attached as a partition . We do this by
* scanning the table ( or all of its leaf partitions ) row by row , except when
* the existing constraints are sufficient to prove that the new partitioning
* constraint must already hold .
* Add an entry to wqueue to have the given partition constraint validated by
* Phase 3 , for the given relation , and all its children .
*
* We first verify whether the given constraint is implied by pre - existing
* relation constraints ; if it is , there ' s no need to scan the table to
* validate , so don ' t queue in that case .
*/
static void
ValidatePartitionConstraints ( List * * wqueue , Relation scanrel ,
List * scanrel_children ,
List * partConstraint ,
bool validate_default )
QueuePartitionConstraintValidation ( List * * wqueue , Relation scanrel ,
List * partConstraint ,
bool validate_default )
{
bool found_whole_row ;
ListCell * lc ;
if ( partConstraint = = NIL )
return ;
/*
* Based on the table ' s existing constraints , determine if we can skip
* scanning the table to validate the partition constraint .
* Based on the table ' s existing constraints , determine whether or not we
* may skip scanning the table .
*/
if ( PartConstraintImpliedByRelConstraint ( scanrel , partConstraint ) )
{
@ -13976,68 +13969,53 @@ ValidatePartitionConstraints(List **wqueue, Relation scanrel,
return ;
}
/* Constraints proved insufficient, so we need to scan the table. */
foreach ( lc , scanrel_children )
/*
* Constraints proved insufficient . For plain relations , queue a validation
* item now ; for partitioned tables , recurse to process each partition .
*/
if ( scanrel - > rd_rel - > relkind = = RELKIND_RELATION )
{
AlteredTableInfo * tab ;
Oid part_relid = lfirst_oid ( lc ) ;
Relation part_rel ;
List * my_partconstr = partConstraint ;
/* Lock already taken */
if ( part_relid ! = RelationGetRelid ( scanrel ) )
part_rel = heap_open ( part_relid , NoLock ) ;
else
part_rel = scanrel ;
/* Grab a work queue entry. */
tab = ATGetQueueEntry ( wqueue , scanrel ) ;
Assert ( tab - > partition_constraint = = NULL ) ;
tab - > partition_constraint = ( Expr * ) linitial ( partConstraint ) ;
tab - > validate_default = validate_default ;
}
else if ( scanrel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE )
{
PartitionDesc partdesc = RelationGetPartitionDesc ( scanrel ) ;
int i ;
/*
* Skip if the partition is itself a partitioned table . We can only
* ever scan RELKIND_RELATION relations .
*/
if ( part_rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE )
for ( i = 0 ; i < partdesc - > nparts ; i + + )
{
if ( part_rel ! = scanrel )
heap_close ( part_rel , NoLock ) ;
continue ;
}
Relation part_rel ;
bool found_whole_row ;
List * thisPartConstraint ;
/*
* This is the minimum lock we need to prevent concurrent data
* additions .
*/
part_rel = heap_open ( partdesc - > oids [ i ] , ShareLock ) ;
if ( part_rel ! = scanrel )
{
/*
* Adjust the constraint for scanrel so that it matches this
* partition ' s attribute numbers .
*/
my_partconstr = map_partition_varattnos ( my_partconstr , 1 ,
part_rel , scanrel ,
& found_whole_row ) ;
thisPartConstraint =
map_partition_varattnos ( partConstraint , 1 ,
part_rel , scanrel , & found_whole_row ) ;
/* There can never be a whole-row reference here */
if ( found_whole_row )
elog ( ERROR , " unexpected whole-row reference found in partition key " ) ;
elog ( ERROR , " unexpected whole-row reference found in partition constraint " ) ;
/* Can we skip scanning this part_rel? */
if ( PartConstraintImpliedByRelConstraint ( part_rel , my_partconstr ) )
{
if ( ! validate_default )
ereport ( INFO ,
( errmsg ( " partition constraint for table \" %s \" is implied by existing constraints " ,
RelationGetRelationName ( part_rel ) ) ) ) ;
else
ereport ( INFO ,
( errmsg ( " updated partition constraint for default partition \" %s \" is implied by existing constraints " ,
RelationGetRelationName ( part_rel ) ) ) ) ;
heap_close ( part_rel , NoLock ) ;
continue ;
}
QueuePartitionConstraintValidation ( wqueue , part_rel ,
thisPartConstraint ,
validate_default ) ;
heap_close ( part_rel , NoLock ) ; /* keep lock till commit */
}
/* Grab a work queue entry. */
tab = ATGetQueueEntry ( wqueue , part_rel ) ;
tab - > partition_constraint = ( Expr * ) linitial ( my_partconstr ) ;
tab - > validate_default = validate_default ;
/* keep our lock until commit */
if ( part_rel ! = scanrel )
heap_close ( part_rel , NoLock ) ;
}
}
@ -14067,8 +14045,8 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
ListCell * l ;
/*
* We must lock the default partition , because attaching a new partition
* will change its partition constraint .
* We must lock the default partition if one exists , because attaching a
* new partition will change its partition constraint .
*/
defaultPartOid =
get_default_oid_from_partdesc ( RelationGetPartitionDesc ( rel ) ) ;
@ -14133,17 +14111,17 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
*
* We do that by checking if rel is a member of the list of attachrel ' s
* partitions provided the latter is partitioned at all . We want to avoid
* having to construct this list again , so we request the strongest lock
* on all partitions . We need the strongest lock , because we may decid e
* to scan them if we find out that the table being attached ( or its leaf
* partitions ) may contain rows that violate the partition constraint . If
* the table has a constraint that would prevent such rows , which by
* definition is present in all the partitions , we need not scan the
* table , nor its partitions . But we cannot risk a deadlock by taking a
* weaker lock now and the stronger one only when needed .
* having to construct this list again , so we request a lock on all
* partitions . We need ShareLock , preventing data changes , because w e
* may decide to scan them if we find out that the table being attached ( or
* its leaf partitions ) may contain rows that violate the partition
* constraint . If the table has a constraint that would prevent such rows ,
* which by definition is present in all the partitions , we need not scan
* the t able , nor its partitions . But we cannot risk a deadlock by taking
* a weaker lock now and the stronger one only when needed .
*/
attachrel_children = find_all_inheritors ( RelationGetRelid ( attachrel ) ,
AccessExclusiv eLock, NULL ) ;
Shar eLock, NULL ) ;
if ( list_member_oid ( attachrel_children , RelationGetRelid ( rel ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DUPLICATE_TABLE ) ,
@ -14291,9 +14269,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
/*
* Run the partition quals through const - simplification similar to
* check constraints . We skip canonicalize_qual , though , because
* partition quals should be in canonical form already ; also , since
* the qual is in implicit - AND format , we ' d have to explicitly convert
* it to explicit - AND format and back again .
* partition quals should be in canonical form already .
*/
partConstraint =
( List * ) eval_const_expressions ( NULL ,
@ -14314,32 +14290,30 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
" unexpected whole-row reference found in partition key " ) ;
/* Validate partition constraints against the table being attached. */
ValidatePartitionConstraints ( wqueue , attachrel , attachrel_children ,
partConstraint , false ) ;
QueuePartitionConstraintValidation ( wqueue , attachrel , partConstraint ,
false ) ;
}
/*
* Check whether default partition has a row that would fit the partition
* being attached .
* If we ' re attaching a partition other than the default partition and a
* default one exists , then that partition ' s partition constraint changes ,
* so add an entry to the work queue to validate it , too . ( We must not
* do this when the partition being attached is the default one ; we
* already did it above ! )
*/
defaultPartOid =
get_default_oid_from_partdesc ( RelationGetPartitionDesc ( rel ) ) ;
if ( OidIsValid ( defaultPartOid ) )
{
Relation defaultrel ;
List * defaultrel_children ;
List * defPartConstraint ;
/* We already have taken a lock on default partition. */
Assert ( ! cmd - > bound - > is_default ) ;
/* we already hold a lock on the default partition */
defaultrel = heap_open ( defaultPartOid , NoLock ) ;
defPartConstraint =
get_proposed_default_constraint ( partBoundConstraint ) ;
defaultrel_children =
find_all_inheritors ( defaultPartOid ,
AccessExclusiveLock , NULL ) ;
ValidatePartitionConstraints ( wqueue , defaultrel ,
defaultrel_children ,
defPartConstraint , true ) ;
QueuePartitionConstraintValidation ( wqueue , defaultrel ,
defPartConstraint , true ) ;
/* keep our lock until commit. */
heap_close ( defaultrel , NoLock ) ;