Don't constraint-exclude partitioned tables as much

We only need to invoke constraint exclusion on partitioned tables when
they are a partition, and they themselves contain a default partition;
it's not necessary otherwise, and it's expensive, so avoid it.  Also, we
were trying once for each clause separately, but we can do it for all
the clauses at once.

While at it, centralize setting of RelOptInfo->partition_qual instead of
computing it in slightly different ways in different places.

Per complaints from Simon Riggs about 4e85642d935e; reviewed by Yuzuko
Hosoya, Kyotaro Horiguchi.

Author: Amit Langote.  I (Álvaro) again mangled the patch somewhat.
Discussion: https://postgr.es/m/CANP8+j+tMCY=nEcQeqQam85=uopLBtX-2vHiLD2bbp7iQQUKpA@mail.gmail.com
This commit is contained in:
Alvaro Herrera 2019-08-13 10:26:04 -04:00
parent 416c75cf38
commit 815ef2f568
2 changed files with 65 additions and 65 deletions

View File

@ -78,6 +78,9 @@ static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel);
static void set_baserel_partition_key_exprs(Relation relation,
RelOptInfo *rel);
static void set_baserel_partition_constraint(Relation relation,
RelOptInfo *rel);
/*
* get_relation_info -
@ -1267,25 +1270,9 @@ get_relation_constraints(PlannerInfo *root,
*/
if (include_partition && relation->rd_rel->relispartition)
{
List *pcqual = RelationGetPartitionQual(relation);
if (pcqual)
{
/*
* 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.
*/
pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);
/* Fix Vars to have the desired varno */
if (varno != 1)
ChangeVarNodes((Node *) pcqual, 1, varno, 0);
result = list_concat(result, pcqual);
}
/* make sure rel->partition_qual is set */
set_baserel_partition_constraint(relation, rel);
result = list_concat(result, rel->partition_qual);
}
table_close(relation, NoLock);
@ -2149,7 +2136,7 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
rel->boundinfo = partdesc->boundinfo;
rel->nparts = partdesc->nparts;
set_baserel_partition_key_exprs(relation, rel);
rel->partition_qual = RelationGetPartitionQual(relation);
set_baserel_partition_constraint(relation, rel);
}
/*
@ -2324,3 +2311,35 @@ set_baserel_partition_key_exprs(Relation relation,
*/
rel->nullable_partexprs = (List **) palloc0(sizeof(List *) * partnatts);
}
/*
* set_baserel_partition_constraint
*
* Builds the partition constraint for the given base relation and sets it
* in the given RelOptInfo. All Var nodes are restamped with the relid of the
* given relation.
*/
static void
set_baserel_partition_constraint(Relation relation, RelOptInfo *rel)
{
List *partconstr;
if (rel->partition_qual) /* already done */
return;
/*
* 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.
*/
partconstr = RelationGetPartitionQual(relation);
if (partconstr)
{
partconstr = (List *) expression_planner((Expr *) partconstr);
if (rel->relid != 1)
ChangeVarNodes((Node *) partconstr, 1, rel->relid, 0);
rel->partition_qual = partconstr;
}
}

View File

@ -619,31 +619,16 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, PartClauseTarget target,
context->target = target;
/*
* For sub-partitioned tables there's a corner case where if the
* sub-partitioned table shares any partition keys with its parent, then
* it's possible that the partitioning hierarchy allows the parent
* partition to only contain a narrower range of values than the
* sub-partitioned table does. In this case it is possible that we'd
* include partitions that could not possibly have any tuples matching
* 'clauses'. The possibility of such a partition arrangement is perhaps
* unlikely for non-default partitions, but it may be more likely in the
* case of default partitions, so we'll add the parent partition table's
* partition qual to the clause list in this case only. This may result
* in the default partition being eliminated.
* If this partitioned table is in turn a partition, and it shares any
* partition keys with its parent, then it's possible that the hierarchy
* allows the parent a narrower range of values than some of its
* partitions (particularly the default one). This is normally not
* useful, but it can be to prune the default partition.
*/
if (partition_bound_has_default(rel->boundinfo) &&
rel->partition_qual != NIL)
if (partition_bound_has_default(rel->boundinfo) && rel->partition_qual)
{
List *partqual = rel->partition_qual;
partqual = (List *) expression_planner((Expr *) partqual);
/* Fix Vars to have the desired varno */
if (rel->relid != 1)
ChangeVarNodes((Node *) partqual, 1, rel->relid, 0);
/* Make a copy to avoid modifying the passed-in List */
clauses = list_concat_copy(clauses, partqual);
clauses = list_concat_copy(clauses, rel->partition_qual);
}
/* Down into the rabbit-hole. */
@ -867,6 +852,25 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
List *result = NIL;
ListCell *lc;
/*
* If this partitioned relation has a default partition and is itself
* a partition (as evidenced by partition_qual being not NIL), we first
* check if the clauses contradict the partition constraint. If they do,
* there's no need to generate any steps as it'd already be proven that no
* partitions need to be scanned.
*
* This is a measure of last resort only to be used because the default
* partition cannot be pruned using the steps generated from clauses that
* contradict the parent's partition constraint; regular pruning, which is
* cheaper, is sufficient when no default partition exists.
*/
if (partition_bound_has_default(context->rel->boundinfo) &&
predicate_refuted_by(context->rel->partition_qual, clauses, false))
{
context->contradictory = true;
return NIL;
}
memset(keyclauses, 0, sizeof(keyclauses));
foreach(lc, clauses)
{
@ -1019,29 +1023,6 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
*/
}
/*
* If the clause contradicts the partition constraint, mark the clause
* as contradictory and we're done. This is particularly helpful to
* prune the default partition.
*/
if (context->rel->partition_qual)
{
List *partconstr;
partconstr = (List *)
expression_planner((Expr *) context->rel->partition_qual);
if (context->rel->relid != 1)
ChangeVarNodes((Node *) partconstr, 1,
context->rel->relid, 0);
if (predicate_refuted_by(partconstr,
list_make1(clause),
false))
{
context->contradictory = true;
return NIL;
}
}
/*
* See if we can match this clause to any of the partition keys.
*/