PostgreSQL Source Code  git master
nodeWindowAgg.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 "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/regproc.h"
#include "utils/syscache.h"
#include "windowapi.h"
Include dependency graph for nodeWindowAgg.c:

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

Typedefs

typedef struct WindowObjectData WindowObjectData
 
typedef struct WindowStatePerFuncData WindowStatePerFuncData
 
typedef struct WindowStatePerAggData WindowStatePerAggData
 

Functions

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 int row_is_in_frame (WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
 
static void update_frameheadpos (WindowAggState *winstate)
 
static void update_frametailpos (WindowAggState *winstate)
 
static void update_grouptailpos (WindowAggState *winstate)
 
static WindowStatePerAggDatainitialize_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)
 
static TupleTableSlotExecWindowAgg (PlanState *pstate)
 
WindowAggStateExecInitWindowAgg (WindowAgg *node, EState *estate, int eflags)
 
void ExecEndWindowAgg (WindowAggState *node)
 
void ExecReScanWindowAgg (WindowAggState *node)
 
void * WinGetPartitionLocalMemory (WindowObject winobj, Size sz)
 
int64 WinGetCurrentPosition (WindowObject winobj)
 
int64 WinGetPartitionRowCount (WindowObject winobj)
 
void WinSetMarkPosition (WindowObject winobj, int64 markpos)
 
bool WinRowsArePeers (WindowObject winobj, int64 pos1, int64 pos2)
 
Datum WinGetFuncArgInPartition (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgInFrame (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgCurrent (WindowObject winobj, int argno, bool *isnull)
 

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

static void advance_windowaggregate ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate 
)
static

Definition at line 240 of file nodeWindowAgg.c.

References WindowStatePerAggData::aggcontext, WindowFuncExprState::aggfilter, arg, FunctionCallInfoData::arg, FunctionCallInfoData::argnull, WindowFuncExprState::args, Assert, CurrentMemoryContext, datumCopy(), DatumGetBool, DatumGetEOHP(), DatumGetPointer, DatumIsReadWriteExpandedObject, DeleteExpandedObject(), ExprContext::ecxt_per_tuple_memory, ereport, errcode(), errmsg(), ERROR, ExecEvalExpr(), FmgrInfo::fn_strict, FunctionCallInvoke, i, InitFunctionCallInfoData, WindowStatePerAggData::invtransfn_oid, FunctionCallInfoData::isnull, lfirst, MemoryContextGetParent(), MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, OidIsValid, pfree(), WindowAggState::tmpcontext, WindowStatePerAggData::transfn, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueCount, WindowStatePerAggData::transValueIsNull, WindowStatePerFuncData::wfuncstate, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

243 {
244  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
245  int numArguments = perfuncstate->numArguments;
246  FunctionCallInfoData fcinfodata;
247  FunctionCallInfo fcinfo = &fcinfodata;
248  Datum newVal;
249  ListCell *arg;
250  int i;
251  MemoryContext oldContext;
252  ExprContext *econtext = winstate->tmpcontext;
253  ExprState *filter = wfuncstate->aggfilter;
254 
255  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
256 
257  /* Skip anything FILTERed out */
258  if (filter)
259  {
260  bool isnull;
261  Datum res = ExecEvalExpr(filter, econtext, &isnull);
262 
263  if (isnull || !DatumGetBool(res))
264  {
265  MemoryContextSwitchTo(oldContext);
266  return;
267  }
268  }
269 
270  /* We start from 1, since the 0th arg will be the transition value */
271  i = 1;
272  foreach(arg, wfuncstate->args)
273  {
274  ExprState *argstate = (ExprState *) lfirst(arg);
275 
276  fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
277  &fcinfo->argnull[i]);
278  i++;
279  }
280 
281  if (peraggstate->transfn.fn_strict)
282  {
283  /*
284  * For a strict transfn, nothing happens when there's a NULL input; we
285  * just keep the prior transValue. Note transValueCount doesn't
286  * change either.
287  */
288  for (i = 1; i <= numArguments; i++)
289  {
290  if (fcinfo->argnull[i])
291  {
292  MemoryContextSwitchTo(oldContext);
293  return;
294  }
295  }
296 
297  /*
298  * For strict transition functions with initial value NULL we use the
299  * first non-NULL input as the initial state. (We already checked
300  * that the agg's input type is binary-compatible with its transtype,
301  * so straight copy here is OK.)
302  *
303  * We must copy the datum into aggcontext if it is pass-by-ref. We do
304  * not need to pfree the old transValue, since it's NULL.
305  */
306  if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
307  {
308  MemoryContextSwitchTo(peraggstate->aggcontext);
309  peraggstate->transValue = datumCopy(fcinfo->arg[1],
310  peraggstate->transtypeByVal,
311  peraggstate->transtypeLen);
312  peraggstate->transValueIsNull = false;
313  peraggstate->transValueCount = 1;
314  MemoryContextSwitchTo(oldContext);
315  return;
316  }
317 
318  if (peraggstate->transValueIsNull)
319  {
320  /*
321  * Don't call a strict function with NULL inputs. Note it is
322  * possible to get here despite the above tests, if the transfn is
323  * strict *and* returned a NULL on a prior cycle. If that happens
324  * we will propagate the NULL all the way to the end. That can
325  * only happen if there's no inverse transition function, though,
326  * since we disallow transitions back to NULL when there is one.
327  */
328  MemoryContextSwitchTo(oldContext);
329  Assert(!OidIsValid(peraggstate->invtransfn_oid));
330  return;
331  }
332  }
333 
334  /*
335  * OK to call the transition function. Set winstate->curaggcontext while
336  * calling it, for possible use by AggCheckCallContext.
337  */
338  InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
339  numArguments + 1,
340  perfuncstate->winCollation,
341  (void *) winstate, NULL);
342  fcinfo->arg[0] = peraggstate->transValue;
343  fcinfo->argnull[0] = peraggstate->transValueIsNull;
344  winstate->curaggcontext = peraggstate->aggcontext;
345  newVal = FunctionCallInvoke(fcinfo);
346  winstate->curaggcontext = NULL;
347 
348  /*
349  * Moving-aggregate transition functions must not return null, see
350  * advance_windowaggregate_base().
351  */
352  if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
353  ereport(ERROR,
354  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
355  errmsg("moving-aggregate transition function must not return null")));
356 
357  /*
358  * We must track the number of rows included in transValue, since to
359  * remove the last input, advance_windowaggregate_base() mustn't call the
360  * inverse transition function, but simply reset transValue back to its
361  * initial value.
362  */
363  peraggstate->transValueCount++;
364 
365  /*
366  * If pass-by-ref datatype, must copy the new value into aggcontext and
367  * free the prior transValue. But if transfn returned a pointer to its
368  * first input, we don't need to do anything. Also, if transfn returned a
369  * pointer to a R/W expanded object that is already a child of the
370  * aggcontext, assume we can adopt that value without copying it.
371  */
372  if (!peraggstate->transtypeByVal &&
373  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
374  {
375  if (!fcinfo->isnull)
376  {
377  MemoryContextSwitchTo(peraggstate->aggcontext);
379  false,
380  peraggstate->transtypeLen) &&
382  /* do nothing */ ;
383  else
384  newVal = datumCopy(newVal,
385  peraggstate->transtypeByVal,
386  peraggstate->transtypeLen);
387  }
388  if (!peraggstate->transValueIsNull)
389  {
391  false,
392  peraggstate->transtypeLen))
393  DeleteExpandedObject(peraggstate->transValue);
394  else
395  pfree(DatumGetPointer(peraggstate->transValue));
396  }
397  }
398 
399  MemoryContextSwitchTo(oldContext);
400  peraggstate->transValue = newVal;
401  peraggstate->transValueIsNull = fcinfo->isnull;
402 }
MemoryContext curaggcontext
Definition: execnodes.h:2000
ExprState * aggfilter
Definition: execnodes.h:733
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:439
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:226
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:575
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:79
#define OidIsValid(objectId)
Definition: c.h:605
ExprContext * tmpcontext
Definition: execnodes.h:2001
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ERROR
Definition: elog.h:43
bool fn_strict
Definition: fmgr.h:61
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:142
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
#define DatumGetBool(X)
Definition: postgres.h:378
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define ereport(elevel, rest)
Definition: elog.h:122
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
uintptr_t Datum
Definition: postgres.h:367
void DeleteExpandedObject(Datum d)
Datum arg[FUNC_MAX_ARGS]
Definition: fmgr.h:87
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:125
MemoryContext aggcontext
#define DatumGetPointer(X)
Definition: postgres.h:534
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
void * arg

◆ advance_windowaggregate_base()

static bool advance_windowaggregate_base ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate 
)
static

Definition at line 417 of file nodeWindowAgg.c.

References WindowStatePerAggData::aggcontext, WindowFuncExprState::aggfilter, arg, FunctionCallInfoData::arg, FunctionCallInfoData::argnull, WindowFuncExprState::args, Assert, CurrentMemoryContext, datumCopy(), DatumGetBool, DatumGetEOHP(), DatumGetPointer, DatumIsReadWriteExpandedObject, DeleteExpandedObject(), ExprContext::ecxt_per_tuple_memory, elog, ERROR, ExecEvalExpr(), FmgrInfo::fn_strict, FunctionCallInvoke, i, InitFunctionCallInfoData, initialize_windowaggregate(), WindowStatePerAggData::invtransfn, FunctionCallInfoData::isnull, lfirst, MemoryContextGetParent(), MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, WindowAggState::perfunc, pfree(), WindowAggState::tmpcontext, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueCount, WindowStatePerAggData::transValueIsNull, WindowStatePerAggData::wfuncno, WindowStatePerFuncData::wfuncstate, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

420 {
421  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
422  int numArguments = perfuncstate->numArguments;
423  FunctionCallInfoData fcinfodata;
424  FunctionCallInfo fcinfo = &fcinfodata;
425  Datum newVal;
426  ListCell *arg;
427  int i;
428  MemoryContext oldContext;
429  ExprContext *econtext = winstate->tmpcontext;
430  ExprState *filter = wfuncstate->aggfilter;
431 
432  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
433 
434  /* Skip anything FILTERed out */
435  if (filter)
436  {
437  bool isnull;
438  Datum res = ExecEvalExpr(filter, econtext, &isnull);
439 
440  if (isnull || !DatumGetBool(res))
441  {
442  MemoryContextSwitchTo(oldContext);
443  return true;
444  }
445  }
446 
447  /* We start from 1, since the 0th arg will be the transition value */
448  i = 1;
449  foreach(arg, wfuncstate->args)
450  {
451  ExprState *argstate = (ExprState *) lfirst(arg);
452 
453  fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
454  &fcinfo->argnull[i]);
455  i++;
456  }
457 
458  if (peraggstate->invtransfn.fn_strict)
459  {
460  /*
461  * For a strict (inv)transfn, nothing happens when there's a NULL
462  * input; we just keep the prior transValue. Note transValueCount
463  * doesn't change either.
464  */
465  for (i = 1; i <= numArguments; i++)
466  {
467  if (fcinfo->argnull[i])
468  {
469  MemoryContextSwitchTo(oldContext);
470  return true;
471  }
472  }
473  }
474 
475  /* There should still be an added but not yet removed value */
476  Assert(peraggstate->transValueCount > 0);
477 
478  /*
479  * In moving-aggregate mode, the state must never be NULL, except possibly
480  * before any rows have been aggregated (which is surely not the case at
481  * this point). This restriction allows us to interpret a NULL result
482  * from the inverse function as meaning "sorry, can't do an inverse
483  * transition in this case". We already checked this in
484  * advance_windowaggregate, but just for safety, check again.
485  */
486  if (peraggstate->transValueIsNull)
487  elog(ERROR, "aggregate transition value is NULL before inverse transition");
488 
489  /*
490  * We mustn't use the inverse transition function to remove the last
491  * input. Doing so would yield a non-NULL state, whereas we should be in
492  * the initial state afterwards which may very well be NULL. So instead,
493  * we simply re-initialize the aggregate in this case.
494  */
495  if (peraggstate->transValueCount == 1)
496  {
497  MemoryContextSwitchTo(oldContext);
499  &winstate->perfunc[peraggstate->wfuncno],
500  peraggstate);
501  return true;
502  }
503 
504  /*
505  * OK to call the inverse transition function. Set
506  * winstate->curaggcontext while calling it, for possible use by
507  * AggCheckCallContext.
508  */
509  InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
510  numArguments + 1,
511  perfuncstate->winCollation,
512  (void *) winstate, NULL);
513  fcinfo->arg[0] = peraggstate->transValue;
514  fcinfo->argnull[0] = peraggstate->transValueIsNull;
515  winstate->curaggcontext = peraggstate->aggcontext;
516  newVal = FunctionCallInvoke(fcinfo);
517  winstate->curaggcontext = NULL;
518 
519  /*
520  * If the function returns NULL, report failure, forcing a restart.
521  */
522  if (fcinfo->isnull)
523  {
524  MemoryContextSwitchTo(oldContext);
525  return false;
526  }
527 
528  /* Update number of rows included in transValue */
529  peraggstate->transValueCount--;
530 
531  /*
532  * If pass-by-ref datatype, must copy the new value into aggcontext and
533  * free the prior transValue. But if invtransfn returned a pointer to its
534  * first input, we don't need to do anything. Also, if invtransfn
535  * returned a pointer to a R/W expanded object that is already a child of
536  * the aggcontext, assume we can adopt that value without copying it.
537  *
538  * Note: the checks for null values here will never fire, but it seems
539  * best to have this stanza look just like advance_windowaggregate.
540  */
541  if (!peraggstate->transtypeByVal &&
542  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
543  {
544  if (!fcinfo->isnull)
545  {
546  MemoryContextSwitchTo(peraggstate->aggcontext);
548  false,
549  peraggstate->transtypeLen) &&
551  /* do nothing */ ;
552  else
553  newVal = datumCopy(newVal,
554  peraggstate->transtypeByVal,
555  peraggstate->transtypeLen);
556  }
557  if (!peraggstate->transValueIsNull)
558  {
560  false,
561  peraggstate->transtypeLen))
562  DeleteExpandedObject(peraggstate->transValue);
563  else
564  pfree(DatumGetPointer(peraggstate->transValue));
565  }
566  }
567 
568  MemoryContextSwitchTo(oldContext);
569  peraggstate->transValue = newVal;
570  peraggstate->transValueIsNull = fcinfo->isnull;
571 
572  return true;
573 }
MemoryContext curaggcontext
Definition: execnodes.h:2000
ExprState * aggfilter
Definition: execnodes.h:733
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:439
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:226
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:79
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
ExprContext * tmpcontext
Definition: execnodes.h:2001
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ERROR
Definition: elog.h:43
bool fn_strict
Definition: fmgr.h:61
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:142
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
#define DatumGetBool(X)
Definition: postgres.h:378
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
uintptr_t Datum
Definition: postgres.h:367
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
void DeleteExpandedObject(Datum d)
Datum arg[FUNC_MAX_ARGS]
Definition: fmgr.h:87
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:125
MemoryContext aggcontext
#define DatumGetPointer(X)
Definition: postgres.h:534
int i
void * arg
#define elog
Definition: elog.h:219

◆ are_peers()

static bool are_peers ( WindowAggState winstate,
TupleTableSlot slot1,
TupleTableSlot slot2 
)
static

Definition at line 2875 of file nodeWindowAgg.c.

References ExprContext::ecxt_outertuple, ExecQualAndReset(), WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, and WindowAggState::tmpcontext.

Referenced by ExecWindowAgg(), row_is_in_frame(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), and WinRowsArePeers().

2877 {
2878  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2879  ExprContext *econtext = winstate->tmpcontext;
2880 
2881  /* If no ORDER BY, all rows are peers with each other */
2882  if (node->ordNumCols == 0)
2883  return true;
2884 
2885  econtext->ecxt_outertuple = slot1;
2886  econtext->ecxt_innertuple = slot2;
2887  return ExecQualAndReset(winstate->ordEqfunction, econtext);
2888 }
int ordNumCols
Definition: plannodes.h:820
ScanState ss
Definition: execnodes.h:1953
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
ExprState * ordEqfunction
Definition: execnodes.h:1963
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:388
Plan * plan
Definition: execnodes.h:912
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1078 of file nodeWindowAgg.c.

References WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggregatedbase, WindowAggState::aggregatedupto, WindowAggState::buffer, WindowAggState::current_ptr, WindowAggState::currentgroup, WindowAggState::currentpos, EXEC_FLAG_BACKWARD, ExecClearTuple(), ExecCopySlot(), ExecProcNode(), WindowAggState::first_part_slot, WindowAggState::framehead_ptr, WindowAggState::framehead_slot, WindowAggState::framehead_valid, WindowAggState::frameheadgroup, WindowAggState::frameheadpos, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_slot, WindowAggState::frametail_valid, WindowAggState::frametailgroup, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, i, WindowObjectData::markpos, WindowObjectData::markptr, WindowAggState::more_partitions, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAgg::ordNumCols, outerPlan, outerPlanState, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, PlanState::plan, ScanState::ps, WindowObjectData::readptr, WindowObjectData::seekpos, WindowAggState::spooled_rows, WindowAggState::ss, TupIsNull, tuplestore_alloc_read_pointer(), tuplestore_begin_heap(), tuplestore_puttupleslot(), tuplestore_set_eflags(), WindowStatePerFuncData::winobj, and work_mem.

Referenced by ExecWindowAgg().

