PostgreSQL Source Code  git master
nodeWindowAgg.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "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 242 of file nodeWindowAgg.c.

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

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 419 of file nodeWindowAgg.c.

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

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 3044 of file nodeWindowAgg.c.

3046 {
3047  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3048  ExprContext *econtext = winstate->tmpcontext;
3049 
3050  /* If no ORDER BY, all rows are peers with each other */
3051  if (node->ordNumCols == 0)
3052  return true;
3053 
3054  econtext->ecxt_outertuple = slot1;
3055  econtext->ecxt_innertuple = slot2;
3056  return ExecQualAndReset(winstate->ordEqfunction, econtext);
3057 }
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:440
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Plan * plan
Definition: execnodes.h:1117
PlanState ps
Definition: execnodes.h:1564
ScanState ss
Definition: execnodes.h:2555
ExprState * ordEqfunction
Definition: execnodes.h:2565
int ordNumCols
Definition: plannodes.h:1058

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 1081 of file nodeWindowAgg.c.

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

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 663 of file nodeWindowAgg.c.

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

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, MemoryContextReset(), 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 1033 of file nodeWindowAgg.c.

1035 {
1036  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1037  MemoryContext oldContext;
1038 
1040 
1041  /*
1042  * We don't pass any normal arguments to a window function, but we do pass
1043  * it the number of arguments, in order to permit window function
1044  * implementations to support varying numbers of arguments. The real info
1045  * goes through the WindowObject, which is passed via fcinfo->context.
1046  */
1047  InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1048  perfuncstate->numArguments,
1049  perfuncstate->winCollation,
1050  (void *) perfuncstate->winobj, NULL);
1051  /* Just in case, make all the regular argument slots be null */
1052  for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1053  fcinfo->args[argno].isnull = true;
1054  /* Window functions don't have a current aggregate context, either */
1055  winstate->curaggcontext = NULL;
1056 
1057  *result = FunctionCallInvoke(fcinfo);
1058  *isnull = fcinfo->isnull;
1059 
1060  /*
1061  * The window function might have returned a pass-by-ref result that's
1062  * just a pointer into one of the WindowObject's temporary slots. That's
1063  * not a problem if it's the only window function using the WindowObject;
1064  * but if there's more than one function, we'd better copy the result to
1065  * ensure it's not clobbered by later window functions.
1066  */
1067  if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1068  winstate->numfuncs > 1)
1069  *result = datumCopy(*result,
1070  perfuncstate->resulttypeByVal,
1071  perfuncstate->resulttypeLen);
1072 
1073  MemoryContextSwitchTo(oldContext);
1074 }

References WindowAggState::curaggcontext, datumCopy(), ExprContext::ecxt_per_tuple_memory, WindowStatePerFuncData::flinfo, FUNC_MAX_ARGS, FunctionCallInvoke, InitFunctionCallInfoData, LOCAL_FCINFO, MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, 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 2682 of file nodeWindowAgg.c.

2683 {
2685  int i;
2686 
2687  release_partition(node);
2688 
2689  for (i = 0; i < node->numaggs; i++)
2690  {
2691  if (node->peragg[i].aggcontext != node->aggcontext)
2693  }
2696 
2697  pfree(node->perfunc);
2698  pfree(node->peragg);
2699 
2700  outerPlan = outerPlanState(node);
2702 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition: execnodes.h:2601

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, ExecEndNode(), i, MemoryContextDelete(), WindowAggState::numaggs, outerPlan, outerPlanState, WindowAggState::partcontext, WindowAggState::peragg, WindowAggState::perfunc, pfree(), and release_partition().

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2375 of file nodeWindowAgg.c.

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

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(), 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_aclcheck(), OBJECT_FUNCTION, OidIsValid, WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::outerops, PlanState::outeropsfixed, PlanState::outeropsset, outerPlan, outerPlanState, palloc0(), WindowAggState::partcontext, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, WindowAggState::peragg, WindowAggState::perfunc, 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, WindowStatePerFuncData::winCollation, WINDOWAGG_RUN, WindowFunc::winfnoid, WindowStatePerFuncData::winobj, WindowAgg::winref, WindowFunc::winref, and WindowObjectData::winstate.

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2709 of file nodeWindowAgg.c.

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

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 2046 of file nodeWindowAgg.c.

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

586 {
587  MemoryContext oldContext;
588 
590 
591  /*
592  * Apply the agg's finalfn if one is provided, else return transValue.
593  */
594  if (OidIsValid(peraggstate->finalfn_oid))
595  {
596  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
597  int numFinalArgs = peraggstate->numFinalArgs;
598  bool anynull;
599  int i;
600 
601  InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
602  numFinalArgs,
603  perfuncstate->winCollation,
604  (void *) winstate, NULL);
605  fcinfo->args[0].value =
607  peraggstate->transValueIsNull,
608  peraggstate->transtypeLen);
609  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
610  anynull = peraggstate->transValueIsNull;
611 
612  /* Fill any remaining argument positions with nulls */
613  for (i = 1; i < numFinalArgs; i++)
614  {
615  fcinfo->args[i].value = (Datum) 0;
616  fcinfo->args[i].isnull = true;
617  anynull = true;
618  }
619 
620  if (fcinfo->flinfo->fn_strict && anynull)
621  {
622  /* don't call a strict function with NULL inputs */
623  *result = (Datum) 0;
624  *isnull = true;
625  }
626  else
627  {
628  Datum res;
629 
630  winstate->curaggcontext = peraggstate->aggcontext;
631  res = FunctionCallInvoke(fcinfo);
632  winstate->curaggcontext = NULL;
633  *isnull = fcinfo->isnull;
635  fcinfo->isnull,
636  peraggstate->resulttypeLen);
637  }
638  }
639  else
640  {
641  *result =
643  peraggstate->transValueIsNull,
644  peraggstate->transtypeLen);
645  *isnull = peraggstate->transValueIsNull;
646  }
647 
648  MemoryContextSwitchTo(oldContext);
649 }
#define MakeExpandedObjectReadOnly(d, isnull, typlen)

