#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;
}
1.7.1