1079 {
1080  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1081  PlanState *outerPlan = outerPlanState(winstate);
1082  int numfuncs = winstate->numfuncs;
1083  int i;
1084 
1085  winstate->partition_spooled = false;
1086  winstate->framehead_valid = false;
1087  winstate->frametail_valid = false;
1088  winstate->grouptail_valid = false;
1089  winstate->spooled_rows = 0;
1090  winstate->currentpos = 0;
1091  winstate->frameheadpos = 0;
1092  winstate->frametailpos = 0;
1093  winstate->currentgroup = 0;
1094  winstate->frameheadgroup = 0;
1095  winstate->frametailgroup = 0;
1096  winstate->groupheadpos = 0;
1097  winstate->grouptailpos = -1; /* see update_grouptailpos */
1098  ExecClearTuple(winstate->agg_row_slot);
1099  if (winstate->framehead_slot)
1100  ExecClearTuple(winstate->framehead_slot);
1101  if (winstate->frametail_slot)
1102  ExecClearTuple(winstate->frametail_slot);
1103 
1104  /*
1105  * If this is the very first partition, we need to fetch the first input
1106  * row to store in first_part_slot.
1107  */
1108  if (TupIsNull(winstate->first_part_slot))
1109  {
1110  TupleTableSlot *outerslot = ExecProcNode(outerPlan);
1111 
1112  if (!TupIsNull(outerslot))
1113  ExecCopySlot(winstate->first_part_slot, outerslot);
1114  else
1115  {
1116  /* outer plan is empty, so we have nothing to do */
1117  winstate->partition_spooled = true;
1118  winstate->more_partitions = false;
1119  return;
1120  }
1121  }
1122 
1123  /* Create new tuplestore for this partition */
1124  winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1125 
1126  /*
1127  * Set up read pointers for the tuplestore. The current pointer doesn't
1128  * need BACKWARD capability, but the per-window-function read pointers do,
1129  * and the aggregate pointer does if we might need to restart aggregation.
1130  */
1131  winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1132 
1133  /* reset default REWIND capability bit for current ptr */
1134  tuplestore_set_eflags(winstate->buffer, 0);
1135 
1136  /* create read pointers for aggregates, if needed */
1137  if (winstate->numaggs > 0)
1138  {
1139  WindowObject agg_winobj = winstate->agg_winobj;
1140  int readptr_flags = 0;
1141 
1142  /*
1143  * If the frame head is potentially movable, or we have an EXCLUSION
1144  * clause, we might need to restart aggregation ...
1145  */
1147  (winstate->frameOptions & FRAMEOPTION_EXCLUSION))
1148  {
1149  /* ... so create a mark pointer to track the frame head */
1150  agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1151  /* and the read pointer will need BACKWARD capability */
1152  readptr_flags |= EXEC_FLAG_BACKWARD;
1153  }
1154 
1155  agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1156  readptr_flags);
1157  agg_winobj->markpos = -1;
1158  agg_winobj->seekpos = -1;
1159 
1160  /* Also reset the row counters for aggregates */
1161  winstate->aggregatedbase = 0;
1162  winstate->aggregatedupto = 0;
1163  }
1164 
1165  /* create mark and read pointers for each real window function */
1166  for (i = 0; i < numfuncs; i++)
1167  {
1168  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1169 
1170  if (!perfuncstate->plain_agg)
1171  {
1172  WindowObject winobj = perfuncstate->winobj;
1173 
1174  winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1175  0);
1176  winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1178  winobj->markpos = -1;
1179  winobj->seekpos = -1;
1180  }
1181  }
1182 
1183  /*
1184  * If we are in RANGE or GROUPS mode, then determining frame boundaries
1185  * requires physical access to the frame endpoint rows, except in
1186  * degenerate cases. We create read pointers to point to those rows, to
1187  * simplify access and ensure that the tuplestore doesn't discard the
1188  * endpoint rows prematurely. (Must match logic in update_frameheadpos
1189  * and update_frametailpos.)
1190  */
1191  winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1192 
1193  if ((winstate->frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS)) &&
1194  node->ordNumCols != 0)
1195  {
1197  winstate->framehead_ptr =
1198  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1200  winstate->frametail_ptr =
1201  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1202  }
1203 
1204  /*
1205  * If we have an exclusion clause that requires knowing the boundaries of
1206  * the current row's peer group, we create a read pointer to track the
1207  * tail position of the peer group (i.e., first row of the next peer
1208  * group). The head position does not require its own pointer because we
1209  * maintain that as a side effect of advancing the current row.
1210  */
1211  winstate->grouptail_ptr = -1;
1212 
1213  if ((winstate->frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1215  node->ordNumCols != 0)
1216  {
1217  winstate->grouptail_ptr =
1218  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1219  }
1220 
1221  /*
1222  * Store the first tuple into the tuplestore (it's always available now;
1223  * we either read it above, or saved it at the end of previous partition)
1224  */
1225  tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1226  winstate->spooled_rows++;
1227 }
int ordNumCols
Definition: plannodes.h:820
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:528
ScanState ss
Definition: execnodes.h:1953
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:359
int64 groupheadpos
Definition: execnodes.h:1995
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
int64 currentgroup
Definition: execnodes.h:1992
int64 aggregatedupto
Definition: execnodes.h:1976
bool frametail_valid
Definition: execnodes.h:2011
int64 frametailgroup
Definition: execnodes.h:1994
int64 grouptailpos
Definition: execnodes.h:1996
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:510
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
PlanState ps
Definition: execnodes.h:1192
struct WindowObjectData * agg_winobj
Definition: execnodes.h:1974
int64 frameheadpos
Definition: execnodes.h:1971
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:521
#define EXEC_FLAG_BACKWARD
Definition: executor.h:60
#define outerPlanState(node)
Definition: execnodes.h:966
int64 frameheadgroup
Definition: execnodes.h:1993
int64 aggregatedbase
Definition: execnodes.h:1975
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:513
#define TupIsNull(slot)
Definition: tuptable.h:146
Tuplestorestate * buffer
Definition: execnodes.h:1964
#define outerPlan(node)
Definition: plannodes.h:176
int64 spooled_rows
Definition: execnodes.h:1969
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:233
int work_mem
Definition: globals.c:122
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
Plan * plan
Definition: execnodes.h:912
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
bool more_partitions
Definition: execnodes.h:2007
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
int64 frametailpos
Definition: execnodes.h:1972
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:522
bool grouptail_valid
Definition: execnodes.h:2013
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018
int64 currentpos
Definition: execnodes.h:1970
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:383
bool partition_spooled
Definition: execnodes.h:2005
bool framehead_valid
Definition: execnodes.h:2009
int i
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 662 of file nodeWindowAgg.c.

References advance_windowaggregate(), advance_windowaggregate_base(), WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::aggregatedbase, WindowAggState::aggregatedupto, Assert, WindowAggState::currentpos, datumCopy(), DatumGetPointer, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, elog, ERROR, ExecClearTuple(), finalize_windowaggregate(), WindowAggState::frameheadpos, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_EXCLUSION, WindowAggState::frameOptions, i, initialize_windowaggregate(), WindowStatePerAggData::invtransfn_oid, WindowObjectData::markptr, MemoryContextResetAndDeleteChildren, MemoryContextSwitchTo(), WindowAggState::numaggs, OidIsValid, WindowAggState::peragg, WindowAggState::perfunc, pfree(), ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, WindowStatePerAggData::restart, WindowStatePerAggData::resulttypeByVal, WindowStatePerAggData::resulttypeLen, WindowStatePerAggData::resultValue, WindowStatePerAggData::resultValueIsNull, row_is_in_frame(), WindowAggState::ss, WindowAggState::temp_slot_1, WindowAggState::tmpcontext, TupIsNull, update_frameheadpos(), WindowStatePerAggData::wfuncno, window_gettupleslot(), and WinSetMarkPosition().

Referenced by ExecWindowAgg().

663 {
664  WindowStatePerAgg peraggstate;
665  int wfuncno,
666  numaggs,
667  numaggs_restart,
668  i;
669  int64 aggregatedupto_nonrestarted;
670  MemoryContext oldContext;
671  ExprContext *econtext;
672  WindowObject agg_winobj;
673  TupleTableSlot *agg_row_slot;
674  TupleTableSlot *temp_slot;
675 
676  numaggs = winstate->numaggs;
677  if (numaggs == 0)
678  return; /* nothing to do */
679 
680  /* final output execution is in ps_ExprContext */
681  econtext = winstate->ss.ps.ps_ExprContext;
682  agg_winobj = winstate->agg_winobj;
683  agg_row_slot = winstate->agg_row_slot;
684  temp_slot = winstate->temp_slot_1;
685 
686  /*
687  * If the window's frame start clause is UNBOUNDED_PRECEDING and no
688  * exclusion clause is specified, then the window frame consists of a
689  * contiguous group of rows extending forward from the start of the
690  * partition, and rows only enter the frame, never exit it, as the current
691  * row advances forward. This makes it possible to use an incremental
692  * strategy for evaluating aggregates: we run the transition function for
693  * each row added to the frame, and run the final function whenever we
694  * need the current aggregate value. This is considerably more efficient
695  * than the naive approach of re-running the entire aggregate calculation
696  * for each current row. It does assume that the final function doesn't
697  * damage the running transition value, but we have the same assumption in
698  * nodeAgg.c too (when it rescans an existing hash table).
699  *
700  * If the frame start does sometimes move, we can still optimize as above
701  * whenever successive rows share the same frame head, but if the frame
702  * head moves beyond the previous head we try to remove those rows using
703  * the aggregate's inverse transition function. This function restores
704  * the aggregate's current state to what it would be if the removed row
705  * had never been aggregated in the first place. Inverse transition
706  * functions may optionally return NULL, indicating that the function was
707  * unable to remove the tuple from aggregation. If this happens, or if
708  * the aggregate doesn't have an inverse transition function at all, we
709  * must perform the aggregation all over again for all tuples within the
710  * new frame boundaries.
711  *
712  * If there's any exclusion clause, then we may have to aggregate over a
713  * non-contiguous set of rows, so we punt and recalculate for every row.
714  * (For some frame end choices, it might be that the frame is always
715  * contiguous anyway, but that's an optimization to investigate later.)
716  *
717  * In many common cases, multiple rows share the same frame and hence the
718  * same aggregate value. (In particular, if there's no ORDER BY in a RANGE
719  * window, then all rows are peers and so they all have window frame equal
720  * to the whole partition.) We optimize such cases by calculating the
721  * aggregate value once when we reach the first row of a peer group, and
722  * then returning the saved value for all subsequent rows.
723  *
724  * 'aggregatedupto' keeps track of the first row that has not yet been
725  * accumulated into the aggregate transition values. Whenever we start a
726  * new peer group, we accumulate forward to the end of the peer group.
727  */
728 
729  /*
730  * First, update the frame head position.
731  *
732  * The frame head should never move backwards, and the code below wouldn't
733  * cope if it did, so for safety we complain if it does.
734  */
735  update_frameheadpos(winstate);
736  if (winstate->frameheadpos < winstate->aggregatedbase)
737  elog(ERROR, "window frame head moved backward");
738 
739  /*
740  * If the frame didn't change compared to the previous row, we can re-use
741  * the result values that were previously saved at the bottom of this
742  * function. Since we don't know the current frame's end yet, this is not
743  * possible to check for fully. But if the frame end mode is UNBOUNDED
744  * FOLLOWING or CURRENT ROW, no exclusion clause is specified, and the
745  * current row lies within the previous row's frame, then the two frames'
746  * ends must coincide. Note that on the first row aggregatedbase ==
747  * aggregatedupto, meaning this test must fail, so we don't need to check
748  * the "there was no previous row" case explicitly here.
749  */
750  if (winstate->aggregatedbase == winstate->frameheadpos &&
753  !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
754  winstate->aggregatedbase <= winstate->currentpos &&
755  winstate->aggregatedupto > winstate->currentpos)
756  {
757  for (i = 0; i < numaggs; i++)
758  {
759  peraggstate = &winstate->peragg[i];
760  wfuncno = peraggstate->wfuncno;
761  econtext->ecxt_aggvalues[wfuncno] = peraggstate->resultValue;
762  econtext->ecxt_aggnulls[wfuncno] = peraggstate->resultValueIsNull;
763  }
764  return;
765  }
766 
767  /*----------
768  * Initialize restart flags.
769  *
770  * We restart the aggregation:
771  * - if we're processing the first row in the partition, or
772  * - if the frame's head moved and we cannot use an inverse
773  * transition function, or
774  * - we have an EXCLUSION clause, or
775  * - if the new frame doesn't overlap the old one
776  *
777  * Note that we don't strictly need to restart in the last case, but if
778  * we're going to remove all rows from the aggregation anyway, a restart
779  * surely is faster.
780  *----------
781  */
782  numaggs_restart = 0;
783  for (i = 0; i < numaggs; i++)
784  {
785  peraggstate = &winstate->peragg[i];
786  if (winstate->currentpos == 0 ||
787  (winstate->aggregatedbase != winstate->frameheadpos &&
788  !OidIsValid(peraggstate->invtransfn_oid)) ||
789  (winstate->frameOptions & FRAMEOPTION_EXCLUSION) ||
790  winstate->aggregatedupto <= winstate->frameheadpos)
791  {
792  peraggstate->restart = true;
793  numaggs_restart++;
794  }
795  else
796  peraggstate->restart = false;
797  }
798 
799  /*
800  * If we have any possibly-moving aggregates, attempt to advance
801  * aggregatedbase to match the frame's head by removing input rows that
802  * fell off the top of the frame from the aggregations. This can fail,
803  * i.e. advance_windowaggregate_base() can return false, in which case
804  * we'll restart that aggregate below.
805  */
806  while (numaggs_restart < numaggs &&
807  winstate->aggregatedbase < winstate->frameheadpos)
808  {
809  /*
810  * Fetch the next tuple of those being removed. This should never fail
811  * as we should have been here before.
812  */
813  if (!window_gettupleslot(agg_winobj, winstate->aggregatedbase,
814  temp_slot))
815  elog(ERROR, "could not re-fetch previously fetched frame row");
816 
817  /* Set tuple context for evaluation of aggregate arguments */
818  winstate->tmpcontext->ecxt_outertuple = temp_slot;
819 
820  /*
821  * Perform the inverse transition for each aggregate function in the
822  * window, unless it has already been marked as needing a restart.
823  */
824  for (i = 0; i < numaggs; i++)
825  {
826  bool ok;
827 
828  peraggstate = &winstate->peragg[i];
829  if (peraggstate->restart)
830  continue;
831 
832  wfuncno = peraggstate->wfuncno;
833  ok = advance_windowaggregate_base(winstate,
834  &winstate->perfunc[wfuncno],
835  peraggstate);
836  if (!ok)
837  {
838  /* Inverse transition function has failed, must restart */
839  peraggstate->restart = true;
840  numaggs_restart++;
841  }
842  }
843 
844  /* Reset per-input-tuple context after each tuple */
845  ResetExprContext(winstate->tmpcontext);
846 
847  /* And advance the aggregated-row state */
848  winstate->aggregatedbase++;
849  ExecClearTuple(temp_slot);
850  }
851 
852  /*
853  * If we successfully advanced the base rows of all the aggregates,
854  * aggregatedbase now equals frameheadpos; but if we failed for any, we
855  * must forcibly update aggregatedbase.
856  */
857  winstate->aggregatedbase = winstate->frameheadpos;
858 
859  /*
860  * If we created a mark pointer for aggregates, keep it pushed up to frame
861  * head, so that tuplestore can discard unnecessary rows.
862  */
863  if (agg_winobj->markptr >= 0)
864  WinSetMarkPosition(agg_winobj, winstate->frameheadpos);
865 
866  /*
867  * Now restart the aggregates that require it.
868  *
869  * We assume that aggregates using the shared context always restart if
870  * *any* aggregate restarts, and we may thus clean up the shared
871  * aggcontext if that is the case. Private aggcontexts are reset by
872  * initialize_windowaggregate() if their owning aggregate restarts. If we
873  * aren't restarting an aggregate, we need to free any previously saved
874  * result for it, else we'll leak memory.
875  */
876  if (numaggs_restart > 0)
878  for (i = 0; i < numaggs; i++)
879  {
880  peraggstate = &winstate->peragg[i];
881 
882  /* Aggregates using the shared ctx must restart if *any* agg does */
883  Assert(peraggstate->aggcontext != winstate->aggcontext ||
884  numaggs_restart == 0 ||
885  peraggstate->restart);
886 
887  if (peraggstate->restart)
888  {
889  wfuncno = peraggstate->wfuncno;
891  &winstate->perfunc[wfuncno],
892  peraggstate);
893  }
894  else if (!peraggstate->resultValueIsNull)
895  {
896  if (!peraggstate->resulttypeByVal)
897  pfree(DatumGetPointer(peraggstate->resultValue));
898  peraggstate->resultValue = (Datum) 0;
899  peraggstate->resultValueIsNull = true;
900  }
901  }
902 
903  /*
904  * Non-restarted aggregates now contain the rows between aggregatedbase
905  * (i.e., frameheadpos) and aggregatedupto, while restarted aggregates
906  * contain no rows. If there are any restarted aggregates, we must thus
907  * begin aggregating anew at frameheadpos, otherwise we may simply
908  * continue at aggregatedupto. We must remember the old value of
909  * aggregatedupto to know how long to skip advancing non-restarted
910  * aggregates. If we modify aggregatedupto, we must also clear
911  * agg_row_slot, per the loop invariant below.
912  */
913  aggregatedupto_nonrestarted = winstate->aggregatedupto;
914  if (numaggs_restart > 0 &&
915  winstate->aggregatedupto != winstate->frameheadpos)
916  {
917  winstate->aggregatedupto = winstate->frameheadpos;
918  ExecClearTuple(agg_row_slot);
919  }
920 
921  /*
922  * Advance until we reach a row not in frame (or end of partition).
923  *
924  * Note the loop invariant: agg_row_slot is either empty or holds the row
925  * at position aggregatedupto. We advance aggregatedupto after processing
926  * a row.
927  */
928  for (;;)
929  {
930  int ret;
931 
932  /* Fetch next row if we didn't already */
933  if (TupIsNull(agg_row_slot))
934  {
935  if (!window_gettupleslot(agg_winobj, winstate->aggregatedupto,
936  agg_row_slot))
937  break; /* must be end of partition */
938  }
939 
940  /*
941  * Exit loop if no more rows can be in frame. Skip aggregation if
942  * current row is not in frame but there might be more in the frame.
943  */
944  ret = row_is_in_frame(winstate, winstate->aggregatedupto, agg_row_slot);
945  if (ret < 0)
946  break;
947  if (ret == 0)
948  goto next_tuple;
949 
950  /* Set tuple context for evaluation of aggregate arguments */
951  winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
952 
953  /* Accumulate row into the aggregates */
954  for (i = 0; i < numaggs; i++)
955  {
956  peraggstate = &winstate->peragg[i];
957 
958  /* Non-restarted aggs skip until aggregatedupto_nonrestarted */
959  if (!peraggstate->restart &&
960  winstate->aggregatedupto < aggregatedupto_nonrestarted)
961  continue;
962 
963  wfuncno = peraggstate->wfuncno;
964  advance_windowaggregate(winstate,
965  &winstate->perfunc[wfuncno],
966  peraggstate);
967  }
968 
969 next_tuple:
970  /* Reset per-input-tuple context after each tuple */
971  ResetExprContext(winstate->tmpcontext);
972 
973  /* And advance the aggregated-row state */
974  winstate->aggregatedupto++;
975  ExecClearTuple(agg_row_slot);
976  }
977 
978  /* The frame's end is not supposed to move backwards, ever */
979  Assert(aggregatedupto_nonrestarted <= winstate->aggregatedupto);
980 
981  /*
982  * finalize aggregates and fill result/isnull fields.
983  */
984  for (i = 0; i < numaggs; i++)
985  {
986  Datum *result;
987  bool *isnull;
988 
989  peraggstate = &winstate->peragg[i];
990  wfuncno = peraggstate->wfuncno;
991  result = &econtext->ecxt_aggvalues[wfuncno];
992  isnull = &econtext->ecxt_aggnulls[wfuncno];
993  finalize_windowaggregate(winstate,
994  &winstate->perfunc[wfuncno],
995  peraggstate,
996  result, isnull);
997 
998  /*
999  * save the result in case next row shares the same frame.
1000  *
1001  * XXX in some framing modes, eg ROWS/END_CURRENT_ROW, we can know in
1002  * advance that the next row can't possibly share the same frame. Is
1003  * it worth detecting that and skipping this code?
1004  */
1005  if (!peraggstate->resulttypeByVal && !*isnull)
1006  {
1007  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
1008  peraggstate->resultValue =
1009  datumCopy(*result,
1010  peraggstate->resulttypeByVal,
1011  peraggstate->resulttypeLen);
1012  MemoryContextSwitchTo(oldContext);
1013  }
1014  else
1015  {
1016  peraggstate->resultValue = *result;
1017  }
1018  peraggstate->resultValueIsNull = *isnull;
1019  }
1020 }
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:528
Datum * ecxt_aggvalues
Definition: execnodes.h:237
static void finalize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int64 aggregatedupto
Definition: execnodes.h:1976
static void update_frameheadpos(WindowAggState *winstate)
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
#define OidIsValid(objectId)
Definition: c.h:605
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
static bool advance_windowaggregate_base(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
struct WindowObjectData * agg_winobj
Definition: execnodes.h:1974
int64 frameheadpos
Definition: execnodes.h:1971
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ERROR
Definition: elog.h:43
WindowStatePerAgg peragg
Definition: execnodes.h:1961
static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
int64 aggregatedbase
Definition: execnodes.h:1975
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:515
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:513
#define TupIsNull(slot)
Definition: tuptable.h:146
MemoryContext aggcontext
Definition: execnodes.h:1999
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
bool * ecxt_aggnulls
Definition: execnodes.h:239
uintptr_t Datum
Definition: postgres.h:367
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
MemoryContext aggcontext
int64 currentpos
Definition: execnodes.h:1970
#define DatumGetPointer(X)
Definition: postgres.h:534
int i
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
#define elog
Definition: elog.h:219
#define ResetExprContext(econtext)
Definition: executor.h:483

◆ eval_windowfunction()

static void eval_windowfunction ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
Datum result,
bool isnull 
)
static

