Header And Logo

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

Functions

nodeWindowAgg.h File Reference

#include "nodes/execnodes.h"
Include dependency graph for nodeWindowAgg.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

WindowAggStateExecInitWindowAgg (WindowAgg *node, EState *estate, int eflags)
TupleTableSlotExecWindowAgg (WindowAggState *node)
void ExecEndWindowAgg (WindowAggState *node)
void ExecReScanWindowAgg (WindowAggState *node)

Function Documentation

void ExecEndWindowAgg ( WindowAggState node  ) 
WindowAggState* ExecInitWindowAgg ( WindowAgg node,
EState estate,
int  eflags 
)

Definition at line 1398 of file nodeWindowAgg.c.

References ACL_EXECUTE, ACL_KIND_PROC, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), WindowFuncExprState::args, WindowObjectData::argstates, Assert, contain_volatile_functions(), CurrentMemoryContext, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_per_query_memory, elog, WindowAgg::endOffset, equal(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecAssignResultTypeFromTL(), ExecAssignScanTypeFromOuterPlan(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitResultTupleSlot(), ExecInitScanTupleSlot(), ExecSetSlotDescriptor(), execTuplesMatchPrepare(), ExprState::expr, WindowAggState::first_part_slot, fmgr_info_cxt(), fmgr_info_set_expr, WindowAgg::frameOptions, WindowAggState::frameOptions, WindowAggState::funcs, get_func_name(), get_typlenbyval(), GetUserId(), i, initialize_peragg(), InvokeFunctionExecuteHook, lfirst, list_length(), WindowObjectData::localmem, makeNode, WindowObjectData::markptr, NIL, NULL, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::ordEqfunctions, WindowAgg::ordNumCols, WindowAgg::ordOperators, outerPlan, outerPlanState, palloc0(), WindowAggState::partcontext, WindowAggState::partEqfunctions, WindowAgg::partNumCols, WindowAgg::partOperators, WindowAggState::peragg, WindowAggState::perfunc, pg_proc_aclcheck(), WindowAgg::plan, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, PlanState::qual, Plan::qual, WindowObjectData::readptr, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAgg::startOffset, WindowAggState::startOffset, PlanState::state, Plan::targetlist, PlanState::targetlist, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, WindowAggState::tmpcontext, TupleTableSlot::tts_tupleDescriptor, WindowStatePerAggData::wfuncno, WindowFuncExprState::wfuncno, WindowFunc::winfnoid, WindowAgg::winref, WindowFunc::winref, WindowObjectData::winstate, and WindowFuncExprState::xprstate.

Referenced by ExecInitNode().

{
    WindowAggState *winstate;
    Plan       *outerPlan;
    ExprContext *econtext;
    ExprContext *tmpcontext;
    WindowStatePerFunc perfunc;
    WindowStatePerAgg peragg;
    int         numfuncs,
                wfuncno,
                numaggs,
                aggno;
    ListCell   *l;

    /* check for unsupported flags */
    Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

    /*
     * create state structure
     */
    winstate = makeNode(WindowAggState);
    winstate->ss.ps.plan = (Plan *) node;
    winstate->ss.ps.state = estate;

    /*
     * 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, &winstate->ss.ps);
    tmpcontext = winstate->ss.ps.ps_ExprContext;
    winstate->tmpcontext = tmpcontext;
    ExecAssignExprContext(estate, &winstate->ss.ps);

    /* Create long-lived context for storage of partition-local memory etc */
    winstate->partcontext =
        AllocSetContextCreate(CurrentMemoryContext,
                              "WindowAgg_Partition",
                              ALLOCSET_DEFAULT_MINSIZE,
                              ALLOCSET_DEFAULT_INITSIZE,
                              ALLOCSET_DEFAULT_MAXSIZE);

    /* Create mid-lived context for aggregate trans values etc */
    winstate->aggcontext =
        AllocSetContextCreate(CurrentMemoryContext,
                              "WindowAgg_Aggregates",
                              ALLOCSET_DEFAULT_MINSIZE,
                              ALLOCSET_DEFAULT_INITSIZE,
                              ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * tuple table initialization
     */
    ExecInitScanTupleSlot(estate, &winstate->ss);
    ExecInitResultTupleSlot(estate, &winstate->ss.ps);
    winstate->first_part_slot = ExecInitExtraTupleSlot(estate);
    winstate->agg_row_slot = ExecInitExtraTupleSlot(estate);
    winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
    winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);

    winstate->ss.ps.targetlist = (List *)
        ExecInitExpr((Expr *) node->plan.targetlist,
                     (PlanState *) winstate);

    /*
     * WindowAgg nodes never have quals, since they can only occur at the
     * logical top level of a query (ie, after any WHERE or HAVING filters)
     */
    Assert(node->plan.qual == NIL);
    winstate->ss.ps.qual = NIL;

    /*
     * initialize child nodes
     */
    outerPlan = outerPlan(node);
    outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);

    /*
     * initialize source tuple type (which is also the tuple type that we'll
     * store in the tuplestore and use in all our working slots).
     */
    ExecAssignScanTypeFromOuterPlan(&winstate->ss);

    ExecSetSlotDescriptor(winstate->first_part_slot,
                          winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
    ExecSetSlotDescriptor(winstate->agg_row_slot,
                          winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
    ExecSetSlotDescriptor(winstate->temp_slot_1,
                          winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
    ExecSetSlotDescriptor(winstate->temp_slot_2,
                          winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);

    /*
     * Initialize result tuple type and projection info.
     */
    ExecAssignResultTypeFromTL(&winstate->ss.ps);
    ExecAssignProjectionInfo(&winstate->ss.ps, NULL);

    winstate->ss.ps.ps_TupFromTlist = false;

    /* Set up data for comparing tuples */
    if (node->partNumCols > 0)
        winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols,
                                                        node->partOperators);
    if (node->ordNumCols > 0)
        winstate->ordEqfunctions = execTuplesMatchPrepare(node->ordNumCols,
                                                          node->ordOperators);

    /*
     * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
     */
    numfuncs = winstate->numfuncs;
    numaggs = winstate->numaggs;
    econtext = winstate->ss.ps.ps_ExprContext;
    econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
    econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);

    /*
     * allocate per-wfunc/per-agg state information.
     */
    perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
    peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
    winstate->perfunc = perfunc;
    winstate->peragg = peragg;

    wfuncno = -1;
    aggno = -1;
    foreach(l, winstate->funcs)
    {
        WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
        WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
        WindowStatePerFunc perfuncstate;
        AclResult   aclresult;
        int         i;

        if (wfunc->winref != node->winref)      /* planner screwed up? */
            elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
                 wfunc->winref, node->winref);

        /* Look for a previous duplicate window function */
        for (i = 0; i <= wfuncno; i++)
        {
            if (equal(wfunc, perfunc[i].wfunc) &&
                !contain_volatile_functions((Node *) wfunc))
                break;
        }
        if (i <= wfuncno)
        {
            /* Found a match to an existing entry, so just mark it */
            wfuncstate->wfuncno = i;
            continue;
        }

        /* Nope, so assign a new PerAgg record */
        perfuncstate = &perfunc[++wfuncno];

        /* Mark WindowFunc state node with assigned index in the result array */
        wfuncstate->wfuncno = wfuncno;

        /* Check permission to call window function */
        aclresult = pg_proc_aclcheck(wfunc->winfnoid, GetUserId(),
                                     ACL_EXECUTE);
        if (aclresult != ACLCHECK_OK)
            aclcheck_error(aclresult, ACL_KIND_PROC,
                           get_func_name(wfunc->winfnoid));
        InvokeFunctionExecuteHook(wfunc->winfnoid);

        /* Fill in the perfuncstate data */
        perfuncstate->wfuncstate = wfuncstate;
        perfuncstate->wfunc = wfunc;
        perfuncstate->numArguments = list_length(wfuncstate->args);

        fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
                      econtext->ecxt_per_query_memory);
        fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);

        perfuncstate->winCollation = wfunc->inputcollid;

        get_typlenbyval(wfunc->wintype,
                        &perfuncstate->resulttypeLen,
                        &perfuncstate->resulttypeByVal);

        /*
         * If it's really just a plain aggregate function, we'll emulate the
         * Agg environment for it.
         */
        perfuncstate->plain_agg = wfunc->winagg;
        if (wfunc->winagg)
        {
            WindowStatePerAgg peraggstate;

            perfuncstate->aggno = ++aggno;
            peraggstate = &winstate->peragg[aggno];
            initialize_peragg(winstate, wfunc, peraggstate);
            peraggstate->wfuncno = wfuncno;
        }
        else
        {
            WindowObject winobj = makeNode(WindowObjectData);

            winobj->winstate = winstate;
            winobj->argstates = wfuncstate->args;
            winobj->localmem = NULL;
            perfuncstate->winobj = winobj;
        }
    }

    /* Update numfuncs, numaggs to match number of unique functions found */
    winstate->numfuncs = wfuncno + 1;
    winstate->numaggs = aggno + 1;

    /* Set up WindowObject for aggregates, if needed */
    if (winstate->numaggs > 0)
    {
        WindowObject agg_winobj = makeNode(WindowObjectData);

        agg_winobj->winstate = winstate;
        agg_winobj->argstates = NIL;
        agg_winobj->localmem = NULL;
        /* make sure markptr = -1 to invalidate. It may not get used */
        agg_winobj->markptr = -1;
        agg_winobj->readptr = -1;
        winstate->agg_winobj = agg_winobj;
    }

    /* copy frame options to state node for easy access */
    winstate->frameOptions = node->frameOptions;

    /* initialize frame bound offset expressions */
    winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
                                         (PlanState *) winstate);
    winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
                                       (PlanState *) winstate);

    winstate->all_first = true;
    winstate->partition_spooled = false;
    winstate->more_partitions = false;

    return winstate;
}

