PostgreSQL Source Code  git master
planagg.c File Reference
Include dependency graph for planagg.c:

Go to the source code of this file.

Functions

static bool find_minmax_aggs_walker (Node *node, List **context)
 
static bool build_minmax_path (PlannerInfo *root, MinMaxAggInfo *mminfo, Oid eqop, Oid sortop, bool nulls_first)
 
static void minmax_qp_callback (PlannerInfo *root, void *extra)
 
static Oid fetch_agg_sort_op (Oid aggfnoid)
 
void preprocess_minmax_aggregates (PlannerInfo *root)
 

Function Documentation

◆ build_minmax_path()

static bool build_minmax_path ( PlannerInfo root,
MinMaxAggInfo mminfo,
Oid  eqop,
Oid  sortop,
bool  nulls_first 
)
static

Definition at line 342 of file planagg.c.

References PlannerInfo::append_rel_list, apply_projection_to_path(), NullTest::arg, NullTest::argisrow, Assert, assignSortGroupRef(), copyObject, create_pathtarget, PlannerInfo::eq_classes, SortGroupClause::eqop, FLOAT8PASSBYVAL, get_cheapest_fractional_path_for_pathkeys(), SortGroupClause::hashable, PlannerInfo::hasHavingQual, IncrementVarSublevelsUp(), PlannerInfo::init_plans, Int64GetDatum(), InvalidOid, IS_NOT_NULL, PlannerInfo::join_info_list, lcons(), PlannerInfo::limit_tuples, list_make1, list_member(), NullTest::location, makeConst(), makeNode, makeTargetEntry(), minmax_qp_callback(), NIL, SortGroupClause::nulls_first, NullTest::nulltesttype, PlannerInfo::outer_params, palloc(), PlannerInfo::parent_root, parse(), PlannerInfo::parse, MinMaxAggInfo::path, MinMaxAggInfo::pathcost, RelOptInfo::pathlist, PlannerInfo::placeholder_list, PlannerInfo::plan_params, PlannerInfo::processed_tlist, pstrdup(), PlannerInfo::query_level, PlannerInfo::query_pathkeys, query_planner(), RelOptInfo::rows, SortGroupClause::sortop, SS_charge_for_initplans(), SS_identify_outer_params(), Path::startup_cost, MinMaxAggInfo::subroot, MinMaxAggInfo::target, SortGroupClause::tleSortGroupRef, Path::total_cost, and PlannerInfo::tuple_fraction.

Referenced by preprocess_minmax_aggregates().

