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/optimizer.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/expandeddatum.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 241 of file nodeWindowAgg.c.

244 {
245  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
246  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
247  int numArguments = perfuncstate->numArguments;
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->args[i].value = ExecEvalExpr(argstate, econtext,
277  &fcinfo->args[i].isnull);
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->args[i].isnull)
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->args[1].value,
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->args[0].value = peraggstate->transValue;
343  fcinfo->args[0].isnull = 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 }
#define OidIsValid(objectId)
Definition: c.h:710
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:320
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
void DeleteExpandedObject(Datum d)
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1175
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:446
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void * arg
#define FUNC_MAX_ARGS
#define lfirst(lc)
Definition: pg_list.h:169
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetBool(X)
Definition: postgres.h:437
#define DatumGetPointer(X)
Definition: postgres.h:593
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:240
bool fn_strict
Definition: fmgr.h:61
MemoryContext curaggcontext
Definition: execnodes.h:2471
ExprContext * tmpcontext
Definition: execnodes.h:2472
ExprState * aggfilter
Definition: execnodes.h:828
MemoryContext aggcontext
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:80

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

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 417 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ are_peers()

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

Definition at line 3030 of file nodeWindowAgg.c.

3032 {
3033  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3034  ExprContext *econtext = winstate->tmpcontext;
3035 
3036  /* If no ORDER BY, all rows are peers with each other */
3037  if (node->ordNumCols == 0)
3038  return true;
3039 
3040  econtext->ecxt_outertuple = slot1;
3041  econtext->ecxt_innertuple = slot2;
3042  return ExecQualAndReset(winstate->ordEqfunction, econtext);
3043 }
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:427
Plan * plan
Definition: execnodes.h:998
PlanState ps
Definition: execnodes.h:1423
ScanState ss
Definition: execnodes.h:2423
ExprState * ordEqfunction
Definition: execnodes.h:2433
int ordNumCols
Definition: plannodes.h:923

References ExecQualAndReset(), if(), 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().

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1079 of file nodeWindowAgg.c.

1080 {
1081  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1082  PlanState *outerPlan = outerPlanState(winstate);
1083  int frameOptions = winstate->frameOptions;
1084  int numfuncs = winstate->numfuncs;
1085  int i;
1086 
1087  winstate->partition_spooled = false;
1088  winstate->framehead_valid = false;
1089  winstate->frametail_valid = false;
1090  winstate->grouptail_valid = false;
1091  winstate->spooled_rows = 0;
1092  winstate->currentpos = 0;
1093  winstate->frameheadpos = 0;
1094  winstate->frametailpos = 0;
1095  winstate->currentgroup = 0;
1096  winstate->frameheadgroup = 0;
1097  winstate->frametailgroup = 0;
1098  winstate->groupheadpos = 0;
1099  winstate->grouptailpos = -1; /* see update_grouptailpos */
1100  ExecClearTuple(winstate->agg_row_slot);
1101  if (winstate->framehead_slot)
1102  ExecClearTuple(winstate->framehead_slot);
1103  if (winstate->frametail_slot)
1104  ExecClearTuple(winstate->frametail_slot);
1105 
1106  /*
1107  * If this is the very first partition, we need to fetch the first input
1108  * row to store in first_part_slot.
1109  */
1110  if (TupIsNull(winstate->first_part_slot))
1111  {
1112  TupleTableSlot *outerslot = ExecProcNode(outerPlan);
1113 
1114  if (!TupIsNull(outerslot))
1115  ExecCopySlot(winstate->first_part_slot, outerslot);
1116  else
1117  {
1118  /* outer plan is empty, so we have nothing to do */
1119  winstate->partition_spooled = true;
1120  winstate->more_partitions = false;
1121  return;
1122  }
1123  }
1124 
1125  /* Create new tuplestore for this partition */
1126  winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1127 
1128  /*
1129  * Set up read pointers for the tuplestore. The current pointer doesn't
1130  * need BACKWARD capability, but the per-window-function read pointers do,
1131  * and the aggregate pointer does if we might need to restart aggregation.
1132  */
1133  winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1134 
1135  /* reset default REWIND capability bit for current ptr */
1136  tuplestore_set_eflags(winstate->buffer, 0);
1137 
1138  /* create read pointers for aggregates, if needed */
1139  if (winstate->numaggs > 0)
1140  {
1141  WindowObject agg_winobj = winstate->agg_winobj;
1142  int readptr_flags = 0;
1143 
1144  /*
1145  * If the frame head is potentially movable, or we have an EXCLUSION
1146  * clause, we might need to restart aggregation ...
1147  */
1148  if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1149  (frameOptions & FRAMEOPTION_EXCLUSION))
1150  {
1151  /* ... so create a mark pointer to track the frame head */
1152  agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1153  /* and the read pointer will need BACKWARD capability */
1154  readptr_flags |= EXEC_FLAG_BACKWARD;
1155  }
1156 
1157  agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1158  readptr_flags);
1159  agg_winobj->markpos = -1;
1160  agg_winobj->seekpos = -1;
1161 
1162  /* Also reset the row counters for aggregates */
1163  winstate->aggregatedbase = 0;
1164  winstate->aggregatedupto = 0;
1165  }
1166 
1167  /* create mark and read pointers for each real window function */
1168  for (i = 0; i < numfuncs; i++)
1169  {
1170  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1171 
1172  if (!perfuncstate->plain_agg)
1173  {
1174  WindowObject winobj = perfuncstate->winobj;
1175 
1176  winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1177  0);
1178  winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1180  winobj->markpos = -1;
1181  winobj->seekpos = -1;
1182  }
1183  }
1184 
1185  /*
1186  * If we are in RANGE or GROUPS mode, then determining frame boundaries
1187  * requires physical access to the frame endpoint rows, except in certain
1188  * degenerate cases. We create read pointers to point to those rows, to
1189  * simplify access and ensure that the tuplestore doesn't discard the
1190  * endpoint rows prematurely. (Must create pointers in exactly the same
1191  * cases that update_frameheadpos and update_frametailpos need them.)
1192  */
1193  winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1194 
1195  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1196  {
1197  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1198  node->ordNumCols != 0) ||
1199  (frameOptions & FRAMEOPTION_START_OFFSET))
1200  winstate->framehead_ptr =
1201  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1202  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1203  node->ordNumCols != 0) ||
1204  (frameOptions & FRAMEOPTION_END_OFFSET))
1205  winstate->frametail_ptr =
1206  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1207  }
1208 
1209  /*
1210  * If we have an exclusion clause that requires knowing the boundaries of
1211  * the current row's peer group, we create a read pointer to track the
1212  * tail position of the peer group (i.e., first row of the next peer
1213  * group). The head position does not require its own pointer because we
1214  * maintain that as a side effect of advancing the current row.
1215  */
1216  winstate->grouptail_ptr = -1;
1217 
1218  if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1220  node->ordNumCols != 0)
1221  {
1222  winstate->grouptail_ptr =
1223  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1224  }
1225 
1226  /*
1227  * Store the first tuple into the tuplestore (it's always available now;
1228  * we either read it above, or saved it at the end of previous partition)
1229  */
1230  tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1231  winstate->spooled_rows++;
1232 }
#define outerPlanState(node)
Definition: execnodes.h:1094
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:254
int work_mem
Definition: globals.c:125
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:545
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:556
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:540
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:544
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:554
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:552
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:536
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:551
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:538
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:558
#define outerPlan(node)
Definition: plannodes.h:172
int64 aggregatedbase
Definition: execnodes.h:2445
int64 frametailgroup
Definition: execnodes.h:2465
int64 frameheadgroup
Definition: execnodes.h:2464
TupleTableSlot * framehead_slot
Definition: execnodes.h:2498
bool frametail_valid
Definition: execnodes.h:2491
bool partition_spooled
Definition: execnodes.h:2485
int64 spooled_rows
Definition: execnodes.h:2439
int64 frameheadpos
Definition: execnodes.h:2441
bool more_partitions
Definition: execnodes.h:2487
int64 grouptailpos
Definition: execnodes.h:2467
int64 currentgroup
Definition: execnodes.h:2463
TupleTableSlot * frametail_slot
Definition: execnodes.h:2499
Tuplestorestate * buffer
Definition: execnodes.h:2434
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2502
struct WindowObjectData * agg_winobj
Definition: execnodes.h:2444
bool framehead_valid
Definition: execnodes.h:2489
int64 groupheadpos
Definition: execnodes.h:2466
bool grouptail_valid
Definition: execnodes.h:2493
int64 currentpos
Definition: execnodes.h:2440
int64 frametailpos
Definition: execnodes.h:2442
TupleTableSlot * first_part_slot
Definition: execnodes.h:2496
int64 aggregatedupto
Definition: execnodes.h:2446
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:383
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:359
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
#define TupIsNull(slot)
Definition: tuptable.h:292

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_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, 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().

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 662 of file nodeWindowAgg.c.

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 ResetExprContext(econtext)
Definition: executor.h:531
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
static void finalize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
static bool advance_windowaggregate_base(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
static void update_frameheadpos(WindowAggState *winstate)
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:543
Datum * ecxt_aggvalues
Definition: execnodes.h:251
bool * ecxt_aggnulls
Definition: execnodes.h:253
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:236
ExprContext * ps_ExprContext
Definition: execnodes.h:1037
MemoryContext aggcontext
Definition: execnodes.h:2470
WindowStatePerAgg peragg
Definition: execnodes.h:2431
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2503

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().

◆ eval_windowfunction()

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

Definition at line 1032 of file nodeWindowAgg.c.

1034 {
1035  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
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  for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1052  fcinfo->args[argno].isnull = true;
1053  /* Window functions don't have a current aggregate context, either */
1054  winstate->curaggcontext = NULL;
1055 
1056  *result = FunctionCallInvoke(fcinfo);
1057  *isnull = fcinfo->isnull;
1058 
1059  /*
1060  * Make sure pass-by-ref data is allocated in the appropriate context. (We
1061  * need this in case the function returns a pointer into some short-lived
1062  * tuple, as is entirely possible.)
1063  */
1064  if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1066  DatumGetPointer(*result)))
1067  *result = datumCopy(*result,
1068  perfuncstate->resulttypeByVal,
1069  perfuncstate->resulttypeLen);
1070 
1071  MemoryContextSwitchTo(oldContext);
1072 }
bool MemoryContextContains(MemoryContext context, void *pointer)
Definition: mcxt.c:758

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

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2659 of file nodeWindowAgg.c.