void ExecReScanWindowAgg ( WindowAggState node  ) 

Definition at line 1678 of file nodeWindowAgg.c.

References WindowAggState::agg_row_slot, WindowAggState::all_done, WindowAggState::all_first, PlanState::chgParam, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExecClearTuple(), ExecReScan(), WindowAggState::first_part_slot, PlanState::lefttree, MemSet, NULL, WindowAggState::numfuncs, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_TupFromTlist, release_partition(), WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_1, and WindowAggState::temp_slot_2.

Referenced by ExecReScan().

{
    ExprContext *econtext = node->ss.ps.ps_ExprContext;

    node->all_done = false;

    node->ss.ps.ps_TupFromTlist = false;
    node->all_first = true;

    /* release tuplestore et al */
    release_partition(node);

    /* release all temp tuples, but especially first_part_slot */
    ExecClearTuple(node->ss.ss_ScanTupleSlot);
    ExecClearTuple(node->first_part_slot);
    ExecClearTuple(node->agg_row_slot);
    ExecClearTuple(node->temp_slot_1);
    ExecClearTuple(node->temp_slot_2);

    /* Forget current wfunc values */
    MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
    MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);

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

TupleTableSlot* ExecWindowAgg ( WindowAggState node  ) 

Definition at line 1190 of file nodeWindowAgg.c.

