Re: Better solution to final adjustment of combining Aggrefs

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Better solution to final adjustment of combining Aggrefs
Дата
Msg-id 20095.1466892732@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Better solution to final adjustment of combining Aggrefs  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> The patch is not quite finished: as noted in the XXX comment, it'd be
> a good idea to refactor apply_partialaggref_adjustment so that it can
> share code with this function, to ensure they produce identical
> representations of the lower partial Aggref.  But that will just make
> the patch longer, not any more interesting, so I left it out for now.

Here's an extended version that includes that refactoring.  I ended up
deciding that apply_partialaggref_adjustment was useless: stripped of the
actual Aggref-modification logic, it's little more than an iteration over
a pathtarget list, and the fact that examining the top-level nodes is
sufficient seems like an artifact of its one existing caller rather than
general-purpose functionality.  It also had no business being in tlist.c
AFAICS (the fact that putting it there required doubling the length of
tlist.c's #include list should have clued somebody that it didn't belong
there...).  So I moved the loop into make_partialgroup_input_target and
created a separate function for the Aggref-munging part.

While at that I noticed that make_partialgroup_input_target was
misleadingly named and documented: what it produces is the output target
for the partial aggregation step, not the input.  And for that matter
its argument is not what the rest of planner.c calls the final_target.
So this attempts to clean that up as well.

            regards, tom lane

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 2372311..322a18d 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 23,28 ****
--- 23,29 ----
  #include "access/sysattr.h"
  #include "access/xact.h"
  #include "catalog/pg_constraint_fn.h"
+ #include "catalog/pg_type.h"
  #include "executor/executor.h"
  #include "executor/nodeAgg.h"
  #include "foreign/fdwapi.h"
*************** static RelOptInfo *create_ordered_paths(
*** 140,147 ****
                       double limit_tuples);
  static PathTarget *make_group_input_target(PlannerInfo *root,
                          PathTarget *final_target);
! static PathTarget *make_partialgroup_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,
--- 141,148 ----
                       double limit_tuples);
  static PathTarget *make_group_input_target(PlannerInfo *root,
                          PathTarget *final_target);