2660 {
2662  int i;
2663 
2664  release_partition(node);
2665 
2669  ExecClearTuple(node->temp_slot_1);
2670  ExecClearTuple(node->temp_slot_2);
2671  if (node->framehead_slot)
2673  if (node->frametail_slot)
2675 
2676  /*
2677  * Free both the expr contexts.
2678  */
2679  ExecFreeExprContext(&node->ss.ps);
2680  node->ss.ps.ps_ExprContext = node->tmpcontext;
2681  ExecFreeExprContext(&node->ss.ps);
2682 
2683  for (i = 0; i < node->numaggs; i++)
2684  {
2685  if (node->peragg[i].aggcontext != node->aggcontext)
2687  }
2690 
2691  pfree(node->perfunc);
2692  pfree(node->peragg);
2693 
2694  outerPlan = outerPlanState(node);
2696 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:556
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
static void release_partition(WindowAggState *winstate)
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1426
MemoryContext partcontext
Definition: execnodes.h:2469
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2504

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().

◆ ExecInitWindowAgg()

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

Definition at line 2352 of file nodeWindowAgg.c.

2353 {
2354  WindowAggState *winstate;
2355  Plan *outerPlan;
2356  ExprContext *econtext;
2357  ExprContext *tmpcontext;
2358  WindowStatePerFunc perfunc;
2359  WindowStatePerAgg peragg;
2360  int frameOptions = node->frameOptions;
2361  int numfuncs,
2362  wfuncno,
2363  numaggs,
2364  aggno;
2365  TupleDesc scanDesc;
2366  ListCell *l;
2367 
2368  /* check for unsupported flags */
2369  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2370 
2371  /*
2372  * create state structure
2373  */
2374  winstate = makeNode(WindowAggState);
2375  winstate->ss.ps.plan = (Plan *) node;
2376  winstate->ss.ps.state = estate;
2377  winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2378 
2379  /*
2380  * Create expression contexts. We need two, one for per-input-tuple
2381  * processing and one for per-output-tuple processing. We cheat a little
2382  * by using ExecAssignExprContext() to build both.
2383  */
2384  ExecAssignExprContext(estate, &winstate->ss.ps);
2385  tmpcontext = winstate->ss.ps.ps_ExprContext;
2386  winstate->tmpcontext = tmpcontext;
2387  ExecAssignExprContext(estate, &winstate->ss.ps);
2388 
2389  /* Create long-lived context for storage of partition-local memory etc */
2390  winstate->partcontext =
2392  "WindowAgg Partition",
2394 
2395  /*
2396  * Create mid-lived context for aggregate trans values etc.
2397  *
2398  * Note that moving aggregates each use their own private context, not
2399  * this one.
2400  */
2401  winstate->aggcontext =
2403  "WindowAgg Aggregates",
2405 
2406  /* Only the top-level WindowAgg may have a qual */
2407  Assert(node->plan.qual == NIL || node->topWindow);
2408 
2409  /* Initialize the qual */
2410  winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2411  (PlanState *) winstate);
2412 
2413  /*
2414  * Setup the run condition, if we received one from the query planner.
2415  * When set, this may allow us to move into pass-through mode so that we
2416  * don't have to perform any further evaluation of WindowFuncs in the
2417  * current partition or possibly stop returning tuples altogether when all
2418  * tuples are in the same partition.
2419  */
2420  winstate->runcondition = ExecInitQual(node->runCondition,
2421  (PlanState *) winstate);
2422 
2423  /*
2424  * When we're not the top-level WindowAgg node or we are but have a
2425  * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2426  * modes when the runCondition becomes false.
2427  */
2428  winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2429 
2430  /* remember if we're the top-window or we are below the top-window */
2431  winstate->top_window = node->topWindow;
2432 
2433  /*
2434  * initialize child nodes
2435  */
2436  outerPlan = outerPlan(node);
2437  outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2438 
2439  /*
2440  * initialize source tuple type (which is also the tuple type that we'll
2441  * store in the tuplestore and use in all our working slots).
2442  */
2444  scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2445 
2446  /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2447  winstate->ss.ps.outeropsset = true;
2448  winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2449  winstate->ss.ps.outeropsfixed = true;
2450 
2451  /*
2452  * tuple table initialization
2453  */
2454  winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2456  winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2458  winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2460  winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2462 
2463  /*
2464  * create frame head and tail slots only if needed (must create slots in
2465  * exactly the same cases that update_frameheadpos and update_frametailpos
2466  * need them)
2467  */
2468  winstate->framehead_slot = winstate->frametail_slot = NULL;
2469 
2470  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2471  {
2472  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2473  node->ordNumCols != 0) ||
2474  (frameOptions & FRAMEOPTION_START_OFFSET))
2475  winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2477  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2478  node->ordNumCols != 0) ||
2479  (frameOptions & FRAMEOPTION_END_OFFSET))
2480  winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2482  }
2483 
2484  /*
2485  * Initialize result slot, type and projection.
2486  */
2488  ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2489 
2490  /* Set up data for comparing tuples */
2491  if (node->partNumCols > 0)
2492  winstate->partEqfunction =
2493  execTuplesMatchPrepare(scanDesc,
2494  node->partNumCols,
2495  node->partColIdx,
2496  node->partOperators,
2497  node->partCollations,
2498  &winstate->ss.ps);
2499 
2500  if (node->ordNumCols > 0)
2501  winstate->ordEqfunction =
2502  execTuplesMatchPrepare(scanDesc,
2503  node->ordNumCols,
2504  node->ordColIdx,
2505  node->ordOperators,
2506  node->ordCollations,
2507  &winstate->ss.ps);
2508 
2509  /*
2510  * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2511  */
2512  numfuncs = winstate->numfuncs;
2513  numaggs = winstate->numaggs;
2514  econtext = winstate->ss.ps.ps_ExprContext;
2515  econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2516  econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2517 
2518  /*
2519  * allocate per-wfunc/per-agg state information.
2520  */
2521  perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2522  peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2523  winstate->perfunc = perfunc;
2524  winstate->peragg = peragg;
2525 
2526  wfuncno = -1;
2527  aggno = -1;
2528  foreach(l, winstate->funcs)
2529  {
2530  WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2531  WindowFunc *wfunc = wfuncstate->wfunc;
2532  WindowStatePerFunc perfuncstate;
2533  AclResult aclresult;
2534  int i;
2535 
2536  if (wfunc->winref != node->winref) /* planner screwed up? */
2537  elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2538  wfunc->winref, node->winref);
2539 
2540  /* Look for a previous duplicate window function */
2541  for (i = 0; i <= wfuncno; i++)
2542  {
2543  if (equal(wfunc, perfunc[i].wfunc) &&
2544  !contain_volatile_functions((Node *) wfunc))
2545  break;
2546  }
2547  if (i <= wfuncno)
2548  {
2549  /* Found a match to an existing entry, so just mark it */
2550  wfuncstate->wfuncno = i;
2551  continue;
2552  }
2553 
2554  /* Nope, so assign a new PerAgg record */
2555  perfuncstate = &perfunc[++wfuncno];
2556 
2557  /* Mark WindowFunc state node with assigned index in the result array */
2558  wfuncstate->wfuncno = wfuncno;
2559 
2560  /* Check permission to call window function */
2561  aclresult = pg_proc_aclcheck(wfunc->winfnoid, GetUserId(),
2562  ACL_EXECUTE);
2563  if (aclresult != ACLCHECK_OK)
2564  aclcheck_error(aclresult, OBJECT_FUNCTION,
2565  get_func_name(wfunc->winfnoid));
2566  InvokeFunctionExecuteHook(wfunc->winfnoid);
2567 
2568  /* Fill in the perfuncstate data */
2569  perfuncstate->wfuncstate = wfuncstate;
2570  perfuncstate->wfunc = wfunc;
2571  perfuncstate->numArguments = list_length(wfuncstate->args);
2572  perfuncstate->winCollation = wfunc->inputcollid;
2573 
2574  get_typlenbyval(wfunc->wintype,
2575  &perfuncstate->resulttypeLen,
2576  &perfuncstate->resulttypeByVal);
2577 
2578  /*
2579  * If it's really just a plain aggregate function, we'll emulate the
2580  * Agg environment for it.
2581  */
2582  perfuncstate->plain_agg = wfunc->winagg;
2583  if (wfunc->winagg)
2584  {
2585  WindowStatePerAgg peraggstate;
2586 
2587  perfuncstate->aggno = ++aggno;
2588  peraggstate = &winstate->peragg[aggno];
2589  initialize_peragg(winstate, wfunc, peraggstate);
2590  peraggstate->wfuncno = wfuncno;
2591  }
2592  else
2593  {
2595 
2596  winobj->winstate = winstate;
2597  winobj->argstates = wfuncstate->args;
2598  winobj->localmem = NULL;
2599  perfuncstate->winobj = winobj;
2600 
2601  /* It's a real window function, so set up to call it. */
2602  fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2603  econtext->ecxt_per_query_memory);
2604  fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2605  }
2606  }
2607 
2608  /* Update numfuncs, numaggs to match number of unique functions found */
2609  winstate->numfuncs = wfuncno + 1;
2610  winstate->numaggs = aggno + 1;
2611 
2612  /* Set up WindowObject for aggregates, if needed */
2613  if (winstate->numaggs > 0)
2614  {
2615  WindowObject agg_winobj = makeNode(WindowObjectData);
2616 
2617  agg_winobj->winstate = winstate;
2618  agg_winobj->argstates = NIL;
2619  agg_winobj->localmem = NULL;
2620  /* make sure markptr = -1 to invalidate. It may not get used */
2621  agg_winobj->markptr = -1;
2622  agg_winobj->readptr = -1;
2623  winstate->agg_winobj = agg_winobj;
2624  }
2625 
2626  /* Set the status to running */
2627  winstate->status = WINDOWAGG_RUN;
2628 
2629  /* copy frame options to state node for easy access */
2630  winstate->frameOptions = frameOptions;
2631 
2632  /* initialize frame bound offset expressions */
2633  winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2634  (PlanState *) winstate);
2635  winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2636  (PlanState *) winstate);
2637 
2638  /* Lookup in_range support functions if needed */
2639  if (OidIsValid(node->startInRangeFunc))
2640  fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2641  if (OidIsValid(node->endInRangeFunc))
2642  fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2643  winstate->inRangeColl = node->inRangeColl;
2644  winstate->inRangeAsc = node->inRangeAsc;
2645  winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2646 
2647  winstate->all_first = true;
2648  winstate->partition_spooled = false;
2649  winstate->more_partitions = false;
2650 
2651  return winstate;
2652 }
AclResult
Definition: acl.h:181
@ ACLCHECK_OK
Definition: acl.h:182
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3512
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:5071
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:496
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3564
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:160
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:59
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:141
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1831
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1799
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition: execUtils.c:682
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:535
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:2407
@ WINDOWAGG_RUN
Definition: execnodes.h:2415
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:2406
#define EXEC_FLAG_MARK
Definition: executor.h:59
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:135
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2208
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1589
void * palloc0(Size size)
Definition: mcxt.c:1099
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
Oid GetUserId(void)
Definition: miscinit.c:492
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
#define makeNode(_type_)
Definition: nodes.h:621
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:211
@ OBJECT_FUNCTION
Definition: parsenodes.h:2153
#define ACL_EXECUTE
Definition: parsenodes.h:89
static int list_length(const List *l)
Definition: pg_list.h:149
#define NIL
Definition: pg_list.h:65
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:239
Definition: nodes.h:574
bool outeropsset
Definition: execnodes.h:1081
const TupleTableSlotOps * outerops
Definition: execnodes.h:1073
ExprState * qual
Definition: execnodes.h:1019
bool outeropsfixed
Definition: execnodes.h:1077
EState * state
Definition: execnodes.h:1000
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1004
List * qual
Definition: plannodes.h:143
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
ExprState * endOffset
Definition: execnodes.h:2451
FmgrInfo endInRangeFunc
Definition: execnodes.h:2457
FmgrInfo startInRangeFunc
Definition: execnodes.h:2456
ExprState * runcondition
Definition: execnodes.h:2474
WindowAggStatus status
Definition: execnodes.h:2447
bool inRangeNullsFirst
Definition: execnodes.h:2460
ExprState * partEqfunction
Definition: execnodes.h:2432
ExprState * startOffset
Definition: execnodes.h:2450
bool use_pass_through
Definition: execnodes.h:2479
Oid * partOperators
Definition: plannodes.h:921
int partNumCols
Definition: plannodes.h:919
Oid endInRangeFunc
Definition: plannodes.h:934
Node * endOffset
Definition: plannodes.h:929
AttrNumber * ordColIdx
Definition: plannodes.h:924
bool topWindow
Definition: plannodes.h:938
Plan plan
Definition: plannodes.h:917
Oid inRangeColl
Definition: plannodes.h:935
Node * startOffset
Definition: plannodes.h:928
List * runCondition
Definition: plannodes.h:930
Oid startInRangeFunc
Definition: plannodes.h:933
bool inRangeAsc
Definition: plannodes.h:936
Oid * partCollations
Definition: plannodes.h:922
Index winref
Definition: plannodes.h:918
bool inRangeNullsFirst
Definition: plannodes.h:937
Oid * ordCollations
Definition: plannodes.h:926
Oid * ordOperators
Definition: plannodes.h:925
AttrNumber * partColIdx
Definition: plannodes.h:920
int frameOptions
Definition: plannodes.h:927
WindowFunc * wfunc
Definition: execnodes.h:826
Index winref
Definition: primnodes.h:401
WindowAggState * winstate
Definition: nodeWindowAgg.c:64

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, WindowStatePerFuncData::aggno, WindowAggState::all_first, 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, WindowAggState::endInRangeFunc, WindowAgg::endInRangeFunc, WindowAggState::endOffset, WindowAgg::endOffset, equal(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecCreateScanSlotFromOuterPlan(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitQual(), 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_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, WindowAgg::frameOptions, WindowAggState::frametail_slot, WindowAggState::funcs, get_func_name(), get_typlenbyval(), GetUserId(), i, initialize_peragg(), WindowFunc::inputcollid, WindowAggState::inRangeAsc, WindowAgg::inRangeAsc, WindowAggState::inRangeColl, WindowAgg::inRangeColl, WindowAggState::inRangeNullsFirst, WindowAgg::inRangeNullsFirst, InvokeFunctionExecuteHook, lfirst, list_length(), WindowObjectData::localmem, makeNode, WindowObjectData::markptr, WindowAggState::more_partitions, NIL, WindowAggState::numaggs, WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, OBJECT_FUNCTION, OidIsValid, WindowAgg::ordColIdx, WindowAgg::ordCollations, WindowAggState::ordEqfunction, WindowAgg::ordNumCols, WindowAgg::ordOperators, PlanState::outerops, PlanState::outeropsfixed, PlanState::outeropsset, outerPlan, outerPlanState, palloc0(), WindowAgg::partColIdx, WindowAgg::partCollations, WindowAggState::partcontext, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, WindowAgg::partOperators, WindowAggState::peragg, WindowAggState::perfunc, pg_proc_aclcheck(), WindowStatePerFuncData::plain_agg, PlanState::plan, WindowAgg::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::qual, Plan::qual, WindowObjectData::readptr, WindowStatePerFuncData::resulttypeByVal, WindowStatePerFuncData::resulttypeLen, WindowAggState::runcondition, WindowAgg::runCondition, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startInRangeFunc, WindowAgg::startInRangeFunc, WindowAggState::startOffset, WindowAgg::startOffset, PlanState::state, WindowAggState::status, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, WindowAggState::tmpcontext, WindowAggState::top_window, WindowAgg::topWindow, TupleTableSlot::tts_tupleDescriptor, TTSOpsMinimalTuple, TTSOpsVirtual, WindowAggState::use_pass_through, WindowStatePerFuncData::wfunc, WindowFuncExprState::wfunc, WindowStatePerAggData::wfuncno, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WindowFunc::winagg, WindowStatePerFuncData::winCollation, WINDOWAGG_RUN, WindowFunc::winfnoid, WindowStatePerFuncData::winobj, WindowAgg::winref, WindowFunc::winref, WindowObjectData::winstate, and WindowFunc::wintype.

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2703 of file nodeWindowAgg.c.

2704 {
2706  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2707 
2708  node->status = WINDOWAGG_RUN;
2709  node->all_first = true;
2710 
2711  /* release tuplestore et al */
2712  release_partition(node);
2713 
2714  /* release all temp tuples, but especially first_part_slot */
2718  ExecClearTuple(node->temp_slot_1);
2719  ExecClearTuple(node->temp_slot_2);
2720  if (node->framehead_slot)
2722  if (node->frametail_slot)
2724 
2725  /* Forget current wfunc values */
2726  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2727  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2728 
2729  /*
2730  * if chgParam of subnode is not null then plan will be re-scanned by
2731  * first ExecProcNode.
2732  */
2733  if (outerPlan->chgParam == NULL)
2735 }
#define MemSet(start, val, len)
Definition: c.h:1008
void ExecReScan(PlanState *node)
Definition: execAmi.c:78

References WindowAggState::agg_row_slot, WindowAggState::all_first, 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::status, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, and WINDOWAGG_RUN.

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot* ExecWindowAgg ( PlanState pstate)
static

Definition at line 2044 of file nodeWindowAgg.c.

2045 {
2046  WindowAggState *winstate = castNode(WindowAggState, pstate);
2047  TupleTableSlot *slot;
2048  ExprContext *econtext;
2049  int i;
2050  int numfuncs;
2051 
2053 
2054  if (winstate->status == WINDOWAGG_DONE)
2055  return NULL;
2056 
2057  /*
2058  * Compute frame offset values, if any, during first call (or after a
2059  * rescan). These are assumed to hold constant throughout the scan; if
2060  * user gives us a volatile expression, we'll only use its initial value.
2061  */
2062  if (winstate->all_first)
2063  {
2064  int frameOptions = winstate->frameOptions;
2065  ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
2066  Datum value;
2067  bool isnull;
2068  int16 len;
2069  bool byval;
2070 
2071  if (frameOptions & FRAMEOPTION_START_OFFSET)
2072  {
2073  Assert(winstate->startOffset != NULL);
2075  econtext,
2076  &isnull);
2077  if (isnull)
2078  ereport(ERROR,
2079  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2080  errmsg("frame starting offset must not be null")));
2081  /* copy value into query-lifespan context */
2082  get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
2083  &len, &byval);
2084  winstate->startOffsetValue = datumCopy(value, byval, len);
2085  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2086  {
2087  /* value is known to be int8 */
2088  int64 offset = DatumGetInt64(value);
2089 
2090  if (offset < 0)
2091  ereport(ERROR,
2092  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2093  errmsg("frame starting offset must not be negative")));
2094  }
2095  }
2096  if (frameOptions & FRAMEOPTION_END_OFFSET)
2097  {
2098  Assert(winstate->endOffset != NULL);
2100  econtext,
2101  &isnull);
2102  if (isnull)
2103  ereport(ERROR,
2104  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2105  errmsg("frame ending offset must not be null")));
2106  /* copy value into query-lifespan context */
2107  get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2108  &len, &byval);
2109  winstate->endOffsetValue = datumCopy(value, byval, len);
2110  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2111  {
2112  /* value is known to be int8 */
2113  int64 offset = DatumGetInt64(value);
2114 
2115  if (offset < 0)
2116  ereport(ERROR,
2117  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2118  errmsg("frame ending offset must not be negative")));
2119  }
2120  }
2121  winstate->all_first = false;
2122  }
2123 
2124  /* We need to loop as the runCondition or qual may filter out tuples */
2125  for (;;)
2126  {
2127  if (winstate->buffer == NULL)
2128  {
2129  /* Initialize for first partition and set current row = 0 */
2130  begin_partition(winstate);
2131  /* If there are no input rows, we'll detect that and exit below */
2132  }
2133  else
2134  {
2135  /* Advance current row within partition */
2136  winstate->currentpos++;
2137  /* This might mean that the frame moves, too */
2138  winstate->framehead_valid = false;
2139  winstate->frametail_valid = false;
2140  /* we don't need to invalidate grouptail here; see below */
2141  }
2142 
2143  /*
2144  * Spool all tuples up to and including the current row, if we haven't
2145  * already
2146  */
2147  spool_tuples(winstate, winstate->currentpos);
2148 
2149  /* Move to the next partition if we reached the end of this partition */
2150  if (winstate->partition_spooled &&
2151  winstate->currentpos >= winstate->spooled_rows)
2152  {
2153  release_partition(winstate);
2154 
2155  if (winstate->more_partitions)
2156  {
2157  begin_partition(winstate);
2158  Assert(winstate->spooled_rows > 0);
2159 
2160  /* Come out of pass-through mode when changing partition */
2161  winstate->status = WINDOWAGG_RUN;
2162  }
2163  else
2164  {
2165  /* No further partitions? We're done */
2166  winstate->status = WINDOWAGG_DONE;
2167  return NULL;
2168  }
2169  }
2170 
2171  /* final output execution is in ps_ExprContext */
2172  econtext = winstate->ss.ps.ps_ExprContext;
2173 
2174  /* Clear the per-output-tuple context for current row */
2175  ResetExprContext(econtext);
2176 
2177  /*
2178  * Read the current row from the tuplestore, and save in
2179  * ScanTupleSlot. (We can't rely on the outerplan's output slot
2180  * because we may have to read beyond the current row. Also, we have
2181  * to actually copy the row out of the tuplestore, since window
2182  * function evaluation might cause the tuplestore to dump its state to
2183  * disk.)
2184  *
2185  * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2186  * we must also detect entering a new peer group and update associated
2187  * state when that happens. We use temp_slot_2 to temporarily hold
2188  * the previous row for this purpose.
2189  *
2190  * Current row must be in the tuplestore, since we spooled it above.
2191  */
2192  tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
2193  if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2196  winstate->currentpos > 0)
2197  {
2198  ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2199  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2200  winstate->ss.ss_ScanTupleSlot))
2201  elog(ERROR, "unexpected end of tuplestore");
2202  if (!are_peers(winstate, winstate->temp_slot_2,
2203  winstate->ss.ss_ScanTupleSlot))
2204  {
2205  winstate->currentgroup++;
2206  winstate->groupheadpos = winstate->currentpos;
2207  winstate->grouptail_valid = false;
2208  }
2209  ExecClearTuple(winstate->temp_slot_2);
2210  }
2211  else
2212  {
2213  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2214  winstate->ss.ss_ScanTupleSlot))
2215  elog(ERROR, "unexpected end of tuplestore");
2216  }
2217 
2218  /* don't evaluate the window functions when we're in pass-through mode */
2219  if (winstate->status == WINDOWAGG_RUN)
2220  {
2221  /*
2222  * Evaluate true window functions
2223  */
2224  numfuncs = winstate->numfuncs;
2225  for (i = 0; i < numfuncs; i++)
2226  {
2227  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2228 
2229  if (perfuncstate->plain_agg)
2230  continue;
2231  eval_windowfunction(winstate, perfuncstate,
2232  &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2233  &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2234  }
2235 
2236  /*
2237  * Evaluate aggregates
2238  */
2239  if (winstate->numaggs > 0)
2240  eval_windowaggregates(winstate);
2241  }
2242 
2243  /*
2244  * If we have created auxiliary read pointers for the frame or group
2245  * boundaries, force them to be kept up-to-date, because we don't know
2246  * whether the window function(s) will do anything that requires that.
2247  * Failing to advance the pointers would result in being unable to
2248  * trim data from the tuplestore, which is bad. (If we could know in
2249  * advance whether the window functions will use frame boundary info,
2250  * we could skip creating these pointers in the first place ... but
2251  * unfortunately the window function API doesn't require that.)
2252  */
2253  if (winstate->framehead_ptr >= 0)
2254  update_frameheadpos(winstate);
2255  if (winstate->frametail_ptr >= 0)
2256  update_frametailpos(winstate);
2257  if (winstate->grouptail_ptr >= 0)
2258  update_grouptailpos(winstate);
2259 
2260  /*
2261  * Truncate any no-longer-needed rows from the tuplestore.
2262  */
2263  tuplestore_trim(winstate->buffer);
2264 
2265  /*
2266  * Form and return a projection tuple using the windowfunc results and
2267  * the current row. Setting ecxt_outertuple arranges that any Vars
2268  * will be evaluated with respect to that row.
2269  */
2270  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2271 
2272  slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2273 
2274  if (winstate->status == WINDOWAGG_RUN)
2275  {
2276  econtext->ecxt_scantuple = slot;
2277 
2278  /*
2279  * Now evaluate the run condition to see if we need to go into
2280  * pass-through mode, or maybe stop completely.
2281  */
2282  if (!ExecQual(winstate->runcondition, econtext))
2283  {
2284  /*
2285  * Determine which mode to move into. If there is no
2286  * PARTITION BY clause and we're the top-level WindowAgg then
2287  * we're done. This tuple and any future tuples cannot
2288  * possibly match the runcondition. However, when there is a
2289  * PARTITION BY clause or we're not the top-level window we
2290  * can't just stop as we need to either process other
2291  * partitions or ensure WindowAgg nodes above us receive all
2292  * of the tuples they need to process their WindowFuncs.
2293  */
2294  if (winstate->use_pass_through)
2295  {
2296  /*
2297  * STRICT pass-through mode is required for the top window
2298  * when there is a PARTITION BY clause. Otherwise we must
2299  * ensure we store tuples that don't match the
2300  * runcondition so they're available to WindowAggs above.
2301  */
2302  if (winstate->top_window)
2303  {
2305  continue;
2306  }
2307  else
2308  winstate->status = WINDOWAGG_PASSTHROUGH;
2309  }
2310  else
2311  {
2312  /*
2313  * Pass-through not required. We can just return NULL.
2314  * Nothing else will match the runcondition.
2315  */
2316  winstate->status = WINDOWAGG_DONE;
2317  return NULL;
2318  }
2319  }
2320 
2321  /*
2322  * Filter out any tuples we don't need in the top-level WindowAgg.
2323  */
2324  if (!ExecQual(winstate->ss.ps.qual, econtext))
2325  {
2326  InstrCountFiltered1(winstate, 1);
2327  continue;
2328  }
2329 
2330  break;
2331  }
2332 
2333  /*
2334  * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2335  * we're anything apart from the top window.
2336  */
2337  else if (!winstate->top_window)
2338  break;
2339  }
2340 
2341  return slot;
2342 }
signed short int16
Definition: c.h:428
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1102
@ WINDOWAGG_PASSTHROUGH
Definition: execnodes.h:2416
@ WINDOWAGG_DONE
Definition: execnodes.h:2414
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition: execnodes.h:2417
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:363
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:400
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:335
static struct @151 value
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
static void begin_partition(WindowAggState *winstate)
static void update_grouptailpos(WindowAggState *winstate)
static void spool_tuples(WindowAggState *winstate, int64 pos)
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
static void eval_windowaggregates(WindowAggState *winstate)
static void update_frametailpos(WindowAggState *winstate)
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
#define castNode(_type_, nodeptr)
Definition: nodes.h:642
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:537
const void size_t len
#define DatumGetInt64(X)
Definition: postgres.h:651
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:232
Expr * expr
Definition: execnodes.h:95
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1038
Datum startOffsetValue
Definition: execnodes.h:2452
Datum endOffsetValue
Definition: execnodes.h:2453
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1360