Definition at line 1032 of file nodeWindowAgg.c.

References FunctionCallInfoData::argnull, WindowAggState::curaggcontext, CurrentMemoryContext, datumCopy(), DatumGetPointer, ExprContext::ecxt_per_tuple_memory, WindowStatePerFuncData::flinfo, FunctionCallInvoke, InitFunctionCallInfoData, FunctionCallInfoData::isnull, MemoryContextContains(), MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, ScanState::ps, PlanState::ps_ExprContext, WindowStatePerFuncData::resulttypeByVal, WindowStatePerFuncData::resulttypeLen, WindowAggState::ss, WindowStatePerFuncData::winCollation, and WindowStatePerFuncData::winobj.

Referenced by ExecWindowAgg().

1034 {
1035  FunctionCallInfoData fcinfo;
1036  MemoryContext oldContext;
1037 
1039 
1040  /*
1041  * We don't pass any normal arguments to a window function, but we do pass
1042  * it the number of arguments, in order to permit window function
1043  * implementations to support varying numbers of arguments. The real info
1044  * goes through the WindowObject, which is passed via fcinfo->context.
1045  */
1046  InitFunctionCallInfoData(fcinfo, &(perfuncstate->flinfo),
1047  perfuncstate->numArguments,
1048  perfuncstate->winCollation,
1049  (void *) perfuncstate->winobj, NULL);
1050  /* Just in case, make all the regular argument slots be null */
1051  memset(fcinfo.argnull, true, perfuncstate->numArguments);
1052  /* Window functions don't have a current aggregate context, either */
1053  winstate->curaggcontext = NULL;
1054 
1055  *result = FunctionCallInvoke(&fcinfo);
1056  *isnull = fcinfo.isnull;
1057 
1058  /*
1059  * Make sure pass-by-ref data is allocated in the appropriate context. (We
1060  * need this in case the function returns a pointer into some short-lived
1061  * tuple, as is entirely possible.)
1062  */
1063  if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
1065  DatumGetPointer(*result)))
1066  *result = datumCopy(*result,
1067  perfuncstate->resulttypeByVal,
1068  perfuncstate->resulttypeLen);
1069 
1070  MemoryContextSwitchTo(oldContext);
1071 }
MemoryContext curaggcontext
Definition: execnodes.h:2000
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:226
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
PlanState ps
Definition: execnodes.h:1192
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:142
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
bool MemoryContextContains(MemoryContext context, void *pointer)
Definition: mcxt.c:667
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:125
#define DatumGetPointer(X)
Definition: postgres.h:534

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2504 of file nodeWindowAgg.c.

References WindowAggState::agg_row_slot, WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), WindowAggState::first_part_slot, WindowAggState::framehead_slot, WindowAggState::frametail_slot, i, MemoryContextDelete(), WindowAggState::numaggs, outerPlan, outerPlanState, WindowAggState::partcontext, WindowAggState::peragg, WindowAggState::perfunc, pfree(), ScanState::ps, PlanState::ps_ExprContext, release_partition(), WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, and WindowAggState::tmpcontext.

Referenced by ExecEndNode().

2505 {
2507  int i;
2508 
2509  release_partition(node);
2510 
2514  ExecClearTuple(node->temp_slot_1);
2515  ExecClearTuple(node->temp_slot_2);
2516  if (node->framehead_slot)
2518  if (node->frametail_slot)
2520 
2521  /*
2522  * Free both the expr contexts.
2523  */
2524  ExecFreeExprContext(&node->ss.ps);
2525  node->ss.ps.ps_ExprContext = node->tmpcontext;
2526  ExecFreeExprContext(&node->ss.ps);
2527 
2528  for (i = 0; i < node->numaggs; i++)
2529  {
2530  if (node->peragg[i].aggcontext != node->aggcontext)
2532  }
2535 
2536  pfree(node->perfunc);
2537  pfree(node->peragg);
2538 
2539  outerPlan = outerPlanState(node);
2540  ExecEndNode(outerPlan);
2541 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
ScanState ss
Definition: execnodes.h:1953
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
static void release_partition(WindowAggState *winstate)
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:566
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
void pfree(void *pointer)
Definition: mcxt.c:1031
#define outerPlanState(node)
Definition: execnodes.h:966
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
WindowStatePerAgg peragg
Definition: execnodes.h:1961
MemoryContext aggcontext
Definition: execnodes.h:1999
#define outerPlan(node)
Definition: plannodes.h:176
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018
MemoryContext aggcontext
int i
MemoryContext partcontext
Definition: execnodes.h:1998

◆ ExecInitWindowAgg()

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

Definition at line 2238 of file nodeWindowAgg.c.

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, WindowStatePerFuncData::aggno, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, WindowFuncExprState::args, WindowObjectData::argstates, Assert, contain_volatile_functions(), CurrentMemoryContext, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_per_query_memory, elog, WindowAgg::endInRangeFunc, WindowAgg::endOffset, equal(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecCreateScanSlotFromOuterPlan(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitResultTupleSlotTL(), PlanState::ExecProcNode, execTuplesMatchPrepare(), ExecWindowAgg(), WindowAggState::first_part_slot, WindowStatePerFuncData::flinfo, fmgr_info(), fmgr_info_cxt(), fmgr_info_set_expr, WindowAggState::framehead_slot, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAgg::frameOptions, WindowAggState::frameOptions, WindowAggState::frametail_slot, WindowAggState::funcs, get_func_name(), get_typlenbyval(), GetUserId(), i, initialize_peragg(), WindowAgg::inRangeAsc, WindowAgg::inRangeColl, WindowAgg::inRangeNullsFirst, InvokeFunctionExecuteHook, lfirst, list_length(), WindowObjectData::localmem, makeNode, WindowObjectData::markptr, NIL, WindowAggState::numaggs, WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, OBJECT_FUNCTION, OidIsValid, WindowAgg::ordColIdx, WindowAggState::ordEqfunction, WindowAgg::ordNumCols, WindowAgg::ordOperators, outerPlan, outerPlanState, palloc0(), WindowAgg::partColIdx, WindowAggState::partcontext, WindowAggState::partEqfunction, WindowAgg::partNumCols, WindowAgg::partOperators, WindowAggState::peragg, WindowAggState::perfunc, pg_proc_aclcheck(), WindowStatePerFuncData::plain_agg, WindowAgg::plan, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, Plan::qual, PlanState::qual, WindowObjectData::readptr, WindowStatePerFuncData::resulttypeByVal, WindowStatePerFuncData::resulttypeLen, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAgg::startInRangeFunc, WindowAgg::startOffset, WindowAggState::startOffset, PlanState::state, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, WindowAggState::tmpcontext, TupleTableSlot::tts_tupleDescriptor, WindowStatePerFuncData::wfunc, WindowFuncExprState::wfunc, WindowStatePerAggData::wfuncno, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WindowStatePerFuncData::winCollation, WindowFunc::winfnoid, WindowStatePerFuncData::winobj, WindowFunc::winref, WindowAgg::winref, and WindowObjectData::winstate.

Referenced by ExecInitNode().

2239 {
2240  WindowAggState *winstate;
2241  Plan *outerPlan;
2242  ExprContext *econtext;
2243  ExprContext *tmpcontext;
2244  WindowStatePerFunc perfunc;
2245  WindowStatePerAgg peragg;
2246  int frameOptions = node->frameOptions;
2247  int numfuncs,
2248  wfuncno,
2249  numaggs,
2250  aggno;
2251  TupleDesc scanDesc;
2252  ListCell *l;
2253 
2254  /* check for unsupported flags */
2255  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2256 
2257  /*
2258  * create state structure
2259  */
2260  winstate = makeNode(WindowAggState);
2261  winstate->ss.ps.plan = (Plan *) node;
2262  winstate->ss.ps.state = estate;
2263  winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2264 
2265  /*
2266  * Create expression contexts. We need two, one for per-input-tuple
2267  * processing and one for per-output-tuple processing. We cheat a little
2268  * by using ExecAssignExprContext() to build both.
2269  */
2270  ExecAssignExprContext(estate, &winstate->ss.ps);
2271  tmpcontext = winstate->ss.ps.ps_ExprContext;
2272  winstate->tmpcontext = tmpcontext;
2273  ExecAssignExprContext(estate, &winstate->ss.ps);
2274 
2275  /* Create long-lived context for storage of partition-local memory etc */
2276  winstate->partcontext =
2278  "WindowAgg Partition",
2280 
2281  /*
2282  * Create mid-lived context for aggregate trans values etc.
2283  *
2284  * Note that moving aggregates each use their own private context, not
2285  * this one.
2286  */
2287  winstate->aggcontext =
2289  "WindowAgg Aggregates",
2291 
2292  /*
2293  * WindowAgg nodes never have quals, since they can only occur at the
2294  * logical top level of a query (ie, after any WHERE or HAVING filters)
2295  */
2296  Assert(node->plan.qual == NIL);
2297  winstate->ss.ps.qual = NULL;
2298 
2299  /*
2300  * initialize child nodes
2301  */
2302  outerPlan = outerPlan(node);
2303  outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2304 
2305  /*
2306  * initialize source tuple type (which is also the tuple type that we'll
2307  * store in the tuplestore and use in all our working slots).
2308  */
2309  ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss);
2310  scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2311 
2312  /*
2313  * tuple table initialization
2314  */
2315  winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2316  winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2317  winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc);
2318  winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc);
2319 
2320  /*
2321  * create frame head and tail slots only if needed (must match logic in
2322  * update_frameheadpos and update_frametailpos)
2323  */
2324  winstate->framehead_slot = winstate->frametail_slot = NULL;
2325 
2326  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2327  {
2328  if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING))
2329  winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2330  if (!(frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING))
2331  winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2332  }
2333 
2334  /*
2335  * Initialize result slot, type and projection.
2336  */
2337  ExecInitResultTupleSlotTL(estate, &winstate->ss.ps);
2338  ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2339 
2340  /* Set up data for comparing tuples */
2341  if (node->partNumCols > 0)
2342  winstate->partEqfunction =
2343  execTuplesMatchPrepare(scanDesc,
2344  node->partNumCols,
2345  node->partColIdx,
2346  node->partOperators,
2347  &winstate->ss.ps);
2348 
2349  if (node->ordNumCols > 0)
2350  winstate->ordEqfunction =
2351  execTuplesMatchPrepare(scanDesc,
2352  node->ordNumCols,
2353  node->ordColIdx,
2354  node->ordOperators,
2355  &winstate->ss.ps);
2356 
2357  /*
2358  * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2359  */
2360  numfuncs = winstate->numfuncs;
2361  numaggs = winstate->numaggs;
2362  econtext = winstate->ss.ps.ps_ExprContext;
2363  econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2364  econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2365 
2366  /*
2367  * allocate per-wfunc/per-agg state information.
2368  */
2369  perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2370  peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2371  winstate->perfunc = perfunc;
2372  winstate->peragg = peragg;
2373 
2374  wfuncno = -1;
2375  aggno = -1;
2376  foreach(l, winstate->funcs)
2377  {
2378  WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2379  WindowFunc *wfunc = wfuncstate->wfunc;
2380  WindowStatePerFunc perfuncstate;
2381  AclResult aclresult;
2382  int i;
2383 
2384  if (wfunc->winref != node->winref) /* planner screwed up? */
2385  elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2386  wfunc->winref, node->winref);
2387 
2388  /* Look for a previous duplicate window function */
2389  for (i = 0; i <= wfuncno; i++)
2390  {
2391  if (equal(wfunc, perfunc[i].wfunc) &&
2392  !contain_volatile_functions((Node *) wfunc))
2393  break;
2394  }
2395  if (i <= wfuncno)
2396  {
2397  /* Found a match to an existing entry, so just mark it */
2398  wfuncstate->wfuncno = i;
2399  continue;
2400  }
2401 
2402  /* Nope, so assign a new PerAgg record */
2403  perfuncstate = &perfunc[++wfuncno];
2404 
2405  /* Mark WindowFunc state node with assigned index in the result array */
2406  wfuncstate->wfuncno = wfuncno;
2407 
2408  /* Check permission to call window function */
2409  aclresult = pg_proc_aclcheck(wfunc->winfnoid, GetUserId(),
2410  ACL_EXECUTE);
2411  if (aclresult != ACLCHECK_OK)
2412  aclcheck_error(aclresult, OBJECT_FUNCTION,
2413  get_func_name(wfunc->winfnoid));
2414  InvokeFunctionExecuteHook(wfunc->winfnoid);
2415 
2416  /* Fill in the perfuncstate data */
2417  perfuncstate->wfuncstate = wfuncstate;
2418  perfuncstate->wfunc = wfunc;
2419  perfuncstate->numArguments = list_length(wfuncstate->args);
2420 
2421  fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2422  econtext->ecxt_per_query_memory);
2423  fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2424 
2425  perfuncstate->winCollation = wfunc->inputcollid;
2426 
2427  get_typlenbyval(wfunc->wintype,
2428  &perfuncstate->resulttypeLen,
2429  &perfuncstate->resulttypeByVal);
2430 
2431  /*
2432  * If it's really just a plain aggregate function, we'll emulate the
2433  * Agg environment for it.
2434  */
2435  perfuncstate->plain_agg = wfunc->winagg;
2436  if (wfunc->winagg)
2437  {
2438  WindowStatePerAgg peraggstate;
2439 
2440  perfuncstate->aggno = ++aggno;
2441  peraggstate = &winstate->peragg[aggno];
2442  initialize_peragg(winstate, wfunc, peraggstate);
2443  peraggstate->wfuncno = wfuncno;
2444  }
2445  else
2446  {
2448 
2449  winobj->winstate = winstate;
2450  winobj->argstates = wfuncstate->args;
2451  winobj->localmem = NULL;
2452  perfuncstate->winobj = winobj;
2453  }
2454  }
2455 
2456  /* Update numfuncs, numaggs to match number of unique functions found */
2457  winstate->numfuncs = wfuncno + 1;
2458  winstate->numaggs = aggno + 1;
2459 
2460  /* Set up WindowObject for aggregates, if needed */
2461  if (winstate->numaggs > 0)
2462  {
2463  WindowObject agg_winobj = makeNode(WindowObjectData);
2464 
2465  agg_winobj->winstate = winstate;
2466  agg_winobj->argstates = NIL;
2467  agg_winobj->localmem = NULL;
2468  /* make sure markptr = -1 to invalidate. It may not get used */
2469  agg_winobj->markptr = -1;
2470  agg_winobj->readptr = -1;
2471  winstate->agg_winobj = agg_winobj;
2472  }
2473 
2474  /* copy frame options to state node for easy access */
2475  winstate->frameOptions = frameOptions;
2476 
2477  /* initialize frame bound offset expressions */
2478  winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2479  (PlanState *) winstate);
2480  winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2481  (PlanState *) winstate);
2482 
2483  /* Lookup in_range support functions if needed */
2484  if (OidIsValid(node->startInRangeFunc))
2485  fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2486  if (OidIsValid(node->endInRangeFunc))
2487  fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2488  winstate->inRangeColl = node->inRangeColl;
2489  winstate->inRangeAsc = node->inRangeAsc;
2490  winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2491 
2492  winstate->all_first = true;
2493  winstate->partition_spooled = false;
2494  winstate->more_partitions = false;
2495 
2496  return winstate;
2497 }
int ordNumCols
Definition: plannodes.h:820
#define NIL
Definition: pg_list.h:69
List * qual
Definition: plannodes.h:147
ExprState * endOffset
Definition: execnodes.h:1980
Datum * ecxt_aggvalues
Definition: execnodes.h:237
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:1949
Oid inRangeColl
Definition: plannodes.h:829
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2986
Oid GetUserId(void)
Definition: miscinit.c:379
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
AttrNumber * ordColIdx
Definition: plannodes.h:821
Definition: nodes.h:517
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:1948
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:79
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:958
EState * state
Definition: execnodes.h:914
Index winref
Definition: primnodes.h:362
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:510
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
#define OidIsValid(objectId)
Definition: c.h:605
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
Oid * ordOperators
Definition: plannodes.h:822
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc)
Definition: execTuples.c:931
Oid endInRangeFunc
Definition: plannodes.h:828
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
struct WindowObjectData * agg_winobj
Definition: execnodes.h:1974
bool inRangeNullsFirst
Definition: execnodes.h:1989
Node * startOffset
Definition: plannodes.h:824
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3349
ExprState * partEqfunction
Definition: execnodes.h:1962
#define ERROR
Definition: elog.h:43
FmgrInfo endInRangeFunc
Definition: execnodes.h:1986
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1397
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate)
Definition: execUtils.c:598
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define EXEC_FLAG_BACKWARD
Definition: executor.h:60
#define outerPlanState(node)
Definition: execnodes.h:966
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
WindowStatePerAgg peragg
Definition: execnodes.h:1961
ExprState * ordEqfunction
Definition: execnodes.h:1963
ExprState * startOffset
Definition: execnodes.h:1979
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:456
Node * endOffset
Definition: plannodes.h:825
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:107
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:513
int partNumCols
Definition: plannodes.h:817
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
MemoryContext aggcontext
Definition: execnodes.h:1999
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:134
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:179
#define outerPlan(node)
Definition: plannodes.h:176
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
bool inRangeAsc
Definition: plannodes.h:830
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
bool * ecxt_aggnulls
Definition: execnodes.h:239
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
void * palloc0(Size size)
Definition: mcxt.c:955
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:918
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:367
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
AttrNumber * partColIdx
Definition: plannodes.h:818
void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
Definition: execTuples.c:890
Plan * plan
Definition: execnodes.h:912
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
bool more_partitions
Definition: execnodes.h:2007
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, AttrNumber *keyColIdx, Oid *eqOperators, PlanState *parent)
Definition: execGrouping.c:60
Oid * partOperators
Definition: plannodes.h:819
#define makeNode(_type_)
Definition: nodes.h:565
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Index winref
Definition: plannodes.h:816
#define EXEC_FLAG_MARK
Definition: executor.h:61
FmgrInfo startInRangeFunc
Definition: execnodes.h:1985
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018
bool inRangeNullsFirst
Definition: plannodes.h:831
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:428
static int list_length(const List *l)
Definition: pg_list.h:89
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2005
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
ExprState * qual
Definition: execnodes.h:930
Oid startInRangeFunc
Definition: plannodes.h:827
bool partition_spooled
Definition: execnodes.h:2005
#define ACL_EXECUTE
Definition: parsenodes.h:81
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4651
int i
Plan plan
Definition: plannodes.h:815
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508
MemoryContext partcontext
Definition: execnodes.h:1998
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:119
#define elog
Definition: elog.h:219
int frameOptions
Definition: plannodes.h:823
WindowFunc * wfunc
Definition: execnodes.h:731
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2548 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, WindowAggState::framehead_slot, WindowAggState::frametail_slot, MemSet, WindowAggState::numfuncs, outerPlan, outerPlanState, ScanState::ps, PlanState::ps_ExprContext, release_partition(), WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_1, and WindowAggState::temp_slot_2.

