List *idxExprs);
static List *get_relation_constraints(PlannerInfo *root,
Oid relationObjectId, RelOptInfo *rel,
- bool include_notnull);
+ bool include_noinherit,
+ bool include_notnull,
+ bool include_partition);
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
Relation heapRelation);
static List *get_relation_statistics(RelOptInfo *rel, Relation relation);
/*
* get_relation_constraints
*
- * Retrieve the validated CHECK constraint expressions of the given relation.
+ * Retrieve the applicable constraint expressions of the given relation.
*
* Returns a List (possibly empty) of constraint expressions. Each one
* has been canonicalized, and its Vars are changed to have the varno
* indicated by rel->relid. This allows the expressions to be easily
* compared to expressions taken from WHERE.
*
+ * If include_noinherit is true, it's okay to include constraints that
+ * are marked NO INHERIT.
+ *
* If include_notnull is true, "col IS NOT NULL" expressions are generated
* and added to the result for each column that's marked attnotnull.
*
+ * If include_partition is true, and the relation is a partition,
+ * also include the partitioning constraints.
+ *
* Note: at present this is invoked at most once per relation per planner
* run, and in many cases it won't be invoked at all, so there seems no
* point in caching the data in RelOptInfo.
static List *
get_relation_constraints(PlannerInfo *root,
Oid relationObjectId, RelOptInfo *rel,
- bool include_notnull)
+ bool include_noinherit,
+ bool include_notnull,
+ bool include_partition)
{
List *result = NIL;
Index varno = rel->relid;
/*
* If this constraint hasn't been fully validated yet, we must
- * ignore it here.
+ * ignore it here. Also ignore if NO INHERIT and we weren't told
+ * that that's safe.
*/
if (!constr->check[i].ccvalid)
continue;
+ if (constr->check[i].ccnoinherit && !include_noinherit)
+ continue;
cexpr = stringToNode(constr->check[i].ccbin);
}
/*
- * Append partition predicates, if any.
- *
- * For selects, partition pruning uses the parent table's partition bound
- * descriptor, instead of constraint exclusion which is driven by the
- * individual partition's partition constraint.
+ * Add partitioning constraints, if requested.
*/
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
+ if (include_partition && relation->rd_rel->relispartition)
{
List *pcqual = RelationGetPartitionQual(relation);
*
* Detect whether the relation need not be scanned because it has either
* self-inconsistent restrictions, or restrictions inconsistent with the
- * relation's validated CHECK constraints.
+ * relation's applicable constraints.
*
* Note: this examines only rel->relid, rel->reloptkind, and
* rel->baserestrictinfo; therefore it can be called before filling in
relation_excluded_by_constraints(PlannerInfo *root,
RelOptInfo *rel, RangeTblEntry *rte)
{
+ bool include_noinherit;
+ bool include_notnull;
+ bool include_partition = false;
List *safe_restrictions;
List *constraint_pred;
List *safe_constraints;
/* As of now, constraint exclusion works only with simple relations. */
Assert(IS_SIMPLE_REL(rel));
+ /*
+ * If there are no base restriction clauses, we have no hope of proving
+ * anything below, so fall out quickly.
+ */
+ if (rel->baserestrictinfo == NIL)
+ return false;
+
/*
* Regardless of the setting of constraint_exclusion, detect
* constant-FALSE-or-NULL restriction clauses. Because const-folding will
switch (constraint_exclusion)
{
case CONSTRAINT_EXCLUSION_OFF:
-
- /*
- * Don't prune if feature turned off -- except if the relation is
- * a partition. While partprune.c-style partition pruning is not
- * yet in use for all cases (update/delete is not handled), it
- * would be a UI horror to use different user-visible controls
- * depending on such a volatile implementation detail. Therefore,
- * for partitioned tables we use enable_partition_pruning to
- * control this behavior.
- */
- if (root->inhTargetKind == INHKIND_PARTITIONED)
- break;
+ /* In 'off' mode, never make any further tests */
return false;
case CONSTRAINT_EXCLUSION_PARTITION:
/*
* When constraint_exclusion is set to 'partition' we only handle
- * OTHER_MEMBER_RELs, or BASERELs in cases where the result target
- * is an inheritance parent or a partitioned table.
+ * appendrel members. Normally, they are RELOPT_OTHER_MEMBER_REL
+ * relations, but we also consider inherited target relations as
+ * appendrel members for the purposes of constraint exclusion
+ * (since, indeed, they were appendrel members earlier in
+ * inheritance_planner).
+ *
+ * In both cases, partition pruning was already applied, so there
+ * is no need to consider the rel's partition constraints here.
*/
- if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&
- !(rel->reloptkind == RELOPT_BASEREL &&
- root->inhTargetKind != INHKIND_NONE &&
- rel->relid == root->parse->resultRelation))
- return false;
- break;
+ if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL ||
+ (rel->relid == root->parse->resultRelation &&
+ root->inhTargetKind != INHKIND_NONE))
+ break; /* appendrel member, so process it */
+ return false;
case CONSTRAINT_EXCLUSION_ON:
+
+ /*
+ * In 'on' mode, always apply constraint exclusion. If we are
+ * considering a baserel that is a partition (i.e., it was
+ * directly named rather than expanded from a parent table), then
+ * its partition constraints haven't been considered yet, so
+ * include them in the processing here.
+ */
+ if (rel->reloptkind == RELOPT_BASEREL &&
+ !(rel->relid == root->parse->resultRelation &&
+ root->inhTargetKind != INHKIND_NONE))
+ include_partition = true;
break; /* always try to exclude */
}
return true;
/*
- * Only plain relations have constraints. In a partitioning hierarchy,
- * but not with regular table inheritance, it's OK to assume that any
- * constraints that hold for the parent also hold for every child; for
- * instance, table inheritance allows the parent to have constraints
- * marked NO INHERIT, but table partitioning does not. We choose to check
- * whether the partitioning parents can be excluded here; doing so
- * consumes some cycles, but potentially saves us the work of excluding
- * each child individually.
+ * Only plain relations have constraints, so stop here for other rtekinds.
*/
- if (rte->rtekind != RTE_RELATION ||
- (rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE))
+ if (rte->rtekind != RTE_RELATION)
return false;
/*
- * OK to fetch the constraint expressions. Include "col IS NOT NULL"
- * expressions for attnotnull columns, in case we can refute those.
+ * If we are scanning just this table, we can use NO INHERIT constraints,
+ * but not if we're scanning its children too. (Note that partitioned
+ * tables should never have NO INHERIT constraints; but it's not necessary
+ * for us to assume that here.)
+ */
+ include_noinherit = !rte->inh;
+
+ /*
+ * Currently, attnotnull constraints must be treated as NO INHERIT unless
+ * this is a partitioned table. In future we might track their
+ * inheritance status more accurately, allowing this to be refined.
+ */
+ include_notnull = (!rte->inh || rte->relkind == RELKIND_PARTITIONED_TABLE);
+
+ /*
+ * Fetch the appropriate set of constraint expressions.
*/
- constraint_pred = get_relation_constraints(root, rte->relid, rel, true);
+ constraint_pred = get_relation_constraints(root, rte->relid, rel,
+ include_noinherit,
+ include_notnull,
+ include_partition);
/*
* We do not currently enforce that CHECK constraints contain only