#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "executor/nodeAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
#include "utils/datum.h"
Go to the source code of this file.
typedef struct AggHashEntryData* AggHashEntry |
typedef struct AggHashEntryData AggHashEntryData |
typedef struct AggStatePerAggData AggStatePerAggData |
typedef struct AggStatePerGroupData AggStatePerGroupData |
static void advance_aggregates | ( | AggState * | aggstate, | |
AggStatePerGroup | pergroup | |||
) | [static] |
Definition at line 479 of file nodeAgg.c.
References advance_transition_function(), FunctionCallInfoData::arg, FunctionCallInfoData::argnull, Assert, AggStatePerAggData::evalproj, ExecProject(), FmgrInfo::fn_strict, i, NULL, AggState::numaggs, AggStatePerAggData::numArguments, AggStatePerAggData::numInputs, AggStatePerAggData::numSortCols, AggState::peragg, AggStatePerAggData::sortstate, AggStatePerAggData::transfn, TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_values, tuplesort_putdatum(), and tuplesort_puttupleslot().
Referenced by agg_fill_hash_table(), and agg_retrieve_direct().
{ int aggno; for (aggno = 0; aggno < aggstate->numaggs; aggno++) { AggStatePerAgg peraggstate = &aggstate->peragg[aggno]; AggStatePerGroup pergroupstate = &pergroup[aggno]; int nargs = peraggstate->numArguments; int i; TupleTableSlot *slot; /* Evaluate the current input expressions for this aggregate */ slot = ExecProject(peraggstate->evalproj, NULL); if (peraggstate->numSortCols > 0) { /* DISTINCT and/or ORDER BY case */ Assert(slot->tts_nvalid == peraggstate->numInputs); /* * If the transfn is strict, we want to check for nullity before * storing the row in the sorter, to save space if there are a lot * of nulls. Note that we must only check numArguments columns, * not numInputs, since nullity in columns used only for sorting * is not relevant here. */ if (peraggstate->transfn.fn_strict) { for (i = 0; i < nargs; i++) { if (slot->tts_isnull[i]) break; } if (i < nargs) continue; } /* OK, put the tuple into the tuplesort object */ if (peraggstate->numInputs == 1) tuplesort_putdatum(peraggstate->sortstate, slot->tts_values[0], slot->tts_isnull[0]); else tuplesort_puttupleslot(peraggstate->sortstate, slot); } else { /* We can apply the transition function immediately */ FunctionCallInfoData fcinfo; /* Load values into fcinfo */ /* Start from 1, since the 0th arg will be the transition value */ Assert(slot->tts_nvalid >= nargs); for (i = 0; i < nargs; i++) { fcinfo.arg[i + 1] = slot->tts_values[i]; fcinfo.argnull[i + 1] = slot->tts_isnull[i]; } advance_transition_function(aggstate, peraggstate, pergroupstate, &fcinfo); } } }
static void advance_transition_function | ( | AggState * | aggstate, | |
AggStatePerAgg | peraggstate, | |||
AggStatePerGroup | pergroupstate, | |||
FunctionCallInfoData * | fcinfo | |||
) | [static] |
Definition at line 377 of file nodeAgg.c.
References AggStatePerAggData::aggCollation, AggState::aggcontext, FunctionCallInfoData::arg, FunctionCallInfoData::argnull, datumCopy(), DatumGetPointer, ExprContext::ecxt_per_tuple_memory, FmgrInfo::fn_strict, FunctionCallInvoke, i, InitFunctionCallInfoData, FunctionCallInfoData::isnull, MemoryContextSwitchTo(), AggStatePerGroupData::noTransValue, NULL, AggStatePerAggData::numArguments, pfree(), AggState::tmpcontext, AggStatePerAggData::transfn, AggStatePerAggData::transtypeByVal, AggStatePerAggData::transtypeLen, AggStatePerGroupData::transValue, and AggStatePerGroupData::transValueIsNull.
Referenced by advance_aggregates(), process_ordered_aggregate_multi(), and process_ordered_aggregate_single().
{ int numArguments = peraggstate->numArguments; MemoryContext oldContext; Datum newVal; int i; if (peraggstate->transfn.fn_strict) { /* * For a strict transfn, nothing happens when there's a NULL input; we * just keep the prior transValue. */ for (i = 1; i <= numArguments; i++) { if (fcinfo->argnull[i]) return; } if (pergroupstate->noTransValue) { /* * transValue has not been initialized. This is the first non-NULL * input value. We use it as the initial value for transValue. (We * already checked that the agg's input type is binary-compatible * with its transtype, so straight copy here is OK.) * * We must copy the datum into aggcontext if it is pass-by-ref. We * do not need to pfree the old transValue, since it's NULL. */ oldContext = MemoryContextSwitchTo(aggstate->aggcontext); pergroupstate->transValue = datumCopy(fcinfo->arg[1], peraggstate->transtypeByVal, peraggstate->transtypeLen); pergroupstate->transValueIsNull = false; pergroupstate->noTransValue = false; MemoryContextSwitchTo(oldContext); return; } if (pergroupstate->transValueIsNull) { /* * Don't call a strict function with NULL inputs. Note it is * possible to get here despite the above tests, if the transfn is * strict *and* returned a NULL on a prior cycle. If that happens * we will propagate the NULL all the way to the end. */ return; } } /* We run the transition functions in per-input-tuple memory context */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); /* * OK to call the transition function */ InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn), numArguments + 1, peraggstate->aggCollation, (void *) aggstate, NULL); fcinfo->arg[0] = pergroupstate->transValue; fcinfo->argnull[0] = pergroupstate->transValueIsNull; newVal = FunctionCallInvoke(fcinfo); /* * If pass-by-ref datatype, must copy the new value into aggcontext and * pfree the prior transValue. But if transfn returned a pointer to its * first input, we don't need to do anything. */ if (!peraggstate->transtypeByVal && DatumGetPointer(newVal) != DatumGetPointer(pergroupstate->transValue)) { if (!fcinfo->isnull) { MemoryContextSwitchTo(aggstate->aggcontext); newVal = datumCopy(newVal, peraggstate->transtypeByVal, peraggstate->transtypeLen); } if (!pergroupstate->transValueIsNull) pfree(DatumGetPointer(pergroupstate->transValue)); } pergroupstate->transValue = newVal; pergroupstate->transValueIsNull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); }
static void agg_fill_hash_table | ( | AggState * | aggstate | ) | [static] |
Definition at line 1221 of file nodeAgg.c.
References advance_aggregates(), ExprContext::ecxt_outertuple, ExecProcNode(), AggState::hashiter, AggState::hashtable, lookup_hash_entry(), outerPlanState, AggHashEntryData::pergroup, ResetExprContext, ResetTupleHashIterator, AggState::table_filled, AggState::tmpcontext, and TupIsNull.
Referenced by ExecAgg().
{ PlanState *outerPlan; ExprContext *tmpcontext; AggHashEntry entry; TupleTableSlot *outerslot; /* * get state info from node */ outerPlan = outerPlanState(aggstate); /* tmpcontext is the per-input-tuple expression context */ tmpcontext = aggstate->tmpcontext; /* * Process each outer-plan tuple, and then fetch the next one, until we * exhaust the outer plan. */ for (;;) { outerslot = ExecProcNode(outerPlan); if (TupIsNull(outerslot)) break; /* set up for advance_aggregates call */ tmpcontext->ecxt_outertuple = outerslot; /* Find or build hashtable entry for this tuple's group */ entry = lookup_hash_entry(aggstate, outerslot); /* Advance the aggregates */ advance_aggregates(aggstate, entry->pergroup); /* Reset per-input-tuple context after each tuple */ ResetExprContext(tmpcontext); } aggstate->table_filled = true; /* Initialize to walk the hash table */ ResetTupleHashIterator(aggstate->hashtable, &aggstate->hashiter); }
static TupleTableSlot * agg_retrieve_direct | ( | AggState * | aggstate | ) | [static] |
Definition at line 1020 of file nodeAgg.c.
References advance_aggregates(), AggState::agg_done, AGG_PLAIN, AGG_SORTED, AggState::aggcontext, Agg::aggstrategy, ExprContext::ecxt_aggvalues, AggState::eqfunctions, ExecCopySlotTuple(), ExecProcNode(), ExecProject(), ExecQual(), ExecStoreTuple(), execTuplesMatch(), ExprEndResult, finalize_aggregate(), AggState::grp_firstTuple, Agg::grpColIdx, initialize_aggregates(), InstrCountFiltered1, InvalidBuffer, MemoryContextResetAndDeleteChildren(), NULL, AggState::numaggs, Agg::numCols, AggStatePerAggData::numInputs, AggStatePerAggData::numSortCols, outerPlan, outerPlanState, AggState::peragg, AggState::pergroup, PlanState::plan, process_ordered_aggregate_multi(), process_ordered_aggregate_single(), ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, ResetExprContext, AggState::ss, ScanState::ss_ScanTupleSlot, AggState::tmpcontext, and TupIsNull.
Referenced by ExecAgg().
{ Agg *node = (Agg *) aggstate->ss.ps.plan; PlanState *outerPlan; ExprContext *econtext; ExprContext *tmpcontext; Datum *aggvalues; bool *aggnulls; AggStatePerAgg peragg; AggStatePerGroup pergroup; TupleTableSlot *outerslot; TupleTableSlot *firstSlot; int aggno; /* * get state info from node */ outerPlan = outerPlanState(aggstate); /* econtext is the per-output-tuple expression context */ econtext = aggstate->ss.ps.ps_ExprContext; aggvalues = econtext->ecxt_aggvalues; aggnulls = econtext->ecxt_aggnulls; /* tmpcontext is the per-input-tuple expression context */ tmpcontext = aggstate->tmpcontext; peragg = aggstate->peragg; pergroup = aggstate->pergroup; firstSlot = aggstate->ss.ss_ScanTupleSlot; /* * We loop retrieving groups until we find one matching * aggstate->ss.ps.qual */ while (!aggstate->agg_done) { /* * If we don't already have the first tuple of the new group, fetch it * from the outer plan. */ if (aggstate->grp_firstTuple == NULL) { outerslot = ExecProcNode(outerPlan); if (!TupIsNull(outerslot)) { /* * Make a copy of the first input tuple; we will use this for * comparisons (in group mode) and for projection. */ aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot); } else { /* outer plan produced no tuples at all */ aggstate->agg_done = true; /* If we are grouping, we should produce no tuples too */ if (node->aggstrategy != AGG_PLAIN) return NULL; } } /* * Clear the per-output-tuple context for each group, as well as * aggcontext (which contains any pass-by-ref transvalues of the old * group). We also clear any child contexts of the aggcontext; some * aggregate functions store working state in such contexts. */ ResetExprContext(econtext); MemoryContextResetAndDeleteChildren(aggstate->aggcontext); /* * Initialize working state for a new input tuple group */ initialize_aggregates(aggstate, peragg, pergroup); if (aggstate->grp_firstTuple != NULL) { /* * Store the copied first input tuple in the tuple table slot * reserved for it. The tuple will be deleted when it is cleared * from the slot. */ ExecStoreTuple(aggstate->grp_firstTuple, firstSlot, InvalidBuffer, true); aggstate->grp_firstTuple = NULL; /* don't keep two pointers */ /* set up for first advance_aggregates call */ tmpcontext->ecxt_outertuple = firstSlot; /* * Process each outer-plan tuple, and then fetch the next one, * until we exhaust the outer plan or cross a group boundary. */ for (;;) { advance_aggregates(aggstate, pergroup); /* Reset per-input-tuple context after each tuple */ ResetExprContext(tmpcontext); outerslot = ExecProcNode(outerPlan); if (TupIsNull(outerslot)) { /* no more outer-plan tuples available */ aggstate->agg_done = true; break; } /* set up for next advance_aggregates call */ tmpcontext->ecxt_outertuple = outerslot; /* * If we are grouping, check whether we've crossed a group * boundary. */ if (node->aggstrategy == AGG_SORTED) { if (!execTuplesMatch(firstSlot, outerslot, node->numCols, node->grpColIdx, aggstate->eqfunctions, tmpcontext->ecxt_per_tuple_memory)) { /* * Save the first input tuple of the next group. */ aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot); break; } } } } /* * Done scanning input tuple group. Finalize each aggregate * calculation, and stash results in the per-output-tuple context. */ for (aggno = 0; aggno < aggstate->numaggs; aggno++) { AggStatePerAgg peraggstate = &peragg[aggno]; AggStatePerGroup pergroupstate = &pergroup[aggno]; if (peraggstate->numSortCols > 0) { if (peraggstate->numInputs == 1) process_ordered_aggregate_single(aggstate, peraggstate, pergroupstate); else process_ordered_aggregate_multi(aggstate, peraggstate, pergroupstate); } finalize_aggregate(aggstate, peraggstate, pergroupstate, &aggvalues[aggno], &aggnulls[aggno]); } /* * Use the representative input tuple for any references to * non-aggregated input columns in the qual and tlist. (If we are not * grouping, and there are no input rows at all, we will come here * with an empty firstSlot ... but if not grouping, there can't be any * references to non-aggregated input columns, so no problem.) */ econtext->ecxt_outertuple = firstSlot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and loop back to try to process another group. */ if (ExecQual(aggstate->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the aggregate results * and the representative input tuple. */ TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { aggstate->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else InstrCountFiltered1(aggstate, 1); } /* No more groups */ return NULL; }
static TupleTableSlot * agg_retrieve_hash_table | ( | AggState * | aggstate | ) | [static] |
Definition at line 1266 of file nodeAgg.c.
References AggState::agg_done, Assert, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, ExecProject(), ExecQual(), ExecStoreMinimalTuple(), ExprEndResult, finalize_aggregate(), TupleHashEntryData::firstTuple, AggState::hashiter, InstrCountFiltered1, NULL, AggState::numaggs, AggStatePerAggData::numSortCols, AggState::peragg, AggHashEntryData::pergroup, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, PlanState::qual, ResetExprContext, ScanTupleHashTable, AggHashEntryData::shared, AggState::ss, and ScanState::ss_ScanTupleSlot.
Referenced by ExecAgg().
{ ExprContext *econtext; Datum *aggvalues; bool *aggnulls; AggStatePerAgg peragg; AggStatePerGroup pergroup; AggHashEntry entry; TupleTableSlot *firstSlot; int aggno; /* * get state info from node */ /* econtext is the per-output-tuple expression context */ econtext = aggstate->ss.ps.ps_ExprContext; aggvalues = econtext->ecxt_aggvalues; aggnulls = econtext->ecxt_aggnulls; peragg = aggstate->peragg; firstSlot = aggstate->ss.ss_ScanTupleSlot; /* * We loop retrieving groups until we find one satisfying * aggstate->ss.ps.qual */ while (!aggstate->agg_done) { /* * Find the next entry in the hash table */ entry = (AggHashEntry) ScanTupleHashTable(&aggstate->hashiter); if (entry == NULL) { /* No more entries in hashtable, so done */ aggstate->agg_done = TRUE; return NULL; } /* * Clear the per-output-tuple context for each group */ ResetExprContext(econtext); /* * Store the copied first input tuple in the tuple table slot reserved * for it, so that it can be used in ExecProject. */ ExecStoreMinimalTuple(entry->shared.firstTuple, firstSlot, false); pergroup = entry->pergroup; /* * Finalize each aggregate calculation, and stash results in the * per-output-tuple context. */ for (aggno = 0; aggno < aggstate->numaggs; aggno++) { AggStatePerAgg peraggstate = &peragg[aggno]; AggStatePerGroup pergroupstate = &pergroup[aggno]; Assert(peraggstate->numSortCols == 0); finalize_aggregate(aggstate, peraggstate, pergroupstate, &aggvalues[aggno], &aggnulls[aggno]); } /* * Use the representative input tuple for any references to * non-aggregated input columns in the qual and tlist. */ econtext->ecxt_outertuple = firstSlot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and loop back to try to process another group. */ if (ExecQual(aggstate->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the aggregate results * and the representative input tuple. */ TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { aggstate->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else InstrCountFiltered1(aggstate, 1); } /* No more groups */ return NULL; }
int AggCheckCallContext | ( | FunctionCallInfo | fcinfo, | |
MemoryContext * | aggcontext | |||
) |
Definition at line 2014 of file nodeAgg.c.
References FunctionCallInfoData::context, and IsA.
Referenced by array_agg_finalfn(), array_agg_transfn(), bytea_string_agg_finalfn(), float4_accum(), float8_accum(), float8_regr_accum(), int2_avg_accum(), int2_sum(), int4_avg_accum(), int4_sum(), int8inc(), json_agg_finalfn(), json_agg_transfn(), makeStringAggState(), string_agg_finalfn(), and tsa_rewrite_accum().
{ if (fcinfo->context && IsA(fcinfo->context, AggState)) { if (aggcontext) *aggcontext = ((AggState *) fcinfo->context)->aggcontext; return AGG_CONTEXT_AGGREGATE; } if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) { if (aggcontext) *aggcontext = ((WindowAggState *) fcinfo->context)->aggcontext; return AGG_CONTEXT_WINDOW; } /* this is just to prevent "uninitialized variable" warnings */ if (aggcontext) *aggcontext = NULL; return 0; }
Datum aggregate_dummy | ( | PG_FUNCTION_ARGS | ) |
static void build_hash_table | ( | AggState * | aggstate | ) | [static] |
Definition at line 829 of file nodeAgg.c.
References AGG_HASHED, AggState::aggcontext, Agg::aggstrategy, Assert, BuildTupleHashTable(), ExprContext::ecxt_per_tuple_memory, AggState::eqfunctions, Agg::grpColIdx, AggState::hashfunctions, AggState::hashtable, AggState::numaggs, Agg::numCols, Agg::numGroups, PlanState::plan, ScanState::ps, AggState::ss, and AggState::tmpcontext.
Referenced by ExecInitAgg(), and ExecReScanAgg().
{ Agg *node = (Agg *) aggstate->ss.ps.plan; MemoryContext tmpmem = aggstate->tmpcontext->ecxt_per_tuple_memory; Size entrysize; Assert(node->aggstrategy == AGG_HASHED); Assert(node->numGroups > 0); entrysize = sizeof(AggHashEntryData) + (aggstate->numaggs - 1) * sizeof(AggStatePerGroupData); aggstate->hashtable = BuildTupleHashTable(node->numCols, node->grpColIdx, aggstate->eqfunctions, aggstate->hashfunctions, node->numGroups, entrysize, aggstate->aggcontext, tmpmem); }
TupleTableSlot* ExecAgg | ( | AggState * | node | ) |
Definition at line 978 of file nodeAgg.c.
References AggState::agg_done, agg_fill_hash_table(), AGG_HASHED, agg_retrieve_direct(), agg_retrieve_hash_table(), ExecProject(), ExprMultipleResult, PlanState::plan, ScanState::ps, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, AggState::ss, and AggState::table_filled.
Referenced by ExecProcNode().
{ /* * Check to see if we're still projecting out tuples from a previous agg * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ss.ps.ps_TupFromTlist) { TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ node->ss.ps.ps_TupFromTlist = false; } /* * Exit if nothing left to do. (We must do the ps_TupFromTlist check * first, because in some cases agg_done gets set before we emit the final * aggregate tuple, and we have to finish running SRFs for it.) */ if (node->agg_done) return NULL; /* Dispatch based on strategy */ if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED) { if (!node->table_filled) agg_fill_hash_table(node); return agg_retrieve_hash_table(node); } else return agg_retrieve_direct(node); }
void ExecEndAgg | ( | AggState * | node | ) |
Definition at line 1883 of file nodeAgg.c.
References AggState::aggcontext, ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), MemoryContextDelete(), AggState::numaggs, outerPlanState, AggState::peragg, ScanState::ps, PlanState::ps_ExprContext, AggStatePerAggData::sortstate, AggState::ss, ScanState::ss_ScanTupleSlot, AggState::tmpcontext, and tuplesort_end().
Referenced by ExecEndNode().
{ PlanState *outerPlan; int aggno; /* Make sure we have closed any open tuplesorts */ for (aggno = 0; aggno < node->numaggs; aggno++) { AggStatePerAgg peraggstate = &node->peragg[aggno]; if (peraggstate->sortstate) tuplesort_end(peraggstate->sortstate); } /* * Free both the expr contexts. */ ExecFreeExprContext(&node->ss.ps); node->ss.ps.ps_ExprContext = node->tmpcontext; ExecFreeExprContext(&node->ss.ps); /* clean up tuple table */ ExecClearTuple(node->ss.ss_ScanTupleSlot); MemoryContextDelete(node->aggcontext); outerPlan = outerPlanState(node); ExecEndNode(outerPlan); }
Definition at line 1377 of file nodeAgg.c.
References ACL_EXECUTE, ACL_KIND_PROC, aclcheck_error(), ACLCHECK_OK, AggState::agg_done, AGG_HASHED, AggState::aggcontext, Aggref::aggdistinct, Aggref::aggfnoid, AGGFNOID, Aggref::agglevelsup, AggrefExprState::aggno, Aggref::aggorder, AggState::aggs, Agg::aggstrategy, Aggref::aggtype, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_aggregate_agginitval, AggrefExprState::args, Aggref::args, Assert, build_aggregate_fnexprs(), build_hash_table(), contain_volatile_functions(), CurrentMemoryContext, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, elog, enforce_generic_type_consistency(), AggState::eqfunctions, SortGroupClause::eqop, equal(), ereport, errcode(), errmsg(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecAssignResultTypeFromTL(), ExecAssignScanTypeFromOuterPlan(), ExecBuildProjectionInfo(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitResultTupleSlot(), ExecInitScanTupleSlot(), ExecSetSlotDescriptor(), execTuplesHashPrepare(), execTuplesMatchPrepare(), ExecTypeFromTL(), TargetEntry::expr, ExprState::expr, exprCollation(), exprType(), find_hash_columns(), fmgr_info(), fmgr_info_set_expr, FUNC_MAX_ARGS, get_func_name(), get_func_signature(), get_opcode(), get_sortgroupclause_tle(), get_typlenbyval(), GetAggInitVal(), GETSTRUCT, GetUserId(), AggState::grp_firstTuple, Agg::grpOperators, AggState::hash_needed, AggState::hashfunctions, AggState::hashslot, AggState::hashtable, HeapTupleIsValid, i, Aggref::inputcollid, InvokeFunctionExecuteHook, IsBinaryCoercible(), IsPolymorphicType, lfirst, list_length(), makeNode, NULL, SortGroupClause::nulls_first, AggState::numaggs, Agg::numCols, ObjectIdGetDatum, OidIsValid, outerPlan, outerPlanState, palloc(), palloc0(), AggState::peragg, AggState::pergroup, pfree(), pg_proc_aclcheck(), Agg::plan, PlanState::plan, PROCOID, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, Plan::qual, PlanState::qual, ReleaseSysCache(), TargetEntry::resjunk, TargetEntry::resno, SearchSysCache1, SortGroupClause::sortop, AggState::ss, PlanState::state, SysCacheGetAttr(), AggState::table_filled, Plan::targetlist, PlanState::targetlist, AggState::tmpcontext, and AggrefExprState::xprstate.
Referenced by ExecInitNode().
{ AggState *aggstate; AggStatePerAgg peragg; Plan *outerPlan; ExprContext *econtext; int numaggs, aggno; ListCell *l; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); /* * create state structure */ aggstate = makeNode(AggState); aggstate->ss.ps.plan = (Plan *) node; aggstate->ss.ps.state = estate; aggstate->aggs = NIL; aggstate->numaggs = 0; aggstate->eqfunctions = NULL; aggstate->hashfunctions = NULL; aggstate->peragg = NULL; aggstate->agg_done = false; aggstate->pergroup = NULL; aggstate->grp_firstTuple = NULL; aggstate->hashtable = NULL; /* * Create expression contexts. We need two, one for per-input-tuple * processing and one for per-output-tuple processing. We cheat a little * by using ExecAssignExprContext() to build both. */ ExecAssignExprContext(estate, &aggstate->ss.ps); aggstate->tmpcontext = aggstate->ss.ps.ps_ExprContext; ExecAssignExprContext(estate, &aggstate->ss.ps); /* * We also need a long-lived memory context for holding hashtable data * structures and transition values. NOTE: the details of what is stored * in aggcontext and what is stored in the regular per-query memory * context are driven by a simple decision: we want to reset the * aggcontext at group boundaries (if not hashing) and in ExecReScanAgg to * recover no-longer-wanted space. */ aggstate->aggcontext = AllocSetContextCreate(CurrentMemoryContext, "AggContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * tuple table initialization */ ExecInitScanTupleSlot(estate, &aggstate->ss); ExecInitResultTupleSlot(estate, &aggstate->ss.ps); aggstate->hashslot = ExecInitExtraTupleSlot(estate); /* * initialize child expressions * * Note: ExecInitExpr finds Aggrefs for us, and also checks that no aggs * contain other agg calls in their arguments. This would make no sense * under SQL semantics anyway (and it's forbidden by the spec). Because * that is true, we don't need to worry about evaluating the aggs in any * particular order. */ aggstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->plan.targetlist, (PlanState *) aggstate); aggstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->plan.qual, (PlanState *) aggstate); /* * initialize child nodes * * If we are doing a hashed aggregation then the child plan does not need * to handle REWIND efficiently; see ExecReScanAgg. */ if (node->aggstrategy == AGG_HASHED) eflags &= ~EXEC_FLAG_REWIND; outerPlan = outerPlan(node); outerPlanState(aggstate) = ExecInitNode(outerPlan, estate, eflags); /* * initialize source tuple type. */ ExecAssignScanTypeFromOuterPlan(&aggstate->ss); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&aggstate->ss.ps); ExecAssignProjectionInfo(&aggstate->ss.ps, NULL); aggstate->ss.ps.ps_TupFromTlist = false; /* * get the count of aggregates in targetlist and quals */ numaggs = aggstate->numaggs; Assert(numaggs == list_length(aggstate->aggs)); if (numaggs <= 0) { /* * This is not an error condition: we might be using the Agg node just * to do hash-based grouping. Even in the regular case, * constant-expression simplification could optimize away all of the * Aggrefs in the targetlist and qual. So keep going, but force local * copy of numaggs positive so that palloc()s below don't choke. */ numaggs = 1; } /* * If we are grouping, precompute fmgr lookup data for inner loop. We need * both equality and hashing functions to do it by hashing, but only * equality if not hashing. */ if (node->numCols > 0) { if (node->aggstrategy == AGG_HASHED) execTuplesHashPrepare(node->numCols, node->grpOperators, &aggstate->eqfunctions, &aggstate->hashfunctions); else aggstate->eqfunctions = execTuplesMatchPrepare(node->numCols, node->grpOperators); } /* * Set up aggregate-result storage in the output expr context, and also * allocate my private per-agg working storage */ econtext = aggstate->ss.ps.ps_ExprContext; econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numaggs); econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numaggs); peragg = (AggStatePerAgg) palloc0(sizeof(AggStatePerAggData) * numaggs); aggstate->peragg = peragg; if (node->aggstrategy == AGG_HASHED) { build_hash_table(aggstate); aggstate->table_filled = false; /* Compute the columns we actually need to hash on */ aggstate->hash_needed = find_hash_columns(aggstate); } else { AggStatePerGroup pergroup; pergroup = (AggStatePerGroup) palloc0(sizeof(AggStatePerGroupData) * numaggs); aggstate->pergroup = pergroup; } /* * Perform lookups of aggregate function info, and initialize the * unchanging fields of the per-agg data. We also detect duplicate * aggregates (for example, "SELECT sum(x) ... HAVING sum(x) > 0"). When * duplicates are detected, we only make an AggStatePerAgg struct for the * first one. The clones are simply pointed at the same result entry by * giving them duplicate aggno values. */ aggno = -1; foreach(l, aggstate->aggs) { AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l); Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr; AggStatePerAgg peraggstate; Oid inputTypes[FUNC_MAX_ARGS]; int numArguments; int numInputs; int numSortCols; int numDistinctCols; List *sortlist; HeapTuple aggTuple; Form_pg_aggregate aggform; Oid aggtranstype; AclResult aclresult; Oid transfn_oid, finalfn_oid; Expr *transfnexpr, *finalfnexpr; Datum textInitVal; int i; ListCell *lc; /* Planner should have assigned aggregate to correct level */ Assert(aggref->agglevelsup == 0); /* Look for a previous duplicate aggregate */ for (i = 0; i <= aggno; i++) { if (equal(aggref, peragg[i].aggref) && !contain_volatile_functions((Node *) aggref)) break; } if (i <= aggno) { /* Found a match to an existing entry, so just mark it */ aggrefstate->aggno = i; continue; } /* Nope, so assign a new PerAgg record */ peraggstate = &peragg[++aggno]; /* Mark Aggref state node with assigned index in the result array */ aggrefstate->aggno = aggno; /* Fill in the peraggstate data */ peraggstate->aggrefstate = aggrefstate; peraggstate->aggref = aggref; numInputs = list_length(aggref->args); peraggstate->numInputs = numInputs; peraggstate->sortstate = NULL; /* * Get actual datatypes of the inputs. These could be different from * the agg's declared input types, when the agg accepts ANY or a * polymorphic type. */ numArguments = 0; foreach(lc, aggref->args) { TargetEntry *tle = (TargetEntry *) lfirst(lc); if (!tle->resjunk) inputTypes[numArguments++] = exprType((Node *) tle->expr); } peraggstate->numArguments = numArguments; aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggref->aggfnoid)); if (!HeapTupleIsValid(aggTuple)) elog(ERROR, "cache lookup failed for aggregate %u", aggref->aggfnoid); aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); /* Check permission to call aggregate function */ aclresult = pg_proc_aclcheck(aggref->aggfnoid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(aggref->aggfnoid)); InvokeFunctionExecuteHook(aggref->aggfnoid); peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; /* Check that aggregate owner has permission to call component fns */ { HeapTuple procTuple; Oid aggOwner; procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(aggref->aggfnoid)); if (!HeapTupleIsValid(procTuple)) elog(ERROR, "cache lookup failed for function %u", aggref->aggfnoid); aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner; ReleaseSysCache(procTuple); aclresult = pg_proc_aclcheck(transfn_oid, aggOwner, ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(transfn_oid)); InvokeFunctionExecuteHook(transfn_oid); if (OidIsValid(finalfn_oid)) { aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(finalfn_oid)); InvokeFunctionExecuteHook(finalfn_oid); } } /* resolve actual type of transition state, if polymorphic */ aggtranstype = aggform->aggtranstype; if (IsPolymorphicType(aggtranstype)) { /* have to fetch the agg's declared input types... */ Oid *declaredArgTypes; int agg_nargs; (void) get_func_signature(aggref->aggfnoid, &declaredArgTypes, &agg_nargs); Assert(agg_nargs == numArguments); aggtranstype = enforce_generic_type_consistency(inputTypes, declaredArgTypes, agg_nargs, aggtranstype, false); pfree(declaredArgTypes); } /* build expression trees using actual argument & result types */ build_aggregate_fnexprs(inputTypes, numArguments, aggtranstype, aggref->aggtype, aggref->inputcollid, transfn_oid, finalfn_oid, &transfnexpr, &finalfnexpr); fmgr_info(transfn_oid, &peraggstate->transfn); fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn); if (OidIsValid(finalfn_oid)) { fmgr_info(finalfn_oid, &peraggstate->finalfn); fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn); } peraggstate->aggCollation = aggref->inputcollid; get_typlenbyval(aggref->aggtype, &peraggstate->resulttypeLen, &peraggstate->resulttypeByVal); get_typlenbyval(aggtranstype, &peraggstate->transtypeLen, &peraggstate->transtypeByVal); /* * initval is potentially null, so don't try to access it as a struct * field. Must do it the hard way with SysCacheGetAttr. */ textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, Anum_pg_aggregate_agginitval, &peraggstate->initValueIsNull); if (peraggstate->initValueIsNull) peraggstate->initValue = (Datum) 0; else peraggstate->initValue = GetAggInitVal(textInitVal, aggtranstype); /* * If the transfn is strict and the initval is NULL, make sure input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial * transValue. This should have been checked at agg definition time, * but just in case... */ if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull) { if (numArguments < 1 || !IsBinaryCoercible(inputTypes[0], aggtranstype)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate %u needs to have compatible input type and transition type", aggref->aggfnoid))); } /* * Get a tupledesc corresponding to the inputs (including sort * expressions) of the agg. */ peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false); /* Create slot we're going to do argument evaluation in */ peraggstate->evalslot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(peraggstate->evalslot, peraggstate->evaldesc); /* Set up projection info for evaluation */ peraggstate->evalproj = ExecBuildProjectionInfo(aggrefstate->args, aggstate->tmpcontext, peraggstate->evalslot, NULL); /* * If we're doing either DISTINCT or ORDER BY, then we have a list of * SortGroupClause nodes; fish out the data in them and stick them * into arrays. * * Note that by construction, if there is a DISTINCT clause then the * ORDER BY clause is a prefix of it (see transformDistinctClause). */ if (aggref->aggdistinct) { sortlist = aggref->aggdistinct; numSortCols = numDistinctCols = list_length(sortlist); Assert(numSortCols >= list_length(aggref->aggorder)); } else { sortlist = aggref->aggorder; numSortCols = list_length(sortlist); numDistinctCols = 0; } peraggstate->numSortCols = numSortCols; peraggstate->numDistinctCols = numDistinctCols; if (numSortCols > 0) { /* * We don't implement DISTINCT or ORDER BY aggs in the HASHED case * (yet) */ Assert(node->aggstrategy != AGG_HASHED); /* If we have only one input, we need its len/byval info. */ if (numInputs == 1) { get_typlenbyval(inputTypes[0], &peraggstate->inputtypeLen, &peraggstate->inputtypeByVal); } else if (numDistinctCols > 0) { /* we will need an extra slot to store prior values */ peraggstate->uniqslot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(peraggstate->uniqslot, peraggstate->evaldesc); } /* Extract the sort information for use later */ peraggstate->sortColIdx = (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber)); peraggstate->sortOperators = (Oid *) palloc(numSortCols * sizeof(Oid)); peraggstate->sortCollations = (Oid *) palloc(numSortCols * sizeof(Oid)); peraggstate->sortNullsFirst = (bool *) palloc(numSortCols * sizeof(bool)); i = 0; foreach(lc, sortlist) { SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc); TargetEntry *tle = get_sortgroupclause_tle(sortcl, aggref->args); /* the parser should have made sure of this */ Assert(OidIsValid(sortcl->sortop)); peraggstate->sortColIdx[i] = tle->resno; peraggstate->sortOperators[i] = sortcl->sortop; peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr); peraggstate->sortNullsFirst[i] = sortcl->nulls_first; i++; } Assert(i == numSortCols); } if (aggref->aggdistinct) { Assert(numArguments > 0); /* * We need the equal function for each DISTINCT comparison we will * make. */ peraggstate->equalfns = (FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo)); i = 0; foreach(lc, aggref->aggdistinct) { SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc); fmgr_info(get_opcode(sortcl->eqop), &peraggstate->equalfns[i]); i++; } Assert(i == numDistinctCols); } ReleaseSysCache(aggTuple); } /* Update numaggs to match number of unique aggregates found */ aggstate->numaggs = aggno + 1; return aggstate; }
void ExecReScanAgg | ( | AggState * | node | ) |
Definition at line 1914 of file nodeAgg.c.
References AggState::agg_done, AGG_HASHED, AggState::aggcontext, build_hash_table(), PlanState::chgParam, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExecReScan(), AggState::grp_firstTuple, AggState::hashiter, AggState::hashtable, heap_freetuple(), PlanState::lefttree, MemoryContextResetAndDeleteChildren(), MemSet, NULL, AggState::numaggs, AggState::peragg, AggState::pergroup, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, ResetTupleHashIterator, AggStatePerAggData::sortstate, AggState::ss, AggState::table_filled, and tuplesort_end().
Referenced by ExecReScan().
{ ExprContext *econtext = node->ss.ps.ps_ExprContext; int aggno; node->agg_done = false; node->ss.ps.ps_TupFromTlist = false; if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED) { /* * In the hashed case, if we haven't yet built the hash table then we * can just return; nothing done yet, so nothing to undo. If subnode's * chgParam is not NULL then it will be re-scanned by ExecProcNode, * else no reason to re-scan it at all. */ if (!node->table_filled) return; /* * If we do have the hash table and the subplan does not have any * parameter changes, then we can just rescan the existing hash table; * no need to build it again. */ if (node->ss.ps.lefttree->chgParam == NULL) { ResetTupleHashIterator(node->hashtable, &node->hashiter); return; } } /* Make sure we have closed any open tuplesorts */ for (aggno = 0; aggno < node->numaggs; aggno++) { AggStatePerAgg peraggstate = &node->peragg[aggno]; if (peraggstate->sortstate) tuplesort_end(peraggstate->sortstate); peraggstate->sortstate = NULL; } /* Release first tuple of group, if we have made a copy */ if (node->grp_firstTuple != NULL) { heap_freetuple(node->grp_firstTuple); node->grp_firstTuple = NULL; } /* Forget current agg values */ MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numaggs); MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numaggs); /* * Release all temp storage. Note that with AGG_HASHED, the hash table is * allocated in a sub-context of the aggcontext. We're going to rebuild * the hash table from scratch, so we need to use * MemoryContextResetAndDeleteChildren() to avoid leaking the old hash * table's memory context header. */ MemoryContextResetAndDeleteChildren(node->aggcontext); if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED) { /* Rebuild an empty hash table */ build_hash_table(node); node->table_filled = false; } else { /* * Reset the per-group state (in particular, mark transvalues null) */ MemSet(node->pergroup, 0, sizeof(AggStatePerGroupData) * node->numaggs); } /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. */ if (node->ss.ps.lefttree->chgParam == NULL) ExecReScan(node->ss.ps.lefttree); }
static void finalize_aggregate | ( | AggState * | aggstate, | |
AggStatePerAgg | peraggstate, | |||
AggStatePerGroup | pergroupstate, | |||
Datum * | resultVal, | |||
bool * | resultIsNull | |||
) | [static] |
Definition at line 731 of file nodeAgg.c.
References AggStatePerAggData::aggCollation, FunctionCallInfoData::arg, FunctionCallInfoData::argnull, CurrentMemoryContext, datumCopy(), DatumGetPointer, ExprContext::ecxt_per_tuple_memory, AggStatePerAggData::finalfn, AggStatePerAggData::finalfn_oid, FunctionCallInfoData::flinfo, FmgrInfo::fn_strict, FunctionCallInvoke, InitFunctionCallInfoData, FunctionCallInfoData::isnull, MemoryContextContains(), MemoryContextSwitchTo(), NULL, OidIsValid, ScanState::ps, PlanState::ps_ExprContext, AggStatePerAggData::resulttypeByVal, AggStatePerAggData::resulttypeLen, AggState::ss, AggStatePerGroupData::transValue, and AggStatePerGroupData::transValueIsNull.
Referenced by agg_retrieve_direct(), and agg_retrieve_hash_table().
{ MemoryContext oldContext; oldContext = MemoryContextSwitchTo(aggstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory); /* * Apply the agg's finalfn if one is provided, else return transValue. */ if (OidIsValid(peraggstate->finalfn_oid)) { FunctionCallInfoData fcinfo; InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1, peraggstate->aggCollation, (void *) aggstate, NULL); fcinfo.arg[0] = pergroupstate->transValue; fcinfo.argnull[0] = pergroupstate->transValueIsNull; if (fcinfo.flinfo->fn_strict && pergroupstate->transValueIsNull) { /* don't call a strict function with NULL inputs */ *resultVal = (Datum) 0; *resultIsNull = true; } else { *resultVal = FunctionCallInvoke(&fcinfo); *resultIsNull = fcinfo.isnull; } } else { *resultVal = pergroupstate->transValue; *resultIsNull = pergroupstate->transValueIsNull; } /* * If result is pass-by-ref, make sure it is in the right context. */ if (!peraggstate->resulttypeByVal && !*resultIsNull && !MemoryContextContains(CurrentMemoryContext, DatumGetPointer(*resultVal))) *resultVal = datumCopy(*resultVal, peraggstate->resulttypeByVal, peraggstate->resulttypeLen); MemoryContextSwitchTo(oldContext); }
Definition at line 877 of file nodeAgg.c.
References bms_add_member(), bms_first_member(), bms_free(), find_unaggregated_cols(), Agg::grpColIdx, i, lcons_int(), Agg::numCols, PlanState::plan, ScanState::ps, and AggState::ss.
Referenced by ExecInitAgg().
{ Agg *node = (Agg *) aggstate->ss.ps.plan; Bitmapset *colnos; List *collist; int i; /* Find Vars that will be needed in tlist and qual */ colnos = find_unaggregated_cols(aggstate); /* Add in all the grouping columns */ for (i = 0; i < node->numCols; i++) colnos = bms_add_member(colnos, node->grpColIdx[i]); /* Convert to list, using lcons so largest element ends up first */ collist = NIL; while ((i = bms_first_member(colnos)) >= 0) collist = lcons_int(i, collist); bms_free(colnos); return collist; }
Definition at line 789 of file nodeAgg.c.
References find_unaggregated_cols_walker(), NULL, Agg::plan, PlanState::plan, ScanState::ps, Plan::qual, AggState::ss, and Plan::targetlist.
Referenced by find_hash_columns().
{ Agg *node = (Agg *) aggstate->ss.ps.plan; Bitmapset *colnos; colnos = NULL; (void) find_unaggregated_cols_walker((Node *) node->plan.targetlist, &colnos); (void) find_unaggregated_cols_walker((Node *) node->plan.qual, &colnos); return colnos; }
Definition at line 803 of file nodeAgg.c.
References Assert, bms_add_member(), expression_tree_walker(), IsA, NULL, OUTER_VAR, Var::varattno, Var::varlevelsup, and Var::varno.
Referenced by find_unaggregated_cols().
{ if (node == NULL) return false; if (IsA(node, Var)) { Var *var = (Var *) node; /* setrefs.c should have set the varno to OUTER_VAR */ Assert(var->varno == OUTER_VAR); Assert(var->varlevelsup == 0); *colnos = bms_add_member(*colnos, var->varattno); return false; } if (IsA(node, Aggref)) /* do not descend into aggregate exprs */ return false; return expression_tree_walker(node, find_unaggregated_cols_walker, (void *) colnos); }
Definition at line 1867 of file nodeAgg.c.
References getTypeInputInfo(), OidInputFunctionCall(), pfree(), and TextDatumGetCString.
Referenced by ExecInitAgg().
{ Oid typinput, typioparam; char *strInitVal; Datum initVal; getTypeInputInfo(transtype, &typinput, &typioparam); strInitVal = TextDatumGetCString(textInitVal); initVal = OidInputFunctionCall(typinput, strInitVal, typioparam, -1); pfree(strInitVal); return initVal; }
Size hash_agg_entry_size | ( | int | numAggs | ) |
Definition at line 905 of file nodeAgg.c.
References MAXALIGN.
Referenced by choose_hashed_grouping().
{ Size entrysize; /* This must match build_hash_table */ entrysize = sizeof(AggHashEntryData) + (numAggs - 1) * sizeof(AggStatePerGroupData); entrysize = MAXALIGN(entrysize); /* Account for hashtable overhead (assuming fill factor = 1) */ entrysize += 3 * sizeof(void *); return entrysize; }
static void initialize_aggregates | ( | AggState * | aggstate, | |
AggStatePerAgg | peragg, | |||
AggStatePerGroup | pergroup | |||
) | [static] |
Definition at line 292 of file nodeAgg.c.
References AggState::aggcontext, tupleDesc::attrs, datumCopy(), AggStatePerAggData::evaldesc, AggStatePerAggData::initValue, AggStatePerAggData::initValueIsNull, MemoryContextSwitchTo(), AggStatePerGroupData::noTransValue, AggState::numaggs, AggStatePerAggData::numInputs, AggStatePerAggData::numSortCols, AggStatePerAggData::sortColIdx, AggStatePerAggData::sortCollations, AggStatePerAggData::sortNullsFirst, AggStatePerAggData::sortOperators, AggStatePerAggData::sortstate, AggStatePerAggData::transtypeByVal, AggStatePerAggData::transtypeLen, AggStatePerGroupData::transValue, AggStatePerGroupData::transValueIsNull, tuplesort_begin_datum(), tuplesort_begin_heap(), tuplesort_end(), and work_mem.
Referenced by agg_retrieve_direct(), and lookup_hash_entry().
{ int aggno; for (aggno = 0; aggno < aggstate->numaggs; aggno++) { AggStatePerAgg peraggstate = &peragg[aggno]; AggStatePerGroup pergroupstate = &pergroup[aggno]; /* * Start a fresh sort operation for each DISTINCT/ORDER BY aggregate. */ if (peraggstate->numSortCols > 0) { /* * In case of rescan, maybe there could be an uncompleted sort * operation? Clean it up if so. */ if (peraggstate->sortstate) tuplesort_end(peraggstate->sortstate); /* * We use a plain Datum sorter when there's a single input column; * otherwise sort the full tuple. (See comments for * process_ordered_aggregate_single.) */ peraggstate->sortstate = (peraggstate->numInputs == 1) ? tuplesort_begin_datum(peraggstate->evaldesc->attrs[0]->atttypid, peraggstate->sortOperators[0], peraggstate->sortCollations[0], peraggstate->sortNullsFirst[0], work_mem, false) : tuplesort_begin_heap(peraggstate->evaldesc, peraggstate->numSortCols, peraggstate->sortColIdx, peraggstate->sortOperators, peraggstate->sortCollations, peraggstate->sortNullsFirst, work_mem, false); } /* * (Re)set transValue to the initial value. * * Note that when the initial value is pass-by-ref, we must copy it * (into the aggcontext) since we will pfree the transValue later. */ if (peraggstate->initValueIsNull) pergroupstate->transValue = peraggstate->initValue; else { MemoryContext oldContext; oldContext = MemoryContextSwitchTo(aggstate->aggcontext); pergroupstate->transValue = datumCopy(peraggstate->initValue, peraggstate->transtypeByVal, peraggstate->transtypeLen); MemoryContextSwitchTo(oldContext); } pergroupstate->transValueIsNull = peraggstate->initValueIsNull; /* * If the initial value for the transition state doesn't exist in the * pg_aggregate table then we will let the first non-NULL value * returned from the outer procNode become the initial value. (This is * useful for aggregates like max() and min().) The noTransValue flag * signals that we still need to do this. */ pergroupstate->noTransValue = peraggstate->initValueIsNull; } }
static AggHashEntry lookup_hash_entry | ( | AggState * | aggstate, | |
TupleTableSlot * | inputslot | |||
) | [static] |
Definition at line 925 of file nodeAgg.c.
References ExecSetSlotDescriptor(), ExecStoreAllNullTuple(), AggState::hash_needed, AggState::hashslot, AggState::hashtable, initialize_aggregates(), lfirst_int, linitial_int, LookupTupleHashEntry(), NULL, AggState::peragg, AggHashEntryData::pergroup, slot_getsomeattrs(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_tupleDescriptor, and TupleTableSlot::tts_values.
Referenced by agg_fill_hash_table().
{ TupleTableSlot *hashslot = aggstate->hashslot; ListCell *l; AggHashEntry entry; bool isnew; /* if first time through, initialize hashslot by cloning input slot */ if (hashslot->tts_tupleDescriptor == NULL) { ExecSetSlotDescriptor(hashslot, inputslot->tts_tupleDescriptor); /* Make sure all unused columns are NULLs */ ExecStoreAllNullTuple(hashslot); } /* transfer just the needed columns into hashslot */ slot_getsomeattrs(inputslot, linitial_int(aggstate->hash_needed)); foreach(l, aggstate->hash_needed) { int varNumber = lfirst_int(l) - 1; hashslot->tts_values[varNumber] = inputslot->tts_values[varNumber]; hashslot->tts_isnull[varNumber] = inputslot->tts_isnull[varNumber]; } /* find or create the hashtable entry using the filtered tuple */ entry = (AggHashEntry) LookupTupleHashEntry(aggstate->hashtable, hashslot, &isnew); if (isnew) { /* initialize aggregates for new tuple group */ initialize_aggregates(aggstate, aggstate->peragg, entry->pergroup); } return entry; }
static void process_ordered_aggregate_multi | ( | AggState * | aggstate, | |
AggStatePerAgg | peraggstate, | |||
AggStatePerGroup | pergroupstate | |||
) | [static] |
Definition at line 653 of file nodeAgg.c.
References advance_transition_function(), FunctionCallInfoData::arg, FunctionCallInfoData::argnull, ExprContext::ecxt_per_tuple_memory, AggStatePerAggData::equalfns, AggStatePerAggData::evalslot, ExecClearTuple(), execTuplesMatch(), i, MemoryContextReset(), AggStatePerAggData::numArguments, AggStatePerAggData::numDistinctCols, slot_getsomeattrs(), AggStatePerAggData::sortColIdx, AggStatePerAggData::sortstate, AggState::tmpcontext, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, tuplesort_end(), tuplesort_gettupleslot(), tuplesort_performsort(), and AggStatePerAggData::uniqslot.
Referenced by agg_retrieve_direct().
{ MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory; FunctionCallInfoData fcinfo; TupleTableSlot *slot1 = peraggstate->evalslot; TupleTableSlot *slot2 = peraggstate->uniqslot; int numArguments = peraggstate->numArguments; int numDistinctCols = peraggstate->numDistinctCols; bool haveOldValue = false; int i; tuplesort_performsort(peraggstate->sortstate); ExecClearTuple(slot1); if (slot2) ExecClearTuple(slot2); while (tuplesort_gettupleslot(peraggstate->sortstate, true, slot1)) { /* * Extract the first numArguments as datums to pass to the transfn. * (This will help execTuplesMatch too, so do it immediately.) */ slot_getsomeattrs(slot1, numArguments); if (numDistinctCols == 0 || !haveOldValue || !execTuplesMatch(slot1, slot2, numDistinctCols, peraggstate->sortColIdx, peraggstate->equalfns, workcontext)) { /* Load values into fcinfo */ /* Start from 1, since the 0th arg will be the transition value */ for (i = 0; i < numArguments; i++) { fcinfo.arg[i + 1] = slot1->tts_values[i]; fcinfo.argnull[i + 1] = slot1->tts_isnull[i]; } advance_transition_function(aggstate, peraggstate, pergroupstate, &fcinfo); if (numDistinctCols > 0) { /* swap the slot pointers to retain the current tuple */ TupleTableSlot *tmpslot = slot2; slot2 = slot1; slot1 = tmpslot; haveOldValue = true; } } /* Reset context each time, unless execTuplesMatch did it for us */ if (numDistinctCols == 0) MemoryContextReset(workcontext); ExecClearTuple(slot1); } if (slot2) ExecClearTuple(slot2); tuplesort_end(peraggstate->sortstate); peraggstate->sortstate = NULL; }
static void process_ordered_aggregate_single | ( | AggState * | aggstate, | |
AggStatePerAgg | peraggstate, | |||
AggStatePerGroup | pergroupstate | |||
) | [static] |
Definition at line 566 of file nodeAgg.c.
References advance_transition_function(), FunctionCallInfoData::arg, FunctionCallInfoData::argnull, Assert, DatumGetBool, DatumGetPointer, ExprContext::ecxt_per_tuple_memory, AggStatePerAggData::equalfns, FunctionCall2, AggStatePerAggData::inputtypeByVal, MemoryContextReset(), MemoryContextSwitchTo(), AggStatePerAggData::numDistinctCols, pfree(), AggStatePerAggData::sortstate, AggState::tmpcontext, tuplesort_end(), tuplesort_getdatum(), and tuplesort_performsort().
Referenced by agg_retrieve_direct().
{ Datum oldVal = (Datum) 0; bool oldIsNull = true; bool haveOldVal = false; MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory; MemoryContext oldContext; bool isDistinct = (peraggstate->numDistinctCols > 0); Datum *newVal; bool *isNull; FunctionCallInfoData fcinfo; Assert(peraggstate->numDistinctCols < 2); tuplesort_performsort(peraggstate->sortstate); /* Load the column into argument 1 (arg 0 will be transition value) */ newVal = fcinfo.arg + 1; isNull = fcinfo.argnull + 1; /* * Note: if input type is pass-by-ref, the datums returned by the sort are * freshly palloc'd in the per-query context, so we must be careful to * pfree them when they are no longer needed. */ while (tuplesort_getdatum(peraggstate->sortstate, true, newVal, isNull)) { /* * Clear and select the working context for evaluation of the equality * function and transition function. */ MemoryContextReset(workcontext); oldContext = MemoryContextSwitchTo(workcontext); /* * If DISTINCT mode, and not distinct from prior, skip it. * * Note: we assume equality functions don't care about collation. */ if (isDistinct && haveOldVal && ((oldIsNull && *isNull) || (!oldIsNull && !*isNull && DatumGetBool(FunctionCall2(&peraggstate->equalfns[0], oldVal, *newVal))))) { /* equal to prior, so forget this one */ if (!peraggstate->inputtypeByVal && !*isNull) pfree(DatumGetPointer(*newVal)); } else { advance_transition_function(aggstate, peraggstate, pergroupstate, &fcinfo); /* forget the old value, if any */ if (!oldIsNull && !peraggstate->inputtypeByVal) pfree(DatumGetPointer(oldVal)); /* and remember the new one for subsequent equality checks */ oldVal = *newVal; oldIsNull = *isNull; haveOldVal = true; } MemoryContextSwitchTo(oldContext); } if (!oldIsNull && !peraggstate->inputtypeByVal) pfree(DatumGetPointer(oldVal)); tuplesort_end(peraggstate->sortstate); peraggstate->sortstate = NULL; }