Build "other rels" of appendrel baserels in a separate step.

Up to now, otherrel RelOptInfos were built at the same time as baserel
RelOptInfos, thanks to recursion in build_simple_rel().  However,
nothing in query_planner's preprocessing cares at all about otherrels,
only baserels, so we don't really need to build them until just before
we enter make_one_rel.  This has two benefits:

* create_lateral_join_info did a lot of extra work to propagate
lateral-reference information from parents to the correct children.
But if we delay creation of the children till after that, it's
trivial (and much harder to break, too).

* Since we have all the restriction quals correctly assigned to
parent appendrels by this point, it'll be possible to do plan-time
pruning and never make child RelOptInfos at all for partitions that
can be pruned away.  That's not done here, but will be later on.

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:
Tom Lane 2019-03-26 18:21:10 -04:00
parent 8994cc6ffc
commit 53bcf5e3db
6 changed files with 157 additions and 112 deletions

View File

@ -1029,7 +1029,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
/*
* The child rel's RelOptInfo was already created during
* add_base_rels_to_query.
* add_other_rels_to_query.
*/
childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);

View File

@ -90,17 +90,16 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* add_base_rels_to_query
*
* Scan the query's jointree and create baserel RelOptInfos for all
* the base relations (ie, table, subquery, and function RTEs)
* the base relations (e.g., table, subquery, and function RTEs)
* appearing in the jointree.
*
* The initial invocation must pass root->parse->jointree as the value of
* jtnode. Internally, the function recurses through the jointree.
*
* At the end of this process, there should be one baserel RelOptInfo for
* every non-join RTE that is used in the query. Therefore, this routine
* is the only place that should call build_simple_rel with reloptkind
* RELOPT_BASEREL. (Note: build_simple_rel recurses internally to build
* "other rel" RelOptInfos for the members of any appendrels we find here.)
* every non-join RTE that is used in the query. Some of the baserels
* may be appendrel parents, which will require additional "otherrel"
* RelOptInfos for their member rels, but those are added later.
*/
void
add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
@ -133,6 +132,42 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
(int) nodeTag(jtnode));
}
/*
* add_other_rels_to_query
* create "otherrel" RelOptInfos for the children of appendrel baserels
*
* At the end of this process, there should be RelOptInfos for all relations
* that will be scanned by the query.
*/
void
add_other_rels_to_query(PlannerInfo *root)
{
int rti;
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *rel = root->simple_rel_array[rti];
RangeTblEntry *rte = root->simple_rte_array[rti];
/* there may be empty slots corresponding to non-baserel RTEs */
if (rel == NULL)
continue;
/* Ignore any "otherrels" that were already added. */
if (rel->reloptkind != RELOPT_BASEREL)
continue;
/* If it's marked as inheritable, look for children. */
if (rte->inh)
{
/* Only relation and subquery RTEs can have children. */
Assert(rte->rtekind == RTE_RELATION ||
rte->rtekind == RTE_SUBQUERY);
add_appendrel_other_rels(root, rel, rti);
}
}
}
/*****************************************************************************
*
@ -419,7 +454,6 @@ void
create_lateral_join_info(PlannerInfo *root)
{
bool found_laterals = false;
Relids prev_parents PG_USED_FOR_ASSERTS_ONLY = NULL;
Index rti;
ListCell *lc;
@ -618,54 +652,6 @@ create_lateral_join_info(PlannerInfo *root)
bms_add_member(brel2->lateral_referencers, rti);
}
}
/*
* Lastly, propagate lateral_relids and lateral_referencers from appendrel
* parent rels to their child rels. We intentionally give each child rel
* the same minimum parameterization, even though it's quite possible that
* some don't reference all the lateral rels. This is because any append
* path for the parent will have to have the same parameterization for
* every child anyway, and there's no value in forcing extra
* reparameterize_path() calls. Similarly, a lateral reference to the
* parent prevents use of otherwise-movable join rels for each child.
*
* It's possible for child rels to have their own children, in which case
* the topmost parent's lateral info must be propagated all the way down.
* This code handles that case correctly so long as append_rel_list has
* entries for child relationships before grandchild relationships, which
* is an okay assumption right now, but we'll need to be careful to
* preserve it. The assertions below check for incorrect ordering.
*/
foreach(lc, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
RelOptInfo *parentrel = root->simple_rel_array[appinfo->parent_relid];
RelOptInfo *childrel = root->simple_rel_array[appinfo->child_relid];
/*
* If we're processing a subquery of a query with inherited target rel
* (cf. inheritance_planner), append_rel_list may contain entries for
* tables that are not part of the current subquery and hence have no
* RelOptInfo. Ignore them. We can ignore dead rels, too.
*/
if (parentrel == NULL || !IS_SIMPLE_REL(parentrel))
continue;
/* Verify that children are processed before grandchildren */
#ifdef USE_ASSERT_CHECKING
prev_parents = bms_add_member(prev_parents, appinfo->parent_relid);
Assert(!bms_is_member(appinfo->child_relid, prev_parents));
#endif
/* OK, propagate info down */
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Assert(childrel->direct_lateral_relids == NULL);
childrel->direct_lateral_relids = parentrel->direct_lateral_relids;
Assert(childrel->lateral_relids == NULL);
childrel->lateral_relids = parentrel->lateral_relids;
Assert(childrel->lateral_referencers == NULL);
childrel->lateral_referencers = parentrel->lateral_referencers;
}
}

