Postpone generate_gather_paths for topmost scan/join rel.
authorRobert Haas <rhaas@postgresql.org>
Mon, 12 Mar 2018 20:45:15 +0000 (16:45 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 29 Mar 2018 19:40:40 +0000 (15:40 -0400)
Don't call generate_gather_paths for the topmost scan/join relation
when it is initially populated with paths.  Instead, do the work in
grouping_planner.  By itself, this gains nothing; in fact it loses
slightly because we end up calling set_cheapest() for the topmost
scan/join rel twice rather than once.  However, it paves the way for
a future commit which will postpone generate_gather_paths for the
topmost scan/join relation even further, allowing more accurate
costing of parallel paths.

Amit Kapila and Robert Haas.  Earlier versions of this patch (which
different substantially) were reviewed by Dilip Kumar, Amit
Khandekar, Marina Polyakova, and Ashutosh Bapat.

src/backend/optimizer/geqo/geqo_eval.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planner.c

index 0be2a73e05b7dd52ae23a053a5318bfbc5620a99..3ef7d7d8aa222998fb17fada0b5bf9812bad2ae5 100644 (file)
@@ -40,7 +40,7 @@ typedef struct
 } Clump;
 
 static List *merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump,
-                       bool force);
+                       int num_gene, bool force);
 static bool desirable_join(PlannerInfo *root,
                           RelOptInfo *outer_rel, RelOptInfo *inner_rel);
 
@@ -196,7 +196,7 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
                cur_clump->size = 1;
 
                /* Merge it into the clumps list, using only desirable joins */
-               clumps = merge_clump(root, clumps, cur_clump, false);
+               clumps = merge_clump(root, clumps, cur_clump, num_gene, false);
        }
 
        if (list_length(clumps) > 1)
@@ -210,7 +210,7 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
                {
                        Clump      *clump = (Clump *) lfirst(lc);
 
-                       fclumps = merge_clump(root, fclumps, clump, true);
+                       fclumps = merge_clump(root, fclumps, clump, num_gene, true);
                }
                clumps = fclumps;
        }
@@ -235,7 +235,8 @@ gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
  * "desirable" joins.
  */
 static List *
-merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
+merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene,
+                       bool force)
 {
        ListCell   *prev;
        ListCell   *lc;
@@ -267,8 +268,14 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
                                /* Create paths for partitionwise joins. */
                                generate_partitionwise_join_paths(root, joinrel);
 
-                               /* Create GatherPaths for any useful partial paths for rel */
-                               generate_gather_paths(root, joinrel, false);
+                               /*
+                                * Except for the topmost scan/join rel, consider gathering
+                                * partial paths.  We'll do the same for the topmost scan/join
+                                * rel once we know the final targetlist (see
+                                * grouping_planner).
+                                */
+                               if (old_clump->size + new_clump->size < num_gene)
+                                       generate_gather_paths(root, joinrel, false);
 
                                /* Find and save the cheapest paths for this joinrel */
                                set_cheapest(joinrel);
@@ -286,7 +293,7 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
                                 * others.  When no further merge is possible, we'll reinsert
                                 * it into the list.
                                 */
-                               return merge_clump(root, clumps, old_clump, force);
+                               return merge_clump(root, clumps, old_clump, num_gene, force);
                        }
                }
                prev = lc;
index 43f4e757487fbb6f3d434457e3271daf2efa3d45..c4e4db15a6822d1db05dd6dae89371ea539ebfee 100644 (file)
@@ -479,13 +479,20 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
        }
 
        /*
-        * If this is a baserel, consider gathering any partial paths we may have
-        * created for it.  (If we tried to gather inheritance children, we could
+        * If this is a baserel, we should normally consider gathering any partial
+        * paths we may have created for it.
+        *
+        * However, if this is an inheritance child, skip it.  Otherwise, we could
         * end up with a very large number of gather nodes, each trying to grab
-        * its own pool of workers, so don't do this for otherrels.  Instead,
-        * we'll consider gathering partial paths for the parent appendrel.)
+        * its own pool of workers.  Instead, we'll consider gathering partial
+        * paths for the parent appendrel.
+        *
+        * Also, if this is the topmost scan/join rel (that is, the only baserel),
+        * we postpone this until the final scan/join targelist is available (see
+        * grouping_planner).
         */
-       if (rel->reloptkind == RELOPT_BASEREL)
+       if (rel->reloptkind == RELOPT_BASEREL &&
+               bms_membership(root->all_baserels) != BMS_SINGLETON)
                generate_gather_paths(root, rel, false);
 
        /*
@@ -2699,8 +2706,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
                        /* Create paths for partitionwise joins. */
                        generate_partitionwise_join_paths(root, rel);
 
-                       /* Create GatherPaths for any useful partial paths for rel */
-                       generate_gather_paths(root, rel, false);
+                       /*
+                        * Except for the topmost scan/join rel, consider gathering
+                        * partial paths.  We'll do the same for the topmost scan/join rel
+                        * once we know the final targetlist (see grouping_planner).
+                        */
+                       if (lev < levels_needed)
+                               generate_gather_paths(root, rel, false);
 
                        /* Find and save the cheapest paths for this rel */
                        set_cheapest(rel);
index a19f5d0c02a1bb8e880c3b9f365902b5adb5ecc9..26c0feb9df824e5a3d25496634d2cb6ea701eff0 100644 (file)
@@ -1974,6 +1974,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
                        scanjoin_targets = scanjoin_targets_contain_srfs = NIL;
                }
 
+               /*
+                * Generate Gather or Gather Merge paths for the topmost scan/join
+                * relation.  Once that's done, we must re-determine which paths are
+                * cheapest.  (The previously-cheapest path might even have been
+                * pfree'd!)
+                */
+               generate_gather_paths(root, current_rel, false);
+               set_cheapest(current_rel);
+
                /*
                 * Forcibly apply SRF-free scan/join target to all the Paths for the
                 * scan/join rel.