Commit 53bcf5e3 authored by Tom Lane's avatar Tom Lane

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
parent 8994cc6f
...@@ -1029,7 +1029,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, ...@@ -1029,7 +1029,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
/* /*
* The child rel's RelOptInfo was already created during * 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); childrel = find_base_rel(root, childRTindex);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
......
...@@ -90,17 +90,16 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); ...@@ -90,17 +90,16 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* add_base_rels_to_query * add_base_rels_to_query
* *
* Scan the query's jointree and create baserel RelOptInfos for all * 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. * appearing in the jointree.
* *
* The initial invocation must pass root->parse->jointree as the value of * The initial invocation must pass root->parse->jointree as the value of
* jtnode. Internally, the function recurses through the jointree. * jtnode. Internally, the function recurses through the jointree.
* *
* At the end of this process, there should be one baserel RelOptInfo for * 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 * every non-join RTE that is used in the query. Some of the baserels
* is the only place that should call build_simple_rel with reloptkind * may be appendrel parents, which will require additional "otherrel"
* RELOPT_BASEREL. (Note: build_simple_rel recurses internally to build * RelOptInfos for their member rels, but those are added later.
* "other rel" RelOptInfos for the members of any appendrels we find here.)
*/ */
void void
add_base_rels_to_query(PlannerInfo *root, Node *jtnode) add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
...@@ -133,6 +132,42 @@ 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)); (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 ...@@ -419,7 +454,6 @@ void
create_lateral_join_info(PlannerInfo *root) create_lateral_join_info(PlannerInfo *root)
{ {
bool found_laterals = false; bool found_laterals = false;
Relids prev_parents PG_USED_FOR_ASSERTS_ONLY = NULL;
Index rti; Index rti;
ListCell *lc; ListCell *lc;
...@@ -618,54 +652,6 @@ create_lateral_join_info(PlannerInfo *root) ...@@ -618,54 +652,6 @@ create_lateral_join_info(PlannerInfo *root)
bms_add_member(brel2->lateral_referencers, rti); 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;
}
} }
......
...@@ -159,15 +159,13 @@ query_planner(PlannerInfo *root, List *tlist, ...@@ -159,15 +159,13 @@ query_planner(PlannerInfo *root, List *tlist,
setup_append_rel_array(root); setup_append_rel_array(root);
/* /*
* Construct RelOptInfo nodes for all base relations in query, and * Construct RelOptInfo nodes for all base relations used in the query.
* indirectly for all appendrel member relations ("other rels"). This * Appendrel member relations ("other rels") will be added later.
* will give us a RelOptInfo for every "simple" (non-join) rel involved in
* the query.
* *
* Note: the reason we find the rels by searching the jointree and * Note: the reason we find the baserels by searching the jointree, rather
* appendrel list, rather than just scanning the rangetable, is that the * than scanning the rangetable, is that the rangetable may contain RTEs
* rangetable may contain RTEs for rels not actively part of the query, * for rels not actively part of the query, for example views. We don't
* for example views. We don't want to make RelOptInfos for them. * want to make RelOptInfos for them.
*/ */
add_base_rels_to_query(root, (Node *) parse->jointree); add_base_rels_to_query(root, (Node *) parse->jointree);
...@@ -259,6 +257,16 @@ query_planner(PlannerInfo *root, List *tlist, ...@@ -259,6 +257,16 @@ query_planner(PlannerInfo *root, List *tlist,
*/ */
extract_restriction_or_clauses(root); 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. * Ready to do the primary planning.
*/ */
......
...@@ -166,13 +166,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) ...@@ -166,13 +166,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->cheapest_total_path = NULL; rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL; rel->cheapest_unique_path = NULL;
rel->cheapest_parameterized_paths = NIL; rel->cheapest_parameterized_paths = NIL;
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->relid = relid; rel->relid = relid;
rel->rtekind = rte->rtekind; rel->rtekind = rte->rtekind;
/* min_attr, max_attr, attr_needed, attr_widths are set below */ /* min_attr, max_attr, attr_needed, attr_widths are set below */
rel->lateral_vars = NIL; rel->lateral_vars = NIL;
rel->lateral_referencers = NULL;
rel->indexlist = NIL; rel->indexlist = NIL;
rel->statlist = NIL; rel->statlist = NIL;
rel->pages = 0; rel->pages = 0;
...@@ -205,20 +202,44 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) ...@@ -205,20 +202,44 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->partitioned_child_rels = NIL; rel->partitioned_child_rels = NIL;
/* /*
* Pass top parent's relids down the inheritance hierarchy. If the parent * Pass assorted information down the inheritance hierarchy.
* 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.
*/ */
if (parent) if (parent)
{ {
/*
* Each direct or indirect child wants to know the relids of its
* topmost parent.
*/
if (parent->top_parent_relids) if (parent->top_parent_relids)
rel->top_parent_relids = parent->top_parent_relids; rel->top_parent_relids = parent->top_parent_relids;
else else
rel->top_parent_relids = bms_copy(parent->relids); 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 else
{
rel->top_parent_relids = NULL; rel->top_parent_relids = NULL;
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->lateral_referencers = NULL;
}
/* Check type of rtable entry */ /* Check type of rtable entry */
switch (rte->rtekind) switch (rte->rtekind)
...@@ -273,53 +294,80 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) ...@@ -273,53 +294,80 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
root->qual_security_level = Max(root->qual_security_level, root->qual_security_level = Max(root->qual_security_level,
list_length(rte->securityQuals)); 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" * If rel is a partitioned table, then we also need to build a part_rels
* RelOptInfos for its children. They are "other rels" because they are * array so that the child RelOptInfos can be conveniently accessed from
* not in the main join tree, but we will need RelOptInfos to plan access * the parent.
* to them.
*/ */
if (rte->inh) if (rel->part_scheme != NULL)
{ {
ListCell *l; Assert(rel->nparts > 0);
int nparts = rel->nparts; rel->part_rels = (RelOptInfo **)
int cnt_parts = 0; palloc0(sizeof(RelOptInfo *) * rel->nparts);
}
if (nparts > 0)
rel->part_rels = (RelOptInfo **)
palloc(sizeof(RelOptInfo *) * nparts);
foreach(l, root->append_rel_list) foreach(l, root->append_rel_list)
{ {
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
RelOptInfo *childrel; Index childRTindex = appinfo->child_relid;
RangeTblEntry *childrte;
RelOptInfo *childrel;
/* append_rel_list contains all append rels; ignore others */ /* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != relid) if (appinfo->parent_relid != rti)
continue; continue;
childrel = build_simple_rel(root, appinfo->child_relid, /* find the child RTE, which should already exist */
rel); Assert(childRTindex < root->simple_rel_array_size);
childrte = root->simple_rte_array[childRTindex];
Assert(childrte != NULL);
/* Nothing more to do for an unpartitioned table. */ /* build child RelOptInfo, and add to main query data structures */
if (!rel->part_scheme) childrel = build_simple_rel(root, childRTindex, rel);
continue;
/* /*
* The order of partition OIDs in append_rel_list is the same as * If rel is a partitioned table, fill in the part_rels array. The
* the order in the PartitionDesc, so the order of part_rels will * order in which child tables appear in append_rel_list is the same
* also match the PartitionDesc. See expand_partitioned_rtentry. * as the order in which they appear in the parent's PartitionDesc, so
*/ * assigning partitions like this works.
Assert(cnt_parts < nparts); */
rel->part_rels[cnt_parts] = childrel; if (rel->part_scheme != NULL)
cnt_parts++; {
Assert(cnt_parts < rel->nparts);
rel->part_rels[cnt_parts++] = childrel;
} }
/* We should have seen all the child partitions. */ /* Child may itself be an inherited relation. */
Assert(cnt_parts == nparts); 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);
}
} }
return rel; /* We should have filled all of the part_rels array if it's partitioned */
Assert(cnt_parts == rel->nparts);
} }
/* /*
......
...@@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root); ...@@ -279,6 +279,8 @@ extern void setup_simple_rel_arrays(PlannerInfo *root);
extern void setup_append_rel_array(PlannerInfo *root); extern void setup_append_rel_array(PlannerInfo *root);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
RelOptInfo *parent); 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_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root, extern RelOptInfo *build_join_rel(PlannerInfo *root,
......
...@@ -65,6 +65,7 @@ extern int from_collapse_limit; ...@@ -65,6 +65,7 @@ extern int from_collapse_limit;
extern int join_collapse_limit; extern int join_collapse_limit;
extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode); 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 build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars, extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
Relids where_needed, bool create_new_ph); Relids where_needed, bool create_new_ph);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment