diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index da0d7787214..56a50843123 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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); diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 2afc3f1dfe0..62dfac67dcb 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -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; - } } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 3cedd01c98b..c36958de51a 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -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. */ diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 4130514952b..0d40b8d3d2c 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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); } /* diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 574bb85b50a..9e79e1cd63c 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -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, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index b093a3c8ac2..efb07026a81 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -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);