Referenced by ExecReScan().

2549 {
2551  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2552 
2553  node->all_done = false;
2554  node->all_first = true;
2555 
2556  /* release tuplestore et al */
2557  release_partition(node);
2558 
2559  /* release all temp tuples, but especially first_part_slot */
2563  ExecClearTuple(node->temp_slot_1);
2564  ExecClearTuple(node->temp_slot_2);
2565  if (node->framehead_slot)
2567  if (node->frametail_slot)
2569 
2570  /* Forget current wfunc values */
2571  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2572  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2573 
2574  /*
2575  * if chgParam of subnode is not null then plan will be re-scanned by
2576  * first ExecProcNode.
2577  */
2578  if (outerPlan->chgParam == NULL)
2579  ExecReScan(outerPlan);
2580 }
Datum * ecxt_aggvalues
Definition: execnodes.h:237
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
#define MemSet(start, val, len)
Definition: c.h:908
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
static void release_partition(WindowAggState *winstate)
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
PlanState ps
Definition: execnodes.h:1192
#define outerPlanState(node)
Definition: execnodes.h:966
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
Bitmapset * chgParam
Definition: execnodes.h:941
#define outerPlan(node)
Definition: plannodes.h:176
bool * ecxt_aggnulls
Definition: execnodes.h:239
uintptr_t Datum
Definition: postgres.h:367
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018

◆ ExecWindowAgg()

static TupleTableSlot* ExecWindowAgg ( PlanState pstate)
static

Definition at line 2012 of file nodeWindowAgg.c.

References WindowAggState::all_done, WindowAggState::all_first, are_peers(), Assert, begin_partition(), WindowAggState::buffer, castNode, CHECK_FOR_INTERRUPTS, WindowAggState::current_ptr, WindowAggState::currentgroup, 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(), ExecClearTuple(), ExecCopySlot(), ExecEvalExprSwitchContext(), ExecProject(), ExprState::expr, exprType(), WindowAggState::framehead_ptr, WindowAggState::framehead_valid, FRAMEOPTION_END_OFFSET, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_ROWS, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_valid, get_typlenbyval(), WindowAggState::groupheadpos, WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, i, WindowAggState::more_partitions, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, release_partition(), ResetExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startOffset, WindowAggState::startOffsetValue, WindowAggState::temp_slot_2, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), value, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, and WindowObjectData::winstate.

Referenced by ExecInitWindowAgg().

2013 {
2014  WindowAggState *winstate = castNode(WindowAggState, pstate);
2015  ExprContext *econtext;
2016  int i;
2017  int numfuncs;
2018 
2020 
2021  if (winstate->all_done)
2022  return NULL;
2023 
2024  /*
2025  * Compute frame offset values, if any, during first call (or after a
2026  * rescan). These are assumed to hold constant throughout the scan; if
2027  * user gives us a volatile expression, we'll only use its initial value.
2028  */
2029  if (winstate->all_first)
2030  {
2031  int frameOptions = winstate->frameOptions;
2032  ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
2033  Datum value;
2034  bool isnull;
2035  int16 len;
2036  bool byval;
2037 
2038  if (frameOptions & FRAMEOPTION_START_OFFSET)
2039  {
2040  Assert(winstate->startOffset != NULL);
2041  value = ExecEvalExprSwitchContext(winstate->startOffset,
2042  econtext,
2043  &isnull);
2044  if (isnull)
2045  ereport(ERROR,
2046  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2047  errmsg("frame starting offset must not be null")));
2048  /* copy value into query-lifespan context */
2049  get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
2050  &len, &byval);
2051  winstate->startOffsetValue = datumCopy(value, byval, len);
2052  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2053  {
2054  /* value is known to be int8 */
2055  int64 offset = DatumGetInt64(value);
2056 
2057  if (offset < 0)
2058  ereport(ERROR,
2059  (errcode(ERRCODE_INVALID_PRECEDING_FOLLOWING_SIZE),
2060  errmsg("frame starting offset must not be negative")));
2061  }
2062  }
2063  if (frameOptions & FRAMEOPTION_END_OFFSET)
2064  {
2065  Assert(winstate->endOffset != NULL);
2066  value = ExecEvalExprSwitchContext(winstate->endOffset,
2067  econtext,
2068  &isnull);
2069  if (isnull)
2070  ereport(ERROR,
2071  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2072  errmsg("frame ending offset must not be null")));
2073  /* copy value into query-lifespan context */
2074  get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2075  &len, &byval);
2076  winstate->endOffsetValue = datumCopy(value, byval, len);
2077  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2078  {
2079  /* value is known to be int8 */
2080  int64 offset = DatumGetInt64(value);
2081 
2082  if (offset < 0)
2083  ereport(ERROR,
2084  (errcode(ERRCODE_INVALID_PRECEDING_FOLLOWING_SIZE),
2085  errmsg("frame ending offset must not be negative")));
2086  }
2087  }
2088  winstate->all_first = false;
2089  }
2090 
2091  if (winstate->buffer == NULL)
2092  {
2093  /* Initialize for first partition and set current row = 0 */
2094  begin_partition(winstate);
2095  /* If there are no input rows, we'll detect that and exit below */
2096  }
2097  else
2098  {
2099  /* Advance current row within partition */
2100  winstate->currentpos++;
2101  /* This might mean that the frame moves, too */
2102  winstate->framehead_valid = false;
2103  winstate->frametail_valid = false;
2104  /* we don't need to invalidate grouptail here; see below */
2105  }
2106 
2107  /*
2108  * Spool all tuples up to and including the current row, if we haven't
2109  * already
2110  */
2111  spool_tuples(winstate, winstate->currentpos);
2112 
2113  /* Move to the next partition if we reached the end of this partition */
2114  if (winstate->partition_spooled &&
2115  winstate->currentpos >= winstate->spooled_rows)
2116  {
2117  release_partition(winstate);
2118 
2119  if (winstate->more_partitions)
2120  {
2121  begin_partition(winstate);
2122  Assert(winstate->spooled_rows > 0);
2123  }
2124  else
2125  {
2126  winstate->all_done = true;
2127  return NULL;
2128  }
2129  }
2130 
2131  /* final output execution is in ps_ExprContext */
2132  econtext = winstate->ss.ps.ps_ExprContext;
2133 
2134  /* Clear the per-output-tuple context for current row */
2135  ResetExprContext(econtext);
2136 
2137  /*
2138  * Read the current row from the tuplestore, and save in ScanTupleSlot.
2139  * (We can't rely on the outerplan's output slot because we may have to
2140  * read beyond the current row. Also, we have to actually copy the row
2141  * out of the tuplestore, since window function evaluation might cause the
2142  * tuplestore to dump its state to disk.)
2143  *
2144  * In GROUPS mode, or when tracking a group-oriented exclusion clause, we
2145  * must also detect entering a new peer group and update associated state
2146  * when that happens. We use temp_slot_2 to temporarily hold the previous
2147  * row for this purpose.
2148  *
2149  * Current row must be in the tuplestore, since we spooled it above.
2150  */
2151  tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
2152  if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2155  winstate->currentpos > 0)
2156  {
2157  ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2158  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2159  winstate->ss.ss_ScanTupleSlot))
2160  elog(ERROR, "unexpected end of tuplestore");
2161  if (!are_peers(winstate, winstate->temp_slot_2,
2162  winstate->ss.ss_ScanTupleSlot))
2163  {
2164  winstate->currentgroup++;
2165  winstate->groupheadpos = winstate->currentpos;
2166  winstate->grouptail_valid = false;
2167  }
2168  ExecClearTuple(winstate->temp_slot_2);
2169  }
2170  else
2171  {
2172  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2173  winstate->ss.ss_ScanTupleSlot))
2174  elog(ERROR, "unexpected end of tuplestore");
2175  }
2176 
2177  /*
2178  * Evaluate true window functions
2179  */
2180  numfuncs = winstate->numfuncs;
2181  for (i = 0; i < numfuncs; i++)
2182  {
2183  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2184 
2185  if (perfuncstate->plain_agg)
2186  continue;
2187  eval_windowfunction(winstate, perfuncstate,
2188  &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2189  &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2190  }
2191 
2192  /*
2193  * Evaluate aggregates
2194  */
2195  if (winstate->numaggs > 0)
2196  eval_windowaggregates(winstate);
2197 
2198  /*
2199  * If we have created auxiliary read pointers for the frame or group
2200  * boundaries, force them to be kept up-to-date, because we don't know
2201  * whether the window function(s) will do anything that requires that.
2202  * Failing to advance the pointers would result in being unable to trim
2203  * data from the tuplestore, which is bad. (If we could know in advance
2204  * whether the window functions will use frame boundary info, we could
2205  * skip creating these pointers in the first place ... but unfortunately
2206  * the window function API doesn't require that.)
2207  */
2208  if (winstate->framehead_ptr >= 0)
2209  update_frameheadpos(winstate);
2210  if (winstate->frametail_ptr >= 0)
2211  update_frametailpos(winstate);
2212  if (winstate->grouptail_ptr >= 0)
2213  update_grouptailpos(winstate);
2214 
2215  /*
2216  * Truncate any no-longer-needed rows from the tuplestore.
2217  */
2218  tuplestore_trim(winstate->buffer);
2219 
2220  /*
2221  * Form and return a projection tuple using the windowfunc results and the
2222  * current row. Setting ecxt_outertuple arranges that any Vars will be
2223  * evaluated with respect to that row.
2224  */
2225  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2226 
2227  return ExecProject(winstate->ss.ps.ps_ProjInfo);
2228 }
signed short int16
Definition: c.h:312
ExprState * endOffset
Definition: execnodes.h:1980
Datum * ecxt_aggvalues
Definition: execnodes.h:237
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:296
Datum startOffsetValue
Definition: execnodes.h:1981
static void update_grouptailpos(WindowAggState *winstate)
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:948
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1360
ScanState ss
Definition: execnodes.h:1953
int64 groupheadpos
Definition: execnodes.h:1995
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static void update_frametailpos(WindowAggState *winstate)
int64 currentgroup
Definition: execnodes.h:1992
Definition: nodes.h:517
int errcode(int sqlerrcode)
Definition: elog.c:575
bool frametail_valid
Definition: execnodes.h:2011
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
static void release_partition(WindowAggState *winstate)
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:79
static void update_frameheadpos(WindowAggState *winstate)
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:524
static void spool_tuples(WindowAggState *winstate, int64 pos)
PlanState ps
Definition: execnodes.h:1192
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:521
#define ERROR
Definition: elog.h:43
Expr * expr
Definition: execnodes.h:90
#define DatumGetInt64(X)
Definition: postgres.h:592
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
Datum endOffsetValue
Definition: execnodes.h:1982
ExprState * startOffset
Definition: execnodes.h:1979
Tuplestorestate * buffer
Definition: execnodes.h:1964
#define ereport(elevel, rest)
Definition: elog.h:122
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
static void eval_windowaggregates(WindowAggState *winstate)
int64 spooled_rows
Definition: execnodes.h:1969
bool * ecxt_aggnulls
Definition: execnodes.h:239
uintptr_t Datum
Definition: postgres.h:367
static struct @131 value
static void begin_partition(WindowAggState *winstate)
bool more_partitions
Definition: execnodes.h:2007
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:522
bool grouptail_valid
Definition: execnodes.h:2013
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:526
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2005
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:507
int64 currentpos
Definition: execnodes.h:1970
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
bool partition_spooled
Definition: execnodes.h:2005
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool framehead_valid
Definition: execnodes.h:2009
int i
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:324
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
#define ResetExprContext(econtext)
Definition: executor.h:483

◆ finalize_windowaggregate()

static void finalize_windowaggregate ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate,
Datum result,
bool isnull 
)
static

Definition at line 580 of file nodeWindowAgg.c.