References 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, ExprContext::ecxt_scantuple, elog, WindowAggState::endOffset, WindowAggState::endOffsetValue, ereport, errcode(), errmsg(), ERROR, eval_windowaggregates(), eval_windowfunction(), ExecClearTuple(), ExecCopySlot(), ExecEvalExprSwitchContext(), ExecProject(), ExecQual(), 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, InstrCountFiltered1, len, WindowAggState::more_partitions, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::qual, release_partition(), ResetExprContext, WindowAggState::runcondition, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startOffset, WindowAggState::startOffsetValue, WindowAggState::status, WindowAggState::temp_slot_2, WindowAggState::top_window, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), WindowAggState::use_pass_through, value, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WINDOWAGG_DONE, WINDOWAGG_PASSTHROUGH, WINDOWAGG_PASSTHROUGH_STRICT, and WINDOWAGG_RUN.

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

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

Definition at line 579 of file nodeWindowAgg.c.

583 {
584  MemoryContext oldContext;
585 
587 
588  /*
589  * Apply the agg's finalfn if one is provided, else return transValue.
590  */
591  if (OidIsValid(peraggstate->finalfn_oid))
592  {
593  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
594  int numFinalArgs = peraggstate->numFinalArgs;
595  bool anynull;
596  int i;
597 
598  InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
599  numFinalArgs,
600  perfuncstate->winCollation,
601  (void *) winstate, NULL);
602  fcinfo->args[0].value =
604  peraggstate->transValueIsNull,
605  peraggstate->transtypeLen);
606  fcinfo->args[0].isnull = 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->args[i].value = (Datum) 0;
613  fcinfo->args[i].isnull = 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 }
#define MakeExpandedObjectReadOnly(d, isnull, typlen)

