Some preliminary refactoring towards partitionwise join.
authorRobert Haas <[email protected]>
Tue, 14 Mar 2017 22:20:17 +0000 (18:20 -0400)
committerRobert Haas <[email protected]>
Tue, 14 Mar 2017 23:25:47 +0000 (19:25 -0400)
Partitionwise join proposes add a concept of child join relations,
which will have the same relationship with join relations as "other
member" relations do with base relations.  These relations will need
some but not all of the handling that we currently have for join
relations, and some but not all of the handling that we currently have
for appendrels, since they are a mix of the two.  Refactor a little
bit so that the necessary bits of logic are exposed as separate
functions.

Ashutosh Bapat, reviewed and tested by Rajkumar Raghuwanshi and
by me.

Discussion: http://postgr.es/m/CAFjFpRfqotRR6cM3sooBHMHEVdkFfAZ6PyYg4GRZsoMuW08HjQ@mail.gmail.com

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/util/relnode.c

index 4db1d16c79070802446b1580f980a6cda8da40da..43bfd23804c0281c933c165d3f88f46626ae7662 100644 (file)
@@ -129,6 +129,8 @@ 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 void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+                                               List *live_childrels);
 
 
 /*
@@ -1182,19 +1184,11 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 {
        int                     parentRTindex = rti;
        List       *live_childrels = NIL;
-       List       *subpaths = NIL;
-       bool            subpaths_valid = true;
-       List       *partial_subpaths = NIL;
-       bool            partial_subpaths_valid = true;
-       List       *all_child_pathkeys = NIL;
-       List       *all_child_outers = NIL;
        ListCell   *l;
 
        /*
         * Generate access paths for each member relation, and remember the
-        * cheapest path for each one.  Also, identify all pathkeys (orderings)
-        * and parameterizations (required_outer sets) available for the member
-        * relations.
+        * non-dummy children.
         */
        foreach(l, root->append_rel_list)
        {
@@ -1202,7 +1196,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                int                     childRTindex;
                RangeTblEntry *childRTE;
                RelOptInfo *childrel;
-               ListCell   *lcp;
 
                /* append_rel_list contains all append rels; ignore others */
                if (appinfo->parent_relid != parentRTindex)
@@ -1237,6 +1230,45 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                 * Child is live, so add it to the live_childrels list for use below.
                 */
                live_childrels = lappend(live_childrels, childrel);
+       }
+
+       /* Add paths to the "append" relation. */
+       add_paths_to_append_rel(root, rel, live_childrels);
+}
+
+
+/*
+ * add_paths_to_append_rel
+ *             Generate paths for given "append" relation given the set of non-dummy
+ *             child rels.
+ *
+ * The function collects all parameterizations and orderings supported by the
+ * non-dummy children. For every such parameterization or ordering, it creates
+ * an append path collecting one path from each non-dummy child with given
+ * parameterization or ordering. Similarly it collects partial paths from
+ * non-dummy children to create partial append paths.
+ */
+static void
+add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+                                               List *live_childrels)
+{
+       List       *subpaths = NIL;
+       bool            subpaths_valid = true;
+       List       *partial_subpaths = NIL;
+       bool            partial_subpaths_valid = true;
+       List       *all_child_pathkeys = NIL;
+       List       *all_child_outers = NIL;
+       ListCell   *l;
+
+       /*
+        * For every non-dummy child, remember the cheapest path.  Also, identify
+        * all pathkeys (orderings) and parameterizations (required_outer sets)
+        * available for the non-dummy member relations.
+        */
+       foreach(l, live_childrels)
+       {
+               RelOptInfo *childrel = lfirst(l);
+               ListCell   *lcp;
 
                /*
                 * If child has an unparameterized cheapest-total path, add that to
index 0d0068360c128d61a0a64161a292837a29523630..05516689765547970aa84e8121386209274052e9 100644 (file)
@@ -32,6 +32,9 @@ static bool is_dummy_rel(RelOptInfo *rel);
 static void mark_dummy_rel(RelOptInfo *rel);
 static bool restriction_is_constant_false(List *restrictlist,
                                                          bool only_pushed_down);
+static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+                                                       RelOptInfo *rel2, RelOptInfo *joinrel,
+                                                       SpecialJoinInfo *sjinfo, List *restrictlist);
 
 
 /*
@@ -724,6 +727,27 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
                return joinrel;
        }
 
+       /* Add paths to the join relation. */
+       populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,
+                                                               restrictlist);
+
+       bms_free(joinrelids);
+
+       return joinrel;
+}
+
+/*
+ * populate_joinrel_with_paths
+ *       Add paths to the given joinrel for given pair of joining relations. The
+ *       SpecialJoinInfo provides details about the join and the restrictlist
+ *       contains the join clauses and the other clauses applicable for given pair
+ *       of the joining relations.
+ */
+static void
+populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+                                                       RelOptInfo *rel2, RelOptInfo *joinrel,
+                                                       SpecialJoinInfo *sjinfo, List *restrictlist)
+{
        /*
         * Consider paths using each rel as both outer and inner.  Depending on
         * the join type, a provably empty outer or inner rel might mean the join
@@ -868,10 +892,6 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
                        elog(ERROR, "unrecognized join type: %d", (int) sjinfo->jointype);
                        break;
        }
-
-       bms_free(joinrelids);
-
-       return joinrel;
 }
 
 
index caf8291e106d4bf287709827fd6cae83eed5f822..6ab78545c36d8b8fb3c3733979746bb69bb0a0f6 100644 (file)
@@ -49,6 +49,9 @@ static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
 static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel,
                                                  List *joininfo_list,
                                                  List *new_joininfo);
+static void set_foreign_rel_properties(RelOptInfo *joinrel,
+                                                  RelOptInfo *outer_rel, RelOptInfo *inner_rel);
+static void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel);
 
 
 /*
@@ -327,6 +330,82 @@ find_join_rel(PlannerInfo *root, Relids relids)
        return NULL;
 }
 
+/*
+ * set_foreign_rel_properties
+ *             Set up foreign-join fields if outer and inner relation are foreign
+ *             tables (or joins) belonging to the same server and assigned to the same
+ *             user to check access permissions as.
+ *
+ * In addition to an exact match of userid, we allow the case where one side
+ * has zero userid (implying current user) and the other side has explicit
+ * userid that happens to equal the current user; but in that case, pushdown of
+ * the join is only valid for the current user.  The useridiscurrent field
+ * records whether we had to make such an assumption for this join or any
+ * sub-join.
+ *
+ * Otherwise these fields are left invalid, so GetForeignJoinPaths will not be
+ * called for the join relation.
+ *
+ */
+static void
+set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
+                                                  RelOptInfo *inner_rel)
+{
+       if (OidIsValid(outer_rel->serverid) &&
+               inner_rel->serverid == outer_rel->serverid)
+       {
+               if (inner_rel->userid == outer_rel->userid)
+               {
+                       joinrel->serverid = outer_rel->serverid;
+                       joinrel->userid = outer_rel->userid;
+                       joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
+                       joinrel->fdwroutine = outer_rel->fdwroutine;
+               }
+               else if (!OidIsValid(inner_rel->userid) &&
+                                outer_rel->userid == GetUserId())
+               {
+                       joinrel->serverid = outer_rel->serverid;
+                       joinrel->userid = outer_rel->userid;
+                       joinrel->useridiscurrent = true;
+                       joinrel->fdwroutine = outer_rel->fdwroutine;
+               }
+               else if (!OidIsValid(outer_rel->userid) &&
+                                inner_rel->userid == GetUserId())
+               {
+                       joinrel->serverid = outer_rel->serverid;
+                       joinrel->userid = inner_rel->userid;
+                       joinrel->useridiscurrent = true;
+                       joinrel->fdwroutine = outer_rel->fdwroutine;
+               }
+       }
+}
+
+/*
+ * add_join_rel
+ *             Add given join relation to the list of join relations in the given
+ *             PlannerInfo. Also add it to the auxiliary hashtable if there is one.
+ */
+static void
+add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
+{
+       /* GEQO requires us to append the new joinrel to the end of the list! */
+       root->join_rel_list = lappend(root->join_rel_list, joinrel);
+
+       /* store it into the auxiliary hashtable if there is one. */
+       if (root->join_rel_hash)
+       {
+               JoinHashEntry *hentry;
+               bool            found;
+
+               hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
+                                                                                          &(joinrel->relids),
+                                                                                          HASH_ENTER,
+                                                                                          &found);
+               Assert(!found);
+               hentry->join_rel = joinrel;
+       }
+}
+
 /*
  * build_join_rel
  *       Returns relation entry corresponding to the union of two given rels,
@@ -425,46 +504,8 @@ build_join_rel(PlannerInfo *root,
        joinrel->joininfo = NIL;
        joinrel->has_eclass_joins = false;
 
-       /*
-        * Set up foreign-join fields if outer and inner relation are foreign
-        * tables (or joins) belonging to the same server and assigned to the same
-        * user to check access permissions as.  In addition to an exact match of
-        * userid, we allow the case where one side has zero userid (implying
-        * current user) and the other side has explicit userid that happens to
-        * equal the current user; but in that case, pushdown of the join is only
-        * valid for the current user.  The useridiscurrent field records whether
-        * we had to make such an assumption for this join or any sub-join.
-        *
-        * Otherwise these fields are left invalid, so GetForeignJoinPaths will
-        * not be called for the join relation.
-        */
-       if (OidIsValid(outer_rel->serverid) &&
-               inner_rel->serverid == outer_rel->serverid)
-       {
-               if (inner_rel->userid == outer_rel->userid)
-               {
-                       joinrel->serverid = outer_rel->serverid;
-                       joinrel->userid = outer_rel->userid;
-                       joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
-                       joinrel->fdwroutine = outer_rel->fdwroutine;
-               }
-               else if (!OidIsValid(inner_rel->userid) &&
-                                outer_rel->userid == GetUserId())
-               {
-                       joinrel->serverid = outer_rel->serverid;
-                       joinrel->userid = outer_rel->userid;
-                       joinrel->useridiscurrent = true;
-                       joinrel->fdwroutine = outer_rel->fdwroutine;
-               }
-               else if (!OidIsValid(outer_rel->userid) &&
-                                inner_rel->userid == GetUserId())
-               {
-                       joinrel->serverid = outer_rel->serverid;
-                       joinrel->userid = inner_rel->userid;
-                       joinrel->useridiscurrent = true;
-                       joinrel->fdwroutine = outer_rel->fdwroutine;
-               }
-       }
+       /* Compute information relevant to the foreign relations. */
+       set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
 
        /*
         * Create a new tlist containing just the vars that need to be output from
@@ -532,25 +573,8 @@ build_join_rel(PlannerInfo *root,
                is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
                joinrel->consider_parallel = true;
 
-       /*
-        * Add the joinrel to the query's joinrel list, and store it into the
-        * auxiliary hashtable if there is one.  NB: GEQO requires us to append
-        * the new joinrel to the end of the list!
-        */
-       root->join_rel_list = lappend(root->join_rel_list, joinrel);
-
-       if (root->join_rel_hash)
-       {
-               JoinHashEntry *hentry;
-               bool            found;
-
-               hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
-                                                                                          &(joinrel->relids),
-                                                                                          HASH_ENTER,
-                                                                                          &found);
-               Assert(!found);
-               hentry->join_rel = joinrel;
-       }
+       /* Add the joinrel to the PlannerInfo. */
+       add_join_rel(root, joinrel);
 
        /*
         * Also, if dynamic-programming join search is active, add the new joinrel