Age Owner Branch data TLA 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
1237 heikki.linnakangas@i 110 :CBC 35512 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
111 : : {
112 : 35512 : (void) preprocess_aggrefs_walker(clause, root);
113 : 35512 : }
114 : :
115 : : static void
116 : 20424 : 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 [ - + ]: 20424 : 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 : 20424 : aggTuple = SearchSysCache1(AGGFNOID,
150 : : ObjectIdGetDatum(aggref->aggfnoid));
151 [ - + ]: 20424 : if (!HeapTupleIsValid(aggTuple))
1237 heikki.linnakangas@i 152 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for aggregate %u",
153 : : aggref->aggfnoid);
1237 heikki.linnakangas@i 154 :CBC 20424 : aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
155 : 20424 : aggtransfn = aggform->aggtransfn;
156 : 20424 : aggfinalfn = aggform->aggfinalfn;
157 : 20424 : aggcombinefn = aggform->aggcombinefn;
158 : 20424 : aggserialfn = aggform->aggserialfn;
159 : 20424 : aggdeserialfn = aggform->aggdeserialfn;
160 : 20424 : aggtranstype = aggform->aggtranstype;
161 : 20424 : 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 : 20424 : numArguments = get_aggregate_argtypes(aggref, inputTypes);
169 : :
170 : : /* resolve actual type of transition state, if polymorphic */
171 : 20424 : aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
172 : : aggtranstype,
173 : : inputTypes,
174 : : numArguments);
175 : 20424 : 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 : 20424 : aggtranstypmod = -1;
183 [ + + ]: 20424 : if (aggref->args)
184 : : {
185 : 14542 : TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
186 : :
187 [ + + ]: 14542 : if (aggtranstype == exprType((Node *) tle->expr))
188 : 1506 : 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 : 20424 : shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
201 : :
202 : : /* get info about the output value's datatype */
203 : 20424 : get_typlenbyval(aggref->aggtype,
204 : : &resulttypeLen,
205 : : &resulttypeByVal);
206 : :
207 : : /* get initial value */
208 : 20424 : textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
209 : : Anum_pg_aggregate_agginitval,
210 : : &initValueIsNull);
211 [ + + ]: 20424 : if (initValueIsNull)
212 : 12798 : initValue = (Datum) 0;
213 : : else
214 : 7626 : initValue = GetAggInitVal(textInitVal, aggtranstype);
215 : :
216 : 20424 : ReleaseSysCache(aggTuple);
217 : :
218 : : /*
219 : : * 1. See if this is identical to another aggregate function call that
220 : : * we've seen already.
221 : : */
222 : 20424 : aggno = find_compatible_agg(root, aggref, &same_input_transnos);
223 [ + + ]: 20424 : if (aggno != -1)
224 : : {
635 tgl@sss.pgh.pa.us 225 : 614 : AggInfo *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
226 : :
621 drowley@postgresql.o 227 : 614 : agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
1237 heikki.linnakangas@i 228 : 614 : transno = agginfo->transno;
229 : : }
230 : : else
231 : : {
635 tgl@sss.pgh.pa.us 232 : 19810 : AggInfo *agginfo = makeNode(AggInfo);
233 : :
1237 heikki.linnakangas@i 234 : 19810 : agginfo->finalfn_oid = aggfinalfn;
621 drowley@postgresql.o 235 : 19810 : agginfo->aggrefs = list_make1(aggref);
1237 heikki.linnakangas@i 236 : 19810 : agginfo->shareable = shareable;
237 : :
238 : 19810 : aggno = list_length(root->agginfos);
239 : 19810 : 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 [ + + + + ]: 19810 : if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
247 : : {
248 : 1122 : root->numOrderedAggs++;
249 : 1122 : root->hasNonPartialAggs = true;
250 : : }
251 : :
252 : 19810 : 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 : 19810 : 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 [ + + ]: 19810 : if (transno == -1)
268 : : {
635 tgl@sss.pgh.pa.us 269 : 19681 : AggTransInfo *transinfo = makeNode(AggTransInfo);
270 : :
1237 heikki.linnakangas@i 271 : 19681 : transinfo->args = aggref->args;
272 : 19681 : transinfo->aggfilter = aggref->aggfilter;
273 : 19681 : transinfo->transfn_oid = aggtransfn;
274 : 19681 : transinfo->combinefn_oid = aggcombinefn;
275 : 19681 : transinfo->serialfn_oid = aggserialfn;
276 : 19681 : transinfo->deserialfn_oid = aggdeserialfn;
277 : 19681 : transinfo->aggtranstype = aggtranstype;
278 : 19681 : transinfo->aggtranstypmod = aggtranstypmod;
279 : 19681 : transinfo->transtypeLen = transtypeLen;
280 : 19681 : transinfo->transtypeByVal = transtypeByVal;
281 : 19681 : transinfo->aggtransspace = aggtransspace;
282 : 19681 : transinfo->initValue = initValue;
283 : 19681 : transinfo->initValueIsNull = initValueIsNull;
284 : :
285 : 19681 : transno = list_length(root->aggtransinfos);
286 : 19681 : 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 [ + + ]: 19681 : if (!root->hasNonPartialAggs)
293 : : {
294 : : /*
295 : : * If there is no combine function, then partial aggregation
296 : : * is not possible.
297 : : */
298 [ + + ]: 18430 : if (!OidIsValid(transinfo->combinefn_oid))
299 : 559 : 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 : : */
447 drowley@postgresql.o 307 [ + + ]: 17871 : else if (transinfo->aggtranstype == INTERNALOID)
308 : : {
309 : :
310 [ + - ]: 8620 : if (!OidIsValid(transinfo->serialfn_oid) ||
311 [ - + ]: 8620 : !OidIsValid(transinfo->deserialfn_oid))
447 drowley@postgresql.o 312 :UBC 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 : : */
447 drowley@postgresql.o 326 [ + + ]:CBC 8620 : if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
327 [ - + ]: 2140 : transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
328 [ + + ]: 6480 : !agg_args_support_sendreceive(aggref))
329 : 37 : root->hasNonSerialAggs = true;
330 : : }
331 : : }
332 : : }
1237 heikki.linnakangas@i 333 : 19810 : agginfo->transno = transno;
334 : : }
335 : :
336 : : /*
337 : : * Fill in the fields in the Aggref (aggtranstype was set above already)
338 : : */
339 : 20424 : aggref->aggno = aggno;
340 : 20424 : aggref->aggtransno = transno;
341 : 20424 : }
342 : :
343 : : static bool
344 : 93148 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
345 : : {
346 [ + + ]: 93148 : if (node == NULL)
347 : 17800 : return false;
348 [ + + ]: 75348 : if (IsA(node, Aggref))
349 : : {
350 : 20424 : Aggref *aggref = (Aggref *) node;
351 : :
352 : 20424 : 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 : 20424 : return false;
360 : : }
361 [ - + ]: 54924 : Assert(!IsA(node, SubLink));
362 : 54924 : 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 : 20424 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
381 : : List **same_input_transnos)
382 : : {
383 : : ListCell *lc;
384 : : int aggno;
385 : :
386 : 20424 : *same_input_transnos = NIL;
387 : :
388 : : /* we mustn't reuse the aggref if it contains volatile function calls */
389 [ + + ]: 20424 : if (contain_volatile_functions((Node *) newagg))
390 : 187 : 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 : 20237 : aggno = -1;
402 [ + + + + : 25136 : foreach(lc, root->agginfos)
+ + ]
403 : : {
635 tgl@sss.pgh.pa.us 404 : 5513 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
405 : : Aggref *existingRef;
406 : :
1237 heikki.linnakangas@i 407 : 5513 : aggno++;
408 : :
621 drowley@postgresql.o 409 : 5513 : existingRef = linitial_node(Aggref, agginfo->aggrefs);
410 : :
411 : : /* all of the following must be the same or it's no match */
1237 heikki.linnakangas@i 412 [ + + ]: 5513 : if (newagg->inputcollid != existingRef->inputcollid ||
413 [ + + ]: 4906 : newagg->aggtranstype != existingRef->aggtranstype ||
414 [ + + ]: 3095 : newagg->aggstar != existingRef->aggstar ||
415 [ + - ]: 2605 : newagg->aggvariadic != existingRef->aggvariadic ||
416 [ + + ]: 2605 : newagg->aggkind != existingRef->aggkind ||
417 [ + + ]: 2506 : !equal(newagg->args, existingRef->args) ||
418 [ + + ]: 1299 : !equal(newagg->aggorder, existingRef->aggorder) ||
419 [ + - ]: 1296 : !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
420 [ + + ]: 1296 : !equal(newagg->aggfilter, existingRef->aggfilter))
421 : 4321 : continue;
422 : :
423 : : /* if it's the same aggregate function then report exact match */
424 [ + + ]: 1192 : if (newagg->aggfnoid == existingRef->aggfnoid &&
425 [ + - ]: 620 : newagg->aggtype == existingRef->aggtype &&
426 [ + - + + ]: 1240 : newagg->aggcollid == existingRef->aggcollid &&
427 : 620 : equal(newagg->aggdirectargs, existingRef->aggdirectargs))
428 : : {
429 : 614 : list_free(*same_input_transnos);
430 : 614 : *same_input_transnos = NIL;
431 : 614 : 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 [ + + ]: 578 : if (agginfo->shareable)
442 : 575 : *same_input_transnos = lappend_int(*same_input_transnos,
443 : : agginfo->transno);
444 : : }
445 : :
446 : 19623 : 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 : 19810 : 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 [ + + ]: 19810 : if (!shareable)
470 : 60 : return -1;
471 : :
472 [ + + + + : 20169 : foreach(lc, transnos)
+ + ]
473 : : {
474 : 548 : int transno = lfirst_int(lc);
635 tgl@sss.pgh.pa.us 475 : 548 : 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 : : */
1237 heikki.linnakangas@i 483 [ + + ]: 548 : if (aggtransfn != pertrans->transfn_oid ||
484 [ - + ]: 132 : aggtranstype != pertrans->aggtranstype)
485 : 416 : 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 [ + - ]: 132 : if (aggserialfn != pertrans->serialfn_oid ||
495 [ - + ]: 132 : aggdeserialfn != pertrans->deserialfn_oid)
1237 heikki.linnakangas@i 496 :UBC 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 : : */
1237 heikki.linnakangas@i 504 [ - + ]:CBC 132 : if (aggcombinefn != pertrans->combinefn_oid)
1237 heikki.linnakangas@i 505 :UBC 0 : continue;
506 : :
507 : : /*
508 : : * Check that the initial condition matches, too.
509 : : */
1237 heikki.linnakangas@i 510 [ + + + - ]:CBC 132 : if (initValueIsNull && pertrans->initValueIsNull)
511 : 129 : return transno;
512 : :
513 [ + - + - : 168 : if (!initValueIsNull && !pertrans->initValueIsNull &&
+ + ]
514 : 84 : datumIsEqual(initValue, pertrans->initValue,
515 : : transtypeByVal, transtypeLen))
516 : 81 : return transno;
517 : : }
518 : 19621 : return -1;
519 : : }
520 : :
521 : : static Datum
522 : 7626 : GetAggInitVal(Datum textInitVal, Oid transtype)
523 : : {
524 : : Oid typinput,
525 : : typioparam;
526 : : char *strInitVal;
527 : : Datum initVal;
528 : :
529 : 7626 : getTypeInputInfo(transtype, &typinput, &typioparam);
530 : 7626 : strInitVal = TextDatumGetCString(textInitVal);
531 : 7626 : initVal = OidInputFunctionCall(typinput, strInitVal,
532 : : typioparam, -1);
533 : 7626 : pfree(strInitVal);
534 : 7626 : 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 : 19235 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
561 : : {
562 : : ListCell *lc;
563 : :
564 [ + + + + : 40912 : foreach(lc, root->aggtransinfos)
+ + ]
565 : : {
635 tgl@sss.pgh.pa.us 566 : 21677 : AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
567 : :
568 : : /*
569 : : * Add the appropriate component function execution costs to
570 : : * appropriate totals.
571 : : */
1237 heikki.linnakangas@i 572 [ + + ]: 21677 : if (DO_AGGSPLIT_COMBINE(aggsplit))
573 : : {
574 : : /* charge for combining previously aggregated states */
575 : 928 : add_function_cost(root, transinfo->combinefn_oid, NULL,
576 : : &costs->transCost);
577 : : }
578 : : else
579 : 20749 : add_function_cost(root, transinfo->transfn_oid, NULL,
580 : : &costs->transCost);
581 [ + + ]: 21677 : if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
582 [ + + ]: 928 : OidIsValid(transinfo->deserialfn_oid))
583 : 61 : add_function_cost(root, transinfo->deserialfn_oid, NULL,
584 : : &costs->transCost);
585 [ + + ]: 21677 : if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
586 [ + + ]: 928 : OidIsValid(transinfo->serialfn_oid))
587 : 61 : 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 [ + + ]: 21677 : if (!DO_AGGSPLIT_COMBINE(aggsplit))
595 : : {
596 : : /* add the input expressions' cost to per-input-row costs */
597 : : QualCost argcosts;
598 : :
599 : 20749 : cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
600 : 20749 : costs->transCost.startup += argcosts.startup;
601 : 20749 : 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 [ + + ]: 20749 : if (transinfo->aggfilter)
611 : : {
612 : 381 : cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
613 : : root);
614 : 381 : costs->transCost.startup += argcosts.startup;
615 : 381 : 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 [ + + ]: 21677 : if (!transinfo->transtypeByVal)
626 : : {
627 : : int32 avgwidth;
628 : :
629 : : /* Use average width if aggregate definition gave one */
630 [ + + ]: 1415 : if (transinfo->aggtransspace > 0)
631 : 45 : avgwidth = transinfo->aggtransspace;
632 [ + + ]: 1370 : 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 : 3 : avgwidth = ALLOCSET_SMALL_INITSIZE;
641 : : }
642 : : else
643 : : {
644 : 1367 : avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
645 : : }
646 : :
647 : 1415 : avgwidth = MAXALIGN(avgwidth);
648 : 1415 : costs->transitionSpace += avgwidth + 2 * sizeof(void *);
649 : : }
650 [ + + ]: 20262 : 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 [ + + ]: 9976 : if (transinfo->aggtransspace > 0)
662 : 546 : costs->transitionSpace += transinfo->aggtransspace;
663 : : else
664 : 9430 : costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
665 : : }
666 : : }
667 : :
668 [ + + + + : 41041 : foreach(lc, root->agginfos)
+ + ]
669 : : {
635 tgl@sss.pgh.pa.us 670 : 21806 : AggInfo *agginfo = lfirst_node(AggInfo, lc);
621 drowley@postgresql.o 671 : 21806 : Aggref *aggref = linitial_node(Aggref, agginfo->aggrefs);
672 : :
673 : : /*
674 : : * Add the appropriate component function execution costs to
675 : : * appropriate totals.
676 : : */
1237 heikki.linnakangas@i 677 [ + + ]: 21806 : if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
678 [ + + ]: 20878 : OidIsValid(agginfo->finalfn_oid))
679 : 10837 : 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 [ + + ]: 21806 : if (aggref->aggdirectargs)
687 : : {
688 : : QualCost argcosts;
689 : :
690 : 147 : cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
691 : : root);
692 : 147 : costs->finalCost.startup += argcosts.startup;
693 : 147 : costs->finalCost.per_tuple += argcosts.per_tuple;
694 : : }
695 : : }
696 : 19235 : }
|