References WindowStatePerAggData::aggcontext, CurrentMemoryContext, datumCopy(), DatumGetPointer, ExprContext::ecxt_per_tuple_memory, WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, LOCAL_FCINFO, 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().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3008 of file nodeWindowAgg.c.

3009 {
3010  Oid typinput,
3011  typioparam;
3012  char *strInitVal;
3013  Datum initVal;
3014 
3015  getTypeInputInfo(transtype, &typinput, &typioparam);
3016  strInitVal = TextDatumGetCString(textInitVal);
3017  initVal = OidInputFunctionCall(typinput, strInitVal,
3018  typioparam, -1);
3019  pfree(strInitVal);
3020  return initVal;
3021 }
#define TextDatumGetCString(d)
Definition: builtins.h:86
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1630
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2831
unsigned int Oid
Definition: postgres_ext.h:31

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

Referenced by initialize_peragg().

◆ initialize_peragg()

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

Definition at line 2743 of file nodeWindowAgg.c.

2745 {
2746  Oid inputTypes[FUNC_MAX_ARGS];
2747  int numArguments;
2748  HeapTuple aggTuple;
2749  Form_pg_aggregate aggform;
2750  Oid aggtranstype;
2751  AttrNumber initvalAttNo;
2752  AclResult aclresult;
2753  bool use_ma_code;
2754  Oid transfn_oid,
2755  invtransfn_oid,
2756  finalfn_oid;
2757  bool finalextra;
2758  char finalmodify;
2759  Expr *transfnexpr,
2760  *invtransfnexpr,
2761  *finalfnexpr;
2762  Datum textInitVal;
2763  int i;
2764  ListCell *lc;
2765 
2766  numArguments = list_length(wfunc->args);
2767 
2768  i = 0;
2769  foreach(lc, wfunc->args)
2770  {
2771  inputTypes[i++] = exprType((Node *) lfirst(lc));
2772  }
2773 
2774  aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
2775  if (!HeapTupleIsValid(aggTuple))
2776  elog(ERROR, "cache lookup failed for aggregate %u",
2777  wfunc->winfnoid);
2778  aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
2779 
2780  /*
2781  * Figure out whether we want to use the moving-aggregate implementation,
2782  * and collect the right set of fields from the pg_attribute entry.
2783  *
2784  * It's possible that an aggregate would supply a safe moving-aggregate
2785  * implementation and an unsafe normal one, in which case our hand is
2786  * forced. Otherwise, if the frame head can't move, we don't need
2787  * moving-aggregate code. Even if we'd like to use it, don't do so if the
2788  * aggregate's arguments (and FILTER clause if any) contain any calls to
2789  * volatile functions. Otherwise, the difference between restarting and
2790  * not restarting the aggregation would be user-visible.
2791  */
2792  if (!OidIsValid(aggform->aggminvtransfn))
2793  use_ma_code = false; /* sine qua non */
2794  else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2795  aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2796  use_ma_code = true; /* decision forced by safety */
2798  use_ma_code = false; /* non-moving frame head */
2799  else if (contain_volatile_functions((Node *) wfunc))
2800  use_ma_code = false; /* avoid possible behavioral change */
2801  else
2802  use_ma_code = true; /* yes, let's use it */
2803  if (use_ma_code)
2804  {
2805  peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2806  peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2807  peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2808  finalextra = aggform->aggmfinalextra;
2809  finalmodify = aggform->aggmfinalmodify;
2810  aggtranstype = aggform->aggmtranstype;
2811  initvalAttNo = Anum_pg_aggregate_aggminitval;
2812  }
2813  else
2814  {
2815  peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2816  peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2817  peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2818  finalextra = aggform->aggfinalextra;
2819  finalmodify = aggform->aggfinalmodify;
2820  aggtranstype = aggform->aggtranstype;
2821  initvalAttNo = Anum_pg_aggregate_agginitval;
2822  }
2823 
2824  /*
2825  * ExecInitWindowAgg already checked permission to call aggregate function
2826  * ... but we still need to check the component functions
2827  */
2828 
2829  /* Check that aggregate owner has permission to call component fns */
2830  {
2831  HeapTuple procTuple;
2832  Oid aggOwner;
2833 
2834  procTuple = SearchSysCache1(PROCOID,
2835  ObjectIdGetDatum(wfunc->winfnoid));
2836  if (!HeapTupleIsValid(procTuple))
2837  elog(ERROR, "cache lookup failed for function %u",
2838  wfunc->winfnoid);
2839  aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
2840  ReleaseSysCache(procTuple);
2841 
2842  aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
2843  ACL_EXECUTE);
2844  if (aclresult != ACLCHECK_OK)
2845  aclcheck_error(aclresult, OBJECT_FUNCTION,
2846  get_func_name(transfn_oid));
2847  InvokeFunctionExecuteHook(transfn_oid);
2848 
2849  if (OidIsValid(invtransfn_oid))
2850  {
2851  aclresult = pg_proc_aclcheck(invtransfn_oid, aggOwner,
2852  ACL_EXECUTE);
2853  if (aclresult != ACLCHECK_OK)
2854  aclcheck_error(aclresult, OBJECT_FUNCTION,
2855  get_func_name(invtransfn_oid));
2856  InvokeFunctionExecuteHook(invtransfn_oid);
2857  }
2858 
2859  if (OidIsValid(finalfn_oid))
2860  {
2861  aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
2862  ACL_EXECUTE);
2863  if (aclresult != ACLCHECK_OK)
2864  aclcheck_error(aclresult, OBJECT_FUNCTION,
2865  get_func_name(finalfn_oid));
2866  InvokeFunctionExecuteHook(finalfn_oid);
2867  }
2868  }
2869 
2870  /*
2871  * If the selected finalfn isn't read-only, we can't run this aggregate as
2872  * a window function. This is a user-facing error, so we take a bit more
2873  * care with the error message than elsewhere in this function.
2874  */
2875  if (finalmodify != AGGMODIFY_READ_ONLY)
2876  ereport(ERROR,
2877  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2878  errmsg("aggregate function %s does not support use as a window function",
2879  format_procedure(wfunc->winfnoid))));
2880 
2881  /* Detect how many arguments to pass to the finalfn */
2882  if (finalextra)
2883  peraggstate->numFinalArgs = numArguments + 1;
2884  else
2885  peraggstate->numFinalArgs = 1;
2886 
2887  /* resolve actual type of transition state, if polymorphic */
2888  aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
2889  aggtranstype,
2890  inputTypes,
2891  numArguments);
2892 
2893  /* build expression trees using actual argument & result types */
2894  build_aggregate_transfn_expr(inputTypes,
2895  numArguments,
2896  0, /* no ordered-set window functions yet */
2897  false, /* no variadic window functions yet */
2898  aggtranstype,
2899  wfunc->inputcollid,
2900  transfn_oid,
2901  invtransfn_oid,
2902  &transfnexpr,
2903  &invtransfnexpr);
2904 
2905  /* set up infrastructure for calling the transfn(s) and finalfn */
2906  fmgr_info(transfn_oid, &peraggstate->transfn);
2907  fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
2908 
2909  if (OidIsValid(invtransfn_oid))
2910  {
2911  fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
2912  fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
2913  }
2914 
2915  if (OidIsValid(finalfn_oid))
2916  {
2917  build_aggregate_finalfn_expr(inputTypes,
2918  peraggstate->numFinalArgs,
2919  aggtranstype,
2920  wfunc->wintype,
2921  wfunc->inputcollid,
2922  finalfn_oid,
2923  &finalfnexpr);
2924  fmgr_info(finalfn_oid, &peraggstate->finalfn);
2925  fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
2926  }
2927 
2928  /* get info about relevant datatypes */
2929  get_typlenbyval(wfunc->wintype,
2930  &peraggstate->resulttypeLen,
2931  &peraggstate->resulttypeByVal);
2932  get_typlenbyval(aggtranstype,
2933  &peraggstate->transtypeLen,
2934  &peraggstate->transtypeByVal);
2935 
2936  /*
2937  * initval is potentially null, so don't try to access it as a struct
2938  * field. Must do it the hard way with SysCacheGetAttr.
2939  */
2940  textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
2941  &peraggstate->initValueIsNull);
2942 
2943  if (peraggstate->initValueIsNull)
2944  peraggstate->initValue = (Datum) 0;
2945  else
2946  peraggstate->initValue = GetAggInitVal(textInitVal,
2947  aggtranstype);
2948 
2949  /*
2950  * If the transfn is strict and the initval is NULL, make sure input type
2951  * and transtype are the same (or at least binary-compatible), so that
2952  * it's OK to use the first input value as the initial transValue. This
2953  * should have been checked at agg definition time, but we must check
2954  * again in case the transfn's strictness property has been changed.
2955  */
2956  if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
2957  {
2958  if (numArguments < 1 ||
2959  !IsBinaryCoercible(inputTypes[0], aggtranstype))
2960  ereport(ERROR,
2961  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2962  errmsg("aggregate %u needs to have compatible input type and transition type",
2963  wfunc->winfnoid)));
2964  }
2965 
2966  /*
2967  * Insist that forward and inverse transition functions have the same
2968  * strictness setting. Allowing them to differ would require handling
2969  * more special cases in advance_windowaggregate and
2970  * advance_windowaggregate_base, for no discernible benefit. This should
2971  * have been checked at agg definition time, but we must check again in
2972  * case either function's strictness property has been changed.
2973  */
2974  if (OidIsValid(invtransfn_oid) &&
2975  peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
2976  ereport(ERROR,
2977  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2978  errmsg("strictness of aggregate's forward and inverse transition functions must match")));
2979 
2980  /*
2981  * Moving aggregates use their own aggcontext.
2982  *
2983  * This is necessary because they might restart at different times, so we
2984  * might never be able to reset the shared context otherwise. We can't
2985  * make it the aggregates' responsibility to clean up after themselves,
2986  * because strict aggregates must be restarted whenever we remove their
2987  * last non-NULL input, which the aggregate won't be aware is happening.
2988  * Also, just pfree()ing the transValue upon restarting wouldn't help,
2989  * since we'd miss any indirectly referenced data. We could, in theory,
2990  * make the memory allocation rules for moving aggregates different than
2991  * they have historically been for plain aggregates, but that seems grotty
2992  * and likely to lead to memory leaks.
2993  */
2994  if (OidIsValid(invtransfn_oid))
2995  peraggstate->aggcontext =
2997  "WindowAgg Per Aggregate",
2999  else
3000  peraggstate->aggcontext = winstate->aggcontext;
3001 
3002  ReleaseSysCache(aggTuple);
3003 
3004  return peraggstate;
3005 }
int16 AttrNumber
Definition: attnum.h:21
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
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:2084
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:1916
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:1976
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define InvalidOid
Definition: postgres_ext.h:36
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
List * args
Definition: primnodes.h:399
Oid winfnoid
Definition: primnodes.h:395
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1173
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1434
@ AGGFNOID
Definition: syscache.h:34
@ PROCOID
Definition: syscache.h:79

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, WindowFunc::inputcollid, 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, WindowFunc::winfnoid, and WindowFunc::wintype.

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 206 of file nodeWindowAgg.c.

