@@ -480,10 +480,9 @@ static void RemoveInheritance(Relation child_rel, Relation parent_rel);
480480static ObjectAddress ATExecAttachPartition (List * * wqueue , Relation rel ,
481481 PartitionCmd * cmd );
482482static void AttachPartitionEnsureIndexes (Relation rel , Relation attachrel );
483- static void ValidatePartitionConstraints (List * * wqueue , Relation scanrel ,
484- List * scanrel_children ,
485- List * partConstraint ,
486- bool validate_default );
483+ static void QueuePartitionConstraintValidation (List * * wqueue , Relation scanrel ,
484+ List * partConstraint ,
485+ bool validate_default );
487486static void CloneRowTriggersToPartition (Relation parent , Relation partition );
488487static ObjectAddress ATExecDetachPartition (Relation rel , RangeVar * name );
489488static ObjectAddress ATExecAttachPartitionIdx (List * * wqueue , Relation rel ,
@@ -13939,29 +13938,23 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
1393913938}
1394013939
1394113940/*
13942- * ValidatePartitionConstraints
13941+ * QueuePartitionConstraintValidation
1394313942 *
13944- * Check whether all rows in the given table obey the given partition
13945- * constraint; if so, it can be attached as a partition. We do this by
13946- * scanning the table (or all of its leaf partitions) row by row, except when
13947- * the existing constraints are sufficient to prove that the new partitioning
13948- * constraint must already hold.
13943+ * Add an entry to wqueue to have the given partition constraint validated by
13944+ * Phase 3, for the given relation, and all its children.
13945+ *
13946+ * We first verify whether the given constraint is implied by pre-existing
13947+ * relation constraints; if it is, there's no need to scan the table to
13948+ * validate, so don't queue in that case.
1394913949 */
1395013950static void
13951- ValidatePartitionConstraints (List * * wqueue , Relation scanrel ,
13952- List * scanrel_children ,
13953- List * partConstraint ,
13954- bool validate_default )
13951+ QueuePartitionConstraintValidation (List * * wqueue , Relation scanrel ,
13952+ List * partConstraint ,
13953+ bool validate_default )
1395513954{
13956- bool found_whole_row ;
13957- ListCell * lc ;
13958-
13959- if (partConstraint == NIL )
13960- return ;
13961-
1396213955 /*
13963- * Based on the table's existing constraints, determine if we can skip
13964- * scanning the table to validate the partition constraint .
13956+ * Based on the table's existing constraints, determine whether or not we
13957+ * may skip scanning the table .
1396513958 */
1396613959 if (PartConstraintImpliedByRelConstraint (scanrel , partConstraint ))
1396713960 {
@@ -13976,68 +13969,53 @@ ValidatePartitionConstraints(List **wqueue, Relation scanrel,
1397613969 return ;
1397713970 }
1397813971
13979- /* Constraints proved insufficient, so we need to scan the table. */
13980- foreach (lc , scanrel_children )
13972+ /*
13973+ * Constraints proved insufficient. For plain relations, queue a validation
13974+ * item now; for partitioned tables, recurse to process each partition.
13975+ */
13976+ if (scanrel -> rd_rel -> relkind == RELKIND_RELATION )
1398113977 {
1398213978 AlteredTableInfo * tab ;
13983- Oid part_relid = lfirst_oid (lc );
13984- Relation part_rel ;
13985- List * my_partconstr = partConstraint ;
1398613979
13987- /* Lock already taken */
13988- if (part_relid != RelationGetRelid (scanrel ))
13989- part_rel = heap_open (part_relid , NoLock );
13990- else
13991- part_rel = scanrel ;
13980+ /* Grab a work queue entry. */
13981+ tab = ATGetQueueEntry (wqueue , scanrel );
13982+ Assert (tab -> partition_constraint == NULL );
13983+ tab -> partition_constraint = (Expr * ) linitial (partConstraint );
13984+ tab -> validate_default = validate_default ;
13985+ }
13986+ else if (scanrel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
13987+ {
13988+ PartitionDesc partdesc = RelationGetPartitionDesc (scanrel );
13989+ int i ;
1399213990
13993- /*
13994- * Skip if the partition is itself a partitioned table. We can only
13995- * ever scan RELKIND_RELATION relations.
13996- */
13997- if (part_rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
13991+ for (i = 0 ; i < partdesc -> nparts ; i ++ )
1399813992 {
13999- if (part_rel != scanrel )
14000- heap_close (part_rel , NoLock );
14001- continue ;
14002- }
13993+ Relation part_rel ;
13994+ bool found_whole_row ;
13995+ List * thisPartConstraint ;
13996+
13997+ /*
13998+ * This is the minimum lock we need to prevent concurrent data
13999+ * additions.
14000+ */
14001+ part_rel = heap_open (partdesc -> oids [i ], ShareLock );
1400314002
14004- if (part_rel != scanrel )
14005- {
1400614003 /*
1400714004 * Adjust the constraint for scanrel so that it matches this
1400814005 * partition's attribute numbers.
1400914006 */
14010- my_partconstr = map_partition_varattnos ( my_partconstr , 1 ,
14011- part_rel , scanrel ,
14012- & found_whole_row );
14007+ thisPartConstraint =
14008+ map_partition_varattnos ( partConstraint , 1 ,
14009+ part_rel , scanrel , & found_whole_row );
1401314010 /* There can never be a whole-row reference here */
1401414011 if (found_whole_row )
14015- elog (ERROR , "unexpected whole-row reference found in partition key " );
14012+ elog (ERROR , "unexpected whole-row reference found in partition constraint " );
1401614013
14017- /* Can we skip scanning this part_rel? */
14018- if (PartConstraintImpliedByRelConstraint (part_rel , my_partconstr ))
14019- {
14020- if (!validate_default )
14021- ereport (INFO ,
14022- (errmsg ("partition constraint for table \"%s\" is implied by existing constraints" ,
14023- RelationGetRelationName (part_rel ))));
14024- else
14025- ereport (INFO ,
14026- (errmsg ("updated partition constraint for default partition \"%s\" is implied by existing constraints" ,
14027- RelationGetRelationName (part_rel ))));
14028- heap_close (part_rel , NoLock );
14029- continue ;
14030- }
14014+ QueuePartitionConstraintValidation (wqueue , part_rel ,
14015+ thisPartConstraint ,
14016+ validate_default );
14017+ heap_close (part_rel , NoLock ); /* keep lock till commit */
1403114018 }
14032-
14033- /* Grab a work queue entry. */
14034- tab = ATGetQueueEntry (wqueue , part_rel );
14035- tab -> partition_constraint = (Expr * ) linitial (my_partconstr );
14036- tab -> validate_default = validate_default ;
14037-
14038- /* keep our lock until commit */
14039- if (part_rel != scanrel )
14040- heap_close (part_rel , NoLock );
1404114019 }
1404214020}
1404314021
@@ -14067,8 +14045,8 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1406714045 ListCell * l ;
1406814046
1406914047 /*
14070- * We must lock the default partition, because attaching a new partition
14071- * will change its partition constraint.
14048+ * We must lock the default partition if one exists , because attaching a
14049+ * new partition will change its partition constraint.
1407214050 */
1407314051 defaultPartOid =
1407414052 get_default_oid_from_partdesc (RelationGetPartitionDesc (rel ));
@@ -14133,17 +14111,17 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1413314111 *
1413414112 * We do that by checking if rel is a member of the list of attachrel's
1413514113 * partitions provided the latter is partitioned at all. We want to avoid
14136- * having to construct this list again, so we request the strongest lock
14137- * on all partitions. We need the strongest lock , because we may decide
14138- * to scan them if we find out that the table being attached (or its leaf
14139- * partitions) may contain rows that violate the partition constraint. If
14140- * the table has a constraint that would prevent such rows, which by
14141- * definition is present in all the partitions, we need not scan the
14142- * table, nor its partitions. But we cannot risk a deadlock by taking a
14143- * weaker lock now and the stronger one only when needed.
14114+ * having to construct this list again, so we request a lock on all
14115+ * partitions. We need ShareLock, preventing data changes , because we
14116+ * may decide to scan them if we find out that the table being attached (or
14117+ * its leaf partitions) may contain rows that violate the partition
14118+ * constraint. If the table has a constraint that would prevent such rows,
14119+ * which by definition is present in all the partitions, we need not scan
14120+ * the table, nor its partitions. But we cannot risk a deadlock by taking
14121+ * a weaker lock now and the stronger one only when needed.
1414414122 */
1414514123 attachrel_children = find_all_inheritors (RelationGetRelid (attachrel ),
14146- AccessExclusiveLock , NULL );
14124+ ShareLock , NULL );
1414714125 if (list_member_oid (attachrel_children , RelationGetRelid (rel )))
1414814126 ereport (ERROR ,
1414914127 (errcode (ERRCODE_DUPLICATE_TABLE ),
@@ -14291,9 +14269,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1429114269 /*
1429214270 * Run the partition quals through const-simplification similar to
1429314271 * check constraints. We skip canonicalize_qual, though, because
14294- * partition quals should be in canonical form already; also, since
14295- * the qual is in implicit-AND format, we'd have to explicitly convert
14296- * it to explicit-AND format and back again.
14272+ * partition quals should be in canonical form already.
1429714273 */
1429814274 partConstraint =
1429914275 (List * ) eval_const_expressions (NULL ,
@@ -14314,32 +14290,30 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
1431414290 "unexpected whole-row reference found in partition key" );
1431514291
1431614292 /* Validate partition constraints against the table being attached. */
14317- ValidatePartitionConstraints (wqueue , attachrel , attachrel_children ,
14318- partConstraint , false);
14293+ QueuePartitionConstraintValidation (wqueue , attachrel , partConstraint ,
14294+ false);
1431914295 }
1432014296
1432114297 /*
14322- * Check whether default partition has a row that would fit the partition
14323- * being attached.
14298+ * If we're attaching a partition other than the default partition and a
14299+ * default one exists, then that partition's partition constraint changes,
14300+ * so add an entry to the work queue to validate it, too. (We must not
14301+ * do this when the partition being attached is the default one; we
14302+ * already did it above!)
1432414303 */
14325- defaultPartOid =
14326- get_default_oid_from_partdesc (RelationGetPartitionDesc (rel ));
1432714304 if (OidIsValid (defaultPartOid ))
1432814305 {
1432914306 Relation defaultrel ;
14330- List * defaultrel_children ;
1433114307 List * defPartConstraint ;
1433214308
14333- /* We already have taken a lock on default partition. */
14309+ Assert (!cmd -> bound -> is_default );
14310+
14311+ /* we already hold a lock on the default partition */
1433414312 defaultrel = heap_open (defaultPartOid , NoLock );
1433514313 defPartConstraint =
1433614314 get_proposed_default_constraint (partBoundConstraint );
14337- defaultrel_children =
14338- find_all_inheritors (defaultPartOid ,
14339- AccessExclusiveLock , NULL );
14340- ValidatePartitionConstraints (wqueue , defaultrel ,
14341- defaultrel_children ,
14342- defPartConstraint , true);
14315+ QueuePartitionConstraintValidation (wqueue , defaultrel ,
14316+ defPartConstraint , true);
1434314317
1434414318 /* keep our lock until commit. */
1434514319 heap_close (defaultrel , NoLock );
0 commit comments