mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Speed up planning when partitions can be pruned at plan time.
Previously, the planner created RangeTblEntry and RelOptInfo structs for every partition of a partitioned table, even though many of them might later be deemed uninteresting thanks to partition pruning logic. This incurred significant overhead when there are many partitions. Arrange to postpone creation of these data structures until after we've processed the query enough to identify restriction quals for the partitioned table, and then apply partition pruning before not after creation of each partition's data structures. In this way we need not open the partition relations at all for partitions that the planner has no real interest in. For queries that can be proven at plan time to access only a small number of partitions, this patch improves the practical maximum number of partitions from under 100 to perhaps a few thousand. Amit Langote, reviewed at various times by Dilip Kumar, Jesper Pedersen, Yoshikazu Imai, and David Rowley Discussion: https://postgr.es/m/9d7c5112-cb99-6a47-d3be-cf1ee6862a1d@lab.ntt.co.jp
This commit is contained in:
@ -139,9 +139,6 @@ static void subquery_push_qual(Query *subquery,
|
||||
static void recurse_push_qual(Node *setOp, Query *topquery,
|
||||
RangeTblEntry *rte, Index rti, Node *qual);
|
||||
static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
|
||||
static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
|
||||
RelOptInfo *childrel,
|
||||
RangeTblEntry *childRTE, AppendRelInfo *appinfo);
|
||||
|
||||
|
||||
/*
|
||||
@ -396,8 +393,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
else if (rte->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
/*
|
||||
* A partitioned table without any partitions is marked as
|
||||
* a dummy rel.
|
||||
* We could get here if asked to scan a partitioned table
|
||||
* with ONLY. In that case we shouldn't scan any of the
|
||||
* partitions, so mark it as a dummy rel.
|
||||
*/
|
||||
set_dummy_rel_pathlist(rel);
|
||||
}
|
||||
@ -946,8 +944,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
double *parent_attrsizes;
|
||||
int nattrs;
|
||||
ListCell *l;
|
||||
Relids live_children = NULL;
|
||||
bool did_pruning = false;
|
||||
|
||||
/* Guard against stack overflow due to overly deep inheritance tree. */
|
||||
check_stack_depth();
|
||||
@ -965,21 +961,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
if (rte->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
rel->partitioned_child_rels = list_make1_int(rti);
|
||||
|
||||
/*
|
||||
* If the partitioned relation has any baserestrictinfo quals then we
|
||||
* attempt to use these quals to prune away partitions that cannot
|
||||
* possibly contain any tuples matching these quals. In this case we'll
|
||||
* store the relids of all partitions which could possibly contain a
|
||||
* matching tuple, and skip anything else in the loop below.
|
||||
*/
|
||||
if (enable_partition_pruning &&
|
||||
rte->relkind == RELKIND_PARTITIONED_TABLE &&
|
||||
rel->baserestrictinfo != NIL)
|
||||
{
|
||||
live_children = prune_append_rel_partitions(rel);
|
||||
did_pruning = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a partitioned baserel, set the consider_partitionwise_join
|
||||
* flag; currently, we only consider partitionwise joins with the baserel
|
||||
@ -1034,30 +1015,17 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
childrel = find_base_rel(root, childRTindex);
|
||||
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
|
||||
|
||||
if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))
|
||||
{
|
||||
/* This partition was pruned; skip it. */
|
||||
set_dummy_rel_pathlist(childrel);
|
||||
/* We may have already proven the child to be dummy. */
|
||||
if (IS_DUMMY_REL(childrel))
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to copy the parent's targetlist and quals to the child,
|
||||
* with appropriate substitution of variables. If any constant false
|
||||
* or NULL clauses turn up, we can disregard the child right away. If
|
||||
* not, we can apply constraint exclusion with just the
|
||||
* baserestrictinfo quals.
|
||||
* with appropriate substitution of variables. However, the
|
||||
* baserestrictinfo quals were already copied/substituted when the
|
||||
* child RelOptInfo was built. So we don't need any additional setup
|
||||
* before applying constraint exclusion.
|
||||
*/
|
||||
if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
|
||||
{
|
||||
/*
|
||||
* Some restriction clause reduced to constant FALSE or NULL after
|
||||
* substitution, so this child need not be scanned.
|
||||
*/
|
||||
set_dummy_rel_pathlist(childrel);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (relation_excluded_by_constraints(root, childrel, childRTE))
|
||||
{
|
||||
/*
|
||||
@ -1069,7 +1037,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
}
|
||||
|
||||
/*
|
||||
* CE failed, so finish copying/modifying targetlist and join quals.
|
||||
* Constraint exclusion failed, so copy the parent's join quals and
|
||||
* targetlist to the child, with appropriate variable substitutions.
|
||||
*
|
||||
* NB: the resulting childrel->reltarget->exprs may contain arbitrary
|
||||
* expressions, which otherwise would not occur in a rel's targetlist.
|
||||
@ -3596,133 +3565,6 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
list_free(live_children);
|
||||
}
|
||||
|
||||
/*
|
||||
* apply_child_basequals
|
||||
* Populate childrel's quals based on rel's quals, translating them using
|
||||
* appinfo.
|
||||
*
|
||||
* If any of the resulting clauses evaluate to false or NULL, we return false
|
||||
* and don't apply any quals. Caller can mark the relation as a dummy rel in
|
||||
* this case, since it needn't be scanned.
|
||||
*
|
||||
* If any resulting clauses evaluate to true, they're unnecessary and we don't
|
||||
* apply then.
|
||||
*/
|
||||
static bool
|
||||
apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
|
||||
RelOptInfo *childrel, RangeTblEntry *childRTE,
|
||||
AppendRelInfo *appinfo)
|
||||
{
|
||||
List *childquals;
|
||||
Index cq_min_security;
|
||||
ListCell *lc;
|
||||
|
||||
/*
|
||||
* The child rel's targetlist might contain non-Var expressions, which
|
||||
* means that substitution into the quals could produce opportunities for
|
||||
* const-simplification, and perhaps even pseudoconstant quals. Therefore,
|
||||
* transform each RestrictInfo separately to see if it reduces to a
|
||||
* constant or pseudoconstant. (We must process them separately to keep
|
||||
* track of the security level of each qual.)
|
||||
*/
|
||||
childquals = NIL;
|
||||
cq_min_security = UINT_MAX;
|
||||
foreach(lc, rel->baserestrictinfo)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
Node *childqual;
|
||||
ListCell *lc2;
|
||||
|
||||
Assert(IsA(rinfo, RestrictInfo));
|
||||
childqual = adjust_appendrel_attrs(root,
|
||||
(Node *) rinfo->clause,
|
||||
1, &appinfo);
|
||||
childqual = eval_const_expressions(root, childqual);
|
||||
/* check for flat-out constant */
|
||||
if (childqual && IsA(childqual, Const))
|
||||
{
|
||||
if (((Const *) childqual)->constisnull ||
|
||||
!DatumGetBool(((Const *) childqual)->constvalue))
|
||||
{
|
||||
/* Restriction reduces to constant FALSE or NULL */
|
||||
return false;
|
||||
}
|
||||
/* Restriction reduces to constant TRUE, so drop it */
|
||||
continue;
|
||||
}
|
||||
/* might have gotten an AND clause, if so flatten it */
|
||||
foreach(lc2, make_ands_implicit((Expr *) childqual))
|
||||
{
|
||||
Node *onecq = (Node *) lfirst(lc2);
|
||||
bool pseudoconstant;
|
||||
|
||||
/* check for pseudoconstant (no Vars or volatile functions) */
|
||||
pseudoconstant =
|
||||
!contain_vars_of_level(onecq, 0) &&
|
||||
!contain_volatile_functions(onecq);
|
||||
if (pseudoconstant)
|
||||
{
|
||||
/* tell createplan.c to check for gating quals */
|
||||
root->hasPseudoConstantQuals = true;
|
||||
}
|
||||
/* reconstitute RestrictInfo with appropriate properties */
|
||||
childquals = lappend(childquals,
|
||||
make_restrictinfo((Expr *) onecq,
|
||||
rinfo->is_pushed_down,
|
||||
rinfo->outerjoin_delayed,
|
||||
pseudoconstant,
|
||||
rinfo->security_level,
|
||||
NULL, NULL, NULL));
|
||||
/* track minimum security level among child quals */
|
||||
cq_min_security = Min(cq_min_security, rinfo->security_level);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In addition to the quals inherited from the parent, we might have
|
||||
* securityQuals associated with this particular child node. (Currently
|
||||
* this can only happen in appendrels originating from UNION ALL;
|
||||
* inheritance child tables don't have their own securityQuals, see
|
||||
* expand_inherited_rtentry().) Pull any such securityQuals up into the
|
||||
* baserestrictinfo for the child. This is similar to
|
||||
* process_security_barrier_quals() for the parent rel, except that we
|
||||
* can't make any general deductions from such quals, since they don't
|
||||
* hold for the whole appendrel.
|
||||
*/
|
||||
if (childRTE->securityQuals)
|
||||
{
|
||||
Index security_level = 0;
|
||||
|
||||
foreach(lc, childRTE->securityQuals)
|
||||
{
|
||||
List *qualset = (List *) lfirst(lc);
|
||||
ListCell *lc2;
|
||||
|
||||
foreach(lc2, qualset)
|
||||
{
|
||||
Expr *qual = (Expr *) lfirst(lc2);
|
||||
|
||||
/* not likely that we'd see constants here, so no check */
|
||||
childquals = lappend(childquals,
|
||||
make_restrictinfo(qual,
|
||||
true, false, false,
|
||||
security_level,
|
||||
NULL, NULL, NULL));
|
||||
cq_min_security = Min(cq_min_security, security_level);
|
||||
}
|
||||
security_level++;
|
||||
}
|
||||
Assert(security_level <= root->qual_security_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, we've got all the baserestrictinfo quals for this child.
|
||||
*/
|
||||
childrel->baserestrictinfo = childquals;
|
||||
childrel->baserestrict_min_security = cq_min_security;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* DEBUG SUPPORT
|
||||
|
Reference in New Issue
Block a user