@@ -480,10 +480,9 @@ static void RemoveInheritance(Relation child_rel, Relation parent_rel);
480
480
static ObjectAddress ATExecAttachPartition (List * * wqueue , Relation rel ,
481
481
PartitionCmd * cmd );
482
482
static 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 );
487
486
static void CloneRowTriggersToPartition (Relation parent , Relation partition );
488
487
static ObjectAddress ATExecDetachPartition (Relation rel , RangeVar * name );
489
488
static ObjectAddress ATExecAttachPartitionIdx (List * * wqueue , Relation rel ,
@@ -13939,29 +13938,23 @@ PartConstraintImpliedByRelConstraint(Relation scanrel,
13939
13938
}
13940
13939
13941
13940
/*
13942
- * ValidatePartitionConstraints
13941
+ * QueuePartitionConstraintValidation
13943
13942
*
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.
13949
13949
*/
13950
13950
static 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 )
13955
13954
{
13956
- bool found_whole_row ;
13957
- ListCell * lc ;
13958
-
13959
- if (partConstraint == NIL )
13960
- return ;
13961
-
13962
13955
/*
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 .
13965
13958
*/
13966
13959
if (PartConstraintImpliedByRelConstraint (scanrel , partConstraint ))
13967
13960
{
@@ -13976,68 +13969,53 @@ ValidatePartitionConstraints(List **wqueue, Relation scanrel,
13976
13969
return ;
13977
13970
}
13978
13971
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 )
13981
13977
{
13982
13978
AlteredTableInfo * tab ;
13983
- Oid part_relid = lfirst_oid (lc );
13984
- Relation part_rel ;
13985
- List * my_partconstr = partConstraint ;
13986
13979
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 ;
13992
13990
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 ++ )
13998
13992
{
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 );
14003
14002
14004
- if (part_rel != scanrel )
14005
- {
14006
14003
/*
14007
14004
* Adjust the constraint for scanrel so that it matches this
14008
14005
* partition's attribute numbers.
14009
14006
*/
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 );
14013
14010
/* There can never be a whole-row reference here */
14014
14011
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 " );
14016
14013
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 */
14031
14018
}
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 );
14041
14019
}
14042
14020
}
14043
14021
@@ -14067,8 +14045,8 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
14067
14045
ListCell * l ;
14068
14046
14069
14047
/*
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.
14072
14050
*/
14073
14051
defaultPartOid =
14074
14052
get_default_oid_from_partdesc (RelationGetPartitionDesc (rel ));
@@ -14133,17 +14111,17 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
14133
14111
*
14134
14112
* We do that by checking if rel is a member of the list of attachrel's
14135
14113
* 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.
14144
14122
*/
14145
14123
attachrel_children = find_all_inheritors (RelationGetRelid (attachrel ),
14146
- AccessExclusiveLock , NULL );
14124
+ ShareLock , NULL );
14147
14125
if (list_member_oid (attachrel_children , RelationGetRelid (rel )))
14148
14126
ereport (ERROR ,
14149
14127
(errcode (ERRCODE_DUPLICATE_TABLE ),
@@ -14291,9 +14269,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
14291
14269
/*
14292
14270
* Run the partition quals through const-simplification similar to
14293
14271
* 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.
14297
14273
*/
14298
14274
partConstraint =
14299
14275
(List * ) eval_const_expressions (NULL ,
@@ -14314,32 +14290,30 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
14314
14290
"unexpected whole-row reference found in partition key" );
14315
14291
14316
14292
/* Validate partition constraints against the table being attached. */
14317
- ValidatePartitionConstraints (wqueue , attachrel , attachrel_children ,
14318
- partConstraint , false);
14293
+ QueuePartitionConstraintValidation (wqueue , attachrel , partConstraint ,
14294
+ false);
14319
14295
}
14320
14296
14321
14297
/*
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!)
14324
14303
*/
14325
- defaultPartOid =
14326
- get_default_oid_from_partdesc (RelationGetPartitionDesc (rel ));
14327
14304
if (OidIsValid (defaultPartOid ))
14328
14305
{
14329
14306
Relation defaultrel ;
14330
- List * defaultrel_children ;
14331
14307
List * defPartConstraint ;
14332
14308
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 */
14334
14312
defaultrel = heap_open (defaultPartOid , NoLock );
14335
14313
defPartConstraint =
14336
14314
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);
14343
14317
14344
14318
/* keep our lock until commit. */
14345
14319
heap_close (defaultrel , NoLock );
0 commit comments