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,
}
/*
- * 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))
{
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);
}
}
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));
*
* 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 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
- * 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 we
+ * 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 table, 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),
- AccessExclusiveLock, NULL);
+ ShareLock, NULL);
if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
/*
* 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,
"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);