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

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

Typedefs

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

Functions

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

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

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

Definition at line 242 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 419 of file nodeWindowAgg.c.

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

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

3063 {
3064  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3065  ExprContext *econtext = winstate->tmpcontext;
3066 
3067  /* If no ORDER BY, all rows are peers with each other */
3068  if (node->ordNumCols == 0)
3069  return true;
3070 
3071  econtext->ecxt_outertuple = slot1;
3072  econtext->ecxt_innertuple = slot2;
3073  return ExecQualAndReset(winstate->ordEqfunction, econtext);
3074 }
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:439
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Plan * plan
Definition: execnodes.h:1037
PlanState ps
Definition: execnodes.h:1474
ScanState ss
Definition: execnodes.h:2476
ExprState * ordEqfunction
Definition: execnodes.h:2486
int ordNumCols
Definition: plannodes.h:1056

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

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

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1081 of file nodeWindowAgg.c.

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

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

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

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

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

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2682 of file nodeWindowAgg.c.

2683 {
2685  int i;
2686 
2687  release_partition(node);
2688 
2692  ExecClearTuple(node->temp_slot_1);
2693  ExecClearTuple(node->temp_slot_2);
2694  if (node->framehead_slot)
2696  if (node->frametail_slot)
2698 
2699  /*
2700  * Free both the expr contexts.
2701  */
2702  ExecFreeExprContext(&node->ss.ps);
2703  node->ss.ps.ps_ExprContext = node->tmpcontext;
2704  ExecFreeExprContext(&node->ss.ps);
2705 
2706  for (i = 0; i < node->numaggs; i++)
2707  {
2708  if (node->peragg[i].aggcontext != node->aggcontext)
2710  }
2713 
2714  pfree(node->perfunc);
2715  pfree(node->peragg);
2716 
2717  outerPlan = outerPlanState(node);
2719 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:658
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
static void release_partition(WindowAggState *winstate)
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1477
MemoryContext partcontext
Definition: execnodes.h:2522
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2557

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

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

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

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2726 of file nodeWindowAgg.c.

2727 {
2729  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2730 
2731  node->status = WINDOWAGG_RUN;
2732  node->all_first = true;
2733 
2734  /* release tuplestore et al */
2735  release_partition(node);
2736 
2737  /* release all temp tuples, but especially first_part_slot */
2741  ExecClearTuple(node->temp_slot_1);
2742  ExecClearTuple(node->temp_slot_2);
2743  if (node->framehead_slot)
2745  if (node->frametail_slot)
2747 
2748  /* Forget current wfunc values */
2749  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2750  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2751 
2752  /*
2753  * if chgParam of subnode is not null then plan will be re-scanned by
2754  * first ExecProcNode.
2755  */
2756  if (outerPlan->chgParam == NULL)
2758 }
#define MemSet(start, val, len)
Definition: c.h:1004
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 2046 of file nodeWindowAgg.c.

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

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

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

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

Definition at line 582 of file nodeWindowAgg.c.

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

References WindowStatePerAggData::aggcontext, ExprContext::ecxt_per_tuple_memory, WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, LOCAL_FCINFO, MakeExpandedObjectReadOnly, MemoryContextSwitchTo(), WindowStatePerAggData::numFinalArgs, OidIsValid, ScanState::ps, PlanState::ps_ExprContext, res, WindowStatePerAggData::resulttypeLen, WindowAggState::ss, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueIsNull, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3039 of file nodeWindowAgg.c.

3040 {
3041  Oid typinput,
3042  typioparam;
3043  char *strInitVal;
3044  Datum initVal;
3045 
3046  getTypeInputInfo(transtype, &typinput, &typioparam);
3047  strInitVal = TextDatumGetCString(textInitVal);
3048  initVal = OidInputFunctionCall(typinput, strInitVal,
3049  typioparam, -1);
3050  pfree(strInitVal);
3051  return initVal;
3052 }
#define TextDatumGetCString(d)
Definition: builtins.h:95
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1725
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 2766 of file nodeWindowAgg.c.

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 207 of file nodeWindowAgg.c.

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

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

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

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

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

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

Referenced by eval_windowaggregates(), and WinGetFuncArgInFrame().

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1241 of file nodeWindowAgg.c.

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

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

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

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1485 of file nodeWindowAgg.c.

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

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

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

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

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 1985 of file nodeWindowAgg.c.

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

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

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

◆ window_gettupleslot()

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

Definition at line 3084 of file nodeWindowAgg.c.

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

3204 {
3205  Assert(WindowObjectIsValid(winobj));
3206  return winobj->winstate->currentpos;
3207 }
#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 3611 of file nodeWindowAgg.c.

3612 {
3613  WindowAggState *winstate;
3614  ExprContext *econtext;
3615 
3616  Assert(WindowObjectIsValid(winobj));
3617  winstate = winobj->winstate;
3618 
3619  econtext = winstate->ss.ps.ps_ExprContext;
3620 
3621  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3622  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3623  econtext, isnull);
3624 }
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299

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

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