344 {
345  PlannerInfo *subroot;
346  Query *parse;
347  TargetEntry *tle;
348  List *tlist;
349  NullTest *ntest;
350  SortGroupClause *sortcl;
351  RelOptInfo *final_rel;
352  Path *sorted_path;
353  Cost path_cost;
354  double path_fraction;
355 
356  /*
357  * We are going to construct what is effectively a sub-SELECT query, so
358  * clone the current query level's state and adjust it to make it look
359  * like a subquery. Any outer references will now be one level higher
360  * than before. (This means that when we are done, there will be no Vars
361  * of level 1, which is why the subquery can become an initplan.)
362  */
363  subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo));
364  memcpy(subroot, root, sizeof(PlannerInfo));
365  subroot->query_level++;
366  subroot->parent_root = root;
367  /* reset subplan-related stuff */
368  subroot->plan_params = NIL;
369  subroot->outer_params = NULL;
370  subroot->init_plans = NIL;
371 
372  subroot->parse = parse = copyObject(root->parse);
373  IncrementVarSublevelsUp((Node *) parse, 1, 1);
374 
375  /* append_rel_list might contain outer Vars? */
376  subroot->append_rel_list = copyObject(root->append_rel_list);
377  IncrementVarSublevelsUp((Node *) subroot->append_rel_list, 1, 1);
378  /* There shouldn't be any OJ info to translate, as yet */
379  Assert(subroot->join_info_list == NIL);
380  /* and we haven't made equivalence classes, either */
381  Assert(subroot->eq_classes == NIL);
382  /* and we haven't created PlaceHolderInfos, either */
383  Assert(subroot->placeholder_list == NIL);
384 
385  /*----------
386  * Generate modified query of the form
387  * (SELECT col FROM tab
388  * WHERE col IS NOT NULL AND existing-quals
389  * ORDER BY col ASC/DESC
390  * LIMIT 1)
391  *----------
392  */
393  /* single tlist entry that is the aggregate target */
394  tle = makeTargetEntry(copyObject(mminfo->target),
395  (AttrNumber) 1,
396  pstrdup("agg_target"),
397  false);
398  tlist = list_make1(tle);
399  subroot->processed_tlist = parse->targetList = tlist;
400 
401  /* No HAVING, no DISTINCT, no aggregates anymore */
402  parse->havingQual = NULL;
403  subroot->hasHavingQual = false;
404  parse->distinctClause = NIL;
405  parse->hasDistinctOn = false;
406  parse->hasAggs = false;
407 
408  /* Build "target IS NOT NULL" expression */
409  ntest = makeNode(NullTest);
410  ntest->nulltesttype = IS_NOT_NULL;
411  ntest->arg = copyObject(mminfo->target);
412  /* we checked it wasn't a rowtype in find_minmax_aggs_walker */
413  ntest->argisrow = false;
414  ntest->location = -1;
415 
416  /* User might have had that in WHERE already */
417  if (!list_member((List *) parse->jointree->quals, ntest))
418  parse->jointree->quals = (Node *)
419  lcons(ntest, (List *) parse->jointree->quals);
420 
421  /* Build suitable ORDER BY clause */
422  sortcl = makeNode(SortGroupClause);
423  sortcl->tleSortGroupRef = assignSortGroupRef(tle, subroot->processed_tlist);
424  sortcl->eqop = eqop;
425  sortcl->sortop = sortop;
426  sortcl->nulls_first = nulls_first;
427  sortcl->hashable = false; /* no need to make this accurate */
428  parse->sortClause = list_make1(sortcl);
429 
430  /* set up expressions for LIMIT 1 */
431  parse->limitOffset = NULL;
432  parse->limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
433  sizeof(int64),
434  Int64GetDatum(1), false,
436 
437  /*
438  * Generate the best paths for this query, telling query_planner that we
439  * have LIMIT 1.
440  */
441  subroot->tuple_fraction = 1.0;
442  subroot->limit_tuples = 1.0;
443 
444  final_rel = query_planner(subroot, minmax_qp_callback, NULL);
445 
446  /*
447  * Since we didn't go through subquery_planner() to handle the subquery,
448  * we have to do some of the same cleanup it would do, in particular cope
449  * with params and initplans used within this subquery. (This won't
450  * matter if we end up not using the subplan.)
451  */
452  SS_identify_outer_params(subroot);
453  SS_charge_for_initplans(subroot, final_rel);
454 
455  /*
456  * Get the best presorted path, that being the one that's cheapest for
457  * fetching just one row. If there's no such path, fail.
458  */
459  if (final_rel->rows > 1.0)
460  path_fraction = 1.0 / final_rel->rows;
461  else
462  path_fraction = 1.0;
463 
464  sorted_path =
466  subroot->query_pathkeys,
467  NULL,
468  path_fraction);
469  if (!sorted_path)
470  return false;
471 
472  /*
473  * The path might not return exactly what we want, so fix that. (We
474  * assume that this won't change any conclusions about which was the
475  * cheapest path.)
476  */
477  sorted_path = apply_projection_to_path(subroot, final_rel, sorted_path,
478  create_pathtarget(subroot,
479  subroot->processed_tlist));
480 
481  /*
482  * Determine cost to get just the first row of the presorted path.
483  *
484  * Note: cost calculation here should match
485  * compare_fractional_path_costs().
486  */
487  path_cost = sorted_path->startup_cost +
488  path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
489 
490  /* Save state for further processing */
491  mminfo->subroot = subroot;
492  mminfo->path = sorted_path;
493  mminfo->pathcost = path_cost;
494 
495  return true;
496 }
Path * apply_projection_to_path(PlannerInfo *root, RelOptInfo *rel, Path *path, PathTarget *target)
Definition: pathnode.c:2610
Node * limitOffset
Definition: parsenodes.h:160
#define NIL
Definition: pg_list.h:65
static void minmax_qp_callback(PlannerInfo *root, void *extra)
Definition: planagg.c:502
Index assignSortGroupRef(TargetEntry *tle, List *tlist)
Query * parse
Definition: pathnodes.h:179
List * plan_params
Definition: pathnodes.h:193
List * sortClause
Definition: parsenodes.h:158
List * query_pathkeys
Definition: pathnodes.h:298
List * join_info_list
Definition: pathnodes.h:283
void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, int min_sublevels_up)
Definition: rewriteManip.c:773
FromExpr * jointree
Definition: parsenodes.h:138
PlannerInfo * parent_root
Definition: pathnodes.h:185
char * pstrdup(const char *in)
Definition: mcxt.c:1186
bool hasAggs
Definition: parsenodes.h:125
void SS_charge_for_initplans(PlannerInfo *root, RelOptInfo *final_rel)
Definition: subselect.c:2061
Index tleSortGroupRef
Definition: parsenodes.h:1234
Definition: nodes.h:525
RelOptInfo * query_planner(PlannerInfo *root, query_pathkeys_callback qp_callback, void *qp_extra)
Definition: planmain.c:55
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:297
Node * quals
Definition: primnodes.h:1497
bool hasDistinctOn
Definition: parsenodes.h:129
List * targetList
Definition: parsenodes.h:140
#define list_make1(x1)
Definition: pg_list.h:227
double tuple_fraction
Definition: pathnodes.h:336
List * distinctClause
Definition: parsenodes.h:156
bool list_member(const List *list, const void *datum)
Definition: list.c:614
double limit_tuples
Definition: pathnodes.h:337
Cost startup_cost
Definition: pathnodes.h:1127
Expr * arg
Definition: primnodes.h:1205
Node * limitCount
Definition: parsenodes.h:161
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
Path * get_cheapest_fractional_path_for_pathkeys(List *paths, List *pathkeys, Relids required_outer, double fraction)
Definition: pathkeys.c:395
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1699
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:236
List * append_rel_list
Definition: pathnodes.h:290
NullTestType nulltesttype
Definition: primnodes.h:1206
List * init_plans
Definition: pathnodes.h:259
double rows
Definition: pathnodes.h:646
#define InvalidOid
Definition: postgres_ext.h:36
Cost total_cost
Definition: pathnodes.h:1128
List * lcons(void *datum, List *list)
Definition: list.c:454
#define makeNode(_type_)
Definition: nodes.h:573
#define FLOAT8PASSBYVAL
Definition: c.h:497
#define Assert(condition)
Definition: c.h:739
Expr * target
Definition: pathnodes.h:2285
void SS_identify_outer_params(PlannerInfo *root)
Definition: subselect.c:1999
List * eq_classes
Definition: pathnodes.h:266
Bitmapset * outer_params
Definition: pathnodes.h:194
int location
Definition: primnodes.h:1208
Index query_level
Definition: pathnodes.h:183
void * palloc(Size size)
Definition: mcxt.c:949
List * placeholder_list
Definition: pathnodes.h:294
bool hasHavingQual
Definition: pathnodes.h:347
bool argisrow
Definition: primnodes.h:1207
List * pathlist
Definition: pathnodes.h:657
#define copyObject(obj)
Definition: nodes.h:641
Node * havingQual
Definition: parsenodes.h:152
List * processed_tlist
Definition: pathnodes.h:325
Definition: pg_list.h:50
int16 AttrNumber
Definition: attnum.h:21
double Cost
Definition: nodes.h:659
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:648
PlannerInfo * subroot
Definition: pathnodes.h:2286