209 {
210  MemoryContext oldContext;
211 
212  /*
213  * If we're using a private aggcontext, we may reset it here. But if the
214  * context is shared, we don't know which other aggregates may still need
215  * it, so we must leave it to the caller to reset at an appropriate time.
216  */
217  if (peraggstate->aggcontext != winstate->aggcontext)
219 
220  if (peraggstate->initValueIsNull)
221  peraggstate->transValue = peraggstate->initValue;
222  else
223  {
224  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
225  peraggstate->transValue = datumCopy(peraggstate->initValue,
226  peraggstate->transtypeByVal,
227  peraggstate->transtypeLen);
228  MemoryContextSwitchTo(oldContext);
229  }
230  peraggstate->transValueIsNull = peraggstate->initValueIsNull;
231  peraggstate->transValueCount = 0;
232  peraggstate->resultValue = (Datum) 0;
233  peraggstate->resultValueIsNull = true;
234 }

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().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1333 of file nodeWindowAgg.c.

1334 {
1335  int i;
1336 
1337  for (i = 0; i < winstate->numfuncs; i++)
1338  {
1339  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1340 
1341  /* Release any partition-local state of this window function */
1342  if (perfuncstate->winobj)
1343  perfuncstate->winobj->localmem = NULL;
1344  }
1345 
1346  /*
1347  * Release all partition-local memory (in particular, any partition-local
1348  * state that we might have trashed our pointers to in the above loop, and
1349  * any aggregate temp data). We don't rely on retail pfree because some
1350  * aggregates might have allocated data we don't have direct pointers to.
1351  */
1354  for (i = 0; i < winstate->numaggs; i++)
1355  {
1356  if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1358  }
1359 
1360  if (winstate->buffer)
1361  tuplestore_end(winstate->buffer);
1362  winstate->buffer = NULL;
1363  winstate->partition_spooled = false;
1364 }
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453

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().

◆ row_is_in_frame()

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

Definition at line 1383 of file nodeWindowAgg.c.

1384 {
1385  int frameOptions = winstate->frameOptions;
1386 
1387  Assert(pos >= 0); /* else caller error */
1388 
1389  /*
1390  * First, check frame starting conditions. We might as well delegate this
1391  * to update_frameheadpos always; it doesn't add any notable cost.
1392  */
1393  update_frameheadpos(winstate);
1394  if (pos < winstate->frameheadpos)
1395  return 0;
1396 
1397  /*
1398  * Okay so far, now check frame ending conditions. Here, we avoid calling
1399  * update_frametailpos in simple cases, so as not to spool tuples further
1400  * ahead than necessary.
1401  */
1402  if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1403  {
1404  if (frameOptions & FRAMEOPTION_ROWS)
1405  {
1406  /* rows after current row are out of frame */
1407  if (pos > winstate->currentpos)
1408  return -1;
1409  }
1410  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1411  {
1412  /* following row that is not peer is out of frame */
1413  if (pos > winstate->currentpos &&
1414  !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1415  return -1;
1416  }
1417  else
1418  Assert(false);
1419  }
1420  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1421  {
1422  if (frameOptions & FRAMEOPTION_ROWS)
1423  {
1424  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1425 
1426  /* rows after current row + offset are out of frame */
1427  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1428  offset = -offset;
1429 
1430  if (pos > winstate->currentpos + offset)
1431  return -1;
1432  }
1433  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1434  {
1435  /* hard cases, so delegate to update_frametailpos */
1436  update_frametailpos(winstate);
1437  if (pos >= winstate->frametailpos)
1438  return -1;
1439  }
1440  else
1441  Assert(false);
1442  }
1443 
1444  /* Check exclusion clause */
1445  if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1446  {
1447  if (pos == winstate->currentpos)
1448  return 0;
1449  }
1450  else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1451  ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1452  pos != winstate->currentpos))
1453  {
1454  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1455 
1456  /* If no ORDER BY, all rows are peers with each other */
1457  if (node->ordNumCols == 0)
1458  return 0;
1459  /* Otherwise, check the group boundaries */
1460  if (pos >= winstate->groupheadpos)
1461  {
1462  update_grouptailpos(winstate);
1463  if (pos < winstate->grouptailpos)
1464  return 0;
1465  }
1466  }
1467 
1468  /* If we get here, it's in frame */
1469  return 1;
1470 }
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:550
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:547

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, if(), WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, ScanState::ss_ScanTupleSlot, update_frameheadpos(), update_frametailpos(), and update_grouptailpos().

Referenced by eval_windowaggregates(), and WinGetFuncArgInFrame().

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1239 of file nodeWindowAgg.c.

1240 {
1241  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1243  TupleTableSlot *outerslot;
1244  MemoryContext oldcontext;
1245 
1246  if (!winstate->buffer)
1247  return; /* just a safety check */
1248  if (winstate->partition_spooled)
1249  return; /* whole partition done already */
1250 
1251  /*
1252  * When in pass-through mode we can just exhaust all tuples in the current
1253  * partition. We don't need these tuples for any further window function
1254  * evaluation, however, we do need to keep them around if we're not the
1255  * top-level window as another WindowAgg node above must see these.
1256  */
1257  if (winstate->status != WINDOWAGG_RUN)
1258  {
1259  Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1260  winstate->status == WINDOWAGG_PASSTHROUGH_STRICT);
1261 
1262  pos = -1;
1263  }
1264 
1265  /*
1266  * If the tuplestore has spilled to disk, alternate reading and writing
1267  * becomes quite expensive due to frequent buffer flushes. It's cheaper
1268  * to force the entire partition to get spooled in one go.
1269  *
1270  * XXX this is a horrid kluge --- it'd be better to fix the performance
1271  * problem inside tuplestore. FIXME
1272  */
1273  else if (!tuplestore_in_memory(winstate->buffer))
1274  pos = -1;
1275 
1276  outerPlan = outerPlanState(winstate);
1277 
1278  /* Must be in query context to call outerplan */
1280 
1281  while (winstate->spooled_rows <= pos || pos == -1)
1282  {
1283  outerslot = ExecProcNode(outerPlan);
1284  if (TupIsNull(outerslot))
1285  {
1286  /* reached the end of the last partition */
1287  winstate->partition_spooled = true;
1288  winstate->more_partitions = false;
1289  break;
1290  }
1291 
1292  if (node->partNumCols > 0)
1293  {
1294  ExprContext *econtext = winstate->tmpcontext;
1295 
1296  econtext->ecxt_innertuple = winstate->first_part_slot;
1297  econtext->ecxt_outertuple = outerslot;
1298 
1299  /* Check if this tuple still belongs to the current partition */
1300  if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1301  {
1302  /*
1303  * end of partition; copy the tuple for the next cycle.
1304  */
1305  ExecCopySlot(winstate->first_part_slot, outerslot);
1306  winstate->partition_spooled = true;
1307  winstate->more_partitions = true;
1308  break;
1309  }
1310  }
1311 
1312  /*
1313  * Remember the tuple unless we're the top-level window and we're in
1314  * pass-through mode.
1315  */
1316  if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1317  {
1318  /* Still in partition, so save it into the tuplestore */
1319  tuplestore_puttupleslot(winstate->buffer, outerslot);
1320  winstate->spooled_rows++;
1321  }
1322  }
1323 
1324  MemoryContextSwitchTo(oldcontext);
1325 }
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:234
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1455

