]> granicus.if.org Git - postgresql/commitdiff
Minor additional refactoring of planner.c's PathTarget handling.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Mar 2016 15:24:33 +0000 (10:24 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Mar 2016 15:24:55 +0000 (10:24 -0500)
Teach make_group_input_target() and make_window_input_target() to work
entirely with the PathTarget representation of tlists, rather than
constructing a tlist and immediately deconstructing it into PathTarget
format.  In itself this only saves a few palloc's; the bigger picture is
that it opens the door for sharing cost_qual_eval work across all of
planner.c's constructions of PathTargets.  I'll come back to that later.

In support of this, flesh out tlist.c's infrastructure for PathTargets
a bit more.

src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/tlist.c
src/include/optimizer/tlist.h

index a2cd6deb612890ded150234f0ebb205cd5d6d2a9..9d5f9ca62e6b6e7c766ab7ba4ecbb2ef8a9dd9b2 100644 (file)
@@ -128,11 +128,13 @@ static RelOptInfo *create_distinct_paths(PlannerInfo *root,
 static RelOptInfo *create_ordered_paths(PlannerInfo *root,
                                         RelOptInfo *input_rel,
                                         double limit_tuples);
-static PathTarget *make_group_input_target(PlannerInfo *root, List *tlist);
+static PathTarget *make_group_input_target(PlannerInfo *root,
+                                               PathTarget *final_target);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
 static PathTarget *make_window_input_target(PlannerInfo *root,
-                                                List *tlist, List *activeWindows);
+                                                PathTarget *final_target,
+                                                List *activeWindows);
 static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
                                                 List *tlist);
 
@@ -1664,7 +1666,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
                 */
                if (activeWindows)
                        grouping_target = make_window_input_target(root,
-                                                                                                          tlist,
+                                                                                                          final_target,
                                                                                                           activeWindows);
                else
                        grouping_target = final_target;
@@ -1678,7 +1680,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
                have_grouping = (parse->groupClause || parse->groupingSets ||
                                                 parse->hasAggs || root->hasHavingQual);
                if (have_grouping)
-                       scanjoin_target = make_group_input_target(root, tlist);
+                       scanjoin_target = make_group_input_target(root, final_target);
                else
                        scanjoin_target = grouping_target;
 