◆ fetch_agg_sort_op()

static Oid fetch_agg_sort_op ( Oid  aggfnoid)
static

Definition at line 521 of file planagg.c.

References AGGFNOID, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, ReleaseSysCache(), and SearchSysCache1().

Referenced by find_minmax_aggs_walker().

522 {
523  HeapTuple aggTuple;
524  Form_pg_aggregate aggform;
525  Oid aggsortop;
526 
527  /* fetch aggregate entry from pg_aggregate */
528  aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggfnoid));
529  if (!HeapTupleIsValid(aggTuple))
530  return InvalidOid;
531  aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
532  aggsortop = aggform->aggsortop;
533  ReleaseSysCache(aggTuple);
534 
535  return aggsortop;
536 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
unsigned int Oid
Definition: postgres_ext.h:31
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109

◆ find_minmax_aggs_walker()

static bool find_minmax_aggs_walker ( Node node,
List **  context 
)
static

Definition at line 247 of file planagg.c.

References Aggref::aggfilter, Aggref::aggfnoid, MinMaxAggInfo::aggfnoid, Aggref::agglevelsup, Aggref::aggorder, MinMaxAggInfo::aggsortop, Aggref::args, Assert, contain_mutable_functions(), equal(), TargetEntry::expr, expression_tree_walker(), exprType(), fetch_agg_sort_op(), IsA, lappend(), lfirst, linitial, list_length(), makeNode, NIL, OidIsValid, MinMaxAggInfo::param, MinMaxAggInfo::path, MinMaxAggInfo::pathcost, MinMaxAggInfo::subroot, MinMaxAggInfo::target, and type_is_rowtype().

