/*-------------------------------------------------------------------------
*
* nodeWindowAgg.c
* routines to handle WindowAgg nodes.
*
* A WindowAgg node evaluates "window functions" across suitable partitions
* of the input tuple set. Any one WindowAgg works for just a single window
* specification, though it can evaluate multiple window functions sharing
* identical window specifications. The input tuples are required to be
* delivered in sorted order, with the PARTITION BY columns (if any) as
* major sort keys and the ORDER BY columns (if any) as minor sort keys.
* (The planner generates a stack of WindowAggs with intervening Sort nodes
* as needed, if a query involves more than one window specification.)
*
* Since window functions can require access to any or all of the rows in
* the current partition, we accumulate rows of the partition into a
* tuplestore. The window functions are called using the WindowObject API
* so that they can access those rows as needed.
*
* We also support using plain aggregate functions as window functions.
* For these, the regular Agg-node environment is emulated for each partition.
* As required by the SQL spec, the output represents the value of the
* aggregate function over all rows in the current row's window frame.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/executor/nodeWindowAgg.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "windowapi.h"
/*
* All the window function APIs are called with this object, which is passed
* to window functions as fcinfo->context.
*/
typedef struct WindowObjectData
{
NodeTag type;
WindowAggState *winstate; /* parent WindowAggState */
List *argstates; /* ExprState trees for fn's arguments */
void *localmem; /* WinGetPartitionLocalMemory's chunk */
int markptr; /* tuplestore mark pointer for this fn */
int readptr; /* tuplestore read pointer for this fn */
int64 markpos; /* row that markptr is positioned on */
int64 seekpos; /* row that readptr is positioned on */
} WindowObjectData;
/*
* We have one WindowStatePerFunc struct for each window function and
* window aggregate handled by this node.
*/
typedef struct WindowStatePerFuncData
{
/* Links to WindowFunc expr and state nodes this working state is for */
WindowFuncExprState *wfuncstate;
WindowFunc *wfunc;
int numArguments; /* number of arguments */
FmgrInfo flinfo; /* fmgr lookup data for window function */
Oid winCollation; /* collation derived for window function */
/*
* We need the len and byval info for the result of each function in order
* to know how to copy/delete values.
*/
int16 resulttypeLen;
bool resulttypeByVal;
bool plain_agg; /* is it just a plain aggregate function? */
int aggno; /* if so, index of its PerAggData */
WindowObject winobj; /* object used in window function API */
} WindowStatePerFuncData;
/*
* For plain aggregate window functions, we also have one of these.
*/
typedef struct WindowStatePerAggData
{
/* Oids of transition functions */
Oid transfn_oid;
Oid invtransfn_oid; /* may be InvalidOid */
Oid finalfn_oid; /* may be InvalidOid */
/*
* fmgr lookup data for transition functions --- only valid when
* corresponding oid is not InvalidOid. Note in particular that fn_strict
* flags are kept here.
*/
FmgrInfo transfn;
FmgrInfo invtransfn;
FmgrInfo finalfn;
int numFinalArgs; /* number of arguments to pass to finalfn */
/*
* initial value from pg_aggregate entry
*/
Datum initValue;
bool initValueIsNull;
/*
* cached value for current frame boundaries
*/
Datum resultValue;
bool resultValueIsNull;
/*
* We need the len and byval info for the agg's input, result, and
* transition data types in order to know how to copy/delete values.
*/
int16 inputtypeLen,
resulttypeLen,
transtypeLen;
bool inputtypeByVal,
resulttypeByVal,
transtypeByVal;
int wfuncno; /* index of associated PerFuncData */
/* Context holding transition value and possibly other subsidiary data */
MemoryContext aggcontext; /* may be private, or winstate->aggcontext */
/* Current transition value */
Datum transValue; /* current transition value */
bool transValueIsNull;
int64 transValueCount; /* number of currently-aggregated rows */
/* Data local to eval_windowaggregates() */
bool restart; /* need to restart this agg in this cycle? */
} WindowStatePerAggData;
static void initialize_windowaggregate(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate);
static void advance_windowaggregate(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate);
static bool advance_windowaggregate_base(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate);
static void finalize_windowaggregate(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate,
Datum *result, bool *isnull);
static void eval_windowaggregates(WindowAggState *winstate);
static void eval_windowfunction(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
Datum *result, bool *isnull);
static void begin_partition(WindowAggState *winstate);
static void spool_tuples(WindowAggState *winstate, int64 pos);
static void release_partition(WindowAggState *winstate);
static bool row_is_in_frame(WindowAggState *winstate, int64 pos,
TupleTableSlot *slot);
static void update_frameheadpos(WindowObject winobj, TupleTableSlot *slot);
static void update_frametailpos(WindowObject winobj, TupleTableSlot *slot);
static WindowStatePerAggData *initialize_peragg(WindowAggState *winstate,
WindowFunc *wfunc,
WindowStatePerAgg peraggstate);
static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
TupleTableSlot *slot2);
static bool window_gettupleslot(WindowObject winobj, int64 pos,
TupleTableSlot *slot);
/*
* initialize_windowaggregate
* parallel to initialize_aggregates in nodeAgg.c
*/
static void
initialize_windowaggregate(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate)
{
MemoryContext oldContext;
/*
* If we're using a private aggcontext, we may reset it here. But if the
* context is shared, we don't know which other aggregates may still need
* it, so we must leave it to the caller to reset at an appropriate time.
*/
if (peraggstate->aggcontext != winstate->aggcontext)
MemoryContextResetAndDeleteChildren(peraggstate->aggcontext);
if (peraggstate->initValueIsNull)
peraggstate->transValue = peraggstate->initValue;
else
{
oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
peraggstate->transValue = datumCopy(peraggstate->initValue,
peraggstate->transtypeByVal,
peraggstate->transtypeLen);
MemoryContextSwitchTo(oldContext);
}
peraggstate->transValueIsNull = peraggstate->initValueIsNull;
peraggstate->transValueCount = 0;
peraggstate->resultValue = (Datum) 0;
peraggstate->resultValueIsNull = true;
}
/*
* advance_windowaggregate
* parallel to advance_aggregates in nodeAgg.c
*/
static void
advance_windowaggregate(WindowAggState *winstate,
WindowStatePerFunc perfuncstate,
WindowStatePerAgg peraggstate)
{
WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
int numArguments = perfuncstate->numArguments;
FunctionCallInfoData fcinfodata;
FunctionCallInfo fcinfo = &fcinfodata;
Datum newVal;
|