◆ WinGetFuncArgInFrame()

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

Definition at line 3416 of file nodeWindowAgg.c.

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

3331 {
3332  WindowAggState *winstate;
3333  ExprContext *econtext;
3334  TupleTableSlot *slot;
3335  bool gottuple;
3336  int64 abs_pos;
3337 
3338  Assert(WindowObjectIsValid(winobj));
3339  winstate = winobj->winstate;
3340  econtext = winstate->ss.ps.ps_ExprContext;
3341  slot = winstate->temp_slot_1;
3342 
3343  switch (seektype)
3344  {
3345  case WINDOW_SEEK_CURRENT:
3346  abs_pos = winstate->currentpos + relpos;
3347  break;
3348  case WINDOW_SEEK_HEAD:
3349  abs_pos = relpos;
3350  break;
3351  case WINDOW_SEEK_TAIL:
3352  spool_tuples(winstate, -1);
3353  abs_pos = winstate->spooled_rows - 1 + relpos;
3354  break;
3355  default:
3356  elog(ERROR, "unrecognized window seek type: %d", seektype);
3357  abs_pos = 0; /* keep compiler quiet */
3358  break;
3359  }
3360 
3361  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3362 
3363  if (!gottuple)
3364  {
3365  if (isout)
3366  *isout = true;
3367  *isnull = true;
3368  return (Datum) 0;
3369  }
3370  else
3371  {
3372  if (isout)
3373  *isout = false;
3374  if (set_mark)
3375  WinSetMarkPosition(winobj, abs_pos);
3376  econtext->ecxt_outertuple = slot;
3377  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3378  econtext, isnull);
3379  }
3380 }

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

3189 {
3190  Assert(WindowObjectIsValid(winobj));
3191  if (winobj->localmem == NULL)
3192  winobj->localmem =
3194  return winobj->localmem;
3195 }
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1064

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

3219 {
3220  Assert(WindowObjectIsValid(winobj));
3221  spool_tuples(winobj->winstate, -1);
3222  return winobj->winstate->spooled_rows;
3223 }

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

3272 {
3273  WindowAggState *winstate;
3274  WindowAgg *node;
3275  TupleTableSlot *slot1;
3276  TupleTableSlot *slot2;
3277  bool res;
3278 
3279  Assert(WindowObjectIsValid(winobj));
3280  winstate = winobj->winstate;
3281  node = (WindowAgg *) winstate->ss.ps.plan;
3282 
3283  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3284  if (node->ordNumCols == 0)
3285  return true;
3286 
3287  /*
3288  * Note: OK to use temp_slot_2 here because we aren't calling any
3289  * frame-related functions (those tend to clobber temp_slot_2).
3290  */
3291  slot1 = winstate->temp_slot_1;
3292  slot2 = winstate->temp_slot_2;
3293 
3294  if (!window_gettupleslot(winobj, pos1, slot1))
3295  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3296  pos1);
3297  if (!window_gettupleslot(winobj, pos2, slot2))
3298  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3299  pos2);
3300 
3301  res = are_peers(winstate, slot1, slot2);
3302 
3303  ExecClearTuple(slot1);
3304  ExecClearTuple(slot2);
3305 
3306  return res;
3307 }
#define INT64_FORMAT
Definition: c.h:532

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

3237 {
3238  WindowAggState *winstate;
3239 
3240  Assert(WindowObjectIsValid(winobj));
3241  winstate = winobj->winstate;
3242 
3243  if (markpos < winobj->markpos)
3244  elog(ERROR, "cannot move WindowObject's mark position backward");
3245  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3246  if (markpos > winobj->markpos)
3247  {
3248  tuplestore_skiptuples(winstate->buffer,
3249  markpos - winobj->markpos,
3250  true);
3251  winobj->markpos = markpos;
3252  }
3253  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3254  if (markpos > winobj->seekpos)
3255  {
3256  tuplestore_skiptuples(winstate->buffer,
3257  markpos - winobj->seekpos,
3258  true);
3259  winobj->seekpos = markpos;
3260  }
3261 }

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