References WindowAggState::all_done, WindowAggState::all_first, Assert, begin_partition(), WindowAggState::buffer, WindowAggState::current_ptr, WindowAggState::currentpos, datumCopy(), DatumGetInt64, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, elog, WindowAggState::endOffset, WindowAggState::endOffsetValue, ereport, errcode(), errmsg(), ERROR, eval_windowaggregates(), eval_windowfunction(), ExecEvalExprSwitchContext(), ExecProject(), ExprState::expr, ExprEndResult, ExprMultipleResult, exprType(), WindowAggState::framehead_valid, FRAMEOPTION_END_VALUE, FRAMEOPTION_ROWS, FRAMEOPTION_START_VALUE, WindowAggState::frameOptions, WindowAggState::frametail_valid, get_typlenbyval(), i, WindowAggState::more_partitions, NULL, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::ps_TupFromTlist, release_partition(), ResetExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startOffset, WindowAggState::startOffsetValue, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), value, WindowFuncExprState::wfuncno, and WindowStatePerFuncData::wfuncstate.

Referenced by ExecProcNode().

{
    TupleTableSlot *result;
    ExprDoneCond isDone;
    ExprContext *econtext;
    int         i;
    int         numfuncs;

    if (winstate->all_done)
        return NULL;

    /*
     * Check to see if we're still projecting out tuples from a previous
     * output tuple (because there is a function-returning-set in the
     * projection expressions).  If so, try to project another one.
     */
    if (winstate->ss.ps.ps_TupFromTlist)
    {
        TupleTableSlot *result;
        ExprDoneCond isDone;

        result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone);
        if (isDone == ExprMultipleResult)
            return result;
        /* Done with that source tuple... */
        winstate->ss.ps.ps_TupFromTlist = false;
    }

    /*
     * Compute frame offset values, if any, during first call.
     */
    if (winstate->all_first)
    {
        int         frameOptions = winstate->frameOptions;
        ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
        Datum       value;
        bool        isnull;
        int16       len;
        bool        byval;

        if (frameOptions & FRAMEOPTION_START_VALUE)
        {
            Assert(winstate->startOffset != NULL);
            value = ExecEvalExprSwitchContext(winstate->startOffset,
                                              econtext,
                                              &isnull,
                                              NULL);
            if (isnull)
                ereport(ERROR,
                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                         errmsg("frame starting offset must not be null")));
            /* copy value into query-lifespan context */
            get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
                            &len, &byval);
            winstate->startOffsetValue = datumCopy(value, byval, len);
            if (frameOptions & FRAMEOPTION_ROWS)
            {
                /* value is known to be int8 */
                int64       offset = DatumGetInt64(value);

                if (offset < 0)
                    ereport(ERROR,
                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                      errmsg("frame starting offset must not be negative")));
            }
        }
        if (frameOptions & FRAMEOPTION_END_VALUE)
        {
            Assert(winstate->endOffset != NULL);
            value = ExecEvalExprSwitchContext(winstate->endOffset,
                                              econtext,
                                              &isnull,
                                              NULL);
            if (isnull)
                ereport(ERROR,
                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                         errmsg("frame ending offset must not be null")));
            /* copy value into query-lifespan context */
            get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
                            &len, &byval);
            winstate->endOffsetValue = datumCopy(value, byval, len);
            if (frameOptions & FRAMEOPTION_ROWS)
            {
                /* value is known to be int8 */
                int64       offset = DatumGetInt64(value);

                if (offset < 0)
                    ereport(ERROR,
                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                        errmsg("frame ending offset must not be negative")));
            }
        }
        winstate->all_first = false;
    }