References WindowStatePerAggData::aggcontext, FunctionCallInfoData::arg, FunctionCallInfoData::argnull, CurrentMemoryContext, datumCopy(), DatumGetPointer, ExprContext::ecxt_per_tuple_memory, WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, FunctionCallInfoData::flinfo, FmgrInfo::fn_strict, FunctionCallInvoke, i, InitFunctionCallInfoData, FunctionCallInfoData::isnull, MakeExpandedObjectReadOnly, MemoryContextContains(), MemoryContextSwitchTo(), WindowStatePerAggData::numFinalArgs, OidIsValid, ScanState::ps, PlanState::ps_ExprContext, WindowStatePerAggData::resulttypeByVal, WindowStatePerAggData::resulttypeLen, WindowAggState::ss, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueIsNull, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

584 {
585  MemoryContext oldContext;
586 
588 
589  /*
590  * Apply the agg's finalfn if one is provided, else return transValue.
591  */
592  if (OidIsValid(peraggstate->finalfn_oid))
593  {
594  int numFinalArgs = peraggstate->numFinalArgs;
595  FunctionCallInfoData fcinfo;
596  bool anynull;
597  int i;
598 
599  InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
600  numFinalArgs,
601  perfuncstate->winCollation,
602  (void *) winstate, NULL);
603  fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
604  peraggstate->transValueIsNull,
605  peraggstate->transtypeLen);
606  fcinfo.argnull[0] = peraggstate->transValueIsNull;
607  anynull = peraggstate->transValueIsNull;
608 
609  /* Fill any remaining argument positions with nulls */
610  for (i = 1; i < numFinalArgs; i++)
611  {
612  fcinfo.arg[i] = (Datum) 0;
613  fcinfo.argnull[i] = true;
614  anynull = true;
615  }
616 
617  if (fcinfo.flinfo->fn_strict && anynull)
618  {
619  /* don't call a strict function with NULL inputs */
620  *result = (Datum) 0;
621  *isnull = true;
622  }
623  else
624  {
625  winstate->curaggcontext = peraggstate->aggcontext;
626  *result = FunctionCallInvoke(&fcinfo);
627  winstate->curaggcontext = NULL;
628  *isnull = fcinfo.isnull;
629  }
630  }
631  else
632  {
633  /* Don't need MakeExpandedObjectReadOnly; datumCopy will copy it */
634  *result = peraggstate->transValue;
635  *isnull = peraggstate->transValueIsNull;
636  }
637 
638  /*
639  * If result is pass-by-ref, make sure it is in the right context.
640  */
641  if (!peraggstate->resulttypeByVal && !*isnull &&
643  DatumGetPointer(*result)))
644  *result = datumCopy(*result,
645  peraggstate->resulttypeByVal,
646  peraggstate->resulttypeLen);
647  MemoryContextSwitchTo(oldContext);
648 }
MemoryContext curaggcontext
Definition: execnodes.h:2000
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:226
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define OidIsValid(objectId)
Definition: c.h:605
PlanState ps
Definition: execnodes.h:1192
FmgrInfo * flinfo
Definition: fmgr.h:79
bool fn_strict
Definition: fmgr.h:61
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:142
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
uintptr_t Datum
Definition: postgres.h:367
Datum arg[FUNC_MAX_ARGS]
Definition: fmgr.h:87
bool MemoryContextContains(MemoryContext context, void *pointer)
Definition: mcxt.c:667
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:125
MemoryContext aggcontext
#define DatumGetPointer(X)
Definition: postgres.h:534
int i

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 2853 of file nodeWindowAgg.c.

References getTypeInputInfo(), OidInputFunctionCall(), pfree(), and TextDatumGetCString.

Referenced by initialize_peragg().

2854 {
2855  Oid typinput,
2856  typioparam;
2857  char *strInitVal;
2858  Datum initVal;
2859 
2860  getTypeInputInfo(transtype, &typinput, &typioparam);
2861  strInitVal = TextDatumGetCString(textInitVal);
2862  initVal = OidInputFunctionCall(typinput, strInitVal,
2863  typioparam, -1);
2864  pfree(strInitVal);
2865  return initVal;
2866 }
unsigned int Oid
Definition: postgres_ext.h:31
void pfree(void *pointer)
Definition: mcxt.c:1031
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2617
#define TextDatumGetCString(d)
Definition: builtins.h:96
uintptr_t Datum
Definition: postgres.h:367
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1824

◆ initialize_peragg()

static WindowStatePerAggData * initialize_peragg ( WindowAggState winstate,
WindowFunc wfunc,
WindowStatePerAgg  peraggstate 
)
static

Definition at line 2588 of file nodeWindowAgg.c.

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, AGGFNOID, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, WindowFunc::args, build_aggregate_finalfn_expr(), build_aggregate_transfn_expr(), contain_volatile_functions(), CurrentMemoryContext, elog, ereport, errcode(), errmsg(), ERROR, exprType(), WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, fmgr_info(), fmgr_info_set_expr, FmgrInfo::fn_strict, format_procedure(), FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, FUNC_MAX_ARGS, get_func_name(), get_typlenbyval(), GetAggInitVal(), GETSTRUCT, HeapTupleIsValid, i, WindowStatePerAggData::initValue, WindowStatePerAggData::initValueIsNull, InvalidOid, InvokeFunctionExecuteHook, WindowStatePerAggData::invtransfn, WindowStatePerAggData::invtransfn_oid, IsBinaryCoercible(), lfirst, list_length(), WindowStatePerAggData::numFinalArgs, OBJECT_FUNCTION, ObjectIdGetDatum, OidIsValid, pg_proc_aclcheck(), PROCOID, ReleaseSysCache(), resolve_aggregate_transtype(), WindowStatePerAggData::resulttypeByVal, WindowStatePerAggData::resulttypeLen, SearchSysCache1(), SysCacheGetAttr(), WindowStatePerAggData::transfn, WindowStatePerAggData::transfn_oid, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, and WindowFunc::winfnoid.

Referenced by ExecInitWindowAgg().

2590 {
2591  Oid inputTypes[FUNC_MAX_ARGS];
2592  int numArguments;
2593  HeapTuple aggTuple;
2594  Form_pg_aggregate aggform;
2595  Oid aggtranstype;
2596  AttrNumber initvalAttNo;
2597  AclResult aclresult;
2598  bool use_ma_code;
2599  Oid transfn_oid,
2600  invtransfn_oid,
2601  finalfn_oid;
2602  bool finalextra;
2603  char finalmodify;
2604  Expr *transfnexpr,
2605  *invtransfnexpr,
2606  *finalfnexpr;
2607  Datum textInitVal;
2608  int i;
2609  ListCell *lc;
2610 
2611  numArguments = list_length(wfunc->args);
2612 
2613  i = 0;
2614  foreach(lc, wfunc->args)
2615  {
2616  inputTypes[i++] = exprType((Node *) lfirst(lc));
2617  }
2618 
2619  aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
2620  if (!HeapTupleIsValid(aggTuple))
2621  elog(ERROR, "cache lookup failed for aggregate %u",
2622  wfunc->winfnoid);
2623  aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
2624 
2625  /*
2626  * Figure out whether we want to use the moving-aggregate implementation,
2627  * and collect the right set of fields from the pg_attribute entry.
2628  *
2629  * It's possible that an aggregate would supply a safe moving-aggregate
2630  * implementation and an unsafe normal one, in which case our hand is
2631  * forced. Otherwise, if the frame head can't move, we don't need
2632  * moving-aggregate code. Even if we'd like to use it, don't do so if the
2633  * aggregate's arguments (and FILTER clause if any) contain any calls to
2634  * volatile functions. Otherwise, the difference between restarting and
2635  * not restarting the aggregation would be user-visible.
2636  */
2637  if (!OidIsValid(aggform->aggminvtransfn))
2638  use_ma_code = false; /* sine qua non */
2639  else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2640  aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2641  use_ma_code = true; /* decision forced by safety */
2643  use_ma_code = false; /* non-moving frame head */
2644  else if (contain_volatile_functions((Node *) wfunc))
2645  use_ma_code = false; /* avoid possible behavioral change */
2646  else
2647  use_ma_code = true; /* yes, let's use it */
2648  if (use_ma_code)
2649  {
2650  peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2651  peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2652  peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2653  finalextra = aggform->aggmfinalextra;
2654  finalmodify = aggform->aggmfinalmodify;
2655  aggtranstype = aggform->aggmtranstype;
2656  initvalAttNo = Anum_pg_aggregate_aggminitval;
2657  }
2658  else
2659  {
2660  peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2661  peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2662  peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2663  finalextra = aggform->aggfinalextra;
2664  finalmodify = aggform->aggfinalmodify;
2665  aggtranstype = aggform->aggtranstype;
2666  initvalAttNo = Anum_pg_aggregate_agginitval;
2667  }
2668 
2669  /*
2670  * ExecInitWindowAgg already checked permission to call aggregate function
2671  * ... but we still need to check the component functions
2672  */
2673 
2674  /* Check that aggregate owner has permission to call component fns */
2675  {
2676  HeapTuple procTuple;
2677  Oid aggOwner;
2678 
2679  procTuple = SearchSysCache1(PROCOID,
2680  ObjectIdGetDatum(wfunc->winfnoid));
2681  if (!HeapTupleIsValid(procTuple))
2682  elog(ERROR, "cache lookup failed for function %u",
2683  wfunc->winfnoid);
2684  aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
2685  ReleaseSysCache(procTuple);
2686 
2687  aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
2688  ACL_EXECUTE);
2689  if (aclresult != ACLCHECK_OK)
2690  aclcheck_error(aclresult, OBJECT_FUNCTION,
2691  get_func_name(transfn_oid));
2692  InvokeFunctionExecuteHook(transfn_oid);
2693 
2694  if (OidIsValid(invtransfn_oid))
2695  {
2696  aclresult = pg_proc_aclcheck(invtransfn_oid, aggOwner,
2697  ACL_EXECUTE);
2698  if (aclresult != ACLCHECK_OK)
2699  aclcheck_error(aclresult, OBJECT_FUNCTION,
2700  get_func_name(invtransfn_oid));
2701  InvokeFunctionExecuteHook(invtransfn_oid);
2702  }
2703 
2704  if (OidIsValid(finalfn_oid))
2705  {
2706  aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
2707  ACL_EXECUTE);
2708  if (aclresult != ACLCHECK_OK)
2709  aclcheck_error(aclresult, OBJECT_FUNCTION,
2710  get_func_name(finalfn_oid));
2711  InvokeFunctionExecuteHook(finalfn_oid);
2712  }
2713  }
2714 
2715  /*
2716  * If the selected finalfn isn't read-only, we can't run this aggregate as
2717  * a window function. This is a user-facing error, so we take a bit more
2718  * care with the error message than elsewhere in this function.
2719  */
2720  if (finalmodify != AGGMODIFY_READ_ONLY)
2721  ereport(ERROR,
2722  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2723  errmsg("aggregate function %s does not support use as a window function",
2724  format_procedure(wfunc->winfnoid))));
2725 
2726  /* Detect how many arguments to pass to the finalfn */
2727  if (finalextra)
2728  peraggstate->numFinalArgs = numArguments + 1;
2729  else
2730  peraggstate->numFinalArgs = 1;
2731 
2732  /* resolve actual type of transition state, if polymorphic */
2733  aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
2734  aggtranstype,
2735  inputTypes,
2736  numArguments);
2737 
2738  /* build expression trees using actual argument & result types */
2739  build_aggregate_transfn_expr(inputTypes,
2740  numArguments,
2741  0, /* no ordered-set window functions yet */
2742  false, /* no variadic window functions yet */
2743  aggtranstype,
2744  wfunc->inputcollid,
2745  transfn_oid,
2746  invtransfn_oid,
2747  &transfnexpr,
2748  &invtransfnexpr);
2749 
2750  /* set up infrastructure for calling the transfn(s) and finalfn */
2751  fmgr_info(transfn_oid, &peraggstate->transfn);
2752  fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
2753 
2754  if (OidIsValid(invtransfn_oid))
2755  {
2756  fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
2757  fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
2758  }
2759 
2760  if (OidIsValid(finalfn_oid))
2761  {
2762  build_aggregate_finalfn_expr(inputTypes,
2763  peraggstate->numFinalArgs,
2764  aggtranstype,
2765  wfunc->wintype,
2766  wfunc->inputcollid,
2767  finalfn_oid,
2768  &finalfnexpr);
2769  fmgr_info(finalfn_oid, &peraggstate->finalfn);
2770  fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
2771  }
2772 
2773  /* get info about relevant datatypes */
2774  get_typlenbyval(wfunc->wintype,
2775  &peraggstate->resulttypeLen,
2776  &peraggstate->resulttypeByVal);
2777  get_typlenbyval(aggtranstype,
2778  &peraggstate->transtypeLen,
2779  &peraggstate->transtypeByVal);
2780 
2781  /*
2782  * initval is potentially null, so don't try to access it as a struct
2783  * field. Must do it the hard way with SysCacheGetAttr.
2784  */
2785  textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
2786  &peraggstate->initValueIsNull);
2787 
2788  if (peraggstate->initValueIsNull)
2789  peraggstate->initValue = (Datum) 0;
2790  else
2791  peraggstate->initValue = GetAggInitVal(textInitVal,
2792  aggtranstype);
2793 
2794  /*
2795  * If the transfn is strict and the initval is NULL, make sure input type
2796  * and transtype are the same (or at least binary-compatible), so that
2797  * it's OK to use the first input value as the initial transValue. This
2798  * should have been checked at agg definition time, but we must check
2799  * again in case the transfn's strictness property has been changed.
2800  */
2801  if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
2802  {
2803  if (numArguments < 1 ||
2804  !IsBinaryCoercible(inputTypes[0], aggtranstype))
2805  ereport(ERROR,
2806  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2807  errmsg("aggregate %u needs to have compatible input type and transition type",
2808  wfunc->winfnoid)));
2809  }
2810 
2811  /*
2812  * Insist that forward and inverse transition functions have the same
2813  * strictness setting. Allowing them to differ would require handling
2814  * more special cases in advance_windowaggregate and
2815  * advance_windowaggregate_base, for no discernible benefit. This should
2816  * have been checked at agg definition time, but we must check again in
2817  * case either function's strictness property has been changed.
2818  */
2819  if (OidIsValid(invtransfn_oid) &&
2820  peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
2821  ereport(ERROR,
2822  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2823  errmsg("strictness of aggregate's forward and inverse transition functions must match")));
2824 
2825  /*
2826  * Moving aggregates use their own aggcontext.
2827  *
2828  * This is necessary because they might restart at different times, so we
2829  * might never be able to reset the shared context otherwise. We can't
2830  * make it the aggregates' responsibility to clean up after themselves,
2831  * because strict aggregates must be restarted whenever we remove their
2832  * last non-NULL input, which the aggregate won't be aware is happening.
2833  * Also, just pfree()ing the transValue upon restarting wouldn't help,
2834  * since we'd miss any indirectly referenced data. We could, in theory,
2835  * make the memory allocation rules for moving aggregates different than
2836  * they have historically been for plain aggregates, but that seems grotty
2837  * and likely to lead to memory leaks.
2838  */
2839  if (OidIsValid(invtransfn_oid))
2840  peraggstate->aggcontext =
2842  "WindowAgg Per Aggregate",
2844  else
2845  peraggstate->aggcontext = winstate->aggcontext;
2846 
2847  ReleaseSysCache(aggTuple);
2848 
2849  return peraggstate;
2850 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
List * args
Definition: primnodes.h:360
Definition: nodes.h:517
int errcode(int sqlerrcode)
Definition: elog.c:575
void build_aggregate_finalfn_expr(Oid *agg_input_types, int num_finalfn_inputs, Oid agg_state_type, Oid agg_result_type, Oid agg_input_collation, Oid finalfn_oid, Expr **finalfnexpr)
Definition: parse_agg.c:2038
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:958
unsigned int Oid
Definition: postgres_ext.h:31
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:510
#define OidIsValid(objectId)
Definition: c.h:605
#define FUNC_MAX_ARGS
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3349
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define ERROR
Definition: elog.h:43
bool fn_strict
Definition: fmgr.h:61
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1397
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:107
Oid winfnoid
Definition: primnodes.h:356
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
MemoryContext aggcontext
Definition: execnodes.h:1999
#define ereport(elevel, rest)
Definition: elog.h:122
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:179
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:132
#define InvalidOid
Definition: postgres_ext.h:36
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:106
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
void build_aggregate_transfn_expr(Oid *agg_input_types, int agg_num_inputs, int agg_num_direct_inputs, bool agg_variadic, Oid agg_state_type, Oid agg_input_collation, Oid transfn_oid, Oid invtransfn_oid, Expr **transfnexpr, Expr **invtransfnexpr)
Definition: parse_agg.c:1901
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:89
MemoryContext aggcontext
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2005
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define ACL_EXECUTE
Definition: parsenodes.h:81
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4651
int i
#define elog
Definition: elog.h:219
int16 AttrNumber
Definition: attnum.h:21
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:1846

◆ initialize_windowaggregate()

static void initialize_windowaggregate ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate 
)
static

Definition at line 205 of file nodeWindowAgg.c.

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, datumCopy(), WindowStatePerAggData::initValue, WindowStatePerAggData::initValueIsNull, MemoryContextResetAndDeleteChildren, MemoryContextSwitchTo(), WindowStatePerAggData::resultValue, WindowStatePerAggData::resultValueIsNull, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueCount, and WindowStatePerAggData::transValueIsNull.

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