! static PathTarget *make_partial_grouping_target(PlannerInfo *root,
!                              PathTarget *grouping_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,
*************** create_grouping_paths(PlannerInfo *root,
*** 3456,3467 ****
          Path       *cheapest_partial_path = linitial(input_rel->partial_pathlist);

          /*
!          * Build target list for partial aggregate paths. We cannot reuse the
!          * final target as Aggrefs must be set in partial mode, and we must
!          * also include Aggrefs from the HAVING clause in the target as these
!          * may not be present in the final target.
           */
!         partial_grouping_target = make_partialgroup_input_target(root, target);

          /* Estimate number of partial groups. */
          dNumPartialGroups = get_number_of_groups(root,
--- 3457,3469 ----
          Path       *cheapest_partial_path = linitial(input_rel->partial_pathlist);

          /*
!          * Build target list for partial aggregate paths.  These paths cannot
!          * just emit the same tlist as regular aggregate paths, because (1) we
!          * must include Vars and Aggrefs needed in HAVING, which might not
!          * appear in the result tlist, and (2) the Aggrefs must be set in
!          * partial mode.
           */
!         partial_grouping_target = make_partial_grouping_target(root, target);

          /* Estimate number of partial groups. */
          dNumPartialGroups = get_number_of_groups(root,
*************** make_group_input_target(PlannerInfo *roo
*** 4317,4362 ****
  }

  /*
!  * make_partialgroup_input_target
!  *      Generate appropriate PathTarget for input for Partial Aggregate nodes.
   *
!  * Similar to make_group_input_target(), only we don't recurse into Aggrefs, as
!  * we need these to remain intact so that they can be found later in Combine
!  * Aggregate nodes during set_combineagg_references(). Vars will be still
!  * pulled out of non-Aggref nodes as these will still be required by the
!  * combine aggregate phase.
   *
!  * We also convert any Aggrefs which we do find and put them into partial mode,
!  * this adjusts the Aggref's return type so that the partially calculated
!  * aggregate value can make its way up the execution tree up to the Finalize
!  * Aggregate node.
   */
  static PathTarget *
! make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
  {
      Query       *parse = root->parse;
!     PathTarget *input_target;
      List       *non_group_cols;
      List       *non_group_exprs;
      int            i;
      ListCell   *lc;

!     input_target = create_empty_pathtarget();
      non_group_cols = NIL;

      i = 0;
!     foreach(lc, final_target->exprs)
      {
          Expr       *expr = (Expr *) lfirst(lc);
!         Index        sgref = get_pathtarget_sortgroupref(final_target, i);

          if (sgref && parse->groupClause &&
              get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL)
          {
              /*
!              * It's a grouping column, so add it to the input target as-is.
               */
!             add_column_to_pathtarget(input_target, expr, sgref);
          }
          else
          {
--- 4319,4366 ----
  }

  /*
!  * make_partial_grouping_target
!  *      Generate appropriate PathTarget for output of partial aggregate
!  *      (or partial grouping, if there are no aggregates) nodes.
   *
!  * A partial aggregation node needs to emit all the same aggregates that
!  * a regular aggregation node would, plus any aggregates used in HAVING;
!  * except that the Aggref nodes should be marked as partial aggregates.
   *
!  * In addition, we'd better emit any Vars and PlaceholderVars that are
!  * used outside of Aggrefs in the aggregation tlist and HAVING.  (Presumably,
!  * these would be Vars that are grouped by or used in grouping expressions.)
!  *
!  * grouping_target is the tlist to be emitted by the topmost aggregation step.
!  * We get the HAVING clause out of *root.
   */
  static PathTarget *
! make_partial_grouping_target(PlannerInfo *root, PathTarget *grouping_target)
  {
      Query       *parse = root->parse;
!     PathTarget *partial_target;
      List       *non_group_cols;
      List       *non_group_exprs;
      int            i;
      ListCell   *lc;

!     partial_target = create_empty_pathtarget();
      non_group_cols = NIL;

      i = 0;
!     foreach(lc, grouping_target->exprs)
      {
          Expr       *expr = (Expr *) lfirst(lc);
!         Index        sgref = get_pathtarget_sortgroupref(grouping_target, i);

          if (sgref && parse->groupClause &&
              get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL)
          {
              /*
!              * It's a grouping column, so add it to the partial_target as-is.
!              * (This allows the upper agg step to repeat the grouping calcs.)
               */
!             add_column_to_pathtarget(partial_target, expr, sgref);
          }
          else
          {
*************** make_partialgroup_input_target(PlannerIn
*** 4371,4405 ****
      }

      /*
!      * If there's a HAVING clause, we'll need the Aggrefs it uses, too.
       */
      if (parse->havingQual)
          non_group_cols = lappend(non_group_cols, parse->havingQual);

      /*
!      * Pull out all the Vars mentioned in non-group cols (plus HAVING), and
!      * 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 will be
!      * ignored and the Aggrefs themselves will be added to the PathTarget.
       */
      non_group_exprs = pull_var_clause((Node *) non_group_cols,
                                        PVC_INCLUDE_AGGREGATES |
                                        PVC_RECURSE_WINDOWFUNCS |
                                        PVC_INCLUDE_PLACEHOLDERS);

!     add_new_columns_to_pathtarget(input_target, non_group_exprs);

      /* clean up cruft */
      list_free(non_group_exprs);
      list_free(non_group_cols);

-     /* Adjust Aggrefs to put them in partial mode. */
-     apply_partialaggref_adjustment(input_target);
-
      /* XXX this causes some redundant cost calculation ... */
!     return set_pathtarget_cost_width(root, input_target);
  }

  /*
--- 4375,4457 ----
      }

      /*
!      * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
       */
      if (parse->havingQual)
          non_group_cols = lappend(non_group_cols, parse->havingQual);

      /*
!      * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
!      * non-group cols (plus HAVING), and add them to the partial_target if not
!      * already present.  (An expression 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.
       */
      non_group_exprs = pull_var_clause((Node *) non_group_cols,
                                        PVC_INCLUDE_AGGREGATES |
                                        PVC_RECURSE_WINDOWFUNCS |
                                        PVC_INCLUDE_PLACEHOLDERS);

!     add_new_columns_to_pathtarget(partial_target, non_group_exprs);
!
!     /*
!      * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
!      * are at the top level of the target list, so we can just scan the list
!      * rather than recursing through the expression trees.
!      */
!     foreach(lc, partial_target->exprs)
!     {
!         Aggref       *aggref = (Aggref *) lfirst(lc);
!
!         if (IsA(aggref, Aggref))
!         {
!             Aggref       *newaggref;
!
!             /*
!              * We shouldn't need to copy the substructure of the Aggref node,
!              * but flat-copy the node itself to avoid damaging other trees.
!              */
!             newaggref = makeNode(Aggref);
!             memcpy(newaggref, aggref, sizeof(Aggref));
!
!             /* XXX assume serialization required */
!             mark_partial_aggref(newaggref, true);
!
!             lfirst(lc) = newaggref;
!         }
!     }

      /* clean up cruft */
      list_free(non_group_exprs);
      list_free(non_group_cols);

      /* XXX this causes some redundant cost calculation ... */
!     return set_pathtarget_cost_width(root, partial_target);
! }
!
! /*
!  * mark_partial_aggref
!  *      Adjust an Aggref to make it represent the output of partial aggregation.
!  *
!  * The Aggref node is modified in-place; caller must do any copying required.
!  */
! void
! mark_partial_aggref(Aggref *agg, bool serialize)
! {
!     /* aggtranstype should be computed by this point */
!     Assert(OidIsValid(agg->aggtranstype));
!
!     /*
!      * Normally, a partial aggregate returns the aggregate's transition type;
!      * but if that's INTERNAL and we're serializing, it returns BYTEA instead.
!      */
!     if (agg->aggtranstype == INTERNALOID && serialize)
!         agg->aggoutputtype = BYTEAOID;
!     else
!         agg->aggoutputtype = agg->aggtranstype;
!
!     /* flag it as partial */
!     agg->aggpartial = true;
  }

  /*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 17edc27..e02cf18 100644
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
*************** static Node *fix_scan_expr_mutator(Node
*** 104,111 ****
  static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
  static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
  static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset);
! static void set_combineagg_references(PlannerInfo *root, Plan *plan,
!                           int rtoffset);
  static void set_dummy_tlist_references(Plan *plan, int rtoffset);
  static indexed_tlist *build_tlist_index(List *tlist);
  static Var *search_indexed_tlist_for_var(Var *var,
--- 104,110 ----
  static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
  static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
  static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset);
! static Node *convert_combining_aggrefs(Node *node, void *context);
  static void set_dummy_tlist_references(Plan *plan, int rtoffset);
  static indexed_tlist *build_tlist_index(List *tlist);
  static Var *search_indexed_tlist_for_var(Var *var,
*************** static Var *search_indexed_tlist_for_sor
*** 119,126 ****
                                        Index sortgroupref,
                                        indexed_tlist *itlist,
                                        Index newvarno);
- static Var *search_indexed_tlist_for_partial_aggref(Aggref *aggref,
-                                       indexed_tlist *itlist, Index newvarno);
  static List *fix_join_expr(PlannerInfo *root,
                List *clauses,
                indexed_tlist *outer_itlist,
--- 118,123 ----
*************** static Node *fix_upper_expr(PlannerInfo
*** 135,147 ****
                 int rtoffset);
  static Node *fix_upper_expr_mutator(Node *node,
                         fix_upper_expr_context *context);
- static Node *fix_combine_agg_expr(PlannerInfo *root,
-                      Node *node,
-                      indexed_tlist *subplan_itlist,
-                      Index newvarno,
-                      int rtoffset);
- static Node *fix_combine_agg_expr_mutator(Node *node,
-                              fix_upper_expr_context *context);
  static List *set_returning_clause_references(PlannerInfo *root,
                                  List *rlist,
                                  Plan *topplan,
--- 132,137 ----
*************** static bool extract_query_dependencies_w
*** 171,190 ****
   * 3. We adjust Vars in upper plan nodes to refer to the outputs of their
   * subplans.
   *
!  * 4. PARAM_MULTIEXPR Params are replaced by regular PARAM_EXEC Params,
   * now that we have finished planning all MULTIEXPR subplans.
   *
!  * 5. We compute regproc OIDs for operators (ie, we look up the function
   * that implements each op).
   *
!  * 6. We create lists of specific objects that the plan depends on.
   * This will be used by plancache.c to drive invalidation of cached plans.
   * Relation dependencies are represented by OIDs, and everything else by
   * PlanInvalItems (this distinction is motivated by the shared-inval APIs).
   * Currently, relations and user-defined functions are the only types of
   * objects that are explicitly tracked this way.
   *
!  * 7. We assign every plan node in the tree a unique ID.
   *
   * We also perform one final optimization step, which is to delete
   * SubqueryScan plan nodes that aren't doing anything useful (ie, have
--- 161,183 ----
   * 3. We adjust Vars in upper plan nodes to refer to the outputs of their
   * subplans.
   *
!  * 4. Aggrefs in Agg plan nodes need to be adjusted in some cases involving
!  * partial aggregation or minmax aggregate optimization.
!  *
!  * 5. PARAM_MULTIEXPR Params are replaced by regular PARAM_EXEC Params,
   * now that we have finished planning all MULTIEXPR subplans.
   *
!  * 6. We compute regproc OIDs for operators (ie, we look up the function
   * that implements each op).
   *
!  * 7. We create lists of specific objects that the plan depends on.
   * This will be used by plancache.c to drive invalidation of cached plans.
   * Relation dependencies are represented by OIDs, and everything else by
   * PlanInvalItems (this distinction is motivated by the shared-inval APIs).
   * Currently, relations and user-defined functions are the only types of
   * objects that are explicitly tracked this way.
   *
!  * 8. We assign every plan node in the tree a unique ID.
   *
   * We also perform one final optimization step, which is to delete
   * SubqueryScan plan nodes that aren't doing anything useful (ie, have
*************** set_plan_refs(PlannerInfo *root, Plan *p
*** 678,692 ****
              break;
          case T_Agg:
              {
!                 Agg           *aggplan = (Agg *) plan;

!                 if (aggplan->combineStates)
!                     set_combineagg_references(root, plan, rtoffset);
!                 else
!                     set_upper_references(root, plan, rtoffset);

!                 break;
              }
          case T_Group:
              set_upper_references(root, plan, rtoffset);
              break;
--- 671,697 ----
              break;
          case T_Agg:
              {
!                 Agg           *agg = (Agg *) plan;

!                 /*
!                  * If this node is combining partial-aggregation results, we
!                  * must convert its Aggrefs to contain references to the
!                  * partial-aggregate subexpressions that will be available
!                  * from the child plan node.
!                  */
!                 if (agg->combineStates)
!                 {
!                     plan->targetlist = (List *)
!                         convert_combining_aggrefs((Node *) plan->targetlist,
!                                                   NULL);
!                     plan->qual = (List *)
!                         convert_combining_aggrefs((Node *) plan->qual,
!                                                   NULL);
!                 }

!                 set_upper_references(root, plan, rtoffset);
              }
+             break;
          case T_Group:
              set_upper_references(root, plan, rtoffset);
              break;
*************** set_upper_references(PlannerInfo *root,
*** 1720,1789 ****
  }

  /*
!  * set_combineagg_references
!  *      This serves the same function as set_upper_references(), but treats
!  *      Aggrefs differently. Here we transform Aggref nodes args to suit the
!  *      combine aggregate phase. This means that the Aggref->args are converted
!  *      to reference the corresponding aggregate function in the subplan rather
!  *      than simple Var(s), as would be the case for a non-combine aggregate
!  *      node.
   */
! static void
! set_combineagg_references(PlannerInfo *root, Plan *plan, int rtoffset)
  {
!     Plan       *subplan = plan->lefttree;
!     indexed_tlist *subplan_itlist;
!     List       *output_targetlist;
!     ListCell   *l;

!     Assert(IsA(plan, Agg));
!     Assert(((Agg *) plan)->combineStates);

!     subplan_itlist = build_tlist_index(subplan->targetlist);

!     output_targetlist = NIL;

!     foreach(l, plan->targetlist)
!     {
!         TargetEntry *tle = (TargetEntry *) lfirst(l);
!         Node       *newexpr;

!         /* If it's a non-Var sort/group item, first try to match by sortref */
!         if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
!         {
!             newexpr = (Node *)
!                 search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
!                                                       tle->ressortgroupref,
!                                                       subplan_itlist,
!                                                       OUTER_VAR);
!             if (!newexpr)
!                 newexpr = fix_combine_agg_expr(root,
!                                                (Node *) tle->expr,
!                                                subplan_itlist,
!                                                OUTER_VAR,
!                                                rtoffset);
!         }
!         else
!             newexpr = fix_combine_agg_expr(root,
!                                            (Node *) tle->expr,
!                                            subplan_itlist,
!                                            OUTER_VAR,
!                                            rtoffset);
!         tle = flatCopyTargetEntry(tle);
!         tle->expr = (Expr *) newexpr;
!         output_targetlist = lappend(output_targetlist, tle);
      }
!
!     plan->targetlist = output_targetlist;
!
!     plan->qual = (List *)
!         fix_combine_agg_expr(root,
!                              (Node *) plan->qual,
!                              subplan_itlist,
!                              OUTER_VAR,
!                              rtoffset);
!
!     pfree(subplan_itlist);
  }

  /*
--- 1725,1792 ----
  }

  /*
!  * Recursively scan an expression tree and convert Aggrefs to the proper
!  * intermediate form for combining aggregates.  This means (1) replacing each
!  * one's argument list with a single argument that is the original Aggref
!  * modified to show partial aggregation and (2) changing the upper Aggref to
!  * show combining aggregation.
!  *
!  * After this step, set_upper_references will replace the partial Aggrefs
!  * with Vars referencing the lower Agg plan node's outputs, so that the final
!  * form seen by the executor is a combining Aggref with a Var as input.
!  *
!  * It's rather messy to postpone this step until setrefs.c; ideally it'd be
!  * done in createplan.c.  The difficulty is that once we modify the Aggref
!  * expressions, they will no longer be equal() to their original form and
!  * so cross-plan-node-level matches will fail.  So this has to happen after
!  * the plan node above the Agg has resolved its subplan references.
   */
! static Node *
! convert_combining_aggrefs(Node *node, void *context)
  {
!     if (node == NULL)
!         return NULL;
!     if (IsA(node, Aggref))
!     {
!         Aggref       *orig_agg = (Aggref *) node;
!         Aggref       *child_agg;
!         Aggref       *parent_agg;

!         /*
!          * Since aggregate calls can't be nested, we needn't recurse into the
!          * arguments.  But for safety, flat-copy the Aggref node itself rather
!          * than modifying it in-place.
!          */
!         child_agg = makeNode(Aggref);
!         memcpy(child_agg, orig_agg, sizeof(Aggref));

!         /*
!          * For the parent Aggref, we want to copy all the fields of the
!          * original aggregate *except* the args list.  Rather than explicitly
!          * knowing what they all are here, we can momentarily modify child_agg
!          * to provide a source for copyObject.
!          */
!         child_agg->args = NIL;
!         parent_agg = (Aggref *) copyObject(child_agg);
!         child_agg->args = orig_agg->args;

!         /*
!          * Now, set up child_agg to represent the first phase of partial
!          * aggregation.  XXX assume serialization required.
!          */
!         mark_partial_aggref(child_agg, true);

!         /*
!          * And set up parent_agg to represent the second phase.
!          */
!         parent_agg->args = list_make1(makeTargetEntry((Expr *) child_agg,
!                                                       1, NULL, false));
!         parent_agg->aggcombine = true;

!         return (Node *) parent_agg;
      }
!     return expression_tree_mutator(node, convert_combining_aggrefs,
!                                    (void *) context);
  }

  /*
*************** search_indexed_tlist_for_sortgroupref(No
*** 2053,2126 ****
  }

  /*
-  * search_indexed_tlist_for_partial_aggref - find an Aggref in an indexed tlist
-  *
-  * Aggrefs for partial aggregates have their aggoutputtype adjusted to set it
-  * to the aggregate state's type, or serialization type. This means that a
-  * standard equal() comparison won't match when comparing an Aggref which is
-  * in partial mode with an Aggref which is not. Here we manually compare all of
-  * the fields apart from aggoutputtype.
-  */
- static Var *
- search_indexed_tlist_for_partial_aggref(Aggref *aggref, indexed_tlist *itlist,
-                                         Index newvarno)
- {
-     ListCell   *lc;
-
-     foreach(lc, itlist->tlist)
-     {
-         TargetEntry *tle = (TargetEntry *) lfirst(lc);
-
-         if (IsA(tle->expr, Aggref))
-         {
-             Aggref       *tlistaggref = (Aggref *) tle->expr;
-             Var           *newvar;
-
-             if (aggref->aggfnoid != tlistaggref->aggfnoid)
-                 continue;
-             if (aggref->aggtype != tlistaggref->aggtype)
-                 continue;
-             /* ignore aggoutputtype */
-             if (aggref->aggcollid != tlistaggref->aggcollid)
-                 continue;
-             if (aggref->inputcollid != tlistaggref->inputcollid)
-                 continue;
-             /* ignore aggtranstype and aggargtypes, should be redundant */
-             if (!equal(aggref->aggdirectargs, tlistaggref->aggdirectargs))
-                 continue;
-             if (!equal(aggref->args, tlistaggref->args))
-                 continue;
-             if (!equal(aggref->aggorder, tlistaggref->aggorder))
-                 continue;
-             if (!equal(aggref->aggdistinct, tlistaggref->aggdistinct))
-                 continue;
-             if (!equal(aggref->aggfilter, tlistaggref->aggfilter))
-                 continue;
-             if (aggref->aggstar != tlistaggref->aggstar)
-                 continue;
-             if (aggref->aggvariadic != tlistaggref->aggvariadic)
-                 continue;
-
-             /*
-              * it would be harmless to compare aggcombine and aggpartial, but
-              * it's also unnecessary
-              */
-             if (aggref->aggkind != tlistaggref->aggkind)
-                 continue;
-             if (aggref->agglevelsup != tlistaggref->agglevelsup)
-                 continue;
-
-             newvar = makeVarFromTargetEntry(newvarno, tle);
-             newvar->varnoold = 0;        /* wasn't ever a plain Var */
-             newvar->varoattno = 0;
-
-             return newvar;
-         }
-     }
-     return NULL;
- }
-
- /*
   * fix_join_expr
   *       Create a new set of targetlist entries or join qual clauses by
   *       changing the varno/varattno values of variables in the clauses
--- 2056,2061 ----
*************** fix_upper_expr_mutator(Node *node, fix_u
*** 2391,2496 ****
  }

  /*
-  * fix_combine_agg_expr
-  *      Like fix_upper_expr() but additionally adjusts the Aggref->args of
-  *      Aggrefs so that they references the corresponding Aggref in the subplan.
-  */
- static Node *
- fix_combine_agg_expr(PlannerInfo *root,
-                      Node *node,
-                      indexed_tlist *subplan_itlist,
-                      Index newvarno,
-                      int rtoffset)
- {
-     fix_upper_expr_context context;
-
-     context.root = root;
-     context.subplan_itlist = subplan_itlist;
-     context.newvarno = newvarno;
-     context.rtoffset = rtoffset;
-     return fix_combine_agg_expr_mutator(node, &context);
- }
-
- static Node *
- fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context)
- {
-     Var           *newvar;
-
-     if (node == NULL)
-         return NULL;
-     if (IsA(node, Var))
-     {
-         Var           *var = (Var *) node;
-
-         newvar = search_indexed_tlist_for_var(var,
-                                               context->subplan_itlist,
-                                               context->newvarno,
-                                               context->rtoffset);
-         if (!newvar)
-             elog(ERROR, "variable not found in subplan target list");
-         return (Node *) newvar;
-     }
-     if (IsA(node, PlaceHolderVar))
-     {
-         PlaceHolderVar *phv = (PlaceHolderVar *) node;
-
-         /* See if the PlaceHolderVar has bubbled up from a lower plan node */
-         if (context->subplan_itlist->has_ph_vars)
-         {
-             newvar = search_indexed_tlist_for_non_var((Node *) phv,
-                                                       context->subplan_itlist,
-                                                       context->newvarno);
-             if (newvar)
-                 return (Node *) newvar;
-         }
-         /* If not supplied by input plan, evaluate the contained expr */
-         return fix_upper_expr_mutator((Node *) phv->phexpr, context);
-     }
-     if (IsA(node, Param))
-         return fix_param_node(context->root, (Param *) node);
-     if (IsA(node, Aggref))
-     {
-         Aggref       *aggref = (Aggref *) node;
-
-         newvar = search_indexed_tlist_for_partial_aggref(aggref,
-                                                      context->subplan_itlist,
-                                                          context->newvarno);
-         if (newvar)
-         {
-             Aggref       *newaggref;
-             TargetEntry *newtle;
-
-             /*
-              * Now build a new TargetEntry for the Aggref's arguments which is
-              * a single Var which references the corresponding AggRef in the
-              * node below.
-              */
-             newtle = makeTargetEntry((Expr *) newvar, 1, NULL, false);
-             newaggref = (Aggref *) copyObject(aggref);
-             newaggref->args = list_make1(newtle);
-             newaggref->aggcombine = true;
-
-             return (Node *) newaggref;
-         }
-         else
-             elog(ERROR, "Aggref not found in subplan target list");
-     }
-     /* Try matching more complex expressions too, if tlist has any */
-     if (context->subplan_itlist->has_non_vars)
-     {
-         newvar = search_indexed_tlist_for_non_var(node,
-                                                   context->subplan_itlist,
-                                                   context->newvarno);
-         if (newvar)
-             return (Node *) newvar;
-     }
-     fix_expr_common(context->root, node);
-     return expression_tree_mutator(node,
-                                    fix_combine_agg_expr_mutator,
-                                    (void *) context);
- }
-
- /*
   * set_returning_clause_references
   *        Perform setrefs.c's work on a RETURNING targetlist
   *
--- 2326,2331 ----
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 5fa80ac..68096b3 100644
*** a/src/backend/optimizer/util/tlist.c
--- b/src/backend/optimizer/util/tlist.c
***************
*** 14,25 ****
   */
  #include "postgres.h"

- #include "access/htup_details.h"
- #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/tlist.h"
- #include "utils/syscache.h"


  /*****************************************************************************
--- 14,22 ----
*************** apply_pathtarget_labeling_to_tlist(List
*** 762,812 ****
          i++;
      }
  }
-
- /*
-  * apply_partialaggref_adjustment
-  *      Convert PathTarget to be suitable for a partial aggregate node. We simply
-  *      adjust any Aggref nodes found in the target and set the aggoutputtype
-  *      appropriately. This allows exprType() to return the
-  *      actual type that will be produced.
-  *
-  * Note: We expect 'target' to be a flat target list and not have Aggrefs buried
-  * within other expressions.
-  */
- void
- apply_partialaggref_adjustment(PathTarget *target)
- {
-     ListCell   *lc;
-
-     foreach(lc, target->exprs)
-     {
-         Aggref       *aggref = (Aggref *) lfirst(lc);
-
-         if (IsA(aggref, Aggref))
-         {
-             Aggref       *newaggref;
-
-             newaggref = (Aggref *) copyObject(aggref);
-
-             /*
-              * Normally, a partial aggregate returns the aggregate's
-              * transition type, but if that's INTERNAL, it returns BYTEA
-              * instead.  (XXX this assumes we're doing parallel aggregate with
-              * serialization; later we might need an argument to tell this
-              * function whether we're doing parallel or just local partial
-              * aggregation.)
-              */
-             Assert(OidIsValid(newaggref->aggtranstype));
-
-             if (newaggref->aggtranstype == INTERNALOID)
-                 newaggref->aggoutputtype = BYTEAOID;
-             else
-                 newaggref->aggoutputtype = newaggref->aggtranstype;
-
-             /* flag it as partial */
-             newaggref->aggpartial = true;
-
-             lfirst(lc) = newaggref;
-         }
-     }
- }
--- 759,761 ----
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index 4161bcf..0d20976 100644
*** a/src/include/optimizer/planner.h
--- b/src/include/optimizer/planner.h
*************** extern bool is_dummy_plan(Plan *plan);
*** 46,51 ****
--- 46,53 ----
  extern RowMarkType select_rowmark_type(RangeTblEntry *rte,
                      LockClauseStrength strength);

+ extern void mark_partial_aggref(Aggref *agg, bool serialize);
+
  extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
                               double tuple_fraction);

diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index de58db1..0d745a0 100644
*** a/src/include/optimizer/tlist.h
--- b/src/include/optimizer/tlist.h
*************** extern void add_column_to_pathtarget(Pat
*** 61,67 ****
  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);
- extern void apply_partialaggref_adjustment(PathTarget *target);

  /* Convenience macro to get a PathTarget with valid cost/width fields */
  #define create_pathtarget(root, tlist) \
--- 61,66 ----

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Better solution to final adjustment of combining Aggrefs
Следующее
От: Steve Crawford
Дата:
Сообщение: Re: Bug in to_timestamp().