View File

@ -159,15 +159,13 @@ query_planner(PlannerInfo *root, List *tlist,
setup_append_rel_array(root);
/*
* Construct RelOptInfo nodes for all base relations in query, and
* indirectly for all appendrel member relations ("other rels"). This
* will give us a RelOptInfo for every "simple" (non-join) rel involved in
* the query.
* Construct RelOptInfo nodes for all base relations used in the query.
* Appendrel member relations ("other rels") will be added later.
*
* Note: the reason we find the rels by searching the jointree and
* appendrel list, rather than just scanning the rangetable, is that the
* rangetable may contain RTEs for rels not actively part of the query,
* for example views. We don't want to make RelOptInfos for them.
* Note: the reason we find the baserels by searching the jointree, rather
* than scanning the rangetable, is that the rangetable may contain RTEs
* for rels not actively part of the query, for example views. We don't
* want to make RelOptInfos for them.
*/
add_base_rels_to_query(root, (Node *) parse->jointree);
@ -259,6 +257,16 @@ query_planner(PlannerInfo *root, List *tlist,
*/
extract_restriction_or_clauses(root);
/*
* Now expand appendrels by adding "otherrels" for their children. We
* delay this to the end so that we have as much information as possible
* available for each baserel, including all restriction clauses. That
* let us prune away partitions that don't satisfy a restriction clause.
* Also note that some information such as lateral_relids is propagated
* from baserels to otherrels here, so we must have computed it already.
*/
add_other_rels_to_query(root);
/*
* Ready to do the primary planning.
*/

View File

@ -166,13 +166,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
rel->cheapest_parameterized_paths = NIL;
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->relid = relid;
rel->rtekind = rte->rtekind;
/* min_attr, max_attr, attr_needed, attr_widths are set below */
rel->lateral_vars = NIL;
rel->lateral_referencers = NULL;
rel->indexlist = NIL;
rel->statlist = NIL;
rel->pages = 0;
@ -205,20 +202,44 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->partitioned_child_rels = NIL;
/*
* Pass top parent's relids down the inheritance hierarchy. If the parent
* has top_parent_relids set, it's a direct or an indirect child of the
* top parent indicated by top_parent_relids. By extension this child is
* also an indirect child of that parent.
* Pass assorted information down the inheritance hierarchy.
*/
if (parent)
{
/*
* Each direct or indirect child wants to know the relids of its
* topmost parent.
*/
if (parent->top_parent_relids)
rel->top_parent_relids = parent->top_parent_relids;
else
rel->top_parent_relids = bms_copy(parent->relids);
/*
* Also propagate lateral-reference information from appendrel parent
* rels to their child rels. We intentionally give each child rel the
* same minimum parameterization, even though it's quite possible that
* some don't reference all the lateral rels. This is because any
* append path for the parent will have to have the same
* parameterization for every child anyway, and there's no value in
* forcing extra reparameterize_path() calls. Similarly, a lateral
* reference to the parent prevents use of otherwise-movable join rels
* for each child.
*
* It's possible for child rels to have their own children, in which
* case the topmost parent's lateral info propagates all the way down.
*/
rel->direct_lateral_relids = parent->direct_lateral_relids;
rel->lateral_relids = parent->lateral_relids;
rel->lateral_referencers = parent->lateral_referencers;
}
else
{
rel->top_parent_relids = NULL;
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->lateral_referencers = NULL;
}
/* Check type of rtable entry */
switch (rte->rtekind)
@ -273,53 +294,80 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
root->qual_security_level = Max(root->qual_security_level,
list_length(rte->securityQuals));
return rel;
}
/*
* add_appendrel_other_rels
* Add "other rel" RelOptInfos for the children of an appendrel baserel
*
* "rel" is a relation that (still) has the rte->inh flag set, meaning it
* has appendrel children listed in root->append_rel_list. We need to build
* a RelOptInfo for each child relation so that we can plan scans on them.
* (The parent relation might be a partitioned table, a table with
* traditional inheritance children, or a flattened UNION ALL subquery.)
*/
void
add_appendrel_other_rels(PlannerInfo *root, RelOptInfo *rel, Index rti)
{
int cnt_parts = 0;
ListCell *l;
/*
* If this rel is an appendrel parent, recurse to build "other rel"
* RelOptInfos for its children. They are "other rels" because they are
* not in the main join tree, but we will need RelOptInfos to plan access
* to them.
* If rel is a partitioned table, then we also need to build a part_rels
* array so that the child RelOptInfos can be conveniently accessed from
* the parent.
*/
if (rte->inh)
if (rel->part_scheme != NULL)
{
ListCell *l;
int nparts = rel->nparts;
int cnt_parts = 0;
if (nparts > 0)
rel->part_rels = (RelOptInfo **)
palloc(sizeof(RelOptInfo *) * nparts);
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
RelOptInfo *childrel;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != relid)
continue;
childrel = build_simple_rel(root, appinfo->child_relid,
rel);
/* Nothing more to do for an unpartitioned table. */
if (!rel->part_scheme)
continue;
/*
* The order of partition OIDs in append_rel_list is the same as
* the order in the PartitionDesc, so the order of part_rels will
* also match the PartitionDesc. See expand_partitioned_rtentry.
*/
Assert(cnt_parts < nparts);
rel->part_rels[cnt_parts] = childrel;
cnt_parts++;
}
/* We should have seen all the child partitions. */
Assert(cnt_parts == nparts);
Assert(rel->nparts > 0);
rel->part_rels = (RelOptInfo **)
palloc0(sizeof(RelOptInfo *) * rel->nparts);
}
return rel;
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
Index childRTindex = appinfo->child_relid;
RangeTblEntry *childrte;
RelOptInfo *childrel;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != rti)
continue;
/* find the child RTE, which should already exist */
Assert(childRTindex < root->simple_rel_array_size);
childrte = root->simple_rte_array[childRTindex];
Assert(childrte != NULL);
/* build child RelOptInfo, and add to main query data structures */
childrel = build_simple_rel(root, childRTindex, rel);
/*
* If rel is a partitioned table, fill in the part_rels array. The
* order in which child tables appear in append_rel_list is the same
* as the order in which they appear in the parent's PartitionDesc, so
* assigning partitions like this works.
*/
if (rel->part_scheme != NULL)
{
Assert(cnt_parts < rel->nparts);
rel->part_rels[cnt_parts++] = childrel;
}
/* Child may itself be an inherited relation. */
if (childrte->inh)
{
/* Only relation and subquery RTEs can have children. */
Assert(childrte->rtekind == RTE_RELATION ||
childrte->rtekind == RTE_SUBQUERY);
add_appendrel_other_rels(root, childrel, childRTindex);
}
}
/* We should have filled all of the part_rels array if it's partitioned */
Assert(cnt_parts == rel->nparts);
}
/*

View File

@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root);
extern void setup_append_rel_array(PlannerInfo *root);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
RelOptInfo *parent);
extern void add_appendrel_other_rels(PlannerInfo *root, RelOptInfo *rel,
Index rti);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root,

View File

@ -65,6 +65,7 @@ extern int from_collapse_limit;
extern int join_collapse_limit;
extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
extern void add_other_rels_to_query(PlannerInfo *root);
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
Relids where_needed, bool create_new_ph);