Referenced by preprocess_minmax_aggregates().

248 {
249  if (node == NULL)
250  return false;
251  if (IsA(node, Aggref))
252  {
253  Aggref *aggref = (Aggref *) node;
254  Oid aggsortop;
255  TargetEntry *curTarget;
256  MinMaxAggInfo *mminfo;
257  ListCell *l;
258 
259  Assert(aggref->agglevelsup == 0);
260  if (list_length(aggref->args) != 1)
261  return true; /* it couldn't be MIN/MAX */
262 
263  /*
264  * ORDER BY is usually irrelevant for MIN/MAX, but it can change the
265  * outcome if the aggsortop's operator class recognizes non-identical
266  * values as equal. For example, 4.0 and 4.00 are equal according to
267  * numeric_ops, yet distinguishable. If MIN() receives more than one
268  * value equal to 4.0 and no value less than 4.0, it is unspecified
269  * which of those equal values MIN() returns. An ORDER BY expression
270  * that differs for each of those equal values of the argument
271  * expression makes the result predictable once again. This is a
272  * niche requirement, and we do not implement it with subquery paths.
273  * In any case, this test lets us reject ordered-set aggregates
274  * quickly.
275  */
276  if (aggref->aggorder != NIL)
277  return true;
278  /* note: we do not care if DISTINCT is mentioned ... */
279 
280  /*
281  * We might implement the optimization when a FILTER clause is present
282  * by adding the filter to the quals of the generated subquery. For
283  * now, just punt.
284  */
285  if (aggref->aggfilter != NULL)
286  return true;
287 
288  aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
289  if (!OidIsValid(aggsortop))
290  return true; /* not a MIN/MAX aggregate */
291 
292  curTarget = (TargetEntry *) linitial(aggref->args);
293 
294  if (contain_mutable_functions((Node *) curTarget->expr))
295  return true; /* not potentially indexable */
296 
297  if (type_is_rowtype(exprType((Node *) curTarget->expr)))
298  return true; /* IS NOT NULL would have weird semantics */
299 
300  /*
301  * Check whether it's already in the list, and add it if not.
302  */
303  foreach(l, *context)
304  {
305  mminfo = (MinMaxAggInfo *) lfirst(l);
306  if (mminfo->aggfnoid == aggref->aggfnoid &&
307  equal(mminfo->target, curTarget->expr))
308  return false;
309  }
310 
311  mminfo = makeNode(MinMaxAggInfo);
312  mminfo->aggfnoid = aggref->aggfnoid;
313  mminfo->aggsortop = aggsortop;
314  mminfo->target = curTarget->expr;
315  mminfo->subroot = NULL; /* don't compute path yet */
316  mminfo->path = NULL;
317  mminfo->pathcost = 0;
318  mminfo->param = NULL;
319 
320  *context = lappend(*context, mminfo);
321 
322  /*
323  * We need not recurse into the argument, since it can't contain any
324  * aggregates.
325  */
326  return false;
327  }
328  Assert(!IsA(node, SubLink));
330  (void *) context);
331 }
#define NIL
Definition: pg_list.h:65
static bool find_minmax_aggs_walker(Node *node, List **context)
Definition: planagg.c:247
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3011
Param * param
Definition: pathnodes.h:2289
Definition: nodes.h:525
List * args
Definition: primnodes.h:305
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
#define linitial(l)
Definition: pg_list.h:195
List * aggorder
Definition: primnodes.h:306
Index agglevelsup
Definition: primnodes.h:313
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2433
List * lappend(List *list, void *datum)
Definition: list.c:322
Oid aggfnoid
Definition: primnodes.h:298
#define makeNode(_type_)
Definition: nodes.h:573
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
Expr * target
Definition: pathnodes.h:2285
Expr * expr
Definition: primnodes.h:1393
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1839
static int list_length(const List *l)
Definition: pg_list.h:169
Expr * aggfilter
Definition: primnodes.h:308
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:645
static Oid fetch_agg_sort_op(Oid aggfnoid)
Definition: planagg.c:521
PlannerInfo * subroot
Definition: pathnodes.h:2286