References Assert(), WindowAggState::buffer, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_per_query_memory, ExecCopySlot(), ExecProcNode(), ExecQualAndReset(), WindowAggState::first_part_slot, if(), 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::status, WindowAggState::tmpcontext, TupIsNull, tuplestore_in_memory(), tuplestore_puttupleslot(), WINDOWAGG_PASSTHROUGH, WINDOWAGG_PASSTHROUGH_STRICT, and WINDOWAGG_RUN.

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

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1483 of file nodeWindowAgg.c.

1484 {
1485  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1486  int frameOptions = winstate->frameOptions;
1487  MemoryContext oldcontext;
1488 
1489  if (winstate->framehead_valid)
1490  return; /* already known for current row */
1491 
1492  /* We may be called in a short-lived context */
1494 
1495  if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1496  {
1497  /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1498  winstate->frameheadpos = 0;
1499  winstate->framehead_valid = true;
1500  }
1501  else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1502  {
1503  if (frameOptions & FRAMEOPTION_ROWS)
1504  {
1505  /* In ROWS mode, frame head is the same as current */
1506  winstate->frameheadpos = winstate->currentpos;
1507  winstate->framehead_valid = true;
1508  }
1509  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1510  {
1511  /* If no ORDER BY, all rows are peers with each other */
1512  if (node->ordNumCols == 0)
1513  {
1514  winstate->frameheadpos = 0;
1515  winstate->framehead_valid = true;
1516  MemoryContextSwitchTo(oldcontext);
1517  return;
1518  }
1519 
1520  /*
1521  * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1522  * first row that is a peer of current row. We keep a copy of the
1523  * last-known frame head row in framehead_slot, and advance as
1524  * necessary. Note that if we reach end of partition, we will
1525  * leave frameheadpos = end+1 and framehead_slot empty.
1526  */
1528  winstate->framehead_ptr);
1529  if (winstate->frameheadpos == 0 &&
1530  TupIsNull(winstate->framehead_slot))
1531  {
1532  /* fetch first row into framehead_slot, if we didn't already */
1533  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1534  winstate->framehead_slot))
1535  elog(ERROR, "unexpected end of tuplestore");
1536  }
1537 
1538  while (!TupIsNull(winstate->framehead_slot))
1539  {
1540  if (are_peers(winstate, winstate->framehead_slot,
1541  winstate->ss.ss_ScanTupleSlot))
1542  break; /* this row is the correct frame head */
1543  /* Note we advance frameheadpos even if the fetch fails */
1544  winstate->frameheadpos++;
1545  spool_tuples(winstate, winstate->frameheadpos);
1546  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1547  winstate->framehead_slot))
1548  break; /* end of partition */
1549  }
1550  winstate->framehead_valid = true;
1551  }
1552  else
1553  Assert(false);
1554  }
1555  else if (frameOptions & FRAMEOPTION_START_OFFSET)
1556  {
1557  if (frameOptions & FRAMEOPTION_ROWS)
1558  {
1559  /* In ROWS mode, bound is physically n before/after current */
1560  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1561 
1562  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1563  offset = -offset;
1564 
1565  winstate->frameheadpos = winstate->currentpos + offset;
1566  /* frame head can't go before first row */
1567  if (winstate->frameheadpos < 0)
1568  winstate->frameheadpos = 0;
1569  else if (winstate->frameheadpos > winstate->currentpos + 1)
1570  {
1571  /* make sure frameheadpos is not past end of partition */
1572  spool_tuples(winstate, winstate->frameheadpos - 1);
1573  if (winstate->frameheadpos > winstate->spooled_rows)
1574  winstate->frameheadpos = winstate->spooled_rows;
1575  }
1576  winstate->framehead_valid = true;
1577  }
1578  else if (frameOptions & FRAMEOPTION_RANGE)
1579  {
1580  /*
1581  * In RANGE START_OFFSET mode, frame head is the first row that
1582  * satisfies the in_range constraint relative to the current row.
1583  * We keep a copy of the last-known frame head row in
1584  * framehead_slot, and advance as necessary. Note that if we
1585  * reach end of partition, we will leave frameheadpos = end+1 and
1586  * framehead_slot empty.
1587  */
1588  int sortCol = node->ordColIdx[0];
1589  bool sub,
1590  less;
1591 
1592  /* We must have an ordering column */
1593  Assert(node->ordNumCols == 1);
1594 
1595  /* Precompute flags for in_range checks */
1596  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1597  sub = true; /* subtract startOffset from current row */
1598  else
1599  sub = false; /* add it */
1600  less = false; /* normally, we want frame head >= sum */
1601  /* If sort order is descending, flip both flags */
1602  if (!winstate->inRangeAsc)
1603  {
1604  sub = !sub;
1605  less = true;
1606  }
1607 
1609  winstate->framehead_ptr);
1610  if (winstate->frameheadpos == 0 &&
1611  TupIsNull(winstate->framehead_slot))
1612  {
1613  /* fetch first row into framehead_slot, if we didn't already */
1614  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1615  winstate->framehead_slot))
1616  elog(ERROR, "unexpected end of tuplestore");
1617  }
1618 
1619  while (!TupIsNull(winstate->framehead_slot))
1620  {
1621  Datum headval,
1622  currval;
1623  bool headisnull,
1624  currisnull;
1625 
1626  headval = slot_getattr(winstate->framehead_slot, sortCol,
1627  &headisnull);
1628  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1629  &currisnull);
1630  if (headisnull || currisnull)
1631  {
1632  /* order of the rows depends only on nulls_first */
1633  if (winstate->inRangeNullsFirst)
1634  {
1635  /* advance head if head is null and curr is not */
1636  if (!headisnull || currisnull)
1637  break;
1638  }
1639  else
1640  {
1641  /* advance head if head is not null and curr is null */
1642  if (headisnull || !currisnull)
1643  break;
1644  }
1645  }
1646  else
1647  {
1649  winstate->inRangeColl,
1650  headval,
1651  currval,
1652  winstate->startOffsetValue,
1653  BoolGetDatum(sub),
1654  BoolGetDatum(less))))
1655  break; /* this row is the correct frame head */
1656  }
1657  /* Note we advance frameheadpos even if the fetch fails */
1658  winstate->frameheadpos++;
1659  spool_tuples(winstate, winstate->frameheadpos);
1660  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1661  winstate->framehead_slot))
1662  break; /* end of partition */
1663  }
1664  winstate->framehead_valid = true;
1665  }
1666  else if (frameOptions & FRAMEOPTION_GROUPS)
1667  {
1668  /*
1669  * In GROUPS START_OFFSET mode, frame head is the first row of the
1670  * first peer group whose number satisfies the offset constraint.
1671  * We keep a copy of the last-known frame head row in
1672  * framehead_slot, and advance as necessary. Note that if we
1673  * reach end of partition, we will leave frameheadpos = end+1 and
1674  * framehead_slot empty.
1675  */
1676  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1677  int64 minheadgroup;
1678 
1679  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1680  minheadgroup = winstate->currentgroup - offset;
1681  else
1682  minheadgroup = winstate->currentgroup + offset;
1683 
1685  winstate->framehead_ptr);
1686  if (winstate->frameheadpos == 0 &&
1687  TupIsNull(winstate->framehead_slot))
1688  {
1689  /* fetch first row into framehead_slot, if we didn't already */
1690  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1691  winstate->framehead_slot))
1692  elog(ERROR, "unexpected end of tuplestore");
1693  }
1694 
1695  while (!TupIsNull(winstate->framehead_slot))
1696  {
1697  if (winstate->frameheadgroup >= minheadgroup)
1698  break; /* this row is the correct frame head */
1699  ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1700  /* Note we advance frameheadpos even if the fetch fails */
1701  winstate->frameheadpos++;
1702  spool_tuples(winstate, winstate->frameheadpos);
1703  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1704  winstate->framehead_slot))
1705  break; /* end of partition */
1706  if (!are_peers(winstate, winstate->temp_slot_2,
1707  winstate->framehead_slot))
1708  winstate->frameheadgroup++;
1709  }
1710  ExecClearTuple(winstate->temp_slot_2);
1711  winstate->framehead_valid = true;
1712  }
1713  else
1714  Assert(false);
1715  }
1716  else
1717  Assert(false);
1718 
1719  MemoryContextSwitchTo(oldcontext);
1720 }
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1208
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:546
#define BoolGetDatum(X)
Definition: postgres.h:446
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381

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(), if(), 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().

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1733 of file nodeWindowAgg.c.