208 {
209  MemoryContext oldContext;
210 
211  /*
212  * If we're using a private aggcontext, we may reset it here. But if the
213  * context is shared, we don't know which other aggregates may still need
214  * it, so we must leave it to the caller to reset at an appropriate time.
215  */
216  if (peraggstate->aggcontext != winstate->aggcontext)
218 
219  if (peraggstate->initValueIsNull)
220  peraggstate->transValue = peraggstate->initValue;
221  else
222  {
223  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
224  peraggstate->transValue = datumCopy(peraggstate->initValue,
225  peraggstate->transtypeByVal,
226  peraggstate->transtypeLen);
227  MemoryContextSwitchTo(oldContext);
228  }
229  peraggstate->transValueIsNull = peraggstate->initValueIsNull;
230  peraggstate->transValueCount = 0;
231  peraggstate->resultValue = (Datum) 0;
232  peraggstate->resultValueIsNull = true;
233 }
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
MemoryContext aggcontext
Definition: execnodes.h:1999
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
uintptr_t Datum
Definition: postgres.h:367
MemoryContext aggcontext

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1307 of file nodeWindowAgg.c.

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, i, WindowObjectData::localmem, MemoryContextResetAndDeleteChildren, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partcontext, WindowAggState::partition_spooled, WindowAggState::peragg, WindowAggState::perfunc, tuplestore_end(), and WindowStatePerFuncData::winobj.

Referenced by ExecEndWindowAgg(), ExecReScanWindowAgg(), and ExecWindowAgg().

1308 {
1309  int i;
1310 
1311  for (i = 0; i < winstate->numfuncs; i++)
1312  {
1313  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1314 
1315  /* Release any partition-local state of this window function */
1316  if (perfuncstate->winobj)
1317  perfuncstate->winobj->localmem = NULL;
1318  }
1319 
1320  /*
1321  * Release all partition-local memory (in particular, any partition-local
1322  * state that we might have trashed our pointers to in the above loop, and
1323  * any aggregate temp data). We don't rely on retail pfree because some
1324  * aggregates might have allocated data we don't have direct pointers to.
1325  */
1328  for (i = 0; i < winstate->numaggs; i++)
1329  {
1330  if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1332  }
1333 
1334  if (winstate->buffer)
1335  tuplestore_end(winstate->buffer);
1336  winstate->buffer = NULL;
1337  winstate->partition_spooled = false;
1338 }
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
WindowStatePerAgg peragg
Definition: execnodes.h:1961
Tuplestorestate * buffer
Definition: execnodes.h:1964
MemoryContext aggcontext
Definition: execnodes.h:1999
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
MemoryContext aggcontext
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
bool partition_spooled
Definition: execnodes.h:2005
int i
MemoryContext partcontext
Definition: execnodes.h:1998

◆ row_is_in_frame()

static int row_is_in_frame ( WindowAggState winstate,
int64  pos,
TupleTableSlot slot 
)
static

Definition at line 1357 of file nodeWindowAgg.c.

References are_peers(), Assert, WindowAggState::currentpos, DatumGetInt64, WindowAggState::endOffsetValue, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, WindowAggState::frameOptions, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, ScanState::ss_ScanTupleSlot, update_frameheadpos(), update_frametailpos(), and update_grouptailpos().

Referenced by eval_windowaggregates(), and WinGetFuncArgInFrame().

1358 {
1359  int frameOptions = winstate->frameOptions;
1360 
1361  Assert(pos >= 0); /* else caller error */
1362 
1363  /*
1364  * First, check frame starting conditions. We might as well delegate this
1365  * to update_frameheadpos always; it doesn't add any notable cost.
1366  */
1367  update_frameheadpos(winstate);
1368  if (pos < winstate->frameheadpos)
1369  return 0;
1370 
1371  /*
1372  * Okay so far, now check frame ending conditions. Here, we avoid calling
1373  * update_frametailpos in simple cases, so as not to spool tuples further
1374  * ahead than necessary.
1375  */
1376  if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1377  {
1378  if (frameOptions & FRAMEOPTION_ROWS)
1379  {
1380  /* rows after current row are out of frame */
1381  if (pos > winstate->currentpos)
1382  return -1;
1383  }
1384  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1385  {
1386  /* following row that is not peer is out of frame */
1387  if (pos > winstate->currentpos &&
1388  !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1389  return -1;
1390  }
1391  else
1392  Assert(false);
1393  }
1394  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1395  {
1396  if (frameOptions & FRAMEOPTION_ROWS)
1397  {
1398  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1399 
1400  /* rows after current row + offset are out of frame */
1401  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1402  offset = -offset;
1403 
1404  if (pos > winstate->currentpos + offset)
1405  return -1;
1406  }
1407  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1408  {
1409  /* hard cases, so delegate to update_frametailpos */
1410  update_frametailpos(winstate);
1411  if (pos >= winstate->frametailpos)
1412  return -1;
1413  }
1414  else
1415  Assert(false);
1416  }
1417 
1418  /* Check exclusion clause */
1419  if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1420  {
1421  if (pos == winstate->currentpos)
1422  return 0;
1423  }
1424  else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1425  ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1426  pos != winstate->currentpos))
1427  {
1428  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1429 
1430  /* If no ORDER BY, all rows are peers with each other */
1431  if (node->ordNumCols == 0)
1432  return 0;
1433  /* Otherwise, check the group boundaries */
1434  if (pos >= winstate->groupheadpos)
1435  {
1436  update_grouptailpos(winstate);
1437  if (pos < winstate->grouptailpos)
1438  return 0;
1439  }
1440  }
1441 
1442  /* If we get here, it's in frame */
1443  return 1;
1444 }
int ordNumCols
Definition: plannodes.h:820
static void update_grouptailpos(WindowAggState *winstate)
ScanState ss
Definition: execnodes.h:1953
int64 groupheadpos
Definition: execnodes.h:1995
static void update_frametailpos(WindowAggState *winstate)
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
static void update_frameheadpos(WindowAggState *winstate)
PlanState ps
Definition: execnodes.h:1192
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:521
#define DatumGetInt64(X)
Definition: postgres.h:592
Datum endOffsetValue
Definition: execnodes.h:1982
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:515
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:520
Plan * plan
Definition: execnodes.h:912
int64 frametailpos
Definition: execnodes.h:1972
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define Assert(condition)
Definition: c.h:699
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:522
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:526
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:507
int64 currentpos
Definition: execnodes.h:1970
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:517
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1234 of file nodeWindowAgg.c.

References WindowAggState::buffer, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_per_query_memory, ExecCopySlot(), ExecProcNode(), ExecQualAndReset(), WindowAggState::first_part_slot, MemoryContextSwitchTo(), WindowAggState::more_partitions, outerPlan, outerPlanState, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, WindowAggState::spooled_rows, WindowAggState::ss, WindowAggState::tmpcontext, TupIsNull, tuplestore_in_memory(), and tuplestore_puttupleslot().

Referenced by ExecWindowAgg(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), window_gettupleslot(), WinGetFuncArgInPartition(), and WinGetPartitionRowCount().

1235 {
1236  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1238  TupleTableSlot *outerslot;
1239  MemoryContext oldcontext;
1240 
1241  if (!winstate->buffer)
1242  return; /* just a safety check */
1243  if (winstate->partition_spooled)
1244  return; /* whole partition done already */
1245 
1246  /*
1247  * If the tuplestore has spilled to disk, alternate reading and writing
1248  * becomes quite expensive due to frequent buffer flushes. It's cheaper
1249  * to force the entire partition to get spooled in one go.
1250  *
1251  * XXX this is a horrid kluge --- it'd be better to fix the performance
1252  * problem inside tuplestore. FIXME
1253  */
1254  if (!tuplestore_in_memory(winstate->buffer))
1255  pos = -1;
1256 
1257  outerPlan = outerPlanState(winstate);
1258 
1259  /* Must be in query context to call outerplan */
1261 
1262  while (winstate->spooled_rows <= pos || pos == -1)
1263  {
1264  outerslot = ExecProcNode(outerPlan);
1265  if (TupIsNull(outerslot))
1266  {
1267  /* reached the end of the last partition */
1268  winstate->partition_spooled = true;
1269  winstate->more_partitions = false;
1270  break;
1271  }
1272 
1273  if (node->partNumCols > 0)
1274  {
1275  ExprContext *econtext = winstate->tmpcontext;
1276 
1277  econtext->ecxt_innertuple = winstate->first_part_slot;
1278  econtext->ecxt_outertuple = outerslot;
1279 
1280  /* Check if this tuple still belongs to the current partition */
1281  if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1282  {
1283  /*
1284  * end of partition; copy the tuple for the next cycle.
1285  */
1286  ExecCopySlot(winstate->first_part_slot, outerslot);
1287  winstate->partition_spooled = true;
1288  winstate->more_partitions = true;
1289  break;
1290  }
1291  }
1292 
1293  /* Still in partition, so save it into the tuplestore */
1294  tuplestore_puttupleslot(winstate->buffer, outerslot);
1295  winstate->spooled_rows++;
1296  }
1297 
1298  MemoryContextSwitchTo(oldcontext);
1299 }
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1455
ExprState * partEqfunction
Definition: execnodes.h:1962
#define outerPlanState(node)
Definition: execnodes.h:966
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:220
#define TupIsNull(slot)
Definition: tuptable.h:146
int partNumCols
Definition: plannodes.h:817
Tuplestorestate * buffer
Definition: execnodes.h:1964
#define outerPlan(node)
Definition: plannodes.h:176
int64 spooled_rows
Definition: execnodes.h:1969
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:388
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:233
Plan * plan
Definition: execnodes.h:912
bool more_partitions
Definition: execnodes.h:2007
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
bool partition_spooled
Definition: execnodes.h:2005

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1457 of file nodeWindowAgg.c.

References are_peers(), Assert, BoolGetDatum, WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, DatumGetBool, DatumGetInt64, ExprContext::ecxt_per_query_memory, elog, ERROR, ExecClearTuple(), ExecCopySlot(), WindowAggState::framehead_ptr, WindowAggState::framehead_slot, WindowAggState::framehead_valid, WindowAggState::frameheadgroup, WindowAggState::frameheadpos, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, FRAMEOPTION_START_OFFSET_PRECEDING, FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, FunctionCall5Coll(), WindowAggState::inRangeAsc, WindowAggState::inRangeColl, WindowAggState::inRangeNullsFirst, MemoryContextSwitchTo(), WindowAgg::ordColIdx, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, slot_getattr(), spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startInRangeFunc, WindowAggState::startOffsetValue, WindowAggState::temp_slot_2, TupIsNull, tuplestore_gettupleslot(), and tuplestore_select_read_pointer().

Referenced by eval_windowaggregates(), ExecWindowAgg(), row_is_in_frame(), and WinGetFuncArgInFrame().

1458 {
1459  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1460  int frameOptions = winstate->frameOptions;
1461  MemoryContext oldcontext;
1462 
1463  if (winstate->framehead_valid)
1464  return; /* already known for current row */
1465 
1466  /* We may be called in a short-lived context */
1468 
1469  if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1470  {
1471  /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1472  winstate->frameheadpos = 0;
1473  winstate->framehead_valid = true;
1474  }
1475  else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1476  {
1477  if (frameOptions & FRAMEOPTION_ROWS)
1478  {
1479  /* In ROWS mode, frame head is the same as current */
1480  winstate->frameheadpos = winstate->currentpos;
1481  winstate->framehead_valid = true;
1482  }
1483  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1484  {
1485  /* If no ORDER BY, all rows are peers with each other */
1486  if (node->ordNumCols == 0)
1487  {
1488  winstate->frameheadpos = 0;
1489  winstate->framehead_valid = true;
1490  MemoryContextSwitchTo(oldcontext);
1491  return;
1492  }
1493 
1494  /*
1495  * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1496  * first row that is a peer of current row. We keep a copy of the
1497  * last-known frame head row in framehead_slot, and advance as
1498  * necessary. Note that if we reach end of partition, we will
1499  * leave frameheadpos = end+1 and framehead_slot empty.
1500  */
1502  winstate->framehead_ptr);
1503  if (winstate->frameheadpos == 0 &&
1504  TupIsNull(winstate->framehead_slot))
1505  {
1506  /* fetch first row into framehead_slot, if we didn't already */
1507  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1508  winstate->framehead_slot))
1509  elog(ERROR, "unexpected end of tuplestore");
1510  }
1511 
1512  while (!TupIsNull(winstate->framehead_slot))
1513  {
1514  if (are_peers(winstate, winstate->framehead_slot,
1515  winstate->ss.ss_ScanTupleSlot))
1516  break; /* this row is the correct frame head */
1517  /* Note we advance frameheadpos even if the fetch fails */
1518  winstate->frameheadpos++;
1519  spool_tuples(winstate, winstate->frameheadpos);
1520  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1521  winstate->framehead_slot))
1522  break; /* end of partition */
1523  }
1524  winstate->framehead_valid = true;
1525  }
1526  else
1527  Assert(false);
1528  }
1529  else if (frameOptions & FRAMEOPTION_START_OFFSET)
1530  {
1531  if (frameOptions & FRAMEOPTION_ROWS)
1532  {
1533  /* In ROWS mode, bound is physically n before/after current */
1534  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1535 
1536  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1537  offset = -offset;
1538 
1539  winstate->frameheadpos = winstate->currentpos + offset;
1540  /* frame head can't go before first row */
1541  if (winstate->frameheadpos < 0)
1542  winstate->frameheadpos = 0;
1543  else if (winstate->frameheadpos > winstate->currentpos + 1)
1544  {
1545  /* make sure frameheadpos is not past end of partition */
1546  spool_tuples(winstate, winstate->frameheadpos - 1);
1547  if (winstate->frameheadpos > winstate->spooled_rows)
1548  winstate->frameheadpos = winstate->spooled_rows;
1549  }
1550  winstate->framehead_valid = true;
1551  }
1552  else if (frameOptions & FRAMEOPTION_RANGE)
1553  {
1554  /*
1555  * In RANGE START_OFFSET mode, frame head is the first row that
1556  * satisfies the in_range constraint relative to the current row.
1557  * We keep a copy of the last-known frame head row in
1558  * framehead_slot, and advance as necessary. Note that if we
1559  * reach end of partition, we will leave frameheadpos = end+1 and
1560  * framehead_slot empty.
1561  */
1562  int sortCol = node->ordColIdx[0];
1563  bool sub,
1564  less;
1565 
1566  /* Precompute flags for in_range checks */
1567  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1568  sub = true; /* subtract startOffset from current row */
1569  else
1570  sub = false; /* add it */
1571  less = false; /* normally, we want frame head >= sum */
1572  /* If sort order is descending, flip both flags */
1573  if (!winstate->inRangeAsc)
1574  {
1575  sub = !sub;
1576  less = true;
1577  }
1578 
1580  winstate->framehead_ptr);
1581  if (winstate->frameheadpos == 0 &&
1582  TupIsNull(winstate->framehead_slot))
1583  {
1584  /* fetch first row into framehead_slot, if we didn't already */
1585  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1586  winstate->framehead_slot))
1587  elog(ERROR, "unexpected end of tuplestore");
1588  }
1589 
1590  while (!TupIsNull(winstate->framehead_slot))
1591  {
1592  Datum headval,
1593  currval;
1594  bool headisnull,
1595  currisnull;
1596 
1597  headval = slot_getattr(winstate->framehead_slot, sortCol,
1598  &headisnull);
1599  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1600  &currisnull);
1601  if (headisnull || currisnull)
1602  {
1603  /* order of the rows depends only on nulls_first */
1604  if (winstate->inRangeNullsFirst)
1605  {
1606  /* advance head if head is null and curr is not */
1607  if (!headisnull || currisnull)
1608  break;
1609  }
1610  else
1611  {
1612  /* advance head if head is not null and curr is null */
1613  if (headisnull || !currisnull)
1614  break;
1615  }
1616  }
1617  else
1618  {
1620  winstate->inRangeColl,
1621  headval,
1622  currval,
1623  winstate->startOffsetValue,
1624  BoolGetDatum(sub),
1625  BoolGetDatum(less))))
1626  break; /* this row is the correct frame head */
1627  }
1628  /* Note we advance frameheadpos even if the fetch fails */
1629  winstate->frameheadpos++;
1630  spool_tuples(winstate, winstate->frameheadpos);
1631  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1632  winstate->framehead_slot))
1633  break; /* end of partition */
1634  }
1635  winstate->framehead_valid = true;
1636  }
1637  else if (frameOptions & FRAMEOPTION_GROUPS)
1638  {
1639  /*
1640  * In GROUPS START_OFFSET mode, frame head is the first row of the
1641  * first peer group whose number satisfies the offset constraint.
1642  * We keep a copy of the last-known frame head row in
1643  * framehead_slot, and advance as necessary. Note that if we
1644  * reach end of partition, we will leave frameheadpos = end+1 and
1645  * framehead_slot empty.
1646  */
1647  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1648  int64 minheadgroup;
1649 
1650  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1651  minheadgroup = winstate->currentgroup - offset;
1652  else
1653  minheadgroup = winstate->currentgroup + offset;
1654 
1656  winstate->framehead_ptr);
1657  if (winstate->frameheadpos == 0 &&
1658  TupIsNull(winstate->framehead_slot))
1659  {
1660  /* fetch first row into framehead_slot, if we didn't already */
1661  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1662  winstate->framehead_slot))
1663  elog(ERROR, "unexpected end of tuplestore");
1664  }
1665 
1666  while (!TupIsNull(winstate->framehead_slot))
1667  {
1668  if (winstate->frameheadgroup >= minheadgroup)
1669  break; /* this row is the correct frame head */
1670  ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1671  /* Note we advance frameheadpos even if the fetch fails */
1672  winstate->frameheadpos++;
1673  spool_tuples(winstate, winstate->frameheadpos);
1674  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1675  winstate->framehead_slot))
1676  break; /* end of partition */
1677  if (!are_peers(winstate, winstate->temp_slot_2,
1678  winstate->framehead_slot))
1679  winstate->frameheadgroup++;
1680  }
1681  ExecClearTuple(winstate->temp_slot_2);
1682  winstate->framehead_valid = true;
1683  }
1684  else
1685  Assert(false);
1686  }
1687  else
1688  Assert(false);
1689 
1690  MemoryContextSwitchTo(oldcontext);
1691 }
int ordNumCols
Definition: plannodes.h:820
Datum startOffsetValue
Definition: execnodes.h:1981
ScanState ss
Definition: execnodes.h:1953
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1207
ExprContext * ps_ExprContext
Definition: execnodes.h:947
AttrNumber * ordColIdx
Definition: plannodes.h:821
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int64 currentgroup
Definition: execnodes.h:1992
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:510
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:524
static void spool_tuples(WindowAggState *winstate, int64 pos)
PlanState ps
Definition: execnodes.h:1192
bool inRangeNullsFirst
Definition: execnodes.h:1989
int64 frameheadpos
Definition: execnodes.h:1971
#define ERROR
Definition: elog.h:43
#define DatumGetInt64(X)
Definition: postgres.h:592
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
int64 frameheadgroup
Definition: execnodes.h:1993
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:516
#define DatumGetBool(X)
Definition: postgres.h:378
#define TupIsNull(slot)
Definition: tuptable.h:146
Tuplestorestate * buffer
Definition: execnodes.h:1964
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:514
int64 spooled_rows
Definition: execnodes.h:1969
uintptr_t Datum
Definition: postgres.h:367
#define BoolGetDatum(X)
Definition: postgres.h:387
Plan * plan
Definition: execnodes.h:912
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define Assert(condition)
Definition: c.h:699
FmgrInfo startInRangeFunc
Definition: execnodes.h:1985
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:507
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
int64 currentpos
Definition: execnodes.h:1970
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
bool framehead_valid
Definition: execnodes.h:2009
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1518
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508
#define elog
Definition: elog.h:219

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1704 of file nodeWindowAgg.c.