◆ minmax_qp_callback()

static void minmax_qp_callback ( PlannerInfo root,
void *  extra 
)
static

Definition at line 502 of file planagg.c.

References PlannerInfo::distinct_pathkeys, PlannerInfo::group_pathkeys, make_pathkeys_for_sortclauses(), NIL, PlannerInfo::parse, PlannerInfo::query_pathkeys, PlannerInfo::sort_pathkeys, Query::sortClause, Query::targetList, and PlannerInfo::window_pathkeys.

Referenced by build_minmax_path().

503 {
504  root->group_pathkeys = NIL;
505  root->window_pathkeys = NIL;
506  root->distinct_pathkeys = NIL;
507 
508  root->sort_pathkeys =
510  root->parse->sortClause,
511  root->parse->targetList);
512 
513  root->query_pathkeys = root->sort_pathkeys;
514 }
List * group_pathkeys
Definition: pathnodes.h:300
#define NIL
Definition: pg_list.h:65
Query * parse
Definition: pathnodes.h:179
List * sortClause
Definition: parsenodes.h:158
List * query_pathkeys
Definition: pathnodes.h:298
List * make_pathkeys_for_sortclauses(PlannerInfo *root, List *sortclauses, List *tlist)
Definition: pathkeys.c:1071
List * targetList
Definition: parsenodes.h:140
List * sort_pathkeys
Definition: pathnodes.h:303
List * window_pathkeys
Definition: pathnodes.h:301
List * distinct_pathkeys
Definition: pathnodes.h:302

◆ preprocess_minmax_aggregates()

void preprocess_minmax_aggregates ( PlannerInfo root)

Definition at line 72 of file planagg.c.

References add_path(), MinMaxAggInfo::aggsortop, Assert, build_minmax_path(), create_minmaxagg_path(), create_pathtarget, Query::cteList, elog, ERROR, exprCollation(), exprType(), fetch_upper_rel(), find_minmax_aggs_walker(), FromExpr::fromlist, get_equality_op_for_ordering_op(), Query::groupClause, Query::groupingSets, Query::hasAggs, Query::hasWindowFuncs, Query::havingQual, RangeTblEntry::inh, IsA, Query::jointree, lfirst, linitial, list_length(), PlannerInfo::minmax_aggs, NIL, OidIsValid, MinMaxAggInfo::param, parse(), PlannerInfo::parse, planner_rt_fetch, PlannerInfo::processed_tlist, Query::rowMarks, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblRef::rtindex, Query::setOperations, SS_make_initplan_output_param(), MinMaxAggInfo::target, and UPPERREL_GROUP_AGG.