1734 {
1735  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1736  int frameOptions = winstate->frameOptions;
1737  MemoryContext oldcontext;
1738 
1739  if (winstate->frametail_valid)
1740  return; /* already known for current row */
1741 
1742  /* We may be called in a short-lived context */
1744 
1745  if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1746  {
1747  /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1748  spool_tuples(winstate, -1);
1749  winstate->frametailpos = winstate->spooled_rows;
1750  winstate->frametail_valid = true;
1751  }
1752  else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1753  {
1754  if (frameOptions & FRAMEOPTION_ROWS)
1755  {
1756  /* In ROWS mode, exactly the rows up to current are in frame */
1757  winstate->frametailpos = winstate->currentpos + 1;
1758  winstate->frametail_valid = true;
1759  }
1760  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1761  {
1762  /* If no ORDER BY, all rows are peers with each other */
1763  if (node->ordNumCols == 0)
1764  {
1765  spool_tuples(winstate, -1);
1766  winstate->frametailpos = winstate->spooled_rows;
1767  winstate->frametail_valid = true;
1768  MemoryContextSwitchTo(oldcontext);
1769  return;
1770  }
1771 
1772  /*
1773  * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1774  * row that is a peer of current row, frame tail is the row after
1775  * that (if any). We keep a copy of the last-known frame tail row
1776  * in frametail_slot, and advance as necessary. Note that if we
1777  * reach end of partition, we will leave frametailpos = end+1 and
1778  * frametail_slot empty.
1779  */
1781  winstate->frametail_ptr);
1782  if (winstate->frametailpos == 0 &&
1783  TupIsNull(winstate->frametail_slot))
1784  {
1785  /* fetch first row into frametail_slot, if we didn't already */
1786  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1787  winstate->frametail_slot))
1788  elog(ERROR, "unexpected end of tuplestore");
1789  }
1790 
1791  while (!TupIsNull(winstate->frametail_slot))
1792  {
1793  if (winstate->frametailpos > winstate->currentpos &&
1794  !are_peers(winstate, winstate->frametail_slot,
1795  winstate->ss.ss_ScanTupleSlot))
1796  break; /* this row is the frame tail */
1797  /* Note we advance frametailpos even if the fetch fails */
1798  winstate->frametailpos++;
1799  spool_tuples(winstate, winstate->frametailpos);
1800  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1801  winstate->frametail_slot))
1802  break; /* end of partition */
1803  }
1804  winstate->frametail_valid = true;
1805  }
1806  else
1807  Assert(false);
1808  }
1809  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1810  {
1811  if (frameOptions & FRAMEOPTION_ROWS)
1812  {
1813  /* In ROWS mode, bound is physically n before/after current */
1814  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1815 
1816  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1817  offset = -offset;
1818 
1819  winstate->frametailpos = winstate->currentpos + offset + 1;
1820  /* smallest allowable value of frametailpos is 0 */
1821  if (winstate->frametailpos < 0)
1822  winstate->frametailpos = 0;
1823  else if (winstate->frametailpos > winstate->currentpos + 1)
1824  {
1825  /* make sure frametailpos is not past end of partition */
1826  spool_tuples(winstate, winstate->frametailpos - 1);
1827  if (winstate->frametailpos > winstate->spooled_rows)
1828  winstate->frametailpos = winstate->spooled_rows;
1829  }
1830  winstate->frametail_valid = true;
1831  }
1832  else if (frameOptions & FRAMEOPTION_RANGE)
1833  {
1834  /*
1835  * In RANGE END_OFFSET mode, frame end is the last row that
1836  * satisfies the in_range constraint relative to the current row,
1837  * frame tail is the row after that (if any). We keep a copy of
1838  * the last-known frame tail row in frametail_slot, and advance as
1839  * necessary. Note that if we reach end of partition, we will
1840  * leave frametailpos = end+1 and frametail_slot empty.
1841  */
1842  int sortCol = node->ordColIdx[0];
1843  bool sub,
1844  less;
1845 
1846  /* We must have an ordering column */
1847  Assert(node->ordNumCols == 1);
1848 
1849  /* Precompute flags for in_range checks */
1850  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1851  sub = true; /* subtract endOffset from current row */
1852  else
1853  sub = false; /* add it */
1854  less = true; /* normally, we want frame tail <= sum */
1855  /* If sort order is descending, flip both flags */
1856  if (!winstate->inRangeAsc)
1857  {
1858  sub = !sub;
1859  less = false;
1860  }
1861 
1863  winstate->frametail_ptr);
1864  if (winstate->frametailpos == 0 &&
1865  TupIsNull(winstate->frametail_slot))
1866  {
1867  /* fetch first row into frametail_slot, if we didn't already */
1868  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1869  winstate->frametail_slot))
1870  elog(ERROR, "unexpected end of tuplestore");
1871  }
1872 
1873  while (!TupIsNull(winstate->frametail_slot))
1874  {
1875  Datum tailval,
1876  currval;
1877  bool tailisnull,
1878  currisnull;
1879 
1880  tailval = slot_getattr(winstate->frametail_slot, sortCol,
1881  &tailisnull);
1882  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1883  &currisnull);
1884  if (tailisnull || currisnull)
1885  {
1886  /* order of the rows depends only on nulls_first */
1887  if (winstate->inRangeNullsFirst)
1888  {
1889  /* advance tail if tail is null or curr is not */
1890  if (!tailisnull)
1891  break;
1892  }
1893  else
1894  {
1895  /* advance tail if tail is not null or curr is null */
1896  if (!currisnull)
1897  break;
1898  }
1899  }
1900  else
1901  {
1903  winstate->inRangeColl,
1904  tailval,
1905  currval,
1906  winstate->endOffsetValue,
1907  BoolGetDatum(sub),
1908  BoolGetDatum(less))))
1909  break; /* this row is the correct frame tail */
1910  }
1911  /* Note we advance frametailpos even if the fetch fails */
1912  winstate->frametailpos++;
1913  spool_tuples(winstate, winstate->frametailpos);
1914  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1915  winstate->frametail_slot))
1916  break; /* end of partition */
1917  }
1918  winstate->frametail_valid = true;
1919  }
1920  else if (frameOptions & FRAMEOPTION_GROUPS)
1921  {
1922  /*
1923  * In GROUPS END_OFFSET mode, frame end is the last row of the
1924  * last peer group whose number satisfies the offset constraint,
1925  * and frame tail is the row after that (if any). We keep a copy
1926  * of the last-known frame tail row in frametail_slot, and advance
1927  * as necessary. Note that if we reach end of partition, we will
1928  * leave frametailpos = end+1 and frametail_slot empty.
1929  */
1930  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1931  int64 maxtailgroup;
1932 
1933  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1934  maxtailgroup = winstate->currentgroup - offset;
1935  else
1936  maxtailgroup = winstate->currentgroup + offset;
1937 
1939  winstate->frametail_ptr);
1940  if (winstate->frametailpos == 0 &&
1941  TupIsNull(winstate->frametail_slot))
1942  {
1943  /* fetch first row into frametail_slot, if we didn't already */
1944  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1945  winstate->frametail_slot))
1946  elog(ERROR, "unexpected end of tuplestore");
1947  }
1948 
1949  while (!TupIsNull(winstate->frametail_slot))
1950  {
1951  if (winstate->frametailgroup > maxtailgroup)
1952  break; /* this row is the correct frame tail */
1953  ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
1954  /* Note we advance frametailpos even if the fetch fails */
1955  winstate->frametailpos++;
1956  spool_tuples(winstate, winstate->frametailpos);
1957  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1958  winstate->frametail_slot))
1959  break; /* end of partition */
1960  if (!are_peers(winstate, winstate->temp_slot_2,
1961  winstate->frametail_slot))
1962  winstate->frametailgroup++;
1963  }
1964  ExecClearTuple(winstate->temp_slot_2);
1965  winstate->frametail_valid = true;
1966  }
1967  else
1968  Assert(false);
1969  }
1970  else
1971  Assert(false);
1972 
1973  MemoryContextSwitchTo(oldcontext);
1974 }

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(), if(), 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().

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 1983 of file nodeWindowAgg.c.

1984 {
1985  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1986  MemoryContext oldcontext;
1987 
1988  if (winstate->grouptail_valid)
1989  return; /* already known for current row */
1990 
1991  /* We may be called in a short-lived context */
1993 
1994  /* If no ORDER BY, all rows are peers with each other */
1995  if (node->ordNumCols == 0)
1996  {
1997  spool_tuples(winstate, -1);
1998  winstate->grouptailpos = winstate->spooled_rows;
1999  winstate->grouptail_valid = true;
2000  MemoryContextSwitchTo(oldcontext);
2001  return;
2002  }
2003 
2004  /*
2005  * Because grouptail_valid is reset only when current row advances into a
2006  * new peer group, we always reach here knowing that grouptailpos needs to
2007  * be advanced by at least one row. Hence, unlike the otherwise similar
2008  * case for frame tail tracking, we do not need persistent storage of the
2009  * group tail row.
2010  */
2011  Assert(winstate->grouptailpos <= winstate->currentpos);
2013  winstate->grouptail_ptr);
2014  for (;;)
2015  {
2016  /* Note we advance grouptailpos even if the fetch fails */
2017  winstate->grouptailpos++;
2018  spool_tuples(winstate, winstate->grouptailpos);
2019  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2020  winstate->temp_slot_2))
2021  break; /* end of partition */
2022  if (winstate->grouptailpos > winstate->currentpos &&
2023  !are_peers(winstate, winstate->temp_slot_2,
2024  winstate->ss.ss_ScanTupleSlot))
2025  break; /* this row is the group tail */
2026  }
2027  ExecClearTuple(winstate->temp_slot_2);
2028  winstate->grouptail_valid = true;
2029 
2030  MemoryContextSwitchTo(oldcontext);
2031 }

References are_peers(), Assert(), WindowAggState::buffer, WindowAggState::currentpos, ExprContext::ecxt_per_query_memory, ExecClearTuple(), WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, if(), 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().

◆ window_gettupleslot()

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

Definition at line 3053 of file nodeWindowAgg.c.

3054 {
3055  WindowAggState *winstate = winobj->winstate;
3056  MemoryContext oldcontext;
3057 
3058  /* often called repeatedly in a row */
3060 
3061  /* Don't allow passing -1 to spool_tuples here */
3062  if (pos < 0)
3063  return false;
3064 
3065  /* If necessary, fetch the tuple into the spool */
3066  spool_tuples(winstate, pos);
3067 
3068  if (pos >= winstate->spooled_rows)
3069  return false;
3070 
3071  if (pos < winobj->markpos)
3072  elog(ERROR, "cannot fetch row before WindowObject's mark position");
3073 
3075 
3076  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3077 
3078  /*
3079  * Advance or rewind until we are within one tuple of the one we want.
3080  */
3081  if (winobj->seekpos < pos - 1)
3082  {
3083  if (!tuplestore_skiptuples(winstate->buffer,
3084  pos - 1 - winobj->seekpos,
3085  true))
3086  elog(ERROR, "unexpected end of tuplestore");
3087  winobj->seekpos = pos - 1;
3088  }
3089  else if (winobj->seekpos > pos + 1)
3090  {
3091  if (!tuplestore_skiptuples(winstate->buffer,
3092  winobj->seekpos - (pos + 1),
3093  false))
3094  elog(ERROR, "unexpected end of tuplestore");
3095  winobj->seekpos = pos + 1;
3096  }
3097  else if (winobj->seekpos == pos)
3098  {
3099  /*
3100  * There's no API to refetch the tuple at the current position. We
3101  * have to move one tuple forward, and then one backward. (We don't
3102  * do it the other way because we might try to fetch the row before
3103  * our mark, which isn't allowed.) XXX this case could stand to be
3104  * optimized.
3105  */
3106  tuplestore_advance(winstate->buffer, true);
3107  winobj->seekpos++;
3108  }
3109 
3110  /*
3111  * Now we should be on the tuple immediately before or after the one we
3112  * want, so just fetch forwards or backwards as appropriate.
3113  */
3114  if (winobj->seekpos > pos)
3115  {
3116  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3117  elog(ERROR, "unexpected end of tuplestore");
3118  winobj->seekpos--;
3119  }
3120  else
3121  {
3122  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3123  elog(ERROR, "unexpected end of tuplestore");
3124  winobj->seekpos++;
3125  }
3126 
3127  Assert(winobj->seekpos == pos);
3128 
3129  MemoryContextSwitchTo(oldcontext);
3130 
3131  return true;
3132 }
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1110
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1135

References Assert(), WindowAggState::buffer, CHECK_FOR_INTERRUPTS, ExprContext::ecxt_per_query_memory, elog, ERROR, 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().

◆ WinGetCurrentPosition()

int64 WinGetCurrentPosition ( WindowObject  winobj)

Definition at line 3168 of file nodeWindowAgg.c.

3169 {
3170  Assert(WindowObjectIsValid(winobj));
3171  return winobj->winstate->currentpos;
3172 }
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41

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

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

◆ WinGetFuncArgCurrent()

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

Definition at line 3576 of file nodeWindowAgg.c.

