Build "other rels" of appendrel baserels in a separate step.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 26 Mar 2019 22:21:10 +0000 (18:21 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 26 Mar 2019 22:21:10 +0000 (18:21 -0400)
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

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/util/relnode.c
src/include/optimizer/pathnode.h
src/include/optimizer/planmain.h

index da0d7787214c13deabf08bdfe10929c806a42d93..56a50843123492820e4876a71de7d8090486010f 100644 (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);
index 2afc3f1dfe0194707b585233d938d0ea5eb9fe50..62dfac67dcb46498ce1d4e84500e31e8d6b1cf57 100644 (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;
-       }
 }
 
 
index 3cedd01c98b4010a363be493202e52fd84ede504..c36958de51a00b7877e35bdbd872fe86d3786dba 100644 (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.
         */
index 4130514952beccf6176b0717b8b85fcd38823023..0d40b8d3d2ce8c061f4e5b4634a6864be1319dc5 100644 (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);
+               Assert(rel->nparts > 0);
+               rel->part_rels = (RelOptInfo **)
+                       palloc0(sizeof(RelOptInfo *) * rel->nparts);
+       }
 
-               foreach(l, root->append_rel_list)
-               {
-                       AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-                       RelOptInfo *childrel;
+       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 != relid)
-                               continue;
+               /* append_rel_list contains all append rels; ignore others */
+               if (appinfo->parent_relid != rti)
+                       continue;
 
-                       childrel = build_simple_rel(root, appinfo->child_relid,
-                                                                               rel);
+               /* find the child RTE, which should already exist */
+               Assert(childRTindex < root->simple_rel_array_size);
+               childrte = root->simple_rte_array[childRTindex];
+               Assert(childrte != NULL);
 
-                       /* Nothing more to do for an unpartitioned table. */
-                       if (!rel->part_scheme)
-                               continue;
+               /* build child RelOptInfo, and add to main query data structures */
+               childrel = build_simple_rel(root, childRTindex, rel);
 
-                       /*
-                        * 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++;
+               /*
+                * 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;
                }
 
-               /* We should have seen all the child partitions. */
-               Assert(cnt_parts == nparts);
+               /* 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);
+               }
        }
 
-       return rel;
+       /* We should have filled all of the part_rels array if it's partitioned */
+       Assert(cnt_parts == rel->nparts);
 }
 
 /*
index 574bb85b50a8ba13a943d42fa692e7b4a96a044e..9e79e1cd63cb9f651b229457f2f1f4f43623ece3 100644 (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,
index b093a3c8ac2b2cbc1f3573e04548247e86f85b08..efb07026a816cdad5bb4e4ab80fff01a185a272c 100644 (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);