Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * prepagg.c
4 : * Routines to preprocess aggregate function calls
5 : *
6 : * If there are identical aggregate calls in the query, they only need to
7 : * be computed once. Also, some aggregate functions can share the same
8 : * transition state, so that we only need to call the final function for
9 : * them separately. These optimizations are independent of how the
10 : * aggregates are executed.
11 : *
12 : * preprocess_aggrefs() detects those cases, creates AggInfo and
13 : * AggTransInfo structs for each aggregate and transition state that needs
14 : * to be computed, and sets the 'aggno' and 'transno' fields in the Aggrefs
15 : * accordingly. It also resolves polymorphic transition types, and sets
16 : * the 'aggtranstype' fields accordingly.
17 : *
18 : * XXX: The AggInfo and AggTransInfo structs are thrown away after
19 : * planning, so executor startup has to perform some of the same lookups
20 : * of transition functions and initial values that we do here. One day, we
21 : * might want to carry that information to the Agg nodes to save the effort
22 : * at executor startup. The Agg nodes are constructed much later in the
23 : * planning, however, so it's not trivial.
24 : *
25 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
26 : * Portions Copyright (c) 1994, Regents of the University of California
27 : *
28 : *
29 : * IDENTIFICATION
30 : * src/backend/optimizer/prep/prepagg.c
31 : *
32 : *-------------------------------------------------------------------------
33 : */
34 :
35 : #include "postgres.h"
36 :
37 : #include "access/htup_details.h"
38 : #include "catalog/pg_aggregate.h"
39 : #include "catalog/pg_type.h"
40 : #include "nodes/nodeFuncs.h"
41 : #include "nodes/pathnodes.h"
42 : #include "optimizer/cost.h"
43 : #include "optimizer/optimizer.h"
44 : #include "optimizer/plancat.h"
45 : #include "optimizer/prep.h"
46 : #include "parser/parse_agg.h"
47 : #include "utils/builtins.h"
48 : #include "utils/datum.h"
49 : #include "utils/fmgroids.h"
50 : #include "utils/lsyscache.h"
51 : #include "utils/memutils.h"
52 : #include "utils/syscache.h"
53 :
54 : static bool preprocess_aggrefs_walker(Node *node, PlannerInfo *root);
55 : static int find_compatible_agg(PlannerInfo *root, Aggref *newagg,
56 : List **same_input_transnos);
57 : static int find_compatible_trans(PlannerInfo *root, Aggref *newagg,
58 : bool shareable,
59 : Oid aggtransfn, Oid aggtranstype,
60 : int transtypeLen, bool transtypeByVal,
61 : Oid aggcombinefn,
62 : Oid aggserialfn, Oid aggdeserialfn,
63 : Datum initValue, bool initValueIsNull,
64 : List *transnos);
65 : static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
66 :
67 : /* -----------------
68 : * Resolve the transition type of all Aggrefs, and determine which Aggrefs
69 : * can share aggregate or transition state.
70 : *
71 : * Information about the aggregates and transition functions are collected
72 : * in the root->agginfos and root->aggtransinfos lists. The 'aggtranstype',
73 : * 'aggno', and 'aggtransno' fields of each Aggref are filled in.
74 : *
75 : * NOTE: This modifies the Aggrefs in the input expression in-place!
76 : *
77 : * We try to optimize by detecting duplicate aggregate functions so that
78 : * their state and final values are re-used, rather than needlessly being
79 : * re-calculated independently. We also detect aggregates that are not
80 : * the same, but which can share the same transition state.
81 : *
82 : * Scenarios:
83 : *
84 : * 1. Identical aggregate function calls appear in the query:
85 : *
86 : * SELECT SUM(x) FROM ... HAVING SUM(x) > 0
87 : *
88 : * Since these aggregates are identical, we only need to calculate
89 : * the value once. Both aggregates will share the same 'aggno' value.
90 : *
91 : * 2. Two different aggregate functions appear in the query, but the
92 : * aggregates have the same arguments, transition functions and
93 : * initial values (and, presumably, different final functions):
94 : *
95 : * SELECT AVG(x), STDDEV(x) FROM ...
96 : *
97 : * In this case we must create a new AggInfo for the varying aggregate,
98 : * and we need to call the final functions separately, but we need
99 : * only run the transition function once. (This requires that the
100 : * final functions be nondestructive of the transition state, but
101 : * that's required anyway for other reasons.)
102 : *
103 : * For either of these optimizations to be valid, all aggregate properties
104 : * used in the transition phase must be the same, including any modifiers
105 : * such as ORDER BY, DISTINCT and FILTER, and the arguments mustn't
106 : * contain any volatile functions.
107 : * -----------------
108 : */
109 : void
110 73044 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
111 : {
112 73044 : (void) preprocess_aggrefs_walker(clause, root);
113 73044 : }
114 :
115 : static void
116 42100 : preprocess_aggref(Aggref *aggref, PlannerInfo *root)
117 : {
118 : HeapTuple aggTuple;
119 : Form_pg_aggregate aggform;
120 : Oid aggtransfn;
121 : Oid aggfinalfn;
122 : Oid aggcombinefn;
123 : Oid aggserialfn;
124 : Oid aggdeserialfn;
125 : Oid aggtranstype;
126 : int32 aggtranstypmod;
127 : int32 aggtransspace;
128 : bool shareable;
129 : int aggno;
130 : int transno;
131 : List *same_input_transnos;
132 : int16 resulttypeLen;
133 : bool resulttypeByVal;
134 : Datum textInitVal;
135 : Datum initValue;
136 : bool initValueIsNull;
137 : bool transtypeByVal;
138 : int16 transtypeLen;
139 : Oid inputTypes[FUNC_MAX_ARGS];
140 : int numArguments;
141 :
142 : Assert(aggref->agglevelsup == 0);
143 :
144 : /*
145 : * Fetch info about the aggregate from pg_aggregate. Note it's correct to
146 : * ignore the moving-aggregate variant, since what we're concerned with
147 : * here is aggregates not window functions.
148 : */
149 42100 : aggTuple = SearchSysCache1(AGGFNOID,
150 : ObjectIdGetDatum(aggref->aggfnoid));
151 42100 : if (!HeapTupleIsValid(aggTuple))
152 0 : elog(ERROR, "cache lookup failed for aggregate %u",
153 : aggref->aggfnoid);
154 42100 : aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
155 42100 : aggtransfn = aggform->aggtransfn;
156 42100 : aggfinalfn = aggform->aggfinalfn;
157 42100 : aggcombinefn = aggform->aggcombinefn;
158 42100 : aggserialfn = aggform->aggserialfn;
159 42100 : aggdeserialfn = aggform->aggdeserialfn;
160 42100 : aggtranstype = aggform->aggtranstype;
161 42100 : aggtransspace = aggform->aggtransspace;
162 :
163 : /*
164 : * Resolve the possibly-polymorphic aggregate transition type.
165 : */
166 :
167 : /* extract argument types (ignoring any ORDER BY expressions) */
168 42100 : numArguments = get_aggregate_argtypes(aggref, inputTypes);
169 :
170 : /* resolve actual type of transition state, if polymorphic */
171 42100 : aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
172 : aggtranstype,
173 : inputTypes,
174 : numArguments);
175 42100 : aggref->aggtranstype = aggtranstype;
176 :
177 : /*
178 : * If transition state is of same type as first aggregated input, assume
179 : * it's the same typmod (same width) as well. This works for cases like
180 : * MAX/MIN and is probably somewhat reasonable otherwise.
181 : */
182 42100 : aggtranstypmod = -1;
183 42100 : if (aggref->args)
184 : {
185 30180 : TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
186 :
187 30180 : if (aggtranstype == exprType((Node *) tle->expr))
188 3158 : aggtranstypmod = exprTypmod((Node *) tle->expr);
189 : }
190 :
191 : /*
192 : * If finalfn is marked read-write, we can't share transition states; but
193 : * it is okay to share states for AGGMODIFY_SHAREABLE aggs.
194 : *
195 : * In principle, in a partial aggregate, we could share the transition
196 : * state even if the final function is marked as read-write, because the
197 : * partial aggregate doesn't execute the final function. But it's too
198 : * early to know whether we're going perform a partial aggregate.
199 : */
200 42100 : shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
201 :
202 : /* get info about the output value's datatype */
203 42100 : get_typlenbyval(aggref->aggtype,
204 : &resulttypeLen,
205 : &resulttypeByVal);
206 :
207 : /* get initial value */
208 42100 : textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
209 : Anum_pg_aggregate_agginitval,
210 : &initValueIsNull);
211 42100 : if (initValueIsNull)
212 26522 : initValue = (Datum) 0;
213 : else
214 15578 : initValue = GetAggInitVal(textInitVal, aggtranstype);
215 :
216 42100 : ReleaseSysCache(aggTuple);
217 :
218 : /*
219 : * 1. See if this is identical to another aggregate function call that
220 : * we've seen already.
221 : */
222 42100 : aggno = find_compatible_agg(root, aggref, &same_input_transnos);
223 42100 : if (aggno != -1)
224 : {
225 1306 : AggInfo *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
226 :
227 1306 : agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
228 1306 : transno = agginfo->transno;
229 : }
230 : else
231 : {
232 40794 : AggInfo *agginfo = makeNode(AggInfo);
233 :
234 40794 : agginfo->finalfn_oid = aggfinalfn;
235 40794 : agginfo->aggrefs = list_make1(aggref);
236 40794 : agginfo->shareable = shareable;
237 :
238 40794 : aggno = list_length(root->agginfos);
239 40794 : root->agginfos = lappend(root->agginfos, agginfo);
240 :
241 : /*
242 : * Count it, and check for cases requiring ordered input. Note that
243 : * ordered-set aggs always have nonempty aggorder. Any ordered-input
244 : * case also defeats partial aggregation.
245 : */
246 40794 : if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
247 : {
248 2270 : root->numOrderedAggs++;
249 2270 : root->hasNonPartialAggs = true;
250 : }
251 :
252 40794 : get_typlenbyval(aggtranstype,
253 : &transtypeLen,
254 : &transtypeByVal);
255 :
256 : /*
257 : * 2. See if this aggregate can share transition state with another
258 : * aggregate that we've initialized already.
259 : */
260 40794 : transno = find_compatible_trans(root, aggref, shareable,
261 : aggtransfn, aggtranstype,
262 : transtypeLen, transtypeByVal,
263 : aggcombinefn,
264 : aggserialfn, aggdeserialfn,
265 : initValue, initValueIsNull,
266 : same_input_transnos);
267 40794 : if (transno == -1)
268 : {
269 40536 : AggTransInfo *transinfo = makeNode(AggTransInfo);
270 :
271 40536 : transinfo->args = aggref->args;
272 40536 : transinfo->aggfilter = aggref->aggfilter;
273 40536 : transinfo->transfn_oid = aggtransfn;
274 40536 : transinfo->combinefn_oid = aggcombinefn;
275 40536 : transinfo->serialfn_oid = aggserialfn;
276 40536 : transinfo->deserialfn_oid = aggdeserialfn;
277 40536 : transinfo->aggtranstype = aggtranstype;
278 40536 : transinfo->aggtranstypmod = aggtranstypmod;
279 40536 : transinfo->transtypeLen = transtypeLen;
280 40536 : transinfo->transtypeByVal = transtypeByVal;
281 40536 : transinfo->aggtransspace = aggtransspace;
282 40536 : transinfo->initValue = initValue;
283 40536 : transinfo->initValueIsNull = initValueIsNull;
284 :
285 40536 : transno = list_length(root->aggtransinfos);
286 40536 : root->aggtransinfos = lappend(root->aggtransinfos, transinfo);
287 :
288 : /*
289 : * Check whether partial aggregation is feasible, unless we
290 : * already found out that we can't do it.
291 : */
292 40536 : if (!root->hasNonPartialAggs)
293 : {
294 : /*
295 : * If there is no combine function, then partial aggregation
296 : * is not possible.
297 : */
298 37996 : if (!OidIsValid(transinfo->combinefn_oid))
299 1334 : root->hasNonPartialAggs = true;
300 :
301 : /*
302 : * If we have any aggs with transtype INTERNAL then we must
303 : * check whether they have serialization/deserialization
304 : * functions; if not, we can't serialize partial-aggregation
305 : * results.
306 : */
307 36662 : else if (transinfo->aggtranstype == INTERNALOID)
308 : {
309 :
310 17704 : if (!OidIsValid(transinfo->serialfn_oid) ||
311 17704 : !OidIsValid(transinfo->deserialfn_oid))
312 0 : root->hasNonSerialAggs = true;
313 :
314 : /*
315 : * array_agg_serialize and array_agg_deserialize make use
316 : * of the aggregate non-byval input type's send and
317 : * receive functions. There's a chance that the type
318 : * being aggregated has one or both of these functions
319 : * missing. In this case we must not allow the
320 : * aggregate's serial and deserial functions to be used.
321 : * It would be nice not to have special case this and
322 : * instead provide some sort of supporting function within
323 : * the aggregate to do this, but for now, that seems like
324 : * overkill for this one case.
325 : */
326 17704 : if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
327 4670 : transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
328 13034 : !agg_args_support_sendreceive(aggref))
329 86 : root->hasNonSerialAggs = true;
330 : }
331 : }
332 : }
333 40794 : agginfo->transno = transno;
334 : }
335 :
336 : /*
337 : * Fill in the fields in the Aggref (aggtranstype was set above already)
338 : */
339 42100 : aggref->aggno = aggno;
340 42100 : aggref->aggtransno = transno;
341 42100 : }
342 :
343 : static bool
344 194862 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
345 : {
346 194862 : if (node == NULL)
347 36848 : return false;
348 158014 : if (IsA(node, Aggref))
349 : {
350 42100 : Aggref *aggref = (Aggref *) node;
351 :
352 42100 : preprocess_aggref(aggref, root);
353 :
354 : /*
355 : * We assume that the parser checked that there are no aggregates (of
356 : * this level anyway) in the aggregated arguments, direct arguments,
357 : * or filter clause. Hence, we need not recurse into any of them.
358 : */
359 42100 : return false;
360 : }
361 : Assert(!IsA(node, SubLink));
362 115914 : return expression_tree_walker(node, preprocess_aggrefs_walker, root);
363 : }
364 :
365 :
366 : /*
367 : * find_compatible_agg - search for a previously initialized per-Agg struct
368 : *
369 : * Searches the previously looked at aggregates to find one which is compatible
370 : * with this one, with the same input parameters. If no compatible aggregate
371 : * can be found, returns -1.
372 : *
373 : * As a side-effect, this also collects a list of existing, shareable per-Trans
374 : * structs with matching inputs. If no identical Aggref is found, the list is
375 : * passed later to find_compatible_trans, to see if we can at least reuse
376 : * the state value of another aggregate.
377 : */
378 : static int
379 42100 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
380 : List **same_input_transnos)
381 : {
382 : ListCell *lc;
383 : int aggno;
384 :
385 42100 : *same_input_transnos = NIL;
386 :
387 : /* we mustn't reuse the aggref if it contains volatile function calls */
388 42100 : if (contain_volatile_functions((Node *) newagg))
389 398 : return -1;
390 :
391 : /*
392 : * Search through the list of already seen aggregates. If we find an
393 : * existing identical aggregate call, then we can re-use that one. While
394 : * searching, we'll also collect a list of Aggrefs with the same input
395 : * parameters. If no matching Aggref is found, the caller can potentially
396 : * still re-use the transition state of one of them. (At this stage we
397 : * just compare the parsetrees; whether different aggregates share the
398 : * same transition function will be checked later.)
399 : */
400 41702 : aggno = -1;
401 52114 : foreach(lc, root->agginfos)
402 : {
403 11718 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
404 : Aggref *existingRef;
405 :
406 11718 : aggno++;
407 :
408 11718 : existingRef = linitial_node(Aggref, agginfo->aggrefs);
409 :
410 : /* all of the following must be the same or it's no match */
411 11718 : if (newagg->inputcollid != existingRef->inputcollid ||
412 10468 : newagg->aggtranstype != existingRef->aggtranstype ||
413 6822 : newagg->aggstar != existingRef->aggstar ||
414 5842 : newagg->aggvariadic != existingRef->aggvariadic ||
415 5842 : newagg->aggkind != existingRef->aggkind ||
416 5644 : !equal(newagg->args, existingRef->args) ||
417 2676 : !equal(newagg->aggorder, existingRef->aggorder) ||
418 2670 : !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
419 2670 : !equal(newagg->aggfilter, existingRef->aggfilter))
420 9256 : continue;
421 :
422 : /* if it's the same aggregate function then report exact match */
423 2462 : if (newagg->aggfnoid == existingRef->aggfnoid &&
424 1318 : newagg->aggtype == existingRef->aggtype &&
425 2636 : newagg->aggcollid == existingRef->aggcollid &&
426 1318 : equal(newagg->aggdirectargs, existingRef->aggdirectargs))
427 : {
428 1306 : list_free(*same_input_transnos);
429 1306 : *same_input_transnos = NIL;
430 1306 : return aggno;
431 : }
432 :
433 : /*
434 : * Not identical, but it had the same inputs. If the final function
435 : * permits sharing, return its transno to the caller, in case we can
436 : * re-use its per-trans state. (If there's already sharing going on,
437 : * we might report a transno more than once. find_compatible_trans is
438 : * cheap enough that it's not worth spending cycles to avoid that.)
439 : */
440 1156 : if (agginfo->shareable)
441 1150 : *same_input_transnos = lappend_int(*same_input_transnos,
442 : agginfo->transno);
443 : }
444 :
445 40396 : return -1;
446 : }
447 :
448 : /*
449 : * find_compatible_trans - search for a previously initialized per-Trans
450 : * struct
451 : *
452 : * Searches the list of transnos for a per-Trans struct with the same
453 : * transition function and initial condition. (The inputs have already been
454 : * verified to match.)
455 : */
456 : static int
457 40794 : find_compatible_trans(PlannerInfo *root, Aggref *newagg, bool shareable,
458 : Oid aggtransfn, Oid aggtranstype,
459 : int transtypeLen, bool transtypeByVal,
460 : Oid aggcombinefn,
461 : Oid aggserialfn, Oid aggdeserialfn,
462 : Datum initValue, bool initValueIsNull,
463 : List *transnos)
464 : {
465 : ListCell *lc;
466 :
467 : /* If this aggregate can't share transition states, give up */
468 40794 : if (!shareable)
469 120 : return -1;
470 :
471 41512 : foreach(lc, transnos)
472 : {
473 1096 : int transno = lfirst_int(lc);
474 1096 : AggTransInfo *pertrans = list_nth_node(AggTransInfo,
475 : root->aggtransinfos,
476 : transno);
477 :
478 : /*
479 : * if the transfns or transition state types are not the same then the
480 : * state can't be shared.
481 : */
482 1096 : if (aggtransfn != pertrans->transfn_oid ||
483 264 : aggtranstype != pertrans->aggtranstype)
484 832 : continue;
485 :
486 : /*
487 : * The serialization and deserialization functions must match, if
488 : * present, as we're unable to share the trans state for aggregates
489 : * which will serialize or deserialize into different formats.
490 : * Remember that these will be InvalidOid if they're not required for
491 : * this agg node.
492 : */
493 264 : if (aggserialfn != pertrans->serialfn_oid ||
494 264 : aggdeserialfn != pertrans->deserialfn_oid)
495 0 : continue;
496 :
497 : /*
498 : * Combine function must also match. We only care about the combine
499 : * function with partial aggregates, but it's too early in the
500 : * planning to know if we will do partial aggregation, so be
501 : * conservative.
502 : */
503 264 : if (aggcombinefn != pertrans->combinefn_oid)
504 0 : continue;
505 :
506 : /*
507 : * Check that the initial condition matches, too.
508 : */
509 264 : if (initValueIsNull && pertrans->initValueIsNull)
510 258 : return transno;
511 :
512 336 : if (!initValueIsNull && !pertrans->initValueIsNull &&
513 168 : datumIsEqual(initValue, pertrans->initValue,
514 : transtypeByVal, transtypeLen))
515 162 : return transno;
516 : }
517 40416 : return -1;
518 : }
519 :
520 : static Datum
521 15578 : GetAggInitVal(Datum textInitVal, Oid transtype)
522 : {
523 : Oid typinput,
524 : typioparam;
525 : char *strInitVal;
526 : Datum initVal;
527 :
528 15578 : getTypeInputInfo(transtype, &typinput, &typioparam);
529 15578 : strInitVal = TextDatumGetCString(textInitVal);
530 15578 : initVal = OidInputFunctionCall(typinput, strInitVal,
531 : typioparam, -1);
532 15578 : pfree(strInitVal);
533 15578 : return initVal;
534 : }
535 :
536 :
537 : /*
538 : * get_agg_clause_costs
539 : * Process the PlannerInfo's 'aggtransinfos' and 'agginfos' lists
540 : * accumulating the cost information about them.
541 : *
542 : * 'aggsplit' tells us the expected partial-aggregation mode, which affects
543 : * the cost estimates.
544 : *
545 : * NOTE that the costs are ADDED to those already in *costs ... so the caller
546 : * is responsible for zeroing the struct initially.
547 : *
548 : * For each AggTransInfo, we add the cost of an aggregate transition using
549 : * either the transfn or combinefn depending on the 'aggsplit' value. We also
550 : * account for the costs of any aggfilters and any serializations and
551 : * deserializations of the transition state and also estimate the total space
552 : * needed for the transition states as if each aggregate's state was stored in
553 : * memory concurrently (as would be done in a HashAgg plan).
554 : *
555 : * For each AggInfo in the 'agginfos' list we add the cost of running the
556 : * final function and the direct args, if any.
557 : */
558 : void
559 39642 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
560 : {
561 : ListCell *lc;
562 :
563 84182 : foreach(lc, root->aggtransinfos)
564 : {
565 44540 : AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
566 :
567 : /*
568 : * Add the appropriate component function execution costs to
569 : * appropriate totals.
570 : */
571 44540 : if (DO_AGGSPLIT_COMBINE(aggsplit))
572 : {
573 : /* charge for combining previously aggregated states */
574 1856 : add_function_cost(root, transinfo->combinefn_oid, NULL,
575 : &costs->transCost);
576 : }
577 : else
578 42684 : add_function_cost(root, transinfo->transfn_oid, NULL,
579 : &costs->transCost);
580 44540 : if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
581 1856 : OidIsValid(transinfo->deserialfn_oid))
582 122 : add_function_cost(root, transinfo->deserialfn_oid, NULL,
583 : &costs->transCost);
584 44540 : if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
585 1856 : OidIsValid(transinfo->serialfn_oid))
586 122 : add_function_cost(root, transinfo->serialfn_oid, NULL,
587 : &costs->finalCost);
588 :
589 : /*
590 : * These costs are incurred only by the initial aggregate node, so we
591 : * mustn't include them again at upper levels.
592 : */
593 44540 : if (!DO_AGGSPLIT_COMBINE(aggsplit))
594 : {
595 : /* add the input expressions' cost to per-input-row costs */
596 : QualCost argcosts;
597 :
598 42684 : cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
599 42684 : costs->transCost.startup += argcosts.startup;
600 42684 : costs->transCost.per_tuple += argcosts.per_tuple;
601 :
602 : /*
603 : * Add any filter's cost to per-input-row costs.
604 : *
605 : * XXX Ideally we should reduce input expression costs according
606 : * to filter selectivity, but it's not clear it's worth the
607 : * trouble.
608 : */
609 42684 : if (transinfo->aggfilter)
610 : {
611 762 : cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
612 : root);
613 762 : costs->transCost.startup += argcosts.startup;
614 762 : costs->transCost.per_tuple += argcosts.per_tuple;
615 : }
616 : }
617 :
618 : /*
619 : * If the transition type is pass-by-value then it doesn't add
620 : * anything to the required size of the hashtable. If it is
621 : * pass-by-reference then we have to add the estimated size of the
622 : * value itself, plus palloc overhead.
623 : */
624 44540 : if (!transinfo->transtypeByVal)
625 : {
626 : int32 avgwidth;
627 :
628 : /* Use average width if aggregate definition gave one */
629 2902 : if (transinfo->aggtransspace > 0)
630 90 : avgwidth = transinfo->aggtransspace;
631 2812 : else if (transinfo->transfn_oid == F_ARRAY_APPEND)
632 : {
633 : /*
634 : * If the transition function is array_append(), it'll use an
635 : * expanded array as transvalue, which will occupy at least
636 : * ALLOCSET_SMALL_INITSIZE and possibly more. Use that as the
637 : * estimate for lack of a better idea.
638 : */
639 6 : avgwidth = ALLOCSET_SMALL_INITSIZE;
640 : }
641 : else
642 : {
643 2806 : avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
644 : }
645 :
646 2902 : avgwidth = MAXALIGN(avgwidth);
647 2902 : costs->transitionSpace += avgwidth + 2 * sizeof(void *);
648 : }
649 41638 : else if (transinfo->aggtranstype == INTERNALOID)
650 : {
651 : /*
652 : * INTERNAL transition type is a special case: although INTERNAL
653 : * is pass-by-value, it's almost certainly being used as a pointer
654 : * to some large data structure. The aggregate definition can
655 : * provide an estimate of the size. If it doesn't, then we assume
656 : * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
657 : * being kept in a private memory context, as is done by
658 : * array_agg() for instance.
659 : */
660 20658 : if (transinfo->aggtransspace > 0)
661 1274 : costs->transitionSpace += transinfo->aggtransspace;
662 : else
663 19384 : costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
664 : }
665 : }
666 :
667 84440 : foreach(lc, root->agginfos)
668 : {
669 44798 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
670 44798 : Aggref *aggref = linitial_node(Aggref, agginfo->aggrefs);
671 :
672 : /*
673 : * Add the appropriate component function execution costs to
674 : * appropriate totals.
675 : */
676 44798 : if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
677 42942 : OidIsValid(agginfo->finalfn_oid))
678 22404 : add_function_cost(root, agginfo->finalfn_oid, NULL,
679 : &costs->finalCost);
680 :
681 : /*
682 : * If there are direct arguments, treat their evaluation cost like the
683 : * cost of the finalfn.
684 : */
685 44798 : if (aggref->aggdirectargs)
686 : {
687 : QualCost argcosts;
688 :
689 294 : cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
690 : root);
691 294 : costs->finalCost.startup += argcosts.startup;
692 294 : costs->finalCost.per_tuple += argcosts.per_tuple;
693 : }
694 : }
695 39642 : }
|