3577 {
3578  WindowAggState *winstate;
3579  ExprContext *econtext;
3580 
3581  Assert(WindowObjectIsValid(winobj));
3582  winstate = winobj->winstate;
3583 
3584  econtext = winstate->ss.ps.ps_ExprContext;
3585 
3586  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3587  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3588  econtext, isnull);
3589 }
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278

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().

◆ WinGetFuncArgInFrame()

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

Definition at line 3381 of file nodeWindowAgg.c.

3384 {
3385  WindowAggState *winstate;
3386  ExprContext *econtext;
3387  TupleTableSlot *slot;
3388  int64 abs_pos;
3389  int64 mark_pos;
3390 
3391  Assert(WindowObjectIsValid(winobj));
3392  winstate = winobj->winstate;
3393  econtext = winstate->ss.ps.ps_ExprContext;
3394  slot = winstate->temp_slot_1;
3395 
3396  switch (seektype)
3397  {
3398  case WINDOW_SEEK_CURRENT:
3399  elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3400  abs_pos = mark_pos = 0; /* keep compiler quiet */
3401  break;
3402  case WINDOW_SEEK_HEAD:
3403  /* rejecting relpos < 0 is easy and simplifies code below */
3404  if (relpos < 0)
3405  goto out_of_frame;
3406  update_frameheadpos(winstate);
3407  abs_pos = winstate->frameheadpos + relpos;
3408  mark_pos = abs_pos;
3409 
3410  /*
3411  * Account for exclusion option if one is active, but advance only
3412  * abs_pos not mark_pos. This prevents changes of the current
3413  * row's peer group from resulting in trying to fetch a row before
3414  * some previous mark position.
3415  *
3416  * Note that in some corner cases such as current row being
3417  * outside frame, these calculations are theoretically too simple,
3418  * but it doesn't matter because we'll end up deciding the row is
3419  * out of frame. We do not attempt to avoid fetching rows past
3420  * end of frame; that would happen in some cases anyway.
3421  */
3422  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3423  {
3424  case 0:
3425  /* no adjustment needed */
3426  break;
3428  if (abs_pos >= winstate->currentpos &&
3429  winstate->currentpos >= winstate->frameheadpos)
3430  abs_pos++;
3431  break;
3433  update_grouptailpos(winstate);
3434  if (abs_pos >= winstate->groupheadpos &&
3435  winstate->grouptailpos > winstate->frameheadpos)
3436  {
3437  int64 overlapstart = Max(winstate->groupheadpos,
3438  winstate->frameheadpos);
3439 
3440  abs_pos += winstate->grouptailpos - overlapstart;
3441  }
3442  break;
3444  update_grouptailpos(winstate);
3445  if (abs_pos >= winstate->groupheadpos &&
3446  winstate->grouptailpos > winstate->frameheadpos)
3447  {
3448  int64 overlapstart = Max(winstate->groupheadpos,
3449  winstate->frameheadpos);
3450 
3451  if (abs_pos == overlapstart)
3452  abs_pos = winstate->currentpos;
3453  else
3454  abs_pos += winstate->grouptailpos - overlapstart - 1;
3455  }
3456  break;
3457  default:
3458  elog(ERROR, "unrecognized frame option state: 0x%x",
3459  winstate->frameOptions);
3460  break;
3461  }
3462  break;
3463  case WINDOW_SEEK_TAIL:
3464  /* rejecting relpos > 0 is easy and simplifies code below */
3465  if (relpos > 0)
3466  goto out_of_frame;
3467  update_frametailpos(winstate);
3468  abs_pos = winstate->frametailpos - 1 + relpos;
3469 
3470  /*
3471  * Account for exclusion option if one is active. If there is no
3472  * exclusion, we can safely set the mark at the accessed row. But
3473  * if there is, we can only mark the frame start, because we can't
3474  * be sure how far back in the frame the exclusion might cause us
3475  * to fetch in future. Furthermore, we have to actually check
3476  * against frameheadpos here, since it's unsafe to try to fetch a
3477  * row before frame start if the mark might be there already.
3478  */
3479  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3480  {
3481  case 0:
3482  /* no adjustment needed */
3483  mark_pos = abs_pos;
3484  break;
3486  if (abs_pos <= winstate->currentpos &&
3487  winstate->currentpos < winstate->frametailpos)
3488  abs_pos--;
3489  update_frameheadpos(winstate);
3490  if (abs_pos < winstate->frameheadpos)
3491  goto out_of_frame;
3492  mark_pos = winstate->frameheadpos;
3493  break;
3495  update_grouptailpos(winstate);
3496  if (abs_pos < winstate->grouptailpos &&
3497  winstate->groupheadpos < winstate->frametailpos)
3498  {
3499  int64 overlapend = Min(winstate->grouptailpos,
3500  winstate->frametailpos);
3501 
3502  abs_pos -= overlapend - winstate->groupheadpos;
3503  }
3504  update_frameheadpos(winstate);
3505  if (abs_pos < winstate->frameheadpos)
3506  goto out_of_frame;
3507  mark_pos = winstate->frameheadpos;
3508  break;
3510  update_grouptailpos(winstate);
3511  if (abs_pos < winstate->grouptailpos &&
3512  winstate->groupheadpos < winstate->frametailpos)
3513  {
3514  int64 overlapend = Min(winstate->grouptailpos,
3515  winstate->frametailpos);
3516 
3517  if (abs_pos == overlapend - 1)
3518  abs_pos = winstate->currentpos;
3519  else
3520  abs_pos -= overlapend - 1 - winstate->groupheadpos;
3521  }
3522  update_frameheadpos(winstate);
3523  if (abs_pos < winstate->frameheadpos)
3524  goto out_of_frame;
3525  mark_pos = winstate->frameheadpos;
3526  break;
3527  default:
3528  elog(ERROR, "unrecognized frame option state: 0x%x",
3529  winstate->frameOptions);
3530  mark_pos = 0; /* keep compiler quiet */
3531  break;
3532  }
3533  break;
3534  default:
3535  elog(ERROR, "unrecognized window seek type: %d", seektype);
3536  abs_pos = mark_pos = 0; /* keep compiler quiet */
3537  break;
3538  }
3539 
3540  if (!window_gettupleslot(winobj, abs_pos, slot))
3541  goto out_of_frame;
3542 
3543  /* The code above does not detect all out-of-frame cases, so check */
3544  if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3545  goto out_of_frame;
3546 
3547  if (isout)
3548  *isout = false;
3549  if (set_mark)
3550  WinSetMarkPosition(winobj, mark_pos);
3551  econtext->ecxt_outertuple = slot;
3552  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3553  econtext, isnull);
3554 
3555 out_of_frame:
3556  if (isout)
3557  *isout = true;
3558  *isnull = true;
3559  return (Datum) 0;
3560 }
#define Min(x, y)
Definition: c.h:986
#define Max(x, y)
Definition: c.h:980
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32

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().

◆ WinGetFuncArgInPartition()

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

Definition at line 3293 of file nodeWindowAgg.c.

3296 {
3297  WindowAggState *winstate;
3298  ExprContext *econtext;
3299  TupleTableSlot *slot;
3300  bool gottuple;
3301  int64 abs_pos;
3302 
3303  Assert(WindowObjectIsValid(winobj));
3304  winstate = winobj->winstate;
3305  econtext = winstate->ss.ps.ps_ExprContext;
3306  slot = winstate->temp_slot_1;
3307 
3308  switch (seektype)
3309  {
3310  case WINDOW_SEEK_CURRENT:
3311  abs_pos = winstate->currentpos + relpos;
3312  break;
3313  case WINDOW_SEEK_HEAD:
3314  abs_pos = relpos;
3315  break;
3316  case WINDOW_SEEK_TAIL:
3317  spool_tuples(winstate, -1);
3318  abs_pos = winstate->spooled_rows - 1 + relpos;
3319  break;
3320  default:
3321  elog(ERROR, "unrecognized window seek type: %d", seektype);
3322  abs_pos = 0; /* keep compiler quiet */
3323  break;
3324  }
3325 
3326  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3327 
3328  if (!gottuple)
3329  {
3330  if (isout)
3331  *isout = true;
3332  *isnull = true;
3333  return (Datum) 0;
3334  }
3335  else
3336  {
3337  if (isout)
3338  *isout = false;
3339  if (set_mark)
3340  WinSetMarkPosition(winobj, abs_pos);
3341  econtext->ecxt_outertuple = slot;
3342  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3343  econtext, isnull);
3344  }
3345 }

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().

◆ WinGetPartitionLocalMemory()

void* WinGetPartitionLocalMemory ( WindowObject  winobj,
Size  sz 
)

Definition at line 3153 of file nodeWindowAgg.c.

3154 {
3155  Assert(WindowObjectIsValid(winobj));
3156  if (winobj->localmem == NULL)
3157  winobj->localmem =
3159  return winobj->localmem;
3160 }
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:906

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().

◆ WinGetPartitionRowCount()

int64 WinGetPartitionRowCount ( WindowObject  winobj)

Definition at line 3183 of file nodeWindowAgg.c.

3184 {
3185  Assert(WindowObjectIsValid(winobj));
3186  spool_tuples(winobj->winstate, -1);
3187  return winobj->winstate->spooled_rows;
3188 }

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

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

◆ WinRowsArePeers()

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

Definition at line 3236 of file nodeWindowAgg.c.

3237 {
3238  WindowAggState *winstate;
3239  WindowAgg *node;
3240  TupleTableSlot *slot1;
3241  TupleTableSlot *slot2;
3242  bool res;
3243 
3244  Assert(WindowObjectIsValid(winobj));
3245  winstate = winobj->winstate;
3246  node = (WindowAgg *) winstate->ss.ps.plan;
3247 
3248  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3249  if (node->ordNumCols == 0)
3250  return true;
3251 
3252  /*
3253  * Note: OK to use temp_slot_2 here because we aren't calling any
3254  * frame-related functions (those tend to clobber temp_slot_2).
3255  */
3256  slot1 = winstate->temp_slot_1;
3257  slot2 = winstate->temp_slot_2;
3258 
3259  if (!window_gettupleslot(winobj, pos1, slot1))
3260  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3261  pos1);
3262  if (!window_gettupleslot(winobj, pos2, slot2))
3263  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3264  pos2);
3265 
3266  res = are_peers(winstate, slot1, slot2);
3267 
3268  ExecClearTuple(slot1);
3269  ExecClearTuple(slot2);
3270 
3271  return res;
3272 }
#define INT64_FORMAT
Definition: c.h:483

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

Referenced by rank_up(), and window_cume_dist().

◆ WinSetMarkPosition()

void WinSetMarkPosition ( WindowObject  winobj,
int64  markpos 
)

Definition at line 3201 of file nodeWindowAgg.c.

3202 {
3203  WindowAggState *winstate;
3204 
3205  Assert(WindowObjectIsValid(winobj));
3206  winstate = winobj->winstate;
3207 
3208  if (markpos < winobj->markpos)
3209  elog(ERROR, "cannot move WindowObject's mark position backward");
3210  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3211  if (markpos > winobj->markpos)
3212  {
3213  tuplestore_skiptuples(winstate->buffer,
3214  markpos - winobj->markpos,
3215  true);
3216  winobj->markpos = markpos;
3217  }
3218  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3219  if (markpos > winobj->seekpos)
3220  {
3221  tuplestore_skiptuples(winstate->buffer,
3222  markpos - winobj->seekpos,
3223  true);
3224  winobj->seekpos = markpos;
3225  }
3226 }

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().