1 /*-------------------------------------------------------------------------
4 * Routines to handle aggregate nodes.
6 * ExecAgg evaluates each aggregate in the following steps:
8 * transvalue = initcond
9 * foreach input_value do
10 * transvalue = transfunc(transvalue, input_value)
11 * result = finalfunc(transvalue)
13 * If a finalfunc is not supplied then the result is just the ending
14 * value of transvalue.
16 * If transfunc is marked "strict" in pg_proc and initcond is NULL,
17 * then the first non-NULL input_value is assigned directly to transvalue,
18 * and transfunc isn't applied until the second non-NULL input_value.
19 * The agg's input type and transtype must be the same in this case!
21 * If transfunc is marked "strict" then NULL input_values are skipped,
22 * keeping the previous transvalue. If transfunc is not strict then it
23 * is called for every input tuple and must deal with NULL initcond
24 * or NULL input_value for itself.
26 * If finalfunc is marked "strict" then it is not called when the
27 * ending transvalue is NULL, instead a NULL result is created
28 * automatically (this is just the usual handling of strict functions,
29 * of course). A non-strict finalfunc can make its own choice of
30 * what to return for a NULL ending transvalue.
32 * We compute aggregate input expressions and run the transition functions
33 * in a temporary econtext (aggstate->tmpcontext). This is reset at
34 * least once per input tuple, so when the transvalue datatype is
35 * pass-by-reference, we have to be careful to copy it into a longer-lived
36 * memory context, and free the prior value to avoid memory leakage.
37 * We store transvalues in the memory context aggstate->aggcontext,
38 * which is also used for the hashtable structures in AGG_HASHED mode.
39 * The node's regular econtext (aggstate->csstate.cstate.cs_ExprContext)
40 * is used to run finalize functions and compute the output tuple;
41 * this context can be reset once per output tuple.
44 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
45 * Portions Copyright (c) 1994, Regents of the University of California
48 * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.108 2003/06/24 23:14:43 momjian Exp $
50 *-------------------------------------------------------------------------
55 #include "access/heapam.h"
56 #include "catalog/pg_aggregate.h"
57 #include "catalog/pg_operator.h"
58 #include "executor/executor.h"
59 #include "executor/nodeAgg.h"
60 #include "miscadmin.h"
61 #include "nodes/makefuncs.h"
62 #include "optimizer/clauses.h"
63 #include "parser/parse_coerce.h"
64 #include "parser/parse_expr.h"
65 #include "parser/parse_oper.h"
66 #include "utils/acl.h"
67 #include "utils/builtins.h"
68 #include "utils/lsyscache.h"
69 #include "utils/syscache.h"
70 #include "utils/tuplesort.h"
71 #include "utils/datum.h"
75 * AggStatePerAggData - per-aggregate working state for the Agg scan
77 typedef struct AggStatePerAggData
80 * These values are set up during ExecInitAgg() and do not change
84 /* Links to Aggref expr and state nodes this working state is for */
85 AggrefExprState *aggrefstate;
88 /* Oids of transfer functions */
90 Oid finalfn_oid; /* may be InvalidOid */
93 * fmgr lookup data for transfer functions --- only valid when
94 * corresponding oid is not InvalidOid. Note in particular that
95 * fn_strict flags are kept here.
101 * Type of input data and Oid of sort operator to use for it; only
102 * set/used when aggregate has DISTINCT flag. (These are not used
103 * directly by nodeAgg, but must be passed to the Tuplesort object.)
109 * fmgr lookup data for input type's equality operator --- only
110 * set/used when aggregate has DISTINCT flag.
115 * initial value from pg_aggregate entry
118 bool initValueIsNull;
121 * We need the len and byval info for the agg's input, result, and
122 * transition data types in order to know how to copy/delete values.
132 * These values are working state that is initialized at the start of
133 * an input tuple group and updated for each input tuple.
135 * For a simple (non DISTINCT) aggregate, we just feed the input values
136 * straight to the transition function. If it's DISTINCT, we pass the
137 * input values into a Tuplesort object; then at completion of the
138 * input tuple group, we scan the sorted values, eliminate duplicates,
139 * and run the transition function on the rest.
142 Tuplesortstate *sortstate; /* sort object, if a DISTINCT agg */
143 } AggStatePerAggData;
146 * AggStatePerGroupData - per-aggregate-per-group working state
148 * These values are working state that is initialized at the start of
149 * an input tuple group and updated for each input tuple.
151 * In AGG_PLAIN and AGG_SORTED modes, we have a single array of these
152 * structs (pointed to by aggstate->pergroup); we re-use the array for
153 * each input group, if it's AGG_SORTED mode. In AGG_HASHED mode, the
154 * hash table contains an array of these structs for each tuple group.
156 * Logically, the sortstate field belongs in this struct, but we do not
157 * keep it here for space reasons: we don't support DISTINCT aggregates
158 * in AGG_HASHED mode, so there's no reason to use up a pointer field
159 * in every entry of the hashtable.
161 typedef struct AggStatePerGroupData
163 Datum transValue; /* current transition value */
164 bool transValueIsNull;
166 bool noTransValue; /* true if transValue not set yet */
169 * Note: noTransValue initially has the same value as
170 * transValueIsNull, and if true both are cleared to false at the same
171 * time. They are not the same though: if transfn later returns a
172 * NULL, we want to keep that NULL and not auto-replace it with a
173 * later input value. Only the first non-NULL input will be
176 } AggStatePerGroupData;
179 * To implement hashed aggregation, we need a hashtable that stores a
180 * representative tuple and an array of AggStatePerGroup structs for each
181 * distinct set of GROUP BY column values. We compute the hash key from
182 * the GROUP BY columns.
184 typedef struct AggHashEntryData *AggHashEntry;
186 typedef struct AggHashEntryData
188 TupleHashEntryData shared; /* common header for hash table entries */
189 /* per-aggregate transition status array - must be last! */
190 AggStatePerGroupData pergroup[1]; /* VARIABLE LENGTH ARRAY */
191 } AggHashEntryData; /* VARIABLE LENGTH STRUCT */
194 static void initialize_aggregates(AggState *aggstate,
195 AggStatePerAgg peragg,
196 AggStatePerGroup pergroup);
197 static void advance_transition_function(AggState *aggstate,
198 AggStatePerAgg peraggstate,
199 AggStatePerGroup pergroupstate,
200 Datum newVal, bool isNull);
201 static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup);
202 static void process_sorted_aggregate(AggState *aggstate,
203 AggStatePerAgg peraggstate,
204 AggStatePerGroup pergroupstate);
205 static void finalize_aggregate(AggState *aggstate,
206 AggStatePerAgg peraggstate,
207 AggStatePerGroup pergroupstate,
208 Datum *resultVal, bool *resultIsNull);
209 static void build_hash_table(AggState *aggstate);
210 static AggHashEntry lookup_hash_entry(AggState *aggstate,
211 TupleTableSlot *slot);
212 static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
213 static void agg_fill_hash_table(AggState *aggstate);
214 static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
215 static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
216 static Oid resolve_type(Oid type_to_resolve, Oid context_type);
219 * Initialize all aggregates for a new group of input values.
221 * When called, CurrentMemoryContext should be the per-query context.
224 initialize_aggregates(AggState *aggstate,
225 AggStatePerAgg peragg,
226 AggStatePerGroup pergroup)
230 for (aggno = 0; aggno < aggstate->numaggs; aggno++)
232 AggStatePerAgg peraggstate = &peragg[aggno];
233 AggStatePerGroup pergroupstate = &pergroup[aggno];
234 Aggref *aggref = peraggstate->aggref;
237 * Start a fresh sort operation for each DISTINCT aggregate.
239 if (aggref->aggdistinct)
242 * In case of rescan, maybe there could be an uncompleted sort
243 * operation? Clean it up if so.
245 if (peraggstate->sortstate)
246 tuplesort_end(peraggstate->sortstate);
248 peraggstate->sortstate =
249 tuplesort_begin_datum(peraggstate->inputType,
250 peraggstate->sortOperator,
255 * (Re)set transValue to the initial value.
257 * Note that when the initial value is pass-by-ref, we must copy it
258 * (into the aggcontext) since we will pfree the transValue later.
260 if (peraggstate->initValueIsNull)
261 pergroupstate->transValue = peraggstate->initValue;
264 MemoryContext oldContext;
266 oldContext = MemoryContextSwitchTo(aggstate->aggcontext);
267 pergroupstate->transValue = datumCopy(peraggstate->initValue,
268 peraggstate->transtypeByVal,
269 peraggstate->transtypeLen);
270 MemoryContextSwitchTo(oldContext);
272 pergroupstate->transValueIsNull = peraggstate->initValueIsNull;
275 * If the initial value for the transition state doesn't exist in the
276 * pg_aggregate table then we will let the first non-NULL value
277 * returned from the outer procNode become the initial value. (This is
278 * useful for aggregates like max() and min().) The noTransValue flag
279 * signals that we still need to do this.
281 pergroupstate->noTransValue = peraggstate->initValueIsNull;
286 * Given a new input value, advance the transition function of an aggregate.
288 * It doesn't matter which memory context this is called in.
291 advance_transition_function(AggState *aggstate,
292 AggStatePerAgg peraggstate,
293 AggStatePerGroup pergroupstate,
294 Datum newVal, bool isNull)
296 FunctionCallInfoData fcinfo;
297 MemoryContext oldContext;
299 if (peraggstate->transfn.fn_strict)
302 * For a strict transfn, nothing happens at a NULL input
303 * tuple; we just keep the prior transValue.
307 if (pergroupstate->noTransValue)
310 * transValue has not been initialized. This is the first
311 * non-NULL input value. We use it as the initial value for
312 * transValue. (We already checked that the agg's input type
313 * is binary-compatible with its transtype, so straight copy
316 * We must copy the datum into aggcontext if it is pass-by-ref.
317 * We do not need to pfree the old transValue, since it's NULL.
319 oldContext = MemoryContextSwitchTo(aggstate->aggcontext);
320 pergroupstate->transValue = datumCopy(newVal,
321 peraggstate->transtypeByVal,
322 peraggstate->transtypeLen);
323 pergroupstate->transValueIsNull = false;
324 pergroupstate->noTransValue = false;
325 MemoryContextSwitchTo(oldContext);
328 if (pergroupstate->transValueIsNull)
331 * Don't call a strict function with NULL inputs. Note it is
332 * possible to get here despite the above tests, if the
333 * transfn is strict *and* returned a NULL on a prior cycle.
334 * If that happens we will propagate the NULL all the way to
341 /* We run the transition functions in per-input-tuple memory context */
342 oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
345 * OK to call the transition function
347 * This is heavily-used code, so manually zero just the necessary fields
348 * instead of using MemSet(). Compare FunctionCall2().
351 /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
352 fcinfo.context = NULL;
353 fcinfo.resultinfo = NULL;
354 fcinfo.isnull = false;
355 fcinfo.flinfo = &peraggstate->transfn;
357 fcinfo.arg[0] = pergroupstate->transValue;
358 fcinfo.argnull[0] = pergroupstate->transValueIsNull;
359 fcinfo.arg[1] = newVal;
360 fcinfo.argnull[1] = isNull;
361 newVal = FunctionCallInvoke(&fcinfo);
364 * If pass-by-ref datatype, must copy the new value into aggcontext and
365 * pfree the prior transValue. But if transfn returned a pointer to its
366 * first input, we don't need to do anything.
368 if (!peraggstate->transtypeByVal &&
369 DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue))
373 MemoryContextSwitchTo(aggstate->aggcontext);
374 newVal = datumCopy(newVal,
375 peraggstate->transtypeByVal,
376 peraggstate->transtypeLen);
378 if (!pergroupstate->transValueIsNull)
379 pfree(DatumGetPointer(pergroupstate->transValue));
382 pergroupstate->transValue = newVal;
383 pergroupstate->transValueIsNull = fcinfo.isnull;
385 MemoryContextSwitchTo(oldContext);
389 * Advance all the aggregates for one input tuple. The input tuple
390 * has been stored in tmpcontext->ecxt_scantuple, so that it is accessible
391 * to ExecEvalExpr. pergroup is the array of per-group structs to use
392 * (this might be in a hashtable entry).
394 * When called, CurrentMemoryContext should be the per-query context.
397 advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
399 ExprContext *econtext = aggstate->tmpcontext;
402 for (aggno = 0; aggno < aggstate->numaggs; aggno++)
404 AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
405 AggStatePerGroup pergroupstate = &pergroup[aggno];
406 AggrefExprState *aggrefstate = peraggstate->aggrefstate;
407 Aggref *aggref = peraggstate->aggref;
411 newVal = ExecEvalExprSwitchContext(aggrefstate->target, econtext,
414 if (aggref->aggdistinct)
416 /* in DISTINCT mode, we may ignore nulls */
419 tuplesort_putdatum(peraggstate->sortstate, newVal, isNull);
423 advance_transition_function(aggstate, peraggstate, pergroupstate,
430 * Run the transition function for a DISTINCT aggregate. This is called
431 * after we have completed entering all the input values into the sort
432 * object. We complete the sort, read out the values in sorted order,
433 * and run the transition function on each non-duplicate value.
435 * When called, CurrentMemoryContext should be the per-query context.
438 process_sorted_aggregate(AggState *aggstate,
439 AggStatePerAgg peraggstate,
440 AggStatePerGroup pergroupstate)
442 Datum oldVal = (Datum) 0;
443 bool haveOldVal = false;
444 MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
445 MemoryContext oldContext;
449 tuplesort_performsort(peraggstate->sortstate);
452 * Note: if input type is pass-by-ref, the datums returned by the sort
453 * are freshly palloc'd in the per-query context, so we must be
454 * careful to pfree them when they are no longer needed.
457 while (tuplesort_getdatum(peraggstate->sortstate, true,
461 * DISTINCT always suppresses nulls, per SQL spec, regardless of
462 * the transition function's strictness.
468 * Clear and select the working context for evaluation of
469 * the equality function and transition function.
471 MemoryContextReset(workcontext);
472 oldContext = MemoryContextSwitchTo(workcontext);
475 DatumGetBool(FunctionCall2(&peraggstate->equalfn,
478 /* equal to prior, so forget this one */
479 if (!peraggstate->inputtypeByVal)
480 pfree(DatumGetPointer(newVal));
484 advance_transition_function(aggstate, peraggstate, pergroupstate,
486 /* forget the old value, if any */
487 if (haveOldVal && !peraggstate->inputtypeByVal)
488 pfree(DatumGetPointer(oldVal));
489 /* and remember the new one for subsequent equality checks */
494 MemoryContextSwitchTo(oldContext);
497 if (haveOldVal && !peraggstate->inputtypeByVal)
498 pfree(DatumGetPointer(oldVal));
500 tuplesort_end(peraggstate->sortstate);
501 peraggstate->sortstate = NULL;
505 * Compute the final value of one aggregate.
507 * The finalfunction will be run, and the result delivered, in the
508 * output-tuple context; caller's CurrentMemoryContext does not matter.
511 finalize_aggregate(AggState *aggstate,
512 AggStatePerAgg peraggstate,
513 AggStatePerGroup pergroupstate,
514 Datum *resultVal, bool *resultIsNull)
516 MemoryContext oldContext;
518 oldContext = MemoryContextSwitchTo(aggstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
521 * Apply the agg's finalfn if one is provided, else return transValue.
523 if (OidIsValid(peraggstate->finalfn_oid))
525 FunctionCallInfoData fcinfo;
527 MemSet(&fcinfo, 0, sizeof(fcinfo));
528 fcinfo.flinfo = &peraggstate->finalfn;
530 fcinfo.arg[0] = pergroupstate->transValue;
531 fcinfo.argnull[0] = pergroupstate->transValueIsNull;
532 if (fcinfo.flinfo->fn_strict && pergroupstate->transValueIsNull)
534 /* don't call a strict function with NULL inputs */
535 *resultVal = (Datum) 0;
536 *resultIsNull = true;
540 *resultVal = FunctionCallInvoke(&fcinfo);
541 *resultIsNull = fcinfo.isnull;
546 *resultVal = pergroupstate->transValue;
547 *resultIsNull = pergroupstate->transValueIsNull;
551 * If result is pass-by-ref, make sure it is in the right context.
553 if (!peraggstate->resulttypeByVal && !*resultIsNull &&
554 !MemoryContextContains(CurrentMemoryContext,
555 DatumGetPointer(*resultVal)))
556 *resultVal = datumCopy(*resultVal,
557 peraggstate->resulttypeByVal,
558 peraggstate->resulttypeLen);
560 MemoryContextSwitchTo(oldContext);
564 * Initialize the hash table to empty.
566 * The hash table always lives in the aggcontext memory context.
569 build_hash_table(AggState *aggstate)
571 Agg *node = (Agg *) aggstate->ss.ps.plan;
572 MemoryContext tmpmem = aggstate->tmpcontext->ecxt_per_tuple_memory;
575 Assert(node->aggstrategy == AGG_HASHED);
576 Assert(node->numGroups > 0);
578 entrysize = sizeof(AggHashEntryData) +
579 (aggstate->numaggs - 1) * sizeof(AggStatePerGroupData);
581 aggstate->hashtable = BuildTupleHashTable(node->numCols,
583 aggstate->eqfunctions,
584 aggstate->hashfunctions,
587 aggstate->aggcontext,
592 * Find or create a hashtable entry for the tuple group containing the
595 * When called, CurrentMemoryContext should be the per-query context.
598 lookup_hash_entry(AggState *aggstate, TupleTableSlot *slot)
603 entry = (AggHashEntry) LookupTupleHashEntry(aggstate->hashtable,
609 /* initialize aggregates for new tuple group */
610 initialize_aggregates(aggstate, aggstate->peragg, entry->pergroup);
619 * ExecAgg receives tuples from its outer subplan and aggregates over
620 * the appropriate attribute for each aggregate function use (Aggref
621 * node) appearing in the targetlist or qual of the node. The number
622 * of tuples to aggregate over depends on whether grouped or plain
623 * aggregation is selected. In grouped aggregation, we produce a result
624 * row for each group; in plain aggregation there's a single result row
625 * for the whole query. In either case, the value of each aggregate is
626 * stored in the expression context to be used when ExecProject evaluates
630 ExecAgg(AggState *node)
635 if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
637 if (!node->table_filled)
638 agg_fill_hash_table(node);
639 return agg_retrieve_hash_table(node);
643 return agg_retrieve_direct(node);
648 * ExecAgg for non-hashed case
650 static TupleTableSlot *
651 agg_retrieve_direct(AggState *aggstate)
653 Agg *node = (Agg *) aggstate->ss.ps.plan;
654 PlanState *outerPlan;
655 ExprContext *econtext;
656 ExprContext *tmpcontext;
657 ProjectionInfo *projInfo;
660 AggStatePerAgg peragg;
661 AggStatePerGroup pergroup;
662 TupleTableSlot *outerslot;
663 TupleTableSlot *firstSlot;
664 TupleTableSlot *resultSlot;
668 * get state info from node
670 outerPlan = outerPlanState(aggstate);
671 /* econtext is the per-output-tuple expression context */
672 econtext = aggstate->ss.ps.ps_ExprContext;
673 aggvalues = econtext->ecxt_aggvalues;
674 aggnulls = econtext->ecxt_aggnulls;
675 /* tmpcontext is the per-input-tuple expression context */
676 tmpcontext = aggstate->tmpcontext;
677 projInfo = aggstate->ss.ps.ps_ProjInfo;
678 peragg = aggstate->peragg;
679 pergroup = aggstate->pergroup;
680 firstSlot = aggstate->ss.ss_ScanTupleSlot;
683 * We loop retrieving groups until we find one matching
684 * aggstate->ss.ps.qual
688 if (aggstate->agg_done)
692 * If we don't already have the first tuple of the new group,
693 * fetch it from the outer plan.
695 if (aggstate->grp_firstTuple == NULL)
697 outerslot = ExecProcNode(outerPlan);
698 if (!TupIsNull(outerslot))
701 * Make a copy of the first input tuple; we will use this
702 * for comparisons (in group mode) and for projection.
704 aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
708 /* outer plan produced no tuples at all */
709 aggstate->agg_done = true;
710 /* If we are grouping, we should produce no tuples too */
711 if (node->aggstrategy != AGG_PLAIN)
717 * Clear the per-output-tuple context for each group
719 ResetExprContext(econtext);
722 * Initialize working state for a new input tuple group
724 initialize_aggregates(aggstate, peragg, pergroup);
726 if (aggstate->grp_firstTuple != NULL)
729 * Store the copied first input tuple in the tuple table slot
730 * reserved for it. The tuple will be deleted when it is
731 * cleared from the slot.
733 ExecStoreTuple(aggstate->grp_firstTuple,
737 aggstate->grp_firstTuple = NULL; /* don't keep two pointers */
739 /* set up for first advance_aggregates call */
740 tmpcontext->ecxt_scantuple = firstSlot;
743 * Process each outer-plan tuple, and then fetch the next one,
744 * until we exhaust the outer plan or cross a group boundary.
748 advance_aggregates(aggstate, pergroup);
750 /* Reset per-input-tuple context after each tuple */
751 ResetExprContext(tmpcontext);
753 outerslot = ExecProcNode(outerPlan);
754 if (TupIsNull(outerslot))
756 /* no more outer-plan tuples available */
757 aggstate->agg_done = true;
760 /* set up for next advance_aggregates call */
761 tmpcontext->ecxt_scantuple = outerslot;
764 * If we are grouping, check whether we've crossed a group
767 if (node->aggstrategy == AGG_SORTED)
769 if (!execTuplesMatch(firstSlot->val,
771 firstSlot->ttc_tupleDescriptor,
772 node->numCols, node->grpColIdx,
773 aggstate->eqfunctions,
774 tmpcontext->ecxt_per_tuple_memory))
777 * Save the first input tuple of the next group.
779 aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
787 * Done scanning input tuple group. Finalize each aggregate
788 * calculation, and stash results in the per-output-tuple context.
790 for (aggno = 0; aggno < aggstate->numaggs; aggno++)
792 AggStatePerAgg peraggstate = &peragg[aggno];
793 AggStatePerGroup pergroupstate = &pergroup[aggno];
795 if (peraggstate->aggref->aggdistinct)
796 process_sorted_aggregate(aggstate, peraggstate, pergroupstate);
798 finalize_aggregate(aggstate, peraggstate, pergroupstate,
799 &aggvalues[aggno], &aggnulls[aggno]);
803 * If we have no first tuple (ie, the outerPlan didn't return
804 * anything), create a dummy all-nulls input tuple for use by
805 * ExecProject. 99.44% of the time this is a waste of cycles,
806 * because ordinarily the projected output tuple's targetlist
807 * cannot contain any direct (non-aggregated) references to
808 * input columns, so the dummy tuple will not be referenced.
809 * However there are special cases where this isn't so --- in
810 * particular an UPDATE involving an aggregate will have a
811 * targetlist reference to ctid. We need to return a null for
812 * ctid in that situation, not coredump.
814 * The values returned for the aggregates will be the initial
815 * values of the transition functions.
817 if (TupIsNull(firstSlot))
821 /* Should only happen in non-grouped mode */
822 Assert(node->aggstrategy == AGG_PLAIN);
823 Assert(aggstate->agg_done);
825 tupType = firstSlot->ttc_tupleDescriptor;
826 /* watch out for zero-column input tuples, though... */
827 if (tupType && tupType->natts > 0)
829 HeapTuple nullsTuple;
833 dvalues = (Datum *) palloc0(sizeof(Datum) * tupType->natts);
834 dnulls = (char *) palloc(sizeof(char) * tupType->natts);
835 MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
836 nullsTuple = heap_formtuple(tupType, dvalues, dnulls);
837 ExecStoreTuple(nullsTuple,
847 * Form a projection tuple using the aggregate results and the
848 * representative input tuple. Store it in the result tuple slot.
849 * Note we do not support aggregates returning sets ...
851 econtext->ecxt_scantuple = firstSlot;
852 resultSlot = ExecProject(projInfo, NULL);
855 * If the completed tuple does not match the qualifications, it is
856 * ignored and we loop back to try to process another group.
857 * Otherwise, return the tuple.
860 while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
866 * ExecAgg for hashed case: phase 1, read input and build hash table
869 agg_fill_hash_table(AggState *aggstate)
871 PlanState *outerPlan;
872 ExprContext *tmpcontext;
874 TupleTableSlot *outerslot;
877 * get state info from node
879 outerPlan = outerPlanState(aggstate);
880 /* tmpcontext is the per-input-tuple expression context */
881 tmpcontext = aggstate->tmpcontext;
884 * Process each outer-plan tuple, and then fetch the next one,
885 * until we exhaust the outer plan.
889 outerslot = ExecProcNode(outerPlan);
890 if (TupIsNull(outerslot))
892 /* set up for advance_aggregates call */
893 tmpcontext->ecxt_scantuple = outerslot;
895 /* Find or build hashtable entry for this tuple's group */
896 entry = lookup_hash_entry(aggstate, outerslot);
898 /* Advance the aggregates */
899 advance_aggregates(aggstate, entry->pergroup);
901 /* Reset per-input-tuple context after each tuple */
902 ResetExprContext(tmpcontext);
905 aggstate->table_filled = true;
906 /* Initialize to walk the hash table */
907 ResetTupleHashIterator(&aggstate->hashiter);
911 * ExecAgg for hashed case: phase 2, retrieving groups from hash table
913 static TupleTableSlot *
914 agg_retrieve_hash_table(AggState *aggstate)
916 ExprContext *econtext;
917 ProjectionInfo *projInfo;
920 AggStatePerAgg peragg;
921 AggStatePerGroup pergroup;
922 TupleHashTable hashtable;
924 TupleTableSlot *firstSlot;
925 TupleTableSlot *resultSlot;
929 * get state info from node
931 /* econtext is the per-output-tuple expression context */
932 econtext = aggstate->ss.ps.ps_ExprContext;
933 aggvalues = econtext->ecxt_aggvalues;
934 aggnulls = econtext->ecxt_aggnulls;
935 projInfo = aggstate->ss.ps.ps_ProjInfo;
936 peragg = aggstate->peragg;
937 hashtable = aggstate->hashtable;
938 firstSlot = aggstate->ss.ss_ScanTupleSlot;
941 * We loop retrieving groups until we find one satisfying
942 * aggstate->ss.ps.qual
946 if (aggstate->agg_done)
950 * Find the next entry in the hash table
952 entry = (AggHashEntry) ScanTupleHashTable(hashtable,
953 &aggstate->hashiter);
956 /* No more entries in hashtable, so done */
957 aggstate->agg_done = TRUE;
962 * Clear the per-output-tuple context for each group
964 ResetExprContext(econtext);
967 * Store the copied first input tuple in the tuple table slot
968 * reserved for it, so that it can be used in ExecProject.
970 ExecStoreTuple(entry->shared.firstTuple,
975 pergroup = entry->pergroup;
978 * Finalize each aggregate calculation, and stash results in the
979 * per-output-tuple context.
981 for (aggno = 0; aggno < aggstate->numaggs; aggno++)
983 AggStatePerAgg peraggstate = &peragg[aggno];
984 AggStatePerGroup pergroupstate = &pergroup[aggno];
986 Assert(!peraggstate->aggref->aggdistinct);
987 finalize_aggregate(aggstate, peraggstate, pergroupstate,
988 &aggvalues[aggno], &aggnulls[aggno]);
992 * Form a projection tuple using the aggregate results and the
993 * representative input tuple. Store it in the result tuple slot.
994 * Note we do not support aggregates returning sets ...
996 econtext->ecxt_scantuple = firstSlot;
997 resultSlot = ExecProject(projInfo, NULL);
1000 * If the completed tuple does not match the qualifications, it is
1001 * ignored and we loop back to try to process another group.
1002 * Otherwise, return the tuple.
1005 while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
1010 /* -----------------
1013 * Creates the run-time information for the agg node produced by the
1014 * planner and initializes its outer subtree
1018 ExecInitAgg(Agg *node, EState *estate)
1021 AggStatePerAgg peragg;
1023 ExprContext *econtext;
1029 * create state structure
1031 aggstate = makeNode(AggState);
1032 aggstate->ss.ps.plan = (Plan *) node;
1033 aggstate->ss.ps.state = estate;
1035 aggstate->aggs = NIL;
1036 aggstate->numaggs = 0;
1037 aggstate->eqfunctions = NULL;
1038 aggstate->hashfunctions = NULL;
1039 aggstate->peragg = NULL;
1040 aggstate->agg_done = false;
1041 aggstate->pergroup = NULL;
1042 aggstate->grp_firstTuple = NULL;
1043 aggstate->hashtable = NULL;
1046 * Create expression contexts. We need two, one for per-input-tuple
1047 * processing and one for per-output-tuple processing. We cheat a little
1048 * by using ExecAssignExprContext() to build both.
1050 ExecAssignExprContext(estate, &aggstate->ss.ps);
1051 aggstate->tmpcontext = aggstate->ss.ps.ps_ExprContext;
1052 ExecAssignExprContext(estate, &aggstate->ss.ps);
1055 * We also need a long-lived memory context for holding hashtable
1056 * data structures and transition values. NOTE: the details of what
1057 * is stored in aggcontext and what is stored in the regular per-query
1058 * memory context are driven by a simple decision: we want to reset the
1059 * aggcontext in ExecReScanAgg to recover no-longer-wanted space.
1061 aggstate->aggcontext =
1062 AllocSetContextCreate(CurrentMemoryContext,
1064 ALLOCSET_DEFAULT_MINSIZE,
1065 ALLOCSET_DEFAULT_INITSIZE,
1066 ALLOCSET_DEFAULT_MAXSIZE);
1068 #define AGG_NSLOTS 2
1071 * tuple table initialization
1073 ExecInitScanTupleSlot(estate, &aggstate->ss);
1074 ExecInitResultTupleSlot(estate, &aggstate->ss.ps);
1077 * initialize child expressions
1079 * Note: ExecInitExpr finds Aggrefs for us, and also checks that no aggs
1080 * contain other agg calls in their arguments. This would make no sense
1081 * under SQL semantics anyway (and it's forbidden by the spec). Because
1082 * that is true, we don't need to worry about evaluating the aggs in any
1085 aggstate->ss.ps.targetlist = (List *)
1086 ExecInitExpr((Expr *) node->plan.targetlist,
1087 (PlanState *) aggstate);
1088 aggstate->ss.ps.qual = (List *)
1089 ExecInitExpr((Expr *) node->plan.qual,
1090 (PlanState *) aggstate);
1093 * initialize child nodes
1095 outerPlan = outerPlan(node);
1096 outerPlanState(aggstate) = ExecInitNode(outerPlan, estate);
1099 * initialize source tuple type.
1101 ExecAssignScanTypeFromOuterPlan(&aggstate->ss);
1104 * Initialize result tuple type and projection info.
1106 ExecAssignResultTypeFromTL(&aggstate->ss.ps);
1107 ExecAssignProjectionInfo(&aggstate->ss.ps);
1110 * get the count of aggregates in targetlist and quals
1112 numaggs = aggstate->numaggs;
1113 Assert(numaggs == length(aggstate->aggs));
1117 * This is not an error condition: we might be using the Agg node just
1118 * to do hash-based grouping. Even in the regular case,
1119 * constant-expression simplification could optimize away all of the
1120 * Aggrefs in the targetlist and qual. So keep going, but force local
1121 * copy of numaggs positive so that palloc()s below don't choke.
1127 * If we are grouping, precompute fmgr lookup data for inner loop.
1128 * We need both equality and hashing functions to do it by hashing,
1129 * but only equality if not hashing.
1131 if (node->numCols > 0)
1133 if (node->aggstrategy == AGG_HASHED)
1134 execTuplesHashPrepare(ExecGetScanType(&aggstate->ss),
1137 &aggstate->eqfunctions,
1138 &aggstate->hashfunctions);
1140 aggstate->eqfunctions =
1141 execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
1147 * Set up aggregate-result storage in the output expr context, and also
1148 * allocate my private per-agg working storage
1150 econtext = aggstate->ss.ps.ps_ExprContext;
1151 econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numaggs);
1152 econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numaggs);
1154 peragg = (AggStatePerAgg) palloc0(sizeof(AggStatePerAggData) * numaggs);
1155 aggstate->peragg = peragg;
1157 if (node->aggstrategy == AGG_HASHED)
1159 build_hash_table(aggstate);
1160 aggstate->table_filled = false;
1164 AggStatePerGroup pergroup;
1166 pergroup = (AggStatePerGroup) palloc0(sizeof(AggStatePerGroupData) * numaggs);
1167 aggstate->pergroup = pergroup;
1171 * Perform lookups of aggregate function info, and initialize the
1172 * unchanging fields of the per-agg data. We also detect duplicate
1173 * aggregates (for example, "SELECT sum(x) ... HAVING sum(x) > 0").
1174 * When duplicates are detected, we only make an AggStatePerAgg struct
1175 * for the first one. The clones are simply pointed at the same result
1176 * entry by giving them duplicate aggno values.
1179 foreach(alist, aggstate->aggs)
1181 AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(alist);
1182 Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
1183 AggStatePerAgg peraggstate;
1185 Form_pg_aggregate aggform;
1186 AclResult aclresult;
1189 FuncExpr *transfnexpr,
1194 Oid *transfn_arg_types;
1195 List *transfn_args = NIL;
1197 Oid transfn_ret_type;
1198 Oid *finalfn_arg_types = NULL;
1199 List *finalfn_args = NIL;
1200 Oid finalfn_ret_type = InvalidOid;
1201 int finalfn_nargs = 0;
1206 /* Planner should have assigned aggregate to correct level */
1207 Assert(aggref->agglevelsup == 0);
1209 /* Look for a previous duplicate aggregate */
1210 for (i = 0; i <= aggno; i++)
1212 if (equal(aggref, peragg[i].aggref) &&
1213 !contain_volatile_functions((Node *) aggref))
1218 /* Found a match to an existing entry, so just mark it */
1219 aggrefstate->aggno = i;
1223 /* Nope, so assign a new PerAgg record */
1224 peraggstate = &peragg[++aggno];
1226 /* Mark Aggref state node with assigned index in the result array */
1227 aggrefstate->aggno = aggno;
1229 /* Fill in the peraggstate data */
1230 peraggstate->aggrefstate = aggrefstate;
1231 peraggstate->aggref = aggref;
1233 aggTuple = SearchSysCache(AGGFNOID,
1234 ObjectIdGetDatum(aggref->aggfnoid),
1236 if (!HeapTupleIsValid(aggTuple))
1237 elog(ERROR, "ExecAgg: cache lookup failed for aggregate %u",
1239 aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
1241 /* Check permission to call aggregate function */
1242 aclresult = pg_proc_aclcheck(aggref->aggfnoid, GetUserId(),
1244 if (aclresult != ACLCHECK_OK)
1245 aclcheck_error(aclresult, get_func_name(aggref->aggfnoid));
1247 get_typlenbyval(aggref->aggtype,
1248 &peraggstate->resulttypeLen,
1249 &peraggstate->resulttypeByVal);
1250 get_typlenbyval(aggform->aggtranstype,
1251 &peraggstate->transtypeLen,
1252 &peraggstate->transtypeByVal);
1254 peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
1255 peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
1257 /* get the runtime aggregate argument type */
1258 fargs = aggref->args;
1259 agg_rt_type = exprType((Node *) nth(0, fargs));
1261 /* get the transition function argument and return types */
1262 transfn_ret_type = get_func_rettype(transfn_oid);
1263 transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
1265 /* resolve any polymorphic types */
1266 if (transfn_nargs == 2)
1267 /* base type was not ANY */
1269 if (transfn_arg_types[1] == ANYARRAYOID ||
1270 transfn_arg_types[1] == ANYELEMENTOID)
1271 transfn_arg_types[1] = agg_rt_type;
1273 transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
1277 * Build arg list to use on the transfn FuncExpr node. We really
1278 * only care that the node type is correct so that the transfn
1279 * can discover the actual argument types at runtime using
1280 * get_fn_expr_argtype()
1282 arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
1283 -1, COERCE_DONTCARE);
1284 arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
1285 -1, COERCE_DONTCARE);
1286 transfn_args = makeList2(arg0, arg1);
1289 * the state transition function always returns the same type
1290 * as its first argument
1292 if (transfn_ret_type == ANYARRAYOID ||
1293 transfn_ret_type == ANYELEMENTOID)
1294 transfn_ret_type = transfn_arg_types[0];
1296 else if (transfn_nargs == 1)
1298 * base type was ANY, therefore the aggregate return type should
1299 * be non-polymorphic
1302 Oid finaltype = get_func_rettype(aggref->aggfnoid);
1305 * this should have been prevented in AggregateCreate,
1308 if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
1309 elog(ERROR, "aggregate with base type ANY must have a " \
1310 "non-polymorphic return type");
1312 /* see if we have a final function */
1313 if (OidIsValid(finalfn_oid))
1315 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
1316 if (finalfn_nargs != 1)
1317 elog(ERROR, "final function takes unexpected number " \
1318 "of arguments: %d", finalfn_nargs);
1321 * final function argument is always the same as the state
1322 * function return type
1324 if (finalfn_arg_types[0] != ANYARRAYOID &&
1325 finalfn_arg_types[0] != ANYELEMENTOID)
1327 /* if it is not ambiguous, use it */
1328 transfn_ret_type = finalfn_arg_types[0];
1332 /* if it is ambiguous, try to derive it */
1333 finalfn_ret_type = finaltype;
1334 finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
1336 transfn_ret_type = finalfn_arg_types[0];
1340 transfn_ret_type = finaltype;
1342 transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
1346 * Build arg list to use on the transfn FuncExpr node. We really
1347 * only care that the node type is correct so that the transfn
1348 * can discover the actual argument types at runtime using
1349 * get_fn_expr_argtype()
1351 arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
1352 -1, COERCE_DONTCARE);
1353 transfn_args = makeList1(arg0);
1356 elog(ERROR, "state transition function takes unexpected number " \
1357 "of arguments: %d", transfn_nargs);
1359 if (OidIsValid(finalfn_oid))
1361 /* get the final function argument and return types */
1362 if (finalfn_ret_type == InvalidOid)
1363 finalfn_ret_type = get_func_rettype(finalfn_oid);
1365 if (!finalfn_arg_types)
1367 finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
1368 if (finalfn_nargs != 1)
1369 elog(ERROR, "final function takes unexpected number " \
1370 "of arguments: %d", finalfn_nargs);
1374 * final function argument is always the same as the state
1375 * function return type, which by now should have been resolved
1377 if (finalfn_arg_types[0] == ANYARRAYOID ||
1378 finalfn_arg_types[0] == ANYELEMENTOID)
1379 finalfn_arg_types[0] = transfn_ret_type;
1382 * Build arg list to use on the finalfn FuncExpr node. We really
1383 * only care that the node type is correct so that the finalfn
1384 * can discover the actual argument type at runtime using
1385 * get_fn_expr_argtype()
1387 arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
1388 -1, COERCE_DONTCARE);
1389 finalfn_args = makeList1(arg0);
1391 finalfn_ret_type = resolve_type(finalfn_ret_type,
1392 finalfn_arg_types[0]);
1395 fmgr_info(transfn_oid, &peraggstate->transfn);
1396 transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
1398 false, /* cannot be a set */
1399 COERCE_DONTCARE, /* to match any user expr */
1401 peraggstate->transfn.fn_expr = (Node *) transfnexpr;
1403 if (OidIsValid(finalfn_oid))
1405 fmgr_info(finalfn_oid, &peraggstate->finalfn);
1406 finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
1408 false, /* cannot be a set */
1409 COERCE_DONTCARE, /* to match any user expr */
1411 peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
1415 * initval is potentially null, so don't try to access it as a
1416 * struct field. Must do it the hard way with SysCacheGetAttr.
1418 textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
1419 Anum_pg_aggregate_agginitval,
1420 &peraggstate->initValueIsNull);
1422 if (peraggstate->initValueIsNull)
1423 peraggstate->initValue = (Datum) 0;
1425 peraggstate->initValue = GetAggInitVal(textInitVal,
1426 transfn_arg_types[0]);
1429 * If the transfn is strict and the initval is NULL, make sure
1430 * input type and transtype are the same (or at least binary-
1431 * compatible), so that it's OK to use the first input value as
1432 * the initial transValue. This should have been checked at agg
1433 * definition time, but just in case...
1435 if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
1438 * Note: use the type from the input expression here, not from
1439 * pg_proc.proargtypes, because the latter might be 0.
1440 * (Consider COUNT(*).)
1442 Oid inputType = exprType((Node *) aggref->target);
1444 if (!IsBinaryCoercible(inputType, aggform->aggtranstype))
1445 elog(ERROR, "Aggregate %u needs to have compatible input type and transition type",
1449 if (aggref->aggdistinct)
1452 * Note: use the type from the input expression here, not from
1453 * pg_proc.proargtypes, because the latter might be a pseudotype.
1454 * (Consider COUNT(*).)
1456 Oid inputType = exprType((Node *) aggref->target);
1459 /* We don't implement DISTINCT aggs in the HASHED case */
1460 Assert(node->aggstrategy != AGG_HASHED);
1462 peraggstate->inputType = inputType;
1463 get_typlenbyval(inputType,
1464 &peraggstate->inputtypeLen,
1465 &peraggstate->inputtypeByVal);
1467 eq_function = equality_oper_funcid(inputType);
1468 fmgr_info(eq_function, &(peraggstate->equalfn));
1469 peraggstate->sortOperator = ordering_oper_opid(inputType);
1470 peraggstate->sortstate = NULL;
1473 ReleaseSysCache(aggTuple);
1476 /* Update numaggs to match number of unique aggregates found */
1477 aggstate->numaggs = aggno + 1;
1483 GetAggInitVal(Datum textInitVal, Oid transtype)
1491 strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
1493 tup = SearchSysCache(TYPEOID,
1494 ObjectIdGetDatum(transtype),
1496 if (!HeapTupleIsValid(tup))
1497 elog(ERROR, "GetAggInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
1499 typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
1500 typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
1501 ReleaseSysCache(tup);
1503 initVal = OidFunctionCall3(typinput,
1504 CStringGetDatum(strInitVal),
1505 ObjectIdGetDatum(typelem),
1513 ExecCountSlotsAgg(Agg *node)
1515 return ExecCountSlotsNode(outerPlan(node)) +
1516 ExecCountSlotsNode(innerPlan(node)) +
1521 ExecEndAgg(AggState *node)
1523 PlanState *outerPlan;
1526 /* Make sure we have closed any open tuplesorts */
1527 for (aggno = 0; aggno < node->numaggs; aggno++)
1529 AggStatePerAgg peraggstate = &node->peragg[aggno];
1531 if (peraggstate->sortstate)
1532 tuplesort_end(peraggstate->sortstate);
1536 * Free both the expr contexts.
1538 ExecFreeExprContext(&node->ss.ps);
1539 node->ss.ps.ps_ExprContext = node->tmpcontext;
1540 ExecFreeExprContext(&node->ss.ps);
1542 /* clean up tuple table */
1543 ExecClearTuple(node->ss.ss_ScanTupleSlot);
1545 MemoryContextDelete(node->aggcontext);
1547 outerPlan = outerPlanState(node);
1548 ExecEndNode(outerPlan);
1552 ExecReScanAgg(AggState *node, ExprContext *exprCtxt)
1554 ExprContext *econtext = node->ss.ps.ps_ExprContext;
1557 node->agg_done = false;
1559 if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
1562 * In the hashed case, if we haven't yet built the hash table
1563 * then we can just return; nothing done yet, so nothing to undo.
1564 * If subnode's chgParam is not NULL then it will be re-scanned by
1565 * ExecProcNode, else no reason to re-scan it at all.
1567 if (!node->table_filled)
1571 * If we do have the hash table and the subplan does not have any
1572 * parameter changes, then we can just rescan the existing hash
1573 * table; no need to build it again.
1575 if (((PlanState *) node)->lefttree->chgParam == NULL)
1577 ResetTupleHashIterator(&node->hashiter);
1582 /* Make sure we have closed any open tuplesorts */
1583 for (aggno = 0; aggno < node->numaggs; aggno++)
1585 AggStatePerAgg peraggstate = &node->peragg[aggno];
1587 if (peraggstate->sortstate)
1588 tuplesort_end(peraggstate->sortstate);
1589 peraggstate->sortstate = NULL;
1592 /* Release first tuple of group, if we have made a copy */
1593 if (node->grp_firstTuple != NULL)
1595 heap_freetuple(node->grp_firstTuple);
1596 node->grp_firstTuple = NULL;
1599 /* Forget current agg values */
1600 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numaggs);
1601 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numaggs);
1603 /* Release all temp storage */
1604 MemoryContextReset(node->aggcontext);
1606 if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
1608 /* Rebuild an empty hash table */
1609 build_hash_table(node);
1610 node->table_filled = false;
1614 * if chgParam of subnode is not null then plan will be re-scanned by
1615 * first ExecProcNode.
1617 if (((PlanState *) node)->lefttree->chgParam == NULL)
1618 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
1622 * aggregate_dummy - dummy execution routine for aggregate functions
1624 * This function is listed as the implementation (prosrc field) of pg_proc
1625 * entries for aggregate functions. Its only purpose is to throw an error
1626 * if someone mistakenly executes such a function in the normal way.
1628 * Perhaps someday we could assign real meaning to the prosrc field of
1632 aggregate_dummy(PG_FUNCTION_ARGS)
1634 elog(ERROR, "Aggregate function %u called as normal function",
1635 fcinfo->flinfo->fn_oid);
1636 return (Datum) 0; /* keep compiler quiet */
1640 resolve_type(Oid type_to_resolve, Oid context_type)
1644 if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
1645 resolved_type = type_to_resolve;
1646 else if (type_to_resolve == ANYARRAYOID)
1649 Oid context_type_arraytype = get_array_type(context_type);
1651 if (context_type_arraytype != InvalidOid)
1652 resolved_type = context_type_arraytype;
1654 resolved_type = context_type;
1656 else if (type_to_resolve == ANYELEMENTOID)
1659 Oid context_type_elemtype = get_element_type(context_type);
1661 if (context_type_elemtype != InvalidOid)
1662 resolved_type = context_type_elemtype;
1664 resolved_type = context_type;
1667 resolved_type = type_to_resolve;
1669 return resolved_type;