restart:
    if (winstate->buffer == NULL)
    {
        /* Initialize for first partition and set current row = 0 */
        begin_partition(winstate);
        /* If there are no input rows, we'll detect that and exit below */
    }
    else
    {
        /* Advance current row within partition */
        winstate->currentpos++;
        /* This might mean that the frame moves, too */
        winstate->framehead_valid = false;
        winstate->frametail_valid = false;
    }

    /*
     * Spool all tuples up to and including the current row, if we haven't
     * already
     */
    spool_tuples(winstate, winstate->currentpos);

    /* Move to the next partition if we reached the end of this partition */
    if (winstate->partition_spooled &&
        winstate->currentpos >= winstate->spooled_rows)
    {
        release_partition(winstate);

        if (winstate->more_partitions)
        {
            begin_partition(winstate);
            Assert(winstate->spooled_rows > 0);
        }
        else
        {
            winstate->all_done = true;
            return NULL;
        }
    }

    /* final output execution is in ps_ExprContext */
    econtext = winstate->ss.ps.ps_ExprContext;

    /* Clear the per-output-tuple context for current row */
    ResetExprContext(econtext);

    /*
     * Read the current row from the tuplestore, and save in ScanTupleSlot.
     * (We can't rely on the outerplan's output slot because we may have to
     * read beyond the current row.  Also, we have to actually copy the row
     * out of the tuplestore, since window function evaluation might cause the
     * tuplestore to dump its state to disk.)
     *
     * Current row must be in the tuplestore, since we spooled it above.
     */
    tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
    if (!tuplestore_gettupleslot(winstate->buffer, true, true,
                                 winstate->ss.ss_ScanTupleSlot))
        elog(ERROR, "unexpected end of tuplestore");

    /*
     * Evaluate true window functions
     */
    numfuncs = winstate->numfuncs;
    for (i = 0; i < numfuncs; i++)
    {
        WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);

        if (perfuncstate->plain_agg)
            continue;
        eval_windowfunction(winstate, perfuncstate,
              &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
              &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
    }

    /*
     * Evaluate aggregates
     */
    if (winstate->numaggs > 0)
        eval_windowaggregates(winstate);

    /*
     * Truncate any no-longer-needed rows from the tuplestore.
     */
    tuplestore_trim(winstate->buffer);

    /*
     * Form and return a projection tuple using the windowfunc results and the
     * current row.  Setting ecxt_outertuple arranges that any Vars will be
     * evaluated with respect to that row.
     */
    econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
    result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone);

    if (isDone == ExprEndResult)
    {
        /* SRF in tlist returned no rows, so advance to next input tuple */
        goto restart;
    }

    winstate->ss.ps.ps_TupFromTlist =
        (isDone == ExprMultipleResult);
    return result;
}