Referenced by grouping_planner().

73 {
74  Query *parse = root->parse;
75  FromExpr *jtnode;
76  RangeTblRef *rtr;
77  RangeTblEntry *rte;
78  List *aggs_list;
79  RelOptInfo *grouped_rel;
80  ListCell *lc;
81 
82  /* minmax_aggs list should be empty at this point */
83  Assert(root->minmax_aggs == NIL);
84 
85  /* Nothing to do if query has no aggregates */
86  if (!parse->hasAggs)
87  return;
88 
89  Assert(!parse->setOperations); /* shouldn't get here if a setop */
90  Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */
91 
92  /*
93  * Reject unoptimizable cases.
94  *
95  * We don't handle GROUP BY or windowing, because our current
96  * implementations of grouping require looking at all the rows anyway, and
97  * so there's not much point in optimizing MIN/MAX.
98  */
99  if (parse->groupClause || list_length(parse->groupingSets) > 1 ||
100  parse->hasWindowFuncs)
101  return;
102 
103  /*
104  * Reject if query contains any CTEs; there's no way to build an indexscan
105  * on one so we couldn't succeed here. (If the CTEs are unreferenced,
106  * that's not true, but it doesn't seem worth expending cycles to check.)
107  */
108  if (parse->cteList)
109  return;
110 
111  /*
112  * We also restrict the query to reference exactly one table, since join
113  * conditions can't be handled reasonably. (We could perhaps handle a
114  * query containing cartesian-product joins, but it hardly seems worth the
115  * trouble.) However, the single table could be buried in several levels
116  * of FromExpr due to subqueries. Note the "single" table could be an
117  * inheritance parent, too, including the case of a UNION ALL subquery
118  * that's been flattened to an appendrel.
119  */
120  jtnode = parse->jointree;
121  while (IsA(jtnode, FromExpr))
122  {
123  if (list_length(jtnode->fromlist) != 1)
124  return;
125  jtnode = linitial(jtnode->fromlist);
126  }
127  if (!IsA(jtnode, RangeTblRef))
128  return;
129  rtr = (RangeTblRef *) jtnode;
130  rte = planner_rt_fetch(rtr->rtindex, root);
131  if (rte->rtekind == RTE_RELATION)
132  /* ordinary relation, ok */ ;
133  else if (rte->rtekind == RTE_SUBQUERY && rte->inh)
134  /* flattened UNION ALL subquery, ok */ ;
135  else
136  return;
137 
138  /*
139  * Scan the tlist and HAVING qual to find all the aggregates and verify
140  * all are MIN/MAX aggregates. Stop as soon as we find one that isn't.
141  */
142  aggs_list = NIL;
143  if (find_minmax_aggs_walker((Node *) root->processed_tlist, &aggs_list))
144  return;
145  if (find_minmax_aggs_walker(parse->havingQual, &aggs_list))
146  return;
147 
148  /*
149  * OK, there is at least the possibility of performing the optimization.
150  * Build an access path for each aggregate. If any of the aggregates
151  * prove to be non-indexable, give up; there is no point in optimizing
152  * just some of them.
153  */
154  foreach(lc, aggs_list)
155  {
156  MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
157  Oid eqop;
158  bool reverse;
159 
160  /*
161  * We'll need the equality operator that goes with the aggregate's
162  * ordering operator.
163  */
164  eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse);
165  if (!OidIsValid(eqop)) /* shouldn't happen */
166  elog(ERROR, "could not find equality operator for ordering operator %u",
167  mminfo->aggsortop);
168 
169  /*
170  * We can use either an ordering that gives NULLS FIRST or one that
171  * gives NULLS LAST; furthermore there's unlikely to be much
172  * performance difference between them, so it doesn't seem worth
173  * costing out both ways if we get a hit on the first one. NULLS
174  * FIRST is more likely to be available if the operator is a
175  * reverse-sort operator, so try that first if reverse.
176  */
177  if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse))
178  continue;
179  if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse))
180  continue;
181 
182  /* No indexable path for this aggregate, so fail */
183  return;
184  }
185 
186  /*
187  * OK, we can do the query this way. Prepare to create a MinMaxAggPath
188  * node.
189  *
190  * First, create an output Param node for each agg. (If we end up not
191  * using the MinMaxAggPath, we'll waste a PARAM_EXEC slot for each agg,
192  * which is not worth worrying about. We can't wait till create_plan time
193  * to decide whether to make the Param, unfortunately.)
194  */
195  foreach(lc, aggs_list)
196  {
197  MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
198 
199  mminfo->param =
201  exprType((Node *) mminfo->target),
202  -1,
203  exprCollation((Node *) mminfo->target));
204  }
205 
206  /*
207  * Create a MinMaxAggPath node with the appropriate estimated costs and
208  * other needed data, and add it to the UPPERREL_GROUP_AGG upperrel, where
209  * it will compete against the standard aggregate implementation. (It
210  * will likely always win, but we need not assume that here.)
211  *
212  * Note: grouping_planner won't have created this upperrel yet, but it's
213  * fine for us to create it first. We will not have inserted the correct
214  * consider_parallel value in it, but MinMaxAggPath paths are currently
215  * never parallel-safe anyway, so that doesn't matter. Likewise, it
216  * doesn't matter that we haven't filled FDW-related fields in the rel.
217  * Also, because there are no rowmarks, we know that the processed_tlist
218  * doesn't need to change anymore, so making the pathtarget now is safe.
219  */
220  grouped_rel = fetch_upper_rel(root, UPPERREL_GROUP_AGG, NULL);
221  add_path(grouped_rel, (Path *)
222  create_minmaxagg_path(root, grouped_rel,
223  create_pathtarget(root,
224  root->processed_tlist),
225  aggs_list,
226  (List *) parse->havingQual));
227 }
#define NIL
Definition: pg_list.h:65
static bool find_minmax_aggs_walker(Node *node, List **context)
Definition: planagg.c:247
MinMaxAggPath * create_minmaxagg_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, List *mmaggregates, List *quals)
Definition: pathnode.c:3144
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
Query * parse
Definition: pathnodes.h:179
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:422
FromExpr * jointree
Definition: parsenodes.h:138
bool hasAggs
Definition: parsenodes.h:125
Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse)
Definition: lsyscache.c:264
Param * param
Definition: pathnodes.h:2289
List * groupingSets
Definition: parsenodes.h:150
Definition: nodes.h:525
List * minmax_aggs
Definition: pathnodes.h:329
List * fromlist
Definition: primnodes.h:1496
unsigned int Oid
Definition: postgres_ext.h:31
List * rowMarks
Definition: parsenodes.h:163
#define OidIsValid(objectId)
Definition: c.h:645
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:373
RelOptInfo * fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
Definition: relnode.c:1177
#define create_pathtarget(root, tlist)
Definition: tlist.h:54
#define Assert(condition)
Definition: c.h:739
#define lfirst(lc)
Definition: pg_list.h:190
Expr * target
Definition: pathnodes.h:2285
bool hasWindowFuncs
Definition: parsenodes.h:126
Param * SS_make_initplan_output_param(PlannerInfo *root, Oid resulttype, int32 resulttypmod, Oid resultcollation)
Definition: subselect.c:2873
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
static int list_length(const List *l)
Definition: pg_list.h:169
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:719
static bool build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, Oid eqop, Oid sortop, bool nulls_first)
Definition: planagg.c:342
RTEKind rtekind
Definition: parsenodes.h:974
List * cteList
Definition: parsenodes.h:135
Node * setOperations
Definition: parsenodes.h:165
List * groupClause
Definition: parsenodes.h:148
#define elog(elevel,...)
Definition: elog.h:228
Node * havingQual
Definition: parsenodes.h:152
List * processed_tlist
Definition: pathnodes.h:325
Definition: pg_list.h:50
static struct subre * parse(struct vars *, int, int, struct state *, struct state *)
Definition: regcomp.c:648