References WindowStatePerAggData::aggcontext, ExprContext::ecxt_per_tuple_memory, WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, LOCAL_FCINFO, MakeExpandedObjectReadOnly, MemoryContextSwitchTo(), WindowStatePerAggData::numFinalArgs, OidIsValid, ScanState::ps, PlanState::ps_ExprContext, res, 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 3022 of file nodeWindowAgg.c.

3023 {
3024  Oid typinput,
3025  typioparam;
3026  char *strInitVal;
3027  Datum initVal;
3028 
3029  getTypeInputInfo(transtype, &typinput, &typioparam);
3030  strInitVal = TextDatumGetCString(textInitVal);
3031  initVal = OidInputFunctionCall(typinput, strInitVal,
3032  typioparam, -1);
3033  pfree(strInitVal);
3034  return initVal;
3035 }
#define TextDatumGetCString(d)
Definition: builtins.h:98
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1754
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
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 2749 of file nodeWindowAgg.c.

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

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 207 of file nodeWindowAgg.c.

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

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, datumCopy(), WindowStatePerAggData::initValue, WindowStatePerAggData::initValueIsNull, MemoryContextReset(), 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 1335 of file nodeWindowAgg.c.

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

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, i, WindowObjectData::localmem, MemoryContextReset(), 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 1385 of file nodeWindowAgg.c.

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

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 1241 of file nodeWindowAgg.c.

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

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

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::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 1735 of file nodeWindowAgg.c.

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

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::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 1985 of file nodeWindowAgg.c.

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

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 3067 of file nodeWindowAgg.c.

3068 {
3069  WindowAggState *winstate = winobj->winstate;
3070  MemoryContext oldcontext;
3071 
3072  /* often called repeatedly in a row */
3074 
3075  /* Don't allow passing -1 to spool_tuples here */
3076  if (pos < 0)
3077  return false;
3078 
3079  /* If necessary, fetch the tuple into the spool */
3080  spool_tuples(winstate, pos);
3081 
3082  if (pos >= winstate->spooled_rows)
3083  return false;
3084 
3085  if (pos < winobj->markpos)
3086  elog(ERROR, "cannot fetch row before WindowObject's mark position");
3087 
3089 
3090  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3091 
3092  /*
3093  * Advance or rewind until we are within one tuple of the one we want.
3094  */
3095  if (winobj->seekpos < pos - 1)
3096  {
3097  if (!tuplestore_skiptuples(winstate->buffer,
3098  pos - 1 - winobj->seekpos,
3099  true))
3100  elog(ERROR, "unexpected end of tuplestore");
3101  winobj->seekpos = pos - 1;
3102  }
3103  else if (winobj->seekpos > pos + 1)
3104  {
3105  if (!tuplestore_skiptuples(winstate->buffer,
3106  winobj->seekpos - (pos + 1),
3107  false))
3108  elog(ERROR, "unexpected end of tuplestore");
3109  winobj->seekpos = pos + 1;
3110  }
3111  else if (winobj->seekpos == pos)
3112  {
3113  /*
3114  * There's no API to refetch the tuple at the current position. We
3115  * have to move one tuple forward, and then one backward. (We don't
3116  * do it the other way because we might try to fetch the row before
3117  * our mark, which isn't allowed.) XXX this case could stand to be
3118  * optimized.
3119  */
3120  tuplestore_advance(winstate->buffer, true);
3121  winobj->seekpos++;
3122  }
3123 
3124  /*
3125  * Now we should be on the tuple immediately before or after the one we
3126  * want, so just fetch forwards or backwards as appropriate.
3127  *
3128  * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3129  * the fetched tuple. This ensures that the slot's contents remain valid
3130  * through manipulations of the tuplestore, which some callers depend on.
3131  */
3132  if (winobj->seekpos > pos)
3133  {
3134  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3135  elog(ERROR, "unexpected end of tuplestore");
3136  winobj->seekpos--;
3137  }
3138  else
3139  {
3140  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3141  elog(ERROR, "unexpected end of tuplestore");
3142  winobj->seekpos++;
3143  }
3144 
3145  Assert(winobj->seekpos == pos);
3146 
3147  MemoryContextSwitchTo(oldcontext);
3148 
3149  return true;
3150 }
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 3186 of file nodeWindowAgg.c.

