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-2024, 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 77848 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
111 : {
112 77848 : (void) preprocess_aggrefs_walker(clause, root);
113 77848 : }
114 :
115 : static void
116 44334 : 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 44334 : aggTuple = SearchSysCache1(AGGFNOID,
150 : ObjectIdGetDatum(aggref->aggfnoid));
151 44334 : if (!HeapTupleIsValid(aggTuple))
152 0 : elog(ERROR, "cache lookup failed for aggregate %u",
153 : aggref->aggfnoid);
154 44334 : aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
155 44334 : aggtransfn = aggform->aggtransfn;
156 44334 : aggfinalfn = aggform->aggfinalfn;
157 44334 : aggcombinefn = aggform->aggcombinefn;
158 44334 : aggserialfn = aggform->aggserialfn;
159 44334 : aggdeserialfn = aggform->aggdeserialfn;
160 44334 : aggtranstype = aggform->aggtranstype;
161 44334 : 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 44334 : numArguments = get_aggregate_argtypes(aggref, inputTypes);
169 :
170 : /* resolve actual type of transition state, if polymorphic */
171 44334 : aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
172 : aggtranstype,
173 : inputTypes,
174 : numArguments);
175 44334 : 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 44334 : aggtranstypmod = -1;
183 44334 : if (aggref->args)
184 : {
185 32528 : TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
186 :
187 32528 : if (aggtranstype == exprType((Node *) tle->expr))
188 3086 : 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 44334 : shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
201 :
202 : /* get info about the output value's datatype */
203 44334 : get_typlenbyval(aggref->aggtype,
204 : &resulttypeLen,
205 : &resulttypeByVal);
206 :
207 : /* get initial value */
208 44334 : textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
209 : Anum_pg_aggregate_agginitval,
210 : &initValueIsNull);
211 44334 : if (initValueIsNull)
212 26206 : initValue = (Datum) 0;
213 : else
214 18128 : initValue = GetAggInitVal(textInitVal, aggtranstype);
215 :
216 44334 : ReleaseSysCache(aggTuple);
217 :
218 : /*
219 : * 1. See if this is identical to another aggregate function call that
220 : * we've seen already.
221 : */
222 44334 : aggno = find_compatible_agg(root, aggref, &same_input_transnos);
223 44334 : if (aggno != -1)
224 : {
225 1276 : AggInfo *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
226 :
227 1276 : agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
228 1276 : transno = agginfo->transno;
229 : }
230 : else
231 : {
232 43058 : AggInfo *agginfo = makeNode(AggInfo);
233 :
234 43058 : agginfo->finalfn_oid = aggfinalfn;
235 43058 : agginfo->aggrefs = list_make1(aggref);
236 43058 : agginfo->shareable = shareable;
237 :
238 43058 : aggno = list_length(root->agginfos);
239 43058 : 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 43058 : if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
247 : {
248 2264 : root->numOrderedAggs++;
249 2264 : root->hasNonPartialAggs = true;
250 : }
251 :
252 43058 : 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 43058 : 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 43058 : if (transno == -1)
268 : {
269 42800 : AggTransInfo *transinfo = makeNode(AggTransInfo);
270 :
271 42800 : transinfo->args = aggref->args;
272 42800 : transinfo->aggfilter = aggref->aggfilter;
273 42800 : transinfo->transfn_oid = aggtransfn;
274 42800 : transinfo->combinefn_oid = aggcombinefn;
275 42800 : transinfo->serialfn_oid = aggserialfn;
276 42800 : transinfo->deserialfn_oid = aggdeserialfn;
277 42800 : transinfo->aggtranstype = aggtranstype;
278 42800 : transinfo->aggtranstypmod = aggtranstypmod;
279 42800 : transinfo->transtypeLen = transtypeLen;
280 42800 : transinfo->transtypeByVal = transtypeByVal;
281 42800 : transinfo->aggtransspace = aggtransspace;
282 42800 : transinfo->initValue = initValue;
283 42800 : transinfo->initValueIsNull = initValueIsNull;
284 :
285 42800 : transno = list_length(root->aggtransinfos);
286 42800 : 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 42800 : if (!root->hasNonPartialAggs)
293 : {
294 : /*
295 : * If there is no combine function, then partial aggregation
296 : * is not possible.
297 : */
298 40266 : 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 38932 : else if (transinfo->aggtranstype == INTERNALOID)
308 : {
309 :
310 17496 : if (!OidIsValid(transinfo->serialfn_oid) ||
311 17496 : !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 17496 : if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
327 4470 : transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
328 13026 : !agg_args_support_sendreceive(aggref))
329 86 : root->hasNonSerialAggs = true;
330 : }
331 : }
332 : }
333 43058 : agginfo->transno = transno;
334 : }
335 :
336 : /*
337 : * Fill in the fields in the Aggref (aggtranstype was set above already)
338 : */
339 44334 : aggref->aggno = aggno;
340 44334 : aggref->aggtransno = transno;
341 44334 : }
342 :
343 : static bool
344 209414 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
345 : {
346 209414 : if (node == NULL)
347 39250 : return false;
348 170164 : if (IsA(node, Aggref))
349 : {
350 44334 : Aggref *aggref = (Aggref *) node;
351 :
352 44334 : 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 44334 : return false;
360 : }
361 : Assert(!IsA(node, SubLink));
362 125830 : return expression_tree_walker(node, preprocess_aggrefs_walker,
363 : (void *) root);
364 : }
365 :
366 :
367 : /*
368 : * find_compatible_agg - search for a previously initialized per-Agg struct
369 : *
370 : * Searches the previously looked at aggregates to find one which is compatible
371 : * with this one, with the same input parameters. If no compatible aggregate
372 : * can be found, returns -1.
373 : *
374 : * As a side-effect, this also collects a list of existing, shareable per-Trans
375 : * structs with matching inputs. If no identical Aggref is found, the list is
376 : * passed later to find_compatible_trans, to see if we can at least reuse
377 : * the state value of another aggregate.
378 : */
379 : static int
380 44334 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
381 : List **same_input_transnos)
382 : {
383 : ListCell *lc;
384 : int aggno;
385 :
386 44334 : *same_input_transnos = NIL;
387 :
388 : /* we mustn't reuse the aggref if it contains volatile function calls */
389 44334 : if (contain_volatile_functions((Node *) newagg))
390 398 : return -1;
391 :
392 : /*
393 : * Search through the list of already seen aggregates. If we find an
394 : * existing identical aggregate call, then we can re-use that one. While
395 : * searching, we'll also collect a list of Aggrefs with the same input
396 : * parameters. If no matching Aggref is found, the caller can potentially
397 : * still re-use the transition state of one of them. (At this stage we
398 : * just compare the parsetrees; whether different aggregates share the
399 : * same transition function will be checked later.)
400 : */
401 43936 : aggno = -1;
402 53832 : foreach(lc, root->agginfos)
403 : {
404 11172 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
405 : Aggref *existingRef;
406 :
407 11172 : aggno++;
408 :
409 11172 : existingRef = linitial_node(Aggref, agginfo->aggrefs);
410 :
411 : /* all of the following must be the same or it's no match */
412 11172 : if (newagg->inputcollid != existingRef->inputcollid ||
413 9922 : newagg->aggtranstype != existingRef->aggtranstype ||
414 6276 : newagg->aggstar != existingRef->aggstar ||
415 5296 : newagg->aggvariadic != existingRef->aggvariadic ||
416 5296 : newagg->aggkind != existingRef->aggkind ||
417 5098 : !equal(newagg->args, existingRef->args) ||
418 2646 : !equal(newagg->aggorder, existingRef->aggorder) ||
419 2640 : !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
420 2640 : !equal(newagg->aggfilter, existingRef->aggfilter))
421 8740 : continue;
422 :
423 : /* if it's the same aggregate function then report exact match */
424 2432 : if (newagg->aggfnoid == existingRef->aggfnoid &&
425 1288 : newagg->aggtype == existingRef->aggtype &&
426 2576 : newagg->aggcollid == existingRef->aggcollid &&
427 1288 : equal(newagg->aggdirectargs, existingRef->aggdirectargs))
428 : {
429 1276 : list_free(*same_input_transnos);
430 1276 : *same_input_transnos = NIL;
431 1276 : return aggno;
432 : }
433 :
434 : /*
435 : * Not identical, but it had the same inputs. If the final function
436 : * permits sharing, return its transno to the caller, in case we can
437 : * re-use its per-trans state. (If there's already sharing going on,
438 : * we might report a transno more than once. find_compatible_trans is
439 : * cheap enough that it's not worth spending cycles to avoid that.)
440 : */
441 1156 : if (agginfo->shareable)
442 1150 : *same_input_transnos = lappend_int(*same_input_transnos,
443 : agginfo->transno);
444 : }
445 :
446 42660 : return -1;
447 : }
448 :
449 : /*
450 : * find_compatible_trans - search for a previously initialized per-Trans
451 : * struct
452 : *
453 : * Searches the list of transnos for a per-Trans struct with the same
454 : * transition function and initial condition. (The inputs have already been
455 : * verified to match.)
456 : */
457 : static int
458 43058 : find_compatible_trans(PlannerInfo *root, Aggref *newagg, bool shareable,
459 : Oid aggtransfn, Oid aggtranstype,
460 : int transtypeLen, bool transtypeByVal,
461 : Oid aggcombinefn,
462 : Oid aggserialfn, Oid aggdeserialfn,
463 : Datum initValue, bool initValueIsNull,
464 : List *transnos)
465 : {
466 : ListCell *lc;
467 :
468 : /* If this aggregate can't share transition states, give up */
469 43058 : if (!shareable)
470 120 : return -1;
471 :
472 43776 : foreach(lc, transnos)
473 : {
474 1096 : int transno = lfirst_int(lc);
475 1096 : AggTransInfo *pertrans = list_nth_node(AggTransInfo,
476 : root->aggtransinfos,
477 : transno);
478 :
479 : /*
480 : * if the transfns or transition state types are not the same then the
481 : * state can't be shared.
482 : */
483 1096 : if (aggtransfn != pertrans->transfn_oid ||
484 264 : aggtranstype != pertrans->aggtranstype)
485 832 : continue;
486 :
487 : /*
488 : * The serialization and deserialization functions must match, if
489 : * present, as we're unable to share the trans state for aggregates
490 : * which will serialize or deserialize into different formats.
491 : * Remember that these will be InvalidOid if they're not required for
492 : * this agg node.
493 : */
494 264 : if (aggserialfn != pertrans->serialfn_oid ||
495 264 : aggdeserialfn != pertrans->deserialfn_oid)
496 0 : continue;
497 :
498 : /*
499 : * Combine function must also match. We only care about the combine
500 : * function with partial aggregates, but it's too early in the
501 : * planning to know if we will do partial aggregation, so be
502 : * conservative.
503 : */
504 264 : if (aggcombinefn != pertrans->combinefn_oid)
505 0 : continue;
506 :
507 : /*
508 : * Check that the initial condition matches, too.
509 : */
510 264 : if (initValueIsNull && pertrans->initValueIsNull)
511 258 : return transno;
512 :
513 336 : if (!initValueIsNull && !pertrans->initValueIsNull &&
514 168 : datumIsEqual(initValue, pertrans->initValue,
515 : transtypeByVal, transtypeLen))
516 162 : return transno;
517 : }
518 42680 : return -1;
519 : }
520 :
521 : static Datum
522 18128 : GetAggInitVal(Datum textInitVal, Oid transtype)
523 : {
524 : Oid typinput,
525 : typioparam;
526 : char *strInitVal;
527 : Datum initVal;
528 :
529 18128 : getTypeInputInfo(transtype, &typinput, &typioparam);
530 18128 : strInitVal = TextDatumGetCString(textInitVal);
531 18128 : initVal = OidInputFunctionCall(typinput, strInitVal,
532 : typioparam, -1);
533 18128 : pfree(strInitVal);
534 18128 : return initVal;
535 : }
536 :
537 :
538 : /*
539 : * get_agg_clause_costs
540 : * Process the PlannerInfo's 'aggtransinfos' and 'agginfos' lists
541 : * accumulating the cost information about them.
542 : *
543 : * 'aggsplit' tells us the expected partial-aggregation mode, which affects
544 : * the cost estimates.
545 : *
546 : * NOTE that the costs are ADDED to those already in *costs ... so the caller
547 : * is responsible for zeroing the struct initially.
548 : *
549 : * For each AggTransInfo, we add the cost of an aggregate transition using
550 : * either the transfn or combinefn depending on the 'aggsplit' value. We also
551 : * account for the costs of any aggfilters and any serializations and
552 : * deserializations of the transition state and also estimate the total space
553 : * needed for the transition states as if each aggregate's state was stored in
554 : * memory concurrently (as would be done in a HashAgg plan).
555 : *
556 : * For each AggInfo in the 'agginfos' list we add the cost of running the
557 : * final function and the direct args, if any.
558 : */
559 : void
560 42002 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
561 : {
562 : ListCell *lc;
563 :
564 88806 : foreach(lc, root->aggtransinfos)
565 : {
566 46804 : AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
567 :
568 : /*
569 : * Add the appropriate component function execution costs to
570 : * appropriate totals.
571 : */
572 46804 : if (DO_AGGSPLIT_COMBINE(aggsplit))
573 : {
574 : /* charge for combining previously aggregated states */
575 1858 : add_function_cost(root, transinfo->combinefn_oid, NULL,
576 : &costs->transCost);
577 : }
578 : else
579 44946 : add_function_cost(root, transinfo->transfn_oid, NULL,
580 : &costs->transCost);
581 46804 : if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
582 1858 : OidIsValid(transinfo->deserialfn_oid))
583 122 : add_function_cost(root, transinfo->deserialfn_oid, NULL,
584 : &costs->transCost);
585 46804 : if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
586 1858 : OidIsValid(transinfo->serialfn_oid))
587 122 : add_function_cost(root, transinfo->serialfn_oid, NULL,
588 : &costs->finalCost);
589 :
590 : /*
591 : * These costs are incurred only by the initial aggregate node, so we
592 : * mustn't include them again at upper levels.
593 : */
594 46804 : if (!DO_AGGSPLIT_COMBINE(aggsplit))
595 : {
596 : /* add the input expressions' cost to per-input-row costs */
597 : QualCost argcosts;
598 :
599 44946 : cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
600 44946 : costs->transCost.startup += argcosts.startup;
601 44946 : costs->transCost.per_tuple += argcosts.per_tuple;
602 :
603 : /*
604 : * Add any filter's cost to per-input-row costs.
605 : *
606 : * XXX Ideally we should reduce input expression costs according
607 : * to filter selectivity, but it's not clear it's worth the
608 : * trouble.
609 : */
610 44946 : if (transinfo->aggfilter)
611 : {
612 762 : cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
613 : root);
614 762 : costs->transCost.startup += argcosts.startup;
615 762 : costs->transCost.per_tuple += argcosts.per_tuple;
616 : }
617 : }
618 :
619 : /*
620 : * If the transition type is pass-by-value then it doesn't add
621 : * anything to the required size of the hashtable. If it is
622 : * pass-by-reference then we have to add the estimated size of the
623 : * value itself, plus palloc overhead.
624 : */
625 46804 : if (!transinfo->transtypeByVal)
626 : {
627 : int32 avgwidth;
628 :
629 : /* Use average width if aggregate definition gave one */
630 2908 : if (transinfo->aggtransspace > 0)
631 90 : avgwidth = transinfo->aggtransspace;
632 2818 : else if (transinfo->transfn_oid == F_ARRAY_APPEND)
633 : {
634 : /*
635 : * If the transition function is array_append(), it'll use an
636 : * expanded array as transvalue, which will occupy at least
637 : * ALLOCSET_SMALL_INITSIZE and possibly more. Use that as the
638 : * estimate for lack of a better idea.
639 : */
640 6 : avgwidth = ALLOCSET_SMALL_INITSIZE;
641 : }
642 : else
643 : {
644 2812 : avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
645 : }
646 :
647 2908 : avgwidth = MAXALIGN(avgwidth);
648 2908 : costs->transitionSpace += avgwidth + 2 * sizeof(void *);
649 : }
650 43896 : else if (transinfo->aggtranstype == INTERNALOID)
651 : {
652 : /*
653 : * INTERNAL transition type is a special case: although INTERNAL
654 : * is pass-by-value, it's almost certainly being used as a pointer
655 : * to some large data structure. The aggregate definition can
656 : * provide an estimate of the size. If it doesn't, then we assume
657 : * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
658 : * being kept in a private memory context, as is done by
659 : * array_agg() for instance.
660 : */
661 20456 : if (transinfo->aggtransspace > 0)
662 1092 : costs->transitionSpace += transinfo->aggtransspace;
663 : else
664 19364 : costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
665 : }
666 : }
667 :
668 89064 : foreach(lc, root->agginfos)
669 : {
670 47062 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
671 47062 : Aggref *aggref = linitial_node(Aggref, agginfo->aggrefs);
672 :
673 : /*
674 : * Add the appropriate component function execution costs to
675 : * appropriate totals.
676 : */
677 47062 : if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
678 45204 : OidIsValid(agginfo->finalfn_oid))
679 22206 : add_function_cost(root, agginfo->finalfn_oid, NULL,
680 : &costs->finalCost);
681 :
682 : /*
683 : * If there are direct arguments, treat their evaluation cost like the
684 : * cost of the finalfn.
685 : */
686 47062 : if (aggref->aggdirectargs)
687 : {
688 : QualCost argcosts;
689 :
690 294 : cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
691 : root);
692 294 : costs->finalCost.startup += argcosts.startup;
693 294 : costs->finalCost.per_tuple += argcosts.per_tuple;
694 : }
695 : }
696 42002 : }
|