References are_peers(), Assert, BoolGetDatum, WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, DatumGetBool, DatumGetInt64, ExprContext::ecxt_per_query_memory, elog, WindowAggState::endInRangeFunc, WindowAggState::endOffsetValue, ERROR, ExecClearTuple(), ExecCopySlot(), FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_slot, WindowAggState::frametail_valid, WindowAggState::frametailgroup, WindowAggState::frametailpos, FunctionCall5Coll(), WindowAggState::inRangeAsc, WindowAggState::inRangeColl, WindowAggState::inRangeNullsFirst, MemoryContextSwitchTo(), WindowAgg::ordColIdx, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, slot_getattr(), spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_2, TupIsNull, tuplestore_gettupleslot(), and tuplestore_select_read_pointer().

Referenced by ExecWindowAgg(), row_is_in_frame(), and WinGetFuncArgInFrame().

1705 {
1706  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1707  int frameOptions = winstate->frameOptions;
1708  MemoryContext oldcontext;
1709 
1710  if (winstate->frametail_valid)
1711  return; /* already known for current row */
1712 
1713  /* We may be called in a short-lived context */
1715 
1716  if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1717  {
1718  /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1719  spool_tuples(winstate, -1);
1720  winstate->frametailpos = winstate->spooled_rows;
1721  winstate->frametail_valid = true;
1722  }
1723  else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1724  {
1725  if (frameOptions & FRAMEOPTION_ROWS)
1726  {
1727  /* In ROWS mode, exactly the rows up to current are in frame */
1728  winstate->frametailpos = winstate->currentpos + 1;
1729  winstate->frametail_valid = true;
1730  }
1731  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1732  {
1733  /* If no ORDER BY, all rows are peers with each other */
1734  if (node->ordNumCols == 0)
1735  {
1736  spool_tuples(winstate, -1);
1737  winstate->frametailpos = winstate->spooled_rows;
1738  winstate->frametail_valid = true;
1739  MemoryContextSwitchTo(oldcontext);
1740  return;
1741  }
1742 
1743  /*
1744  * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1745  * row that is a peer of current row, frame tail is the row after
1746  * that (if any). We keep a copy of the last-known frame tail row
1747  * in frametail_slot, and advance as necessary. Note that if we
1748  * reach end of partition, we will leave frametailpos = end+1 and
1749  * frametail_slot empty.
1750  */
1752  winstate->frametail_ptr);
1753  if (winstate->frametailpos == 0 &&
1754  TupIsNull(winstate->frametail_slot))
1755  {
1756  /* fetch first row into frametail_slot, if we didn't already */
1757  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1758  winstate->frametail_slot))
1759  elog(ERROR, "unexpected end of tuplestore");
1760  }
1761 
1762  while (!TupIsNull(winstate->frametail_slot))
1763  {
1764  if (winstate->frametailpos > winstate->currentpos &&
1765  !are_peers(winstate, winstate->frametail_slot,
1766  winstate->ss.ss_ScanTupleSlot))
1767  break; /* this row is the frame tail */
1768  /* Note we advance frametailpos even if the fetch fails */
1769  winstate->frametailpos++;
1770  spool_tuples(winstate, winstate->frametailpos);
1771  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1772  winstate->frametail_slot))
1773  break; /* end of partition */
1774  }
1775  winstate->frametail_valid = true;
1776  }
1777  else
1778  Assert(false);
1779  }
1780  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1781  {
1782  if (frameOptions & FRAMEOPTION_ROWS)
1783  {
1784  /* In ROWS mode, bound is physically n before/after current */
1785  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1786 
1787  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1788  offset = -offset;
1789 
1790  winstate->frametailpos = winstate->currentpos + offset + 1;
1791  /* smallest allowable value of frametailpos is 0 */
1792  if (winstate->frametailpos < 0)
1793  winstate->frametailpos = 0;
1794  else if (winstate->frametailpos > winstate->currentpos + 1)
1795  {
1796  /* make sure frametailpos is not past end of partition */
1797  spool_tuples(winstate, winstate->frametailpos - 1);
1798  if (winstate->frametailpos > winstate->spooled_rows)
1799  winstate->frametailpos = winstate->spooled_rows;
1800  }
1801  winstate->frametail_valid = true;
1802  }
1803  else if (frameOptions & FRAMEOPTION_RANGE)
1804  {
1805  /*
1806  * In RANGE END_OFFSET mode, frame end is the last row that
1807  * satisfies the in_range constraint relative to the current row,
1808  * frame tail is the row after that (if any). We keep a copy of
1809  * the last-known frame tail row in frametail_slot, and advance as
1810  * necessary. Note that if we reach end of partition, we will
1811  * leave frametailpos = end+1 and frametail_slot empty.
1812  */
1813  int sortCol = node->ordColIdx[0];
1814  bool sub,
1815  less;
1816 
1817  /* Precompute flags for in_range checks */
1818  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1819  sub = true; /* subtract endOffset from current row */
1820  else
1821  sub = false; /* add it */
1822  less = true; /* normally, we want frame tail <= sum */
1823  /* If sort order is descending, flip both flags */
1824  if (!winstate->inRangeAsc)
1825  {
1826  sub = !sub;
1827  less = false;
1828  }
1829 
1831  winstate->frametail_ptr);
1832  if (winstate->frametailpos == 0 &&
1833  TupIsNull(winstate->frametail_slot))
1834  {
1835  /* fetch first row into frametail_slot, if we didn't already */
1836  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1837  winstate->frametail_slot))
1838  elog(ERROR, "unexpected end of tuplestore");
1839  }
1840 
1841  while (!TupIsNull(winstate->frametail_slot))
1842  {
1843  Datum tailval,
1844  currval;
1845  bool tailisnull,
1846  currisnull;
1847 
1848  tailval = slot_getattr(winstate->frametail_slot, sortCol,
1849  &tailisnull);
1850  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1851  &currisnull);
1852  if (tailisnull || currisnull)
1853  {
1854  /* order of the rows depends only on nulls_first */
1855  if (winstate->inRangeNullsFirst)
1856  {
1857  /* advance tail if tail is null or curr is not */
1858  if (!tailisnull)
1859  break;
1860  }
1861  else
1862  {
1863  /* advance tail if tail is not null or curr is null */
1864  if (!currisnull)
1865  break;
1866  }
1867  }
1868  else
1869  {
1871  winstate->inRangeColl,
1872  tailval,
1873  currval,
1874  winstate->endOffsetValue,
1875  BoolGetDatum(sub),
1876  BoolGetDatum(less))))
1877  break; /* this row is the correct frame tail */
1878  }
1879  /* Note we advance frametailpos even if the fetch fails */
1880  winstate->frametailpos++;
1881  spool_tuples(winstate, winstate->frametailpos);
1882  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1883  winstate->frametail_slot))
1884  break; /* end of partition */
1885  }
1886  winstate->frametail_valid = true;
1887  }
1888  else if (frameOptions & FRAMEOPTION_GROUPS)
1889  {
1890  /*
1891  * In GROUPS END_OFFSET mode, frame end is the last row of the
1892  * last peer group whose number satisfies the offset constraint,
1893  * and frame tail is the row after that (if any). We keep a copy
1894  * of the last-known frame tail row in frametail_slot, and advance
1895  * as necessary. Note that if we reach end of partition, we will
1896  * leave frametailpos = end+1 and frametail_slot empty.
1897  */
1898  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1899  int64 maxtailgroup;
1900 
1901  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1902  maxtailgroup = winstate->currentgroup - offset;
1903  else
1904  maxtailgroup = winstate->currentgroup + offset;
1905 
1907  winstate->frametail_ptr);
1908  if (winstate->frametailpos == 0 &&
1909  TupIsNull(winstate->frametail_slot))
1910  {
1911  /* fetch first row into frametail_slot, if we didn't already */
1912  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1913  winstate->frametail_slot))
1914  elog(ERROR, "unexpected end of tuplestore");
1915  }
1916 
1917  while (!TupIsNull(winstate->frametail_slot))
1918  {
1919  if (winstate->frametailgroup > maxtailgroup)
1920  break; /* this row is the correct frame tail */
1921  ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
1922  /* Note we advance frametailpos even if the fetch fails */
1923  winstate->frametailpos++;
1924  spool_tuples(winstate, winstate->frametailpos);
1925  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1926  winstate->frametail_slot))
1927  break; /* end of partition */
1928  if (!are_peers(winstate, winstate->temp_slot_2,
1929  winstate->frametail_slot))
1930  winstate->frametailgroup++;
1931  }
1932  ExecClearTuple(winstate->temp_slot_2);
1933  winstate->frametail_valid = true;
1934  }
1935  else
1936  Assert(false);
1937  }
1938  else
1939  Assert(false);
1940 
1941  MemoryContextSwitchTo(oldcontext);
1942 }
int ordNumCols
Definition: plannodes.h:820
ScanState ss
Definition: execnodes.h:1953
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1207
ExprContext * ps_ExprContext
Definition: execnodes.h:947
AttrNumber * ordColIdx
Definition: plannodes.h:821
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int64 currentgroup
Definition: execnodes.h:1992
bool frametail_valid
Definition: execnodes.h:2011
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
int64 frametailgroup
Definition: execnodes.h:1994
static void spool_tuples(WindowAggState *winstate, int64 pos)
PlanState ps
Definition: execnodes.h:1192
bool inRangeNullsFirst
Definition: execnodes.h:1989
#define ERROR
Definition: elog.h:43
FmgrInfo endInRangeFunc
Definition: execnodes.h:1986
#define DatumGetInt64(X)
Definition: postgres.h:592
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
Datum endOffsetValue
Definition: execnodes.h:1982
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:515
#define DatumGetBool(X)
Definition: postgres.h:378
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:513
#define TupIsNull(slot)
Definition: tuptable.h:146
Tuplestorestate * buffer
Definition: execnodes.h:1964
int64 spooled_rows
Definition: execnodes.h:1969
uintptr_t Datum
Definition: postgres.h:367
#define BoolGetDatum(X)
Definition: postgres.h:387
Plan * plan
Definition: execnodes.h:912
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
int64 frametailpos
Definition: execnodes.h:1972
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define Assert(condition)
Definition: c.h:699
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:526
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:507
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
int64 currentpos
Definition: execnodes.h:1970
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:517
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1518
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508
#define elog
Definition: elog.h:219

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 1951 of file nodeWindowAgg.c.

References are_peers(), Assert, WindowAggState::buffer, WindowAggState::currentpos, ExprContext::ecxt_per_query_memory, ExecClearTuple(), WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, MemoryContextSwitchTo(), WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_2, tuplestore_gettupleslot(), and tuplestore_select_read_pointer().

Referenced by ExecWindowAgg(), row_is_in_frame(), and WinGetFuncArgInFrame().

1952 {
1953  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1954  MemoryContext oldcontext;
1955 
1956  if (winstate->grouptail_valid)
1957  return; /* already known for current row */
1958 
1959  /* We may be called in a short-lived context */
1961 
1962  /* If no ORDER BY, all rows are peers with each other */
1963  if (node->ordNumCols == 0)
1964  {
1965  spool_tuples(winstate, -1);
1966  winstate->grouptailpos = winstate->spooled_rows;
1967  winstate->grouptail_valid = true;
1968  MemoryContextSwitchTo(oldcontext);
1969  return;
1970  }
1971 
1972  /*
1973  * Because grouptail_valid is reset only when current row advances into a
1974  * new peer group, we always reach here knowing that grouptailpos needs to
1975  * be advanced by at least one row. Hence, unlike the otherwise similar
1976  * case for frame tail tracking, we do not need persistent storage of the
1977  * group tail row.
1978  */
1979  Assert(winstate->grouptailpos <= winstate->currentpos);
1981  winstate->grouptail_ptr);
1982  for (;;)
1983  {
1984  /* Note we advance grouptailpos even if the fetch fails */
1985  winstate->grouptailpos++;
1986  spool_tuples(winstate, winstate->grouptailpos);
1987  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1988  winstate->temp_slot_2))
1989  break; /* end of partition */
1990  if (winstate->grouptailpos > winstate->currentpos &&
1991  !are_peers(winstate, winstate->temp_slot_2,
1992  winstate->ss.ss_ScanTupleSlot))
1993  break; /* this row is the group tail */
1994  }
1995  ExecClearTuple(winstate->temp_slot_2);
1996  winstate->grouptail_valid = true;
1997 
1998  MemoryContextSwitchTo(oldcontext);
1999 }
int ordNumCols
Definition: plannodes.h:820
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
int64 grouptailpos
Definition: execnodes.h:1996
static void spool_tuples(WindowAggState *winstate, int64 pos)
PlanState ps
Definition: execnodes.h:1192
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
Tuplestorestate * buffer
Definition: execnodes.h:1964
int64 spooled_rows
Definition: execnodes.h:1969
Plan * plan
Definition: execnodes.h:912
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
#define Assert(condition)
Definition: c.h:699
bool grouptail_valid
Definition: execnodes.h:2013
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
int64 currentpos
Definition: execnodes.h:1970
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473

◆ window_gettupleslot()

static bool window_gettupleslot ( WindowObject  winobj,
int64  pos,
TupleTableSlot slot 
)
static

Definition at line 2898 of file nodeWindowAgg.c.

References Assert, WindowAggState::buffer, CHECK_FOR_INTERRUPTS, ExprContext::ecxt_per_query_memory, elog, ERROR, WindowObjectData::markpos, MemoryContextSwitchTo(), ScanState::ps, PlanState::ps_ExprContext, WindowObjectData::readptr, WindowObjectData::seekpos, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, tuplestore_advance(), tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_skiptuples(), and WindowObjectData::winstate.

Referenced by eval_windowaggregates(), WinGetFuncArgInFrame(), WinGetFuncArgInPartition(), and WinRowsArePeers().

2899 {
2900  WindowAggState *winstate = winobj->winstate;
2901  MemoryContext oldcontext;
2902 
2903  /* often called repeatedly in a row */
2905 
2906  /* Don't allow passing -1 to spool_tuples here */
2907  if (pos < 0)
2908  return false;
2909 
2910  /* If necessary, fetch the tuple into the spool */
2911  spool_tuples(winstate, pos);
2912 
2913  if (pos >= winstate->spooled_rows)
2914  return false;
2915 
2916  if (pos < winobj->markpos)
2917  elog(ERROR, "cannot fetch row before WindowObject's mark position");
2918 
2920 
2921  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
2922 
2923  /*
2924  * Advance or rewind until we are within one tuple of the one we want.
2925  */
2926  if (winobj->seekpos < pos - 1)
2927  {
2928  if (!tuplestore_skiptuples(winstate->buffer,
2929  pos - 1 - winobj->seekpos,
2930  true))
2931  elog(ERROR, "unexpected end of tuplestore");
2932  winobj->seekpos = pos - 1;
2933  }
2934  else if (winobj->seekpos > pos + 1)
2935  {
2936  if (!tuplestore_skiptuples(winstate->buffer,
2937  winobj->seekpos - (pos + 1),
2938  false))
2939  elog(ERROR, "unexpected end of tuplestore");
2940  winobj->seekpos = pos + 1;
2941  }
2942  else if (winobj->seekpos == pos)
2943  {
2944  /*
2945  * There's no API to refetch the tuple at the current position. We
2946  * have to move one tuple forward, and then one backward. (We don't
2947  * do it the other way because we might try to fetch the row before
2948  * our mark, which isn't allowed.) XXX this case could stand to be
2949  * optimized.
2950  */
2951  tuplestore_advance(winstate->buffer, true);
2952  winobj->seekpos++;
2953  }
2954 
2955  /*
2956  * Now we should be on the tuple immediately before or after the one we
2957  * want, so just fetch forwards or backwards as appropriate.
2958  */
2959  if (winobj->seekpos > pos)
2960  {
2961  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
2962  elog(ERROR, "unexpected end of tuplestore");
2963  winobj->seekpos--;
2964  }
2965  else
2966  {
2967  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
2968  elog(ERROR, "unexpected end of tuplestore");
2969  winobj->seekpos++;
2970  }
2971 
2972  Assert(winobj->seekpos == pos);
2973 
2974  MemoryContextSwitchTo(oldcontext);
2975 
2976  return true;
2977 }
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1110
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static void spool_tuples(WindowAggState *winstate, int64 pos)
PlanState ps
Definition: execnodes.h:1192
#define ERROR
Definition: elog.h:43
Tuplestorestate * buffer
Definition: execnodes.h:1964
int64 spooled_rows
Definition: execnodes.h:1969
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
#define Assert(condition)
Definition: c.h:699
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1135
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219

