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