@@ -3758,10 +3760,10 @@ create_ordered_paths(PlannerInfo *root,
  *
  * If there is grouping or aggregation, the scan/join subplan cannot emit
  * the query's final targetlist; for example, it certainly can't emit any
- * aggregate function calls.  This routine generates the correct target list
+ * aggregate function calls.  This routine generates the correct target
  * for the scan/join subplan.
  *
- * The initial target list passed from the parser already contains entries
+ * The query target list passed from the parser already contains entries
  * for all ORDER BY and GROUP BY expressions, but it will not have entries
  * for variables used only in HAVING clauses; so we need to add those
  * variables to the subplan target list.  Also, we flatten all expressions
@@ -3774,56 +3776,52 @@ create_ordered_paths(PlannerInfo *root,
  * where the a+b target will be used by the Sort/Group steps, and the
  * other targets will be used for computing the final results.
  *
- * 'tlist' is the query's final target list.
+ * 'final_target' is the query's final target list (in PathTarget form)
  *
  * The result is the PathTarget to be computed by the Paths returned from
  * query_planner().
  */
 static PathTarget *
-make_group_input_target(PlannerInfo *root, List *tlist)
+make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 {
        Query      *parse = root->parse;
-       List       *sub_tlist;
+       PathTarget *input_target;
        List       *non_group_cols;
        List       *non_group_vars;
-       ListCell   *tl;
+       int                     i;
+       ListCell   *lc;
 
        /*
-        * We must build a tlist containing all grouping columns, plus any other
-        * Vars mentioned in the targetlist and HAVING qual.
+        * We must build a target containing all grouping columns, plus any other
+        * Vars mentioned in the query's targetlist and HAVING qual.
         */
-       sub_tlist = NIL;
+       input_target = create_empty_pathtarget();
        non_group_cols = NIL;
 
-       foreach(tl, tlist)
+       i = 0;
+       foreach(lc, final_target->exprs)
        {
-               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Expr       *expr = (Expr *) lfirst(lc);
+               Index           sgref = final_target->sortgrouprefs[i];
 
-               if (tle->ressortgroupref && parse->groupClause &&
-                       get_sortgroupref_clause_noerr(tle->ressortgroupref,
-                                                                                 parse->groupClause) != NULL)
+               if (sgref && parse->groupClause &&
+                       get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL)
                {
                        /*
-                        * It's a grouping column, so add it to the result tlist as-is.
+                        * It's a grouping column, so add it to the input target as-is.
                         */
-                       TargetEntry *newtle;
-
-                       newtle = makeTargetEntry(tle->expr,
-                                                                        list_length(sub_tlist) + 1,
-                                                                        NULL,
-                                                                        false);
-                       newtle->ressortgroupref = tle->ressortgroupref;
-                       sub_tlist = lappend(sub_tlist, newtle);
+                       add_column_to_pathtarget(input_target, expr, sgref);
                }
                else
                {
                        /*
                         * Non-grouping column, so just remember the expression for later
-                        * call to pull_var_clause.  There's no need for pull_var_clause
-                        * to examine the TargetEntry node itself.
+                        * call to pull_var_clause.
                         */
-                       non_group_cols = lappend(non_group_cols, tle->expr);
+                       non_group_cols = lappend(non_group_cols, expr);
                }
+
+               i++;
        }
 
        /*
@@ -3834,7 +3832,7 @@ make_group_input_target(PlannerInfo *root, List *tlist)
 
        /*
         * Pull out all the Vars mentioned in non-group cols (plus HAVING), and
-        * add them to the result tlist if not already present.  (A Var used
+        * add them to the input target if not already present.  (A Var used
         * directly as a GROUP BY item will be present already.)  Note this
         * includes Vars used in resjunk items, so we are covering the needs of
         * ORDER BY and window specifications.  Vars used within Aggrefs and
@@ -3844,13 +3842,14 @@ make_group_input_target(PlannerInfo *root, List *tlist)
                                                                         PVC_RECURSE_AGGREGATES |
                                                                         PVC_RECURSE_WINDOWFUNCS |
                                                                         PVC_INCLUDE_PLACEHOLDERS);
-       sub_tlist = add_to_flat_tlist(sub_tlist, non_group_vars);
+       add_new_columns_to_pathtarget(input_target, non_group_vars);
 
        /* clean up cruft */
        list_free(non_group_vars);
        list_free(non_group_cols);
 
-       return create_pathtarget(root, sub_tlist);
+       /* XXX this causes some redundant cost calculation ... */
+       return set_pathtarget_cost_width(root, input_target);
 }
 
 /*
@@ -3964,13 +3963,13 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
  * make_window_input_target
  *       Generate appropriate PathTarget for initial input to WindowAgg nodes.
  *
- * When the query has window functions, this function computes the initial
- * target list to be computed by the node just below the first WindowAgg.
+ * When the query has window functions, this function computes the desired
+ * target to be computed by the node just below the first WindowAgg.
  * This tlist must contain all values needed to evaluate the window functions,
  * compute the final target list, and perform any required final sort step.
  * If multiple WindowAggs are needed, each intermediate one adds its window
- * function results onto this tlist; only the topmost WindowAgg computes the
- * actual desired target list.
+ * function results onto this base tlist; only the topmost WindowAgg computes
+ * the actual desired target list.
  *
  * This function is much like make_group_input_target, though not quite enough
  * like it to share code.  As in that function, we flatten most expressions
@@ -3986,7 +3985,7 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
  * flatten Aggref expressions, since those are to be computed below the
  * window functions and just referenced like Vars above that.
  *
- * 'tlist' is the query's final target list.
+ * 'final_target' is the query's final target list (in PathTarget form)
  * 'activeWindows' is the list of active windows previously identified by
  *                     select_active_windows.
  *
@@ -3995,14 +3994,15 @@ select_active_windows(PlannerInfo *root, WindowFuncLists *wflists)
  */
 static PathTarget *
 make_window_input_target(PlannerInfo *root,
-                                                List *tlist,
+                                                PathTarget *final_target,
                                                 List *activeWindows)
 {
        Query      *parse = root->parse;
+       PathTarget *input_target;
        Bitmapset  *sgrefs;
-       List       *new_tlist;
        List       *flattenable_cols;
        List       *flattenable_vars;
+       int                     i;
        ListCell   *lc;
 
        Assert(parse->hasWindowFuncs);
@@ -4040,52 +4040,49 @@ make_window_input_target(PlannerInfo *root,
        }
 
        /*
-        * Construct a tlist containing all the non-flattenable tlist items, and
-        * save aside the others for a moment.
+        * Construct a target containing all the non-flattenable targetlist items,
+        * and save aside the others for a moment.
         */
-       new_tlist = NIL;
+       input_target = create_empty_pathtarget();
        flattenable_cols = NIL;
 
-       foreach(lc, tlist)
+       i = 0;
+       foreach(lc, final_target->exprs)
        {
-               TargetEntry *tle = (TargetEntry *) lfirst(lc);
+               Expr       *expr = (Expr *) lfirst(lc);
+               Index           sgref = final_target->sortgrouprefs[i];
 
                /*
                 * Don't want to deconstruct window clauses or GROUP BY items.  (Note
                 * that such items can't contain window functions, so it's okay to
                 * compute them below the WindowAgg nodes.)
                 */
-               if (tle->ressortgroupref != 0 &&
-                       bms_is_member(tle->ressortgroupref, sgrefs))
+               if (sgref != 0 && bms_is_member(sgref, sgrefs))
                {
-                       /* Don't want to deconstruct this value, so add to new_tlist */
-                       TargetEntry *newtle;
-
-                       newtle = makeTargetEntry(tle->expr,
-                                                                        list_length(new_tlist) + 1,
-                                                                        NULL,
-                                                                        false);
-                       /* Preserve its sortgroupref marking, in case it's volatile */
-                       newtle->ressortgroupref = tle->ressortgroupref;
-                       new_tlist = lappend(new_tlist, newtle);
+                       /*
+                        * Don't want to deconstruct this value, so add it to the input
+                        * target as-is.
+                        */
+                       add_column_to_pathtarget(input_target, expr, sgref);
                }
                else
                {
                        /*
                         * Column is to be flattened, so just remember the expression for
-                        * later call to pull_var_clause.  There's no need for
-                        * pull_var_clause to examine the TargetEntry node itself.
+                        * later call to pull_var_clause.
                         */
-                       flattenable_cols = lappend(flattenable_cols, tle->expr);
+                       flattenable_cols = lappend(flattenable_cols, expr);
                }
+
+               i++;
        }
 
        /*
         * Pull out all the Vars and Aggrefs mentioned in flattenable columns, and
-        * add them to the result tlist if not already present.  (Some might be
+        * add them to the input target if not already present.  (Some might be
         * there already because they're used directly as window/group clauses.)
         *
-        * Note: it's essential to use PVC_INCLUDE_AGGREGATES here, so that the
+        * Note: it's essential to use PVC_INCLUDE_AGGREGATES here, so that any
         * Aggrefs are placed in the Agg node's tlist and not left to be computed
         * at higher levels.  On the other hand, we should recurse into
         * WindowFuncs to make sure their input expressions are available.
@@ -4094,13 +4091,14 @@ make_window_input_target(PlannerInfo *root,
                                                                           PVC_INCLUDE_AGGREGATES |
                                                                           PVC_RECURSE_WINDOWFUNCS |
                                                                           PVC_INCLUDE_PLACEHOLDERS);
-       new_tlist = add_to_flat_tlist(new_tlist, flattenable_vars);
+       add_new_columns_to_pathtarget(input_target, flattenable_vars);
 
        /* clean up cruft */
        list_free(flattenable_vars);
        list_free(flattenable_cols);
 
-       return create_pathtarget(root, new_tlist);
+       /* XXX this causes some redundant cost calculation ... */
+       return set_pathtarget_cost_width(root, input_target);
 }
 
 /*
index cbc8c2b9fef164f6a3f1aa141b94a2225d3d9bde..9f85dee3872925cb165744c6b99faf00f4cd97b7 100644 (file)
@@ -623,6 +623,17 @@ copy_pathtarget(PathTarget *src)
        return dst;
 }
 
+/*
+ * create_empty_pathtarget
+ *       Create an empty (zero columns, zero cost) PathTarget.
+ */
+PathTarget *
+create_empty_pathtarget(void)
+{
+       /* This is easy, but we don't want callers to hard-wire this ... */
+       return (PathTarget *) palloc0(sizeof(PathTarget));
+}
+
 /*
  * add_column_to_pathtarget
  *             Append a target column to the PathTarget.
@@ -655,6 +666,41 @@ add_column_to_pathtarget(PathTarget *target, Expr *expr, Index sortgroupref)
        }
 }
 
+/*
+ * add_new_column_to_pathtarget
+ *             Append a target column to the PathTarget, but only if it's not
+ *             equal() to any pre-existing target expression.
+ *
+ * The caller cannot specify a sortgroupref, since it would be unclear how
+ * to merge that with a pre-existing column.
+ *
+ * As with make_pathtarget_from_tlist, we leave it to the caller to update
+ * the cost and width fields.
+ */
+void
+add_new_column_to_pathtarget(PathTarget *target, Expr *expr)
+{
+       if (!list_member(target->exprs, expr))
+               add_column_to_pathtarget(target, expr, 0);
+}
+
+/*
+ * add_new_columns_to_pathtarget
+ *             Apply add_new_column_to_pathtarget() for each element of the list.
+ */
+void
+add_new_columns_to_pathtarget(PathTarget *target, List *exprs)
+{
+       ListCell   *lc;
+
+       foreach(lc, exprs)
+       {
+               Expr       *expr = (Expr *) lfirst(lc);
+
+               add_new_column_to_pathtarget(target, expr);
+       }
+}
+
 /*
  * apply_pathtarget_labeling_to_tlist
  *             Apply any sortgrouprefs in the PathTarget to matching tlist entries
index 2c79a80391ec48533f1505ce7f26f72ecebda596..0d745a089148c09efee499f09346f73eb285c61a 100644 (file)
@@ -55,8 +55,11 @@ extern bool grouping_is_hashable(List *groupClause);
 extern PathTarget *make_pathtarget_from_tlist(List *tlist);
 extern List *make_tlist_from_pathtarget(PathTarget *target);
 extern PathTarget *copy_pathtarget(PathTarget *src);
+extern PathTarget *create_empty_pathtarget(void);
 extern void add_column_to_pathtarget(PathTarget *target,
                                                 Expr *expr, Index sortgroupref);
+extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr);
+extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs);
 extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target);
 
 /* Convenience macro to get a PathTarget with valid cost/width fields */