3187 {
3188  Assert(WindowObjectIsValid(winobj));
3189  return winobj->winstate->currentpos;
3190 }
#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 3594 of file nodeWindowAgg.c.

3595 {
3596  WindowAggState *winstate;
3597  ExprContext *econtext;
3598 
3599  Assert(WindowObjectIsValid(winobj));
3600  winstate = winobj->winstate;
3601 
3602  econtext = winstate->ss.ps.ps_ExprContext;
3603 
3604  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3605  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3606  econtext, isnull);
3607 }
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299

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 3399 of file nodeWindowAgg.c.

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

3314 {
3315  WindowAggState *winstate;
3316  ExprContext *econtext;
3317  TupleTableSlot *slot;
3318  bool gottuple;
3319  int64 abs_pos;
3320 
3321  Assert(WindowObjectIsValid(winobj));
3322  winstate = winobj->winstate;
3323  econtext = winstate->ss.ps.ps_ExprContext;
3324  slot = winstate->temp_slot_1;
3325 
3326  switch (seektype)
3327  {
3328  case WINDOW_SEEK_CURRENT:
3329  abs_pos = winstate->currentpos + relpos;
3330  break;
3331  case WINDOW_SEEK_HEAD:
3332  abs_pos = relpos;
3333  break;
3334  case WINDOW_SEEK_TAIL:
3335  spool_tuples(winstate, -1);
3336  abs_pos = winstate->spooled_rows - 1 + relpos;
3337  break;
3338  default:
3339  elog(ERROR, "unrecognized window seek type: %d", seektype);
3340  abs_pos = 0; /* keep compiler quiet */
3341  break;
3342  }
3343 
3344  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3345 
3346  if (!gottuple)
3347  {
3348  if (isout)
3349  *isout = true;
3350  *isnull = true;
3351  return (Datum) 0;
3352  }
3353  else
3354  {
3355  if (isout)
3356  *isout = false;
3357  if (set_mark)
3358  WinSetMarkPosition(winobj, abs_pos);
3359  econtext->ecxt_outertuple = slot;
3360  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3361  econtext, isnull);
3362  }
3363 }

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 3171 of file nodeWindowAgg.c.

3172 {
3173  Assert(WindowObjectIsValid(winobj));
3174  if (winobj->localmem == NULL)
3175  winobj->localmem =
3177  return winobj->localmem;
3178 }
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1214

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 3201 of file nodeWindowAgg.c.

3202 {
3203  Assert(WindowObjectIsValid(winobj));
3204  spool_tuples(winobj->winstate, -1);
3205  return winobj->winstate->spooled_rows;
3206 }

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 3254 of file nodeWindowAgg.c.

3255 {
3256  WindowAggState *winstate;
3257  WindowAgg *node;
3258  TupleTableSlot *slot1;
3259  TupleTableSlot *slot2;
3260  bool res;
3261 
3262  Assert(WindowObjectIsValid(winobj));
3263  winstate = winobj->winstate;
3264  node = (WindowAgg *) winstate->ss.ps.plan;
3265 
3266  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3267  if (node->ordNumCols == 0)
3268  return true;
3269 
3270  /*
3271  * Note: OK to use temp_slot_2 here because we aren't calling any
3272  * frame-related functions (those tend to clobber temp_slot_2).
3273  */
3274  slot1 = winstate->temp_slot_1;
3275  slot2 = winstate->temp_slot_2;
3276 
3277  if (!window_gettupleslot(winobj, pos1, slot1))
3278  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3279  pos1);
3280  if (!window_gettupleslot(winobj, pos2, slot2))
3281  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3282  pos2);
3283 
3284  res = are_peers(winstate, slot1, slot2);
3285 
3286  ExecClearTuple(slot1);
3287  ExecClearTuple(slot2);
3288 
3289  return res;
3290 }
#define INT64_FORMAT
Definition: c.h:548

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 3219 of file nodeWindowAgg.c.

3220 {
3221  WindowAggState *winstate;
3222 
3223  Assert(WindowObjectIsValid(winobj));
3224  winstate = winobj->winstate;
3225 
3226  if (markpos < winobj->markpos)
3227  elog(ERROR, "cannot move WindowObject's mark position backward");
3228  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3229  if (markpos > winobj->markpos)
3230  {
3231  tuplestore_skiptuples(winstate->buffer,
3232  markpos - winobj->markpos,
3233  true);
3234  winobj->markpos = markpos;
3235  }
3236  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3237  if (markpos > winobj->seekpos)
3238  {
3239  tuplestore_skiptuples(winstate->buffer,
3240  markpos - winobj->seekpos,
3241  true);
3242  winobj->seekpos = markpos;
3243  }
3244 }

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