◆ WinGetCurrentPosition()

int64 WinGetCurrentPosition ( WindowObject  winobj)

Definition at line 3013 of file nodeWindowAgg.c.

References Assert, WindowAggState::currentpos, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), window_cume_dist(), window_percent_rank(), window_rank(), and window_row_number().

3014 {
3015  Assert(WindowObjectIsValid(winobj));
3016  return winobj->winstate->currentpos;
3017 }
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Assert(condition)
Definition: c.h:699
int64 currentpos
Definition: execnodes.h:1970

◆ WinGetFuncArgCurrent()

Datum WinGetFuncArgCurrent ( WindowObject  winobj,
int  argno,
bool isnull 
)

Definition at line 3421 of file nodeWindowAgg.c.

References WindowObjectData::argstates, Assert, ExprContext::ecxt_outertuple, ExecEvalExpr(), list_nth(), ScanState::ps, PlanState::ps_ExprContext, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by leadlag_common(), window_nth_value(), and window_ntile().

3422 {
3423  WindowAggState *winstate;
3424  ExprContext *econtext;
3425 
3426  Assert(WindowObjectIsValid(winobj));
3427  winstate = winobj->winstate;
3428 
3429  econtext = winstate->ss.ps.ps_ExprContext;
3430 
3431  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3432  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3433  econtext, isnull);
3434 }
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
PlanState ps
Definition: execnodes.h:1192
void * list_nth(const List *list, int n)
Definition: list.c:410
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699

◆ WinGetFuncArgInFrame()

Datum WinGetFuncArgInFrame ( WindowObject  winobj,
int  argno,
int  relpos,
int  seektype,
bool  set_mark,
bool isnull,
bool isout 
)

Definition at line 3226 of file nodeWindowAgg.c.

References WindowObjectData::argstates, Assert, WindowAggState::currentpos, ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), WindowAggState::frameheadpos, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, WindowAggState::frameOptions, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAggState::grouptailpos, list_nth(), Max, Min, ScanState::ps, PlanState::ps_ExprContext, row_is_in_frame(), WindowAggState::ss, WindowAggState::temp_slot_1, update_frameheadpos(), update_frametailpos(), update_grouptailpos(), window_gettupleslot(), WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, WINDOW_SEEK_TAIL, WindowObjectIsValid, WinSetMarkPosition(), and WindowObjectData::winstate.

Referenced by window_first_value(), window_last_value(), and window_nth_value().

3229 {
3230  WindowAggState *winstate;
3231  ExprContext *econtext;
3232  TupleTableSlot *slot;
3233  int64 abs_pos;
3234  int64 mark_pos;
3235 
3236  Assert(WindowObjectIsValid(winobj));
3237  winstate = winobj->winstate;
3238  econtext = winstate->ss.ps.ps_ExprContext;
3239  slot = winstate->temp_slot_1;
3240 
3241  switch (seektype)
3242  {
3243  case WINDOW_SEEK_CURRENT:
3244  elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3245  abs_pos = mark_pos = 0; /* keep compiler quiet */
3246  break;
3247  case WINDOW_SEEK_HEAD:
3248  /* rejecting relpos < 0 is easy and simplifies code below */
3249  if (relpos < 0)
3250  goto out_of_frame;
3251  update_frameheadpos(winstate);
3252  abs_pos = winstate->frameheadpos + relpos;
3253  mark_pos = abs_pos;
3254 
3255  /*
3256  * Account for exclusion option if one is active, but advance only
3257  * abs_pos not mark_pos. This prevents changes of the current
3258  * row's peer group from resulting in trying to fetch a row before
3259  * some previous mark position.
3260  *
3261  * Note that in some corner cases such as current row being
3262  * outside frame, these calculations are theoretically too simple,
3263  * but it doesn't matter because we'll end up deciding the row is
3264  * out of frame. We do not attempt to avoid fetching rows past
3265  * end of frame; that would happen in some cases anyway.
3266  */
3267  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3268  {
3269  case 0:
3270  /* no adjustment needed */
3271  break;
3273  if (abs_pos >= winstate->currentpos &&
3274  winstate->currentpos >= winstate->frameheadpos)
3275  abs_pos++;
3276  break;
3278  update_grouptailpos(winstate);
3279  if (abs_pos >= winstate->groupheadpos &&
3280  winstate->grouptailpos > winstate->frameheadpos)
3281  {
3282  int64 overlapstart = Max(winstate->groupheadpos,
3283  winstate->frameheadpos);
3284 
3285  abs_pos += winstate->grouptailpos - overlapstart;
3286  }
3287  break;
3289  update_grouptailpos(winstate);
3290  if (abs_pos >= winstate->groupheadpos &&
3291  winstate->grouptailpos > winstate->frameheadpos)
3292  {
3293  int64 overlapstart = Max(winstate->groupheadpos,
3294  winstate->frameheadpos);
3295 
3296  if (abs_pos == overlapstart)
3297  abs_pos = winstate->currentpos;
3298  else
3299  abs_pos += winstate->grouptailpos - overlapstart - 1;
3300  }
3301  break;
3302  default:
3303  elog(ERROR, "unrecognized frame option state: 0x%x",
3304  winstate->frameOptions);
3305  break;
3306  }
3307  break;
3308  case WINDOW_SEEK_TAIL:
3309  /* rejecting relpos > 0 is easy and simplifies code below */
3310  if (relpos > 0)
3311  goto out_of_frame;
3312  update_frametailpos(winstate);
3313  abs_pos = winstate->frametailpos - 1 + relpos;
3314 
3315  /*
3316  * Account for exclusion option if one is active. If there is no
3317  * exclusion, we can safely set the mark at the accessed row. But
3318  * if there is, we can only mark the frame start, because we can't
3319  * be sure how far back in the frame the exclusion might cause us
3320  * to fetch in future. Furthermore, we have to actually check
3321  * against frameheadpos here, since it's unsafe to try to fetch a
3322  * row before frame start if the mark might be there already.
3323  */
3324  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3325  {
3326  case 0:
3327  /* no adjustment needed */
3328  mark_pos = abs_pos;
3329  break;
3331  if (abs_pos <= winstate->currentpos &&
3332  winstate->currentpos < winstate->frametailpos)
3333  abs_pos--;
3334  update_frameheadpos(winstate);
3335  if (abs_pos < winstate->frameheadpos)
3336  goto out_of_frame;
3337  mark_pos = winstate->frameheadpos;
3338  break;
3340  update_grouptailpos(winstate);
3341  if (abs_pos < winstate->grouptailpos &&
3342  winstate->groupheadpos < winstate->frametailpos)
3343  {
3344  int64 overlapend = Min(winstate->grouptailpos,
3345  winstate->frametailpos);
3346 
3347  abs_pos -= overlapend - winstate->groupheadpos;
3348  }
3349  update_frameheadpos(winstate);
3350  if (abs_pos < winstate->frameheadpos)
3351  goto out_of_frame;
3352  mark_pos = winstate->frameheadpos;
3353  break;
3355  update_grouptailpos(winstate);
3356  if (abs_pos < winstate->grouptailpos &&
3357  winstate->groupheadpos < winstate->frametailpos)
3358  {
3359  int64 overlapend = Min(winstate->grouptailpos,
3360  winstate->frametailpos);
3361 
3362  if (abs_pos == overlapend - 1)
3363  abs_pos = winstate->currentpos;
3364  else
3365  abs_pos -= overlapend - 1 - winstate->groupheadpos;
3366  }
3367  update_frameheadpos(winstate);
3368  if (abs_pos < winstate->frameheadpos)
3369  goto out_of_frame;
3370  mark_pos = winstate->frameheadpos;
3371  break;
3372  default:
3373  elog(ERROR, "unrecognized frame option state: 0x%x",
3374  winstate->frameOptions);
3375  mark_pos = 0; /* keep compiler quiet */
3376  break;
3377  }
3378  break;
3379  default:
3380  elog(ERROR, "unrecognized window seek type: %d", seektype);
3381  abs_pos = mark_pos = 0; /* keep compiler quiet */
3382  break;
3383  }
3384 
3385  if (!window_gettupleslot(winobj, abs_pos, slot))
3386  goto out_of_frame;
3387 
3388  /* The code above does not detect all out-of-frame cases, so check */
3389  if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3390  goto out_of_frame;
3391 
3392  if (isout)
3393  *isout = false;
3394  if (set_mark)
3395  WinSetMarkPosition(winobj, mark_pos);
3396  econtext->ecxt_outertuple = slot;
3397  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3398  econtext, isnull);
3399 
3400 out_of_frame:
3401  if (isout)
3402  *isout = true;
3403  *isnull = true;
3404  return (Datum) 0;
3405 }
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:528
static void update_grouptailpos(WindowAggState *winstate)
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
ScanState ss
Definition: execnodes.h:1953
int64 groupheadpos
Definition: execnodes.h:1995
ExprContext * ps_ExprContext
Definition: execnodes.h:947
#define Min(x, y)
Definition: c.h:857
static void update_frametailpos(WindowAggState *winstate)
int64 grouptailpos
Definition: execnodes.h:1996
static void update_frameheadpos(WindowAggState *winstate)
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
PlanState ps
Definition: execnodes.h:1192
int64 frameheadpos
Definition: execnodes.h:1971
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:521
#define ERROR
Definition: elog.h:43
void * list_nth(const List *list, int n)
Definition: list.c:410
static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
uintptr_t Datum
Definition: postgres.h:367
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:520
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Max(x, y)
Definition: c.h:851
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32
int64 frametailpos
Definition: execnodes.h:1972
#define Assert(condition)
Definition: c.h:699
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:522
int64 currentpos
Definition: execnodes.h:1970
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
#define elog
Definition: elog.h:219

◆ WinGetFuncArgInPartition()

Datum WinGetFuncArgInPartition ( WindowObject  winobj,
int  argno,
int  relpos,
int  seektype,
bool  set_mark,
bool isnull,
bool isout 
)

Definition at line 3138 of file nodeWindowAgg.c.

References WindowObjectData::argstates, Assert, WindowAggState::currentpos, ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), list_nth(), ScanState::ps, PlanState::ps_ExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, WindowAggState::temp_slot_1, window_gettupleslot(), WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, WINDOW_SEEK_TAIL, WindowObjectIsValid, WinSetMarkPosition(), and WindowObjectData::winstate.

Referenced by leadlag_common().

3141 {
3142  WindowAggState *winstate;
3143  ExprContext *econtext;
3144  TupleTableSlot *slot;
3145  bool gottuple;
3146  int64 abs_pos;
3147 
3148  Assert(WindowObjectIsValid(winobj));
3149  winstate = winobj->winstate;
3150  econtext = winstate->ss.ps.ps_ExprContext;
3151  slot = winstate->temp_slot_1;
3152 
3153  switch (seektype)
3154  {
3155  case WINDOW_SEEK_CURRENT:
3156  abs_pos = winstate->currentpos + relpos;
3157  break;
3158  case WINDOW_SEEK_HEAD:
3159  abs_pos = relpos;
3160  break;
3161  case WINDOW_SEEK_TAIL:
3162  spool_tuples(winstate, -1);
3163  abs_pos = winstate->spooled_rows - 1 + relpos;
3164  break;
3165  default:
3166  elog(ERROR, "unrecognized window seek type: %d", seektype);
3167  abs_pos = 0; /* keep compiler quiet */
3168  break;
3169  }
3170 
3171  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3172 
3173  if (!gottuple)
3174  {
3175  if (isout)
3176  *isout = true;
3177  *isnull = true;
3178  return (Datum) 0;
3179  }
3180  else
3181  {
3182  if (isout)
3183  *isout = false;
3184  if (set_mark)
3185  WinSetMarkPosition(winobj, abs_pos);
3186  econtext->ecxt_outertuple = slot;
3187  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3188  econtext, isnull);
3189  }
3190 }
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
ScanState ss
Definition: execnodes.h:1953
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
static void spool_tuples(WindowAggState *winstate, int64 pos)
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
PlanState ps
Definition: execnodes.h:1192
#define ERROR
Definition: elog.h:43
void * list_nth(const List *list, int n)
Definition: list.c:410
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
int64 spooled_rows
Definition: execnodes.h:1969
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
uintptr_t Datum
Definition: postgres.h:367
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32
#define Assert(condition)
Definition: c.h:699
int64 currentpos
Definition: execnodes.h:1970
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
#define elog
Definition: elog.h:219

◆ WinGetPartitionLocalMemory()

void* WinGetPartitionLocalMemory ( WindowObject  winobj,
Size  sz 
)

Definition at line 2998 of file nodeWindowAgg.c.

References Assert, WindowObjectData::localmem, MemoryContextAllocZero(), WindowAggState::partcontext, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), window_cume_dist(), window_dense_rank(), window_ntile(), window_percent_rank(), and window_rank().

2999 {
3000  Assert(WindowObjectIsValid(winobj));
3001  if (winobj->localmem == NULL)
3002  winobj->localmem =
3004  return winobj->localmem;
3005 }
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:814
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Assert(condition)
Definition: c.h:699
MemoryContext partcontext
Definition: execnodes.h:1998

◆ WinGetPartitionRowCount()

int64 WinGetPartitionRowCount ( WindowObject  winobj)

Definition at line 3028 of file nodeWindowAgg.c.

References Assert, spool_tuples(), WindowAggState::spooled_rows, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by window_cume_dist(), window_ntile(), and window_percent_rank().

3029 {
3030  Assert(WindowObjectIsValid(winobj));
3031  spool_tuples(winobj->winstate, -1);
3032  return winobj->winstate->spooled_rows;
3033 }
static void spool_tuples(WindowAggState *winstate, int64 pos)
int64 spooled_rows
Definition: execnodes.h:1969
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Assert(condition)
Definition: c.h:699

◆ WinRowsArePeers()

bool WinRowsArePeers ( WindowObject  winobj,
int64  pos1,
int64  pos2 
)

Definition at line 3081 of file nodeWindowAgg.c.

References are_peers(), Assert, elog, ERROR, ExecClearTuple(), INT64_FORMAT, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, window_gettupleslot(), WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), and window_cume_dist().

3082 {
3083  WindowAggState *winstate;
3084  WindowAgg *node;
3085  TupleTableSlot *slot1;
3086  TupleTableSlot *slot2;
3087  bool res;
3088 
3089  Assert(WindowObjectIsValid(winobj));
3090  winstate = winobj->winstate;
3091  node = (WindowAgg *) winstate->ss.ps.plan;
3092 
3093  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3094  if (node->ordNumCols == 0)
3095  return true;
3096 
3097  /*
3098  * Note: OK to use temp_slot_2 here because we aren't calling any
3099  * frame-related functions (those tend to clobber temp_slot_2).
3100  */
3101  slot1 = winstate->temp_slot_1;
3102  slot2 = winstate->temp_slot_2;
3103 
3104  if (!window_gettupleslot(winobj, pos1, slot1))
3105  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3106  pos1);
3107  if (!window_gettupleslot(winobj, pos2, slot2))
3108  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3109  pos2);
3110 
3111  res = are_peers(winstate, slot1, slot2);
3112 
3113  ExecClearTuple(slot1);
3114  ExecClearTuple(slot2);
3115 
3116  return res;
3117 }
int ordNumCols
Definition: plannodes.h:820
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
ScanState ss
Definition: execnodes.h:1953
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
PlanState ps
Definition: execnodes.h:1192
#define ERROR
Definition: elog.h:43
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
Plan * plan
Definition: execnodes.h:912
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Assert(condition)
Definition: c.h:699
#define INT64_FORMAT
Definition: c.h:367
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
#define elog
Definition: elog.h:219

◆ WinSetMarkPosition()

void WinSetMarkPosition ( WindowObject  winobj,
int64  markpos 
)

Definition at line 3046 of file nodeWindowAgg.c.

References Assert, WindowAggState::buffer, elog, ERROR, WindowObjectData::markpos, WindowObjectData::markptr, WindowObjectData::readptr, WindowObjectData::seekpos, tuplestore_select_read_pointer(), tuplestore_skiptuples(), WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by eval_windowaggregates(), rank_up(), window_row_number(), WinGetFuncArgInFrame(), and WinGetFuncArgInPartition().

3047 {
3048  WindowAggState *winstate;
3049 
3050  Assert(WindowObjectIsValid(winobj));
3051  winstate = winobj->winstate;
3052 
3053  if (markpos < winobj->markpos)
3054  elog(ERROR, "cannot move WindowObject's mark position backward");
3055  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3056  if (markpos > winobj->markpos)
3057  {
3058  tuplestore_skiptuples(winstate->buffer,
3059  markpos - winobj->markpos,
3060  true);
3061  winobj->markpos = markpos;
3062  }
3063  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3064  if (markpos > winobj->seekpos)
3065  {
3066  tuplestore_skiptuples(winstate->buffer,
3067  markpos - winobj->seekpos,
3068  true);
3069  winobj->seekpos = markpos;
3070  }
3071 }
#define ERROR
Definition: elog.h:43
Tuplestorestate * buffer
Definition: execnodes.h:1964
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41