Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Typedefs | Functions

nodeAgg.c File Reference

#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"
Include dependency graph for nodeAgg.c:

Go to the source code of this file.

Data Structures

struct  AggStatePerAggData
struct  AggStatePerGroupData
struct  AggHashEntryData

Typedefs

typedef struct AggStatePerAggData AggStatePerAggData
typedef struct AggStatePerGroupData AggStatePerGroupData
typedef struct AggHashEntryDataAggHashEntry
typedef struct AggHashEntryData AggHashEntryData

Functions

static void initialize_aggregates (AggState *aggstate, AggStatePerAgg peragg, AggStatePerGroup pergroup)
static void advance_transition_function (AggState *aggstate, AggStatePerAgg peraggstate, AggStatePerGroup pergroupstate, FunctionCallInfoData *fcinfo)
static void advance_aggregates (AggState *aggstate, AggStatePerGroup pergroup)
static void process_ordered_aggregate_single (AggState *aggstate, AggStatePerAgg peraggstate, AggStatePerGroup pergroupstate)
static void process_ordered_aggregate_multi (AggState *aggstate, AggStatePerAgg peraggstate, AggStatePerGroup pergroupstate)
static void finalize_aggregate (AggState *aggstate, AggStatePerAgg peraggstate, AggStatePerGroup pergroupstate, Datum *resultVal, bool *resultIsNull)
static Bitmapsetfind_unaggregated_cols (AggState *aggstate)
static bool find_unaggregated_cols_walker (Node *node, Bitmapset **colnos)
static void build_hash_table (AggState *aggstate)
static AggHashEntry lookup_hash_entry (AggState *aggstate, TupleTableSlot *inputslot)
static TupleTableSlotagg_retrieve_direct (AggState *aggstate)
static void agg_fill_hash_table (AggState *aggstate)
static TupleTableSlotagg_retrieve_hash_table (AggState *aggstate)
static Datum GetAggInitVal (Datum textInitVal, Oid transtype)
static Listfind_hash_columns (AggState *aggstate)
Size hash_agg_entry_size (int numAggs)
TupleTableSlotExecAgg (AggState *node)
AggStateExecInitAgg (Agg *node, EState *estate, int eflags)
void ExecEndAgg (AggState *node)
void ExecReScanAgg (AggState *node)
int AggCheckCallContext (FunctionCallInfo fcinfo, MemoryContext *aggcontext)
Datum aggregate_dummy (PG_FUNCTION_ARGS)

Typedef Documentation

typedef struct AggHashEntryData* AggHashEntry

Definition at line 247 of file nodeAgg.c.


Function Documentation

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   ) 

Definition at line 2046 of file nodeAgg.c.

References elog, and ERROR.

{
    elog(ERROR, "aggregate function %u called as normal function",
         fcinfo->flinfo->fn_oid);
    return (Datum) 0;           /* keep compiler quiet */
}

static void build_hash_table ( AggState aggstate  )  [static]
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);
}

AggState* ExecInitAgg ( Agg node,
EState estate,
int  eflags 
)

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);
}

static List* find_hash_columns ( AggState aggstate  )  [static]

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

static Bitmapset * find_unaggregated_cols ( AggState aggstate  )  [static]

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

static bool find_unaggregated_cols_walker ( Node node,
Bitmapset **  colnos 
) [static]

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);
}

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
) [static]

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