PostgreSQL Source Code  git master
nodeWindowAgg.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
#include "windowapi.h"
Include dependency graph for nodeWindowAgg.c:

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

Typedefs

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

Functions

static void initialize_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static void advance_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static bool advance_windowaggregate_base (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static void finalize_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
 
static void eval_windowaggregates (WindowAggState *winstate)
 
static void eval_windowfunction (WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
 
static void begin_partition (WindowAggState *winstate)
 
static void spool_tuples (WindowAggState *winstate, int64 pos)
 
static void release_partition (WindowAggState *winstate)
 
static int row_is_in_frame (WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
 
static void update_frameheadpos (WindowAggState *winstate)
 
static void update_frametailpos (WindowAggState *winstate)
 
static void update_grouptailpos (WindowAggState *winstate)
 
static WindowStatePerAggDatainitialize_peragg (WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
 
static Datum GetAggInitVal (Datum textInitVal, Oid transtype)
 
static bool are_peers (WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
 
static bool window_gettupleslot (WindowObject winobj, int64 pos, TupleTableSlot *slot)
 
static TupleTableSlotExecWindowAgg (PlanState *pstate)
 
WindowAggStateExecInitWindowAgg (WindowAgg *node, EState *estate, int eflags)
 
void ExecEndWindowAgg (WindowAggState *node)
 
void ExecReScanWindowAgg (WindowAggState *node)
 
void * WinGetPartitionLocalMemory (WindowObject winobj, Size sz)
 
int64 WinGetCurrentPosition (WindowObject winobj)
 
int64 WinGetPartitionRowCount (WindowObject winobj)
 
void WinSetMarkPosition (WindowObject winobj, int64 markpos)
 
bool WinRowsArePeers (WindowObject winobj, int64 pos1, int64 pos2)
 
Datum WinGetFuncArgInPartition (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgInFrame (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgCurrent (WindowObject winobj, int argno, bool *isnull)
 

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

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

Definition at line 241 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 417 of file nodeWindowAgg.c.

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

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

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

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

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

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

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

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

Referenced by ExecWindowAgg().

◆ eval_windowfunction()

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

Definition at line 1025 of file nodeWindowAgg.c.

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

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

2675 {
2677  int i;
2678 
2679  release_partition(node);
2680 
2684  ExecClearTuple(node->temp_slot_1);
2685  ExecClearTuple(node->temp_slot_2);
2686  if (node->framehead_slot)
2688  if (node->frametail_slot)
2690 
2691  /*
2692  * Free both the expr contexts.
2693  */
2694  ExecFreeExprContext(&node->ss.ps);
2695  node->ss.ps.ps_ExprContext = node->tmpcontext;
2696  ExecFreeExprContext(&node->ss.ps);
2697 
2698  for (i = 0; i < node->numaggs; i++)
2699  {
2700  if (node->peragg[i].aggcontext != node->aggcontext)
2702  }
2705 
2706  pfree(node->perfunc);
2707  pfree(node->peragg);
2708 
2709  outerPlan = outerPlanState(node);
2711 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:656
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:376
static void release_partition(WindowAggState *winstate)
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1456
MemoryContext partcontext
Definition: execnodes.h:2500
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2535

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

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2367 of file nodeWindowAgg.c.

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

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, WindowStatePerFuncData::aggno, WindowAggState::all_first, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, WindowFuncExprState::args, WindowObjectData::argstates, Assert(), contain_volatile_functions(), CurrentMemoryContext, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_per_query_memory, elog(), WindowAggState::endInRangeFunc, WindowAgg::endInRangeFunc, WindowAggState::endOffset, WindowAgg::endOffset, equal(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecCreateScanSlotFromOuterPlan(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitQual(), ExecInitResultTupleSlotTL(), PlanState::ExecProcNode, execTuplesMatchPrepare(), ExecWindowAgg(), WindowAggState::first_part_slot, WindowStatePerFuncData::flinfo, fmgr_info(), fmgr_info_cxt(), fmgr_info_set_expr, WindowAggState::framehead_slot, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, WindowAgg::frameOptions, WindowAggState::frametail_slot, WindowAggState::funcs, get_func_name(), get_typlenbyval(), GetUserId(), i, initialize_peragg(), WindowFunc::inputcollid, WindowAggState::inRangeAsc, WindowAgg::inRangeAsc, WindowAggState::inRangeColl, WindowAgg::inRangeColl, WindowAggState::inRangeNullsFirst, WindowAgg::inRangeNullsFirst, InvokeFunctionExecuteHook, lfirst, list_length(), WindowObjectData::localmem, makeNode, WindowObjectData::markptr, WindowAggState::more_partitions, NIL, WindowAggState::numaggs, WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, object_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, WindowFunc::winagg, WindowStatePerFuncData::winCollation, WINDOWAGG_RUN, WindowFunc::winfnoid, WindowStatePerFuncData::winobj, WindowAgg::winref, WindowFunc::winref, WindowObjectData::winstate, and WindowFunc::wintype.

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2718 of file nodeWindowAgg.c.

2719 {
2721  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2722 
2723  node->status = WINDOWAGG_RUN;
2724  node->all_first = true;
2725 
2726  /* release tuplestore et al */
2727  release_partition(node);
2728 
2729  /* release all temp tuples, but especially first_part_slot */
2733  ExecClearTuple(node->temp_slot_1);
2734  ExecClearTuple(node->temp_slot_2);
2735  if (node->framehead_slot)
2737  if (node->frametail_slot)
2739 
2740  /* Forget current wfunc values */
2741  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2742  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2743 
2744  /*
2745  * if chgParam of subnode is not null then plan will be re-scanned by
2746  * first ExecProcNode.
2747  */
2748  if (outerPlan->chgParam == NULL)
2750 }
#define MemSet(start, val, len)
Definition: c.h:953
void ExecReScan(PlanState *node)
Definition: execAmi.c:78

References WindowAggState::agg_row_slot, WindowAggState::all_first, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExecClearTuple(), ExecReScan(), WindowAggState::first_part_slot, WindowAggState::framehead_slot, WindowAggState::frametail_slot, MemSet, WindowAggState::numfuncs, outerPlan, outerPlanState, ScanState::ps, PlanState::ps_ExprContext, release_partition(), WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::status, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, and WINDOWAGG_RUN.

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot* ExecWindowAgg ( PlanState pstate)
static

Definition at line 2038 of file nodeWindowAgg.c.

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

References WindowAggState::all_first, are_peers(), Assert(), begin_partition(), WindowAggState::buffer, castNode, CHECK_FOR_INTERRUPTS, WindowAggState::current_ptr, WindowAggState::currentgroup, WindowAggState::currentpos, datumCopy(), DatumGetInt64(), ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog(), WindowAggState::endOffset, WindowAggState::endOffsetValue, ereport, errcode(), errmsg(), ERROR, eval_windowaggregates(), eval_windowfunction(), ExecClearTuple(), ExecCopySlot(), ExecEvalExprSwitchContext(), ExecProject(), ExecQual(), ExprState::expr, exprType(), WindowAggState::framehead_ptr, WindowAggState::framehead_valid, FRAMEOPTION_END_OFFSET, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_ROWS, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_valid, get_typlenbyval(), WindowAggState::groupheadpos, WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, i, InstrCountFiltered1, len, WindowAggState::more_partitions, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::qual, release_partition(), ResetExprContext, WindowAggState::runcondition, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startOffset, WindowAggState::startOffsetValue, WindowAggState::status, WindowAggState::temp_slot_2, WindowAggState::top_window, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), WindowAggState::use_pass_through, value, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WINDOWAGG_DONE, WINDOWAGG_PASSTHROUGH, WINDOWAGG_PASSTHROUGH_STRICT, and WINDOWAGG_RUN.

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

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

Definition at line 579 of file nodeWindowAgg.c.

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

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

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

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 206 of file nodeWindowAgg.c.

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

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

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1327 of file nodeWindowAgg.c.

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

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

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

◆ row_is_in_frame()

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

Definition at line 1377